vps-health-api/README.md
le king fu e88a044711 feat(defenseurs): add GET /defenseurs/findings?project=X route
Drill-down endpoint exposing detailed findings per project. Resolves the
HTTP gap for the Vercel admin dashboard, which cannot SSH/Tailscale to
the VPS, plus a future portable /analyse-vulnerabilite skill.

- Project -> agent lookup via /data/defenseurs/agents-map.json (Sergent snapshot)
- findLatestReportForAgent scans REPORTS_DIR + REPORTS_DIR/archive (post-07:30 UTC rotation)
- Filters: category exact match, severity threshold inclusive upward
- Asymmetric severity rule: default hides LOW+INFO; ?severity=LOW returns
  LOW+MEDIUM+HIGH+CRITICAL but still hides INFO; INFO opt-in via explicit param
- Distinguishes "report present + scan clean" (no status field) from
  "no report at all" ({findings:[], status:"no_data"})
- Bootstraps vitest (devDep; runtime stays 0-dep), 14 tests covering auth,
  validation, filters, asymmetry, mtime selection, error paths
- Refactor: export handler so tests can spin up ephemeral servers; server.listen
  guarded by require.main === module

Closes #3

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:56:56 -04:00

64 lines
2.3 KiB
Markdown

# vps-health-api
Lightweight health monitoring API for the VPS. Node 22, HTTP-native, zero runtime deps.
## Endpoints
All endpoints require `Authorization: Bearer $HEALTH_TOKEN`.
| Method | Path | Description |
|--------|------|-------------|
| GET | `/health` | CPU, memory, disk, uptime, Logto reachability |
| GET | `/defenseurs` | Defenseurs executive status (status.json) |
| GET | `/defenseurs/findings?project=X` | Detailed findings for a project's Defenseur |
| GET | `/reports/scans?date=YYYY-MM-DD` | Aggregated scan reports for a UTC date |
### `GET /defenseurs/findings`
Query params:
- `project` (required) — project name, looked up in `agents-map.json` (e.g. `la-suite-booking`)
- `category` (optional) — exact match, one of `deps|secrets|code|acces|infra`
- `severity` (optional) — threshold, one of `CRITICAL|HIGH|MEDIUM|LOW|INFO`
- default (no param): `MEDIUM`, `HIGH`, `CRITICAL`
- `LOW` returns `LOW`+`MEDIUM`+`HIGH`+`CRITICAL` but still hides `INFO`
- `INFO` returns `INFO` only (explicit opt-in)
Responses:
- `200 { agent, project, timestamp, findings: Finding[] }` — report present (empty `findings` if clean scan; no `status` field)
- `200 { findings: [], status: "no_data" }` — no report on file for the agent
- `400` — missing `project` or invalid `category` / `severity`
- `401` — missing or invalid token
- `404` — unknown project
- `500``agents-map.json` unreadable or corrupted
Example:
```
curl -H "Authorization: Bearer $HEALTH_TOKEN" \
"https://health.lacompagniemaximus.com/defenseurs/findings?project=la-suite-booking&severity=HIGH"
```
## Config
| Env var | Default | Purpose |
|---------|---------|---------|
| `PORT` | `3001` | HTTP port |
| `HEALTH_TOKEN` | — | Bearer token (fail-closed if missing) |
| `REPORTS_DIR` | `/data/defenseurs/reports` | Scan reports dir |
| `DEFENSEURS_AGENTS_MAP_PATH` | `/data/defenseurs/agents-map.json` | Project -> agent snapshot |
| `LOGTO_HEALTH_URL` | auth.lacompagniemaximus.com | Logto OIDC discovery URL |
## Bind-mounts (Coolify, read-only)
- `/home/defenseur/defenseurs/status.json` -> `/data/defenseurs/status.json`
- `/home/defenseur/defenseurs/reports/` -> `/data/defenseurs/reports/`
- `/home/defenseur/defenseurs/agents-map.json` -> `/data/defenseurs/agents-map.json`
## Tests
```
npm install
npm test
```