feat(defenseurs): GET /defenseurs/state serves findings-state.json #28

Merged
maximus merged 2 commits from issue-27-defenseurs-state into main 2026-09-25 01:41:35 +00:00

2 commits

Author SHA1 Message Date
le king fu
7c14e581cb test(defenseurs): cover GET /defenseurs/state in the socket-level pass
Four cases on top of the 41 from #26: the file served byte for byte
(non-canonical fixture, compared with cmp so the trailing newline counts),
no_data when absent, 401 without a token and 401 with the ingest token.

Refs #27

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 20:42:56 -04:00
le king fu
a5d1bcdedb feat(defenseurs): serve the Sergent's findings state at GET /defenseurs/state
The Sergent now persists per-finding state (firstSeen, unverified, 30-day
history) in findings-state.json next to status.json. The admin dashboard
needs it for the drill-down, since it is the only source carrying a
finding's age.

- New read route GET /defenseurs/state, path from
  DEFENSEURS_FINDINGS_STATE_PATH (default /data/defenseurs/findings-state.json),
  already covered by the /data/defenseurs mount.
- Extract serveJsonFile() so /defenseurs and /defenseurs/state share one
  contract: raw content, or 200 {status:"no_data"} on any read error.
- Tests: new state.test.js (byte-identical body, no_data, path isolation,
  401 x3, 404 on POST) and the route added to the auth.test.js matrix.
- Docs: README and CLAUDE.md endpoint, config and mount sections; the README
  mount list now matches the real /data/defenseurs directory mount.

Fixes #27

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 20:42:20 -04:00