Skip to content

feat(vault): background poller for canary reads + access logs - #263

Open
LiorFink00 wants to merge 1 commit into
feat/vault-api-routesfrom
feat/vault-poller
Open

feat(vault): background poller for canary reads + access logs#263
LiorFink00 wants to merge 1 commit into
feat/vault-api-routesfrom
feat/vault-poller

Conversation

@LiorFink00

Copy link
Copy Markdown
Collaborator

What

Closes the vault loop (Task 6 of the secret-manager migration, epic #255). A background poller checks each configured secrets manager's audit log for reads of planted canary secrets and alerts on them.

  • CanaryAccessLog table + migration canary_access_logs_v1 (chained off vault_tables_v1), with a (canary_secret_id, event_id) uniqueness key.
  • store.record_canary_access (deduped by the manager's event id) + list_canary_access_logs.
  • services/vault_poller.pypoll_once() + an asyncio interval loop, started from the app lifespan and cancelled on shutdown (try/finally). Interval via THUMPER_VAULT_POLL_INTERVAL (default 30s).
  • GET /api/vault/secrets/{csid}/access-logs to surface recorded reads in the UI (Task 7).

Notable deviation from the enterprise original

Dedup happens before alerting (the enterprise version alerted, then recorded). The AWS plugin polls CloudTrail with a ~20-min lookback window, so the same read re-surfaces on overlapping poll cycles; recording first means a single read alerts exactly once. Covered by test_poll_once_dedupes_repeated_event.

Also

Drops a pre-existing unused import json in tests/test_vault_api.py that was failing lint on the base branch (#262).

Testing

  • test_vault_poller.py (8): no-connections, no-secrets, detect-read, dedup, skip-unconfigured, continue-on-plugin-error, survive-poll-failure.
  • test_vault_store.py (+4): record / dedup-by-event-id / no-event-id-not-deduped / newest-first.
  • test_vault_api.py (+2): access-logs 404 + list.
  • Full local suite green except the known atime flake (test(agent): de-flake the atime tests (read-before-baseline race) #233) and the boto3/hvac-gated plugin tests (deps present in CI). Migration single-head verified; ruff clean.

Stacked

Based on #262#261#260#259#257. Merge those first; this rebases to just the poller.

🤖 Generated with Claude Code

Adds the background poller that closes the vault loop: on an interval it asks
each configured secrets manager's plugin to poll its audit log for reads of any
planted canary secret, records each read, raises an alert, and fans it out via
the existing alerting path.

- CanaryAccessLog table + migration (canary_access_logs_v1, chained off
  vault_tables_v1) with a (canary_secret_id, event_id) uniqueness key.
- store.record_canary_access (deduped by the manager's event id) and
  list_canary_access_logs.
- services/vault_poller.py: poll_once + an asyncio interval loop, started from
  the app lifespan and cancelled on shutdown. THUMPER_VAULT_POLL_INTERVAL (30s).
- GET /api/vault/secrets/{csid}/access-logs to surface recorded reads.

Dedup happens BEFORE alerting (unlike the enterprise original, which alerted
then recorded): the AWS plugin polls CloudTrail with a lookback window, so the
same read re-surfaces on overlapping cycles - recording first means a single
read alerts exactly once. Covered by test_poll_once_dedupes_repeated_event.

Also drops a pre-existing unused `import json` in test_vault_api.py that was
failing lint on the base branch (#262). Ported from the enterprise implementation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DARDAxeg4NM8FKoyGMQZy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant