feat(sofiia-console): add artifacts list endpoint + team onboarding doc

- runbook_artifacts.py: adds list_run_artifacts() returning files with
  names, paths, sizes, mtime_utc from release_artifacts/<run_id>/
- runbook_runs_router.py: adds GET /api/runbooks/runs/{run_id}/artifacts
- docs/runbook/team-onboarding-console.md: one-page team onboarding doc
  covering access, rehearsal run steps, audit auth model (strict, no
  localhost bypass), artifacts location, abort procedure

Made-with: Cursor
This commit is contained in:
Apple
2026-03-03 06:55:49 -08:00
parent e0bea910b9
commit 2962d33a3b
3 changed files with 221 additions and 0 deletions

View File

@@ -0,0 +1,187 @@
# Team Onboarding: Sofiia Console + Runbook Runner (v1)
Документ для нових членів команди. Один раз прочитати, далі — практика.
---
## 1. Доступ до Console
**URL:** https://console.daarion.space
### Отримання ключа
1. Запитати у адміністратора персональний ключ.
2. Адміністратор генерує: `python3 -c "import secrets; print(secrets.token_urlsafe(32))"`
3. Ключ додається до `SOFIIA_CONSOLE_TEAM_KEYS` на NODA1 (`name:key` формат).
4. Перезапуск контейнера не потрібен при зміні env — тільки `docker compose up -d`.
### Логін
1. Відкрити https://console.daarion.space
2. Ввести свій ключ у форму Login
3. Cookie встановлюється автоматично на 7 днів
4. Перевірити: `GET /api/auth/check``{"ok": true, "identity": "user:<your_name>"}`
### Revocation
Якщо ключ скомпрометований — повідомити адміністратора. Він видаляє `name:key` з env та перезапускає контейнер.
---
## 2. Запуск Rehearsal / Release Run
### Крок 1 — Створити run
```bash
POST /api/runbooks/runs
{
"runbook_path": "runbook/rehearsal-v1-30min-checklist.md",
"operator_id": "your_name",
"node_id": "NODA1",
"sofiia_url": "http://localhost:8002"
}
```
Отримаєш `run_id`. Зберегти його.
### Крок 2 — Auto steps (натискати `Next`)
```bash
POST /api/runbooks/runs/{run_id}/next
```
Автоматично виконуються:
| # | Крок | Тип |
|---|------|-----|
| 0 | Health check `/api/health` → 200 | http_check |
| 1 | Metrics `/metrics` → 200 | http_check |
| 2 | Audit auth `/api/audit` → 401 | http_check |
| 3 | Preflight script (STRICT=1) | script |
| 4 | Redis idempotency smoke | script |
| 5 | Generate evidence script | script |
### Крок 3 — Manual steps
Runner зупиниться і поверне `{"type": "manual", "instructions_md": "..."}`.
Виконати дію (наприклад, restart), потім:
```bash
POST /api/runbooks/runs/{run_id}/steps/{step_index}/complete
{
"status": "ok",
"notes": "restart completed",
"operator_id": "your_name"
}
```
**Observation Window step** — внести метрики в `data`:
```json
{
"status": "ok",
"notes": "observation 15 min",
"data": {
"5xx_total": 0,
"replays_delta": 0,
"rate_limited_chat_delta": 0,
"send_latency_p95_ms": null
}
}
```
### Крок 4 — Артефакти
```bash
POST /api/runbooks/runs/{run_id}/evidence
POST /api/runbooks/runs/{run_id}/post_review
```
Де знаходяться файли:
```bash
GET /api/runbooks/runs/{run_id}/artifacts
# → {"files": [{"name": "release_evidence.md", "path": "...", "bytes": 2048}, ...]}
```
Або напряму на NODA1: `${SOFIIA_DATA_DIR}/release_artifacts/<run_id>/`
---
## 3. Audit trail
**Хто може читати audit:**
| Роль | `/api/audit` |
|------|-------------|
| `operator` (головний ключ) | ✅ |
| `user:<name>` (team key) | ✅ |
| localhost без ключа | ❌ (strict auth) |
| без ключа взагалі | ❌ |
> Audit endpoint **не має** localhost bypass. Потрібен ключ завжди.
**Читання audit trail:**
```bash
curl -H "X-API-Key: $YOUR_KEY" \
"https://console.daarion.space/api/audit?limit=20"
```
---
## 4. Якщо щось ламається
**Перевірити стан run:**
```bash
GET /api/runbooks/runs/{run_id}
# → status, current_step, кожен крок зі статусом
```
**Подивитись деталі кроку**у полі `result`:
- `exit_code` — для script кроків
- `stderr_tail` — лог помилки
- `timeout: true` — якщо перевищено timeout
- `ok: false` — крок не пройшов
**Abort run:**
```bash
POST /api/runbooks/runs/{run_id}/abort
{"operator_id": "your_name"}
```
---
## 5. Коли запускати Rehearsal
Обов'язково перед:
- релізом sofiia-console
- підключенням нової ноди
- значними змінами в конфігурації агентів
- масштабуванням (нові ноди в кластері)
---
## 6. Корисні команди (quick ref)
```bash
# Список останніх runs (через API)
GET /api/runbooks/runs # якщо є list endpoint
# Health console
GET /api/health
# Метрики
GET /metrics
# Версія
GET /api/meta/version
# Auth check
GET /api/auth/check
```
---
_Версія документа: v1 · console.daarion.space · Sofiia Console `e0bea910`_

View File

@@ -361,6 +361,31 @@ async def render_release_evidence(run_id: str) -> Dict[str, Any]:
}
async def list_run_artifacts(run_id: str) -> Dict[str, Any]:
"""
List files in release_artifacts/<run_id>/ with sizes and mtimes.
Returns {run_id, dir, files: [{name, path, bytes, mtime_utc}]}.
"""
out_dir = _artifacts_dir(run_id)
files = []
if out_dir.exists():
for f in sorted(out_dir.iterdir()):
if f.is_file():
stat = f.stat()
files.append({
"name": f.name,
"path": str(f),
"bytes": stat.st_size,
"mtime_utc": _iso_utc(stat.st_mtime),
})
return {
"run_id": run_id,
"dir": str(out_dir),
"exists": out_dir.exists(),
"files": files,
}
async def render_post_review(run_id: str) -> Dict[str, Any]:
"""
Generate post-release review markdown from run DB data.

View File

@@ -82,6 +82,15 @@ async def complete_step(
return {"ok": True, "run_id": run_id, "step_index": step_index, "next_step": step_index + 1}
@runbook_runs_router.get("/{run_id}/artifacts")
async def list_artifacts(
run_id: str,
_auth: str = Depends(require_auth),
):
"""List generated artifacts for a run (paths, sizes, mtimes)."""
return await artifacts.list_run_artifacts(run_id)
@runbook_runs_router.post("/{run_id}/evidence")
async def generate_evidence(
run_id: str,