Skip to content

Commit 8d495e3

Browse files
author
BESS Solutions
committed
feat: phase 2 - MkDocs docs site, PyPI package, API reference, runbook
- mkdocs.yml: Material theme, dark/light mode, structured nav with all sections - .github/workflows/docs.yml: auto-deploy to GitHub Pages on push to main - .github/workflows/pypi.yml: Trusted Publisher OIDC pipeline (TestPyPI + PyPI) - pyproject.toml: [build-system] hatchling + [project] PEP 621 metadata for bessai-edge - docs/index.md: MkDocs landing page with architecture diagram, features, status table - docs/api_reference.md: full REST API docs (/health, /metrics, /api/v1/status, /api/v1/dispatch) - docs/runbook.md: updated with v1.4.0 operational procedures and escalation matrix
1 parent da70746 commit 8d495e3

7 files changed

Lines changed: 736 additions & 180 deletions

File tree

.github/workflows/docs.yml

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
name: Deploy Documentation
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- "docs/**"
8+
- "mkdocs.yml"
9+
- ".github/workflows/docs.yml"
10+
pull_request:
11+
branches: [main]
12+
paths:
13+
- "docs/**"
14+
- "mkdocs.yml"
15+
workflow_dispatch:
16+
17+
permissions:
18+
contents: write # Required to push to gh-pages
19+
pages: write
20+
id-token: write
21+
22+
jobs:
23+
# ─────────────────────────────────────────────────────────────────
24+
# Build documentation (runs on every PR to validate)
25+
# ─────────────────────────────────────────────────────────────────
26+
build:
27+
name: Build Docs
28+
runs-on: ubuntu-latest
29+
30+
steps:
31+
- uses: actions/checkout@v4
32+
with:
33+
fetch-depth: 0 # Full history for git-revision-date-localized
34+
35+
- uses: actions/setup-python@v5
36+
with:
37+
python-version: "3.11"
38+
cache: pip
39+
40+
- name: Install MkDocs + plugins
41+
run: |
42+
pip install \
43+
mkdocs-material==9.5.* \
44+
mkdocs-minify-plugin==0.8.* \
45+
mkdocs-git-revision-date-localized-plugin==1.2.* \
46+
mike==2.1.*
47+
48+
- name: Build documentation
49+
run: mkdocs build --strict --site-dir site/
50+
51+
- name: Upload built site (artifact)
52+
uses: actions/upload-artifact@v4
53+
with:
54+
name: docs-site
55+
path: site/
56+
retention-days: 7
57+
58+
# ─────────────────────────────────────────────────────────────────
59+
# Deploy to GitHub Pages (only on push to main)
60+
# ─────────────────────────────────────────────────────────────────
61+
deploy:
62+
name: Deploy to GitHub Pages
63+
runs-on: ubuntu-latest
64+
needs: [build]
65+
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
66+
67+
steps:
68+
- uses: actions/checkout@v4
69+
with:
70+
fetch-depth: 0
71+
72+
- uses: actions/setup-python@v5
73+
with:
74+
python-version: "3.11"
75+
cache: pip
76+
77+
- name: Install MkDocs + plugins
78+
run: |
79+
pip install \
80+
mkdocs-material==9.5.* \
81+
mkdocs-minify-plugin==0.8.* \
82+
mkdocs-git-revision-date-localized-plugin==1.2.* \
83+
mike==2.1.*
84+
85+
- name: Configure Git identity
86+
run: |
87+
git config user.name "github-actions[bot]"
88+
git config user.email "github-actions[bot]@users.noreply.github.com"
89+
90+
- name: Deploy to GitHub Pages (gh-pages branch)
91+
run: mkdocs gh-deploy --force --clean --verbose

.github/workflows/pypi.yml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
name: Publish to PyPI
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*.*.*" # Only on version tags: v1.4.0, v2.0.0-rc1, etc.
7+
workflow_dispatch:
8+
inputs:
9+
environment:
10+
description: "Target: testpypi or pypi"
11+
required: true
12+
default: testpypi
13+
type: choice
14+
options:
15+
- testpypi
16+
- pypi
17+
18+
permissions:
19+
contents: read
20+
id-token: write # Required for Trusted Publisher (OIDC, no secrets needed)
21+
22+
jobs:
23+
# ─────────────────────────────────────────────────────────────────
24+
# Build source distribution and wheel
25+
# ─────────────────────────────────────────────────────────────────
26+
build:
27+
name: Build distribution
28+
runs-on: ubuntu-latest
29+
30+
steps:
31+
- uses: actions/checkout@v4
32+
33+
- uses: actions/setup-python@v5
34+
with:
35+
python-version: "3.11"
36+
37+
- name: Install build tools
38+
run: pip install build twine
39+
40+
- name: Build sdist + wheel
41+
run: python -m build
42+
43+
- name: Verify distribution
44+
run: twine check dist/*
45+
46+
- name: Upload distributions
47+
uses: actions/upload-artifact@v4
48+
with:
49+
name: dist
50+
path: dist/
51+
52+
# ─────────────────────────────────────────────────────────────────
53+
# Publish to TestPyPI (always on tag push, for validation)
54+
# ─────────────────────────────────────────────────────────────────
55+
publish-testpypi:
56+
name: Publish to TestPyPI
57+
runs-on: ubuntu-latest
58+
needs: [build]
59+
environment:
60+
name: testpypi
61+
url: https://test.pypi.org/project/bessai-edge/
62+
63+
steps:
64+
- name: Download distributions
65+
uses: actions/download-artifact@v4
66+
with:
67+
name: dist
68+
path: dist/
69+
70+
- name: Publish to TestPyPI
71+
uses: pypa/gh-action-pypi-publish@release/v1
72+
with:
73+
repository-url: https://test.pypi.org/legacy/
74+
75+
# ─────────────────────────────────────────────────────────────────
76+
# Publish to PyPI (requires manual approval via GitHub Environment)
77+
# ─────────────────────────────────────────────────────────────────
78+
publish-pypi:
79+
name: Publish to PyPI
80+
runs-on: ubuntu-latest
81+
needs: [publish-testpypi]
82+
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '-rc') && !contains(github.ref, '-beta')
83+
environment:
84+
name: pypi
85+
url: https://pypi.org/project/bessai-edge/
86+
87+
steps:
88+
- name: Download distributions
89+
uses: actions/download-artifact@v4
90+
with:
91+
name: dist
92+
path: dist/
93+
94+
- name: Publish to PyPI
95+
uses: pypa/gh-action-pypi-publish@release/v1

docs/api_reference.md

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
# API Reference — BESSAI Edge Gateway
2+
3+
> **Base URL (local):** `http://localhost:8000`
4+
> **Authentication:** `X-API-Key: <DASHBOARD_API_KEY>` (required in production mode)
5+
6+
---
7+
8+
## Health & Status
9+
10+
### `GET /health`
11+
12+
Returns gateway liveness and basic operational state.
13+
14+
**Authentication:** None required
15+
16+
```bash
17+
curl http://localhost:8000/health
18+
```
19+
20+
**Response 200 OK:**
21+
```json
22+
{
23+
"status": "healthy",
24+
"site_id": "SITE-CL-001",
25+
"uptime_s": 127.4,
26+
"last_cycle_ts": "2026-02-21T17:45:00+00:00",
27+
"safety_status": "ok"
28+
}
29+
```
30+
31+
**Response fields:**
32+
33+
| Field | Type | Description |
34+
|---|---|---|
35+
| `status` | `string` | `"healthy"` \| `"degraded"` \| `"error"` |
36+
| `site_id` | `string` | SITE_ID from config |
37+
| `uptime_s` | `float` | Seconds since gateway start |
38+
| `last_cycle_ts` | `string` | ISO-8601 timestamp of last Modbus cycle |
39+
| `safety_status` | `string` | `"ok"` \| `"safety_block_active"` |
40+
41+
---
42+
43+
### `GET /metrics`
44+
45+
Returns Prometheus-format metrics for scraping.
46+
47+
**Authentication:** None required (Prometheus scrape endpoint)
48+
49+
```bash
50+
curl http://localhost:8000/metrics
51+
```
52+
53+
**Key metrics:**
54+
55+
| Metric | Type | Description |
56+
|---|---|---|
57+
| `bess_cycles_total` | Counter | Total Modbus polling cycles |
58+
| `bess_safety_blocks_total` | Counter | Times SafetyGuard blocked a command |
59+
| `bess_last_soc_pct` | Gauge | Current SOC in % |
60+
| `bess_last_power_kw` | Gauge | Current active power in kW |
61+
| `bess_cycle_duration_seconds` | Histogram | Polling cycle latency |
62+
| `bess_ids_anomaly_score` | Gauge | AI-IDS anomaly score (0.0–1.0) |
63+
| `bess_ids_alerts_total` | Counter | AI-IDS alerts triggered |
64+
| `bess_onnx_inference_ms` | Histogram | ONNX model inference latency |
65+
| `bess_publish_errors_total` | Counter | GCP Pub/Sub publish failures |
66+
| `bess_fleet_sites_active` | Gauge | Active sites in fleet |
67+
68+
---
69+
70+
## Operational API
71+
72+
### `GET /api/v1/status`
73+
74+
Returns full real-time operational snapshot.
75+
76+
**Authentication:** `X-API-Key` header required
77+
78+
```bash
79+
curl http://localhost:8000/api/v1/status \
80+
-H "X-API-Key: your-key-here"
81+
```
82+
83+
**Response 200 OK:**
84+
```json
85+
{
86+
"site_id": "SITE-CL-001",
87+
"timestamp": "2026-02-21T17:45:00+00:00",
88+
"modbus": {
89+
"connected": true,
90+
"inverter_ip": "192.168.1.100",
91+
"last_read_ms": 45.2
92+
},
93+
"bess": {
94+
"soc_pct": 72.4,
95+
"power_kw": 150.0,
96+
"voltage_v": 820.5,
97+
"temperature_c": 28.3,
98+
"status_code": 0
99+
},
100+
"safety": {
101+
"status": "ok",
102+
"blocks_total": 0,
103+
"active_constraints": []
104+
},
105+
"ai_ids": {
106+
"anomaly_score": 0.12,
107+
"alert_active": false,
108+
"model_version": "isolation_forest_v1"
109+
},
110+
"cloud": {
111+
"pubsub_connected": true,
112+
"publish_errors_total": 0,
113+
"last_publish_ts": "2026-02-21T17:44:58+00:00"
114+
}
115+
}
116+
```
117+
118+
---
119+
120+
### `POST /api/v1/dispatch`
121+
122+
Sends a dispatch command to the inverter (write via Modbus FC06).
123+
124+
**Authentication:** `X-API-Key` header required
125+
126+
!!! warning "Safety-Critical Endpoint"
127+
All dispatch commands pass through `SafetyGuard.check_safety()` before execution.
128+
Commands that violate SOC or temperature limits are rejected with HTTP 422.
129+
130+
```bash
131+
curl -X POST http://localhost:8000/api/v1/dispatch \
132+
-H "X-API-Key: your-key-here" \
133+
-H "Content-Type: application/json" \
134+
-d '{"target_kw": 100.0, "source": "arbitrage_engine"}'
135+
```
136+
137+
**Request body:**
138+
139+
| Field | Type | Required | Description |
140+
|---|---|---|---|
141+
| `target_kw` | `float` || Target active power in kW. Positive = charge, negative = discharge |
142+
| `source` | `string` | No | Command source identifier for audit logging |
143+
144+
**Response 200 OK:**
145+
```json
146+
{
147+
"status": "executed",
148+
"target_kw": 100.0,
149+
"safety_check": "passed",
150+
"modbus_register": 40101,
151+
"latency_ms": 47.3
152+
}
153+
```
154+
155+
**Response 422 Unprocessable Entity (Safety Block):**
156+
```json
157+
{
158+
"status": "rejected",
159+
"reason": "SOC below minimum threshold (5.0%). Current: 4.8%",
160+
"safety_constraint": "soc_min"
161+
}
162+
```
163+
164+
---
165+
166+
## Error Codes
167+
168+
| HTTP Status | Meaning |
169+
|---|---|
170+
| `200` | Success |
171+
| `401` | Missing or invalid `X-API-Key` |
172+
| `422` | Command rejected by SafetyGuard |
173+
| `503` | Gateway degraded — Modbus disconnected |
174+
| `500` | Internal error (check logs) |
175+
176+
---
177+
178+
## SDK / Client Libraries
179+
180+
```python
181+
# Python client (future: bessai-edge-client package)
182+
from bessai_edge import Client
183+
184+
client = Client("http://localhost:8000", api_key="your-key")
185+
status = client.status()
186+
client.dispatch(target_kw=100.0, source="my-app")
187+
```

0 commit comments

Comments
 (0)