feat(audit): publish sequenced heartbeat and reconciliation evidence
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a06ec5-7e2b-7743-ac08-719e1b0f42e2
This commit is contained in:
parent
fce60099c4
commit
b9349782f4
32 changed files with 839 additions and 111 deletions
|
|
@ -195,11 +195,11 @@ effect of how it was built, not extra work:
|
|||
recovery procedure with no data-loss risk (`maximum_data_loss: PT0S`
|
||||
applies trivially since there is no persisted data).
|
||||
|
||||
## 9. Kings Guard mapping (prep now, cheap; enforcement later)
|
||||
## 9. King's Guard observation mapping
|
||||
|
||||
Nothing below requires `kings-guard` to exist yet. It is preparation so
|
||||
`qonto-assistant` needs zero rework once a sentinel-mesh or decision plane
|
||||
does exist.
|
||||
King's Guard has observed the structured allow/deny stream. The source keeps
|
||||
ownership of its genome and emission claims; the observer independently checks
|
||||
the records and completeness evidence it receives.
|
||||
|
||||
### 9.1 Security Genome record
|
||||
|
||||
|
|
@ -210,21 +210,29 @@ nothing else), data classification, and recovery expectations.
|
|||
|
||||
### 9.2 Audit stream is already observation-shaped
|
||||
|
||||
`AuditLogger`'s existing event shape (actor, capability, decision,
|
||||
deny_reason, latency, upstream HTTP status, policy version — see
|
||||
`tests/test_audit.py`, `tests/test_audit_parity.py`) already matches Kings
|
||||
Guard's Immune Observation contract closely enough that no schema rework
|
||||
should be needed later — just a new consumer pointed at the same stream.
|
||||
`AuditLogger`'s event shape includes actor, capability, decision, deny reason,
|
||||
latency, upstream HTTP status, policy version, identity binding, and egress
|
||||
destination (see `tests/test_audit.py`, `tests/test_audit_parity.py`). Every
|
||||
record also carries a process-instance id and monotonic stream sequence.
|
||||
|
||||
The source-owned declaration in `specs/audit-emission-cadence.yaml` classifies
|
||||
`audit.deny` as load-bearing and low-volume. A startup heartbeat, periodic
|
||||
heartbeat (24 hours by default), and best-effort shutdown heartbeat publish
|
||||
cumulative and window transition counts. Consumers compare those counts and
|
||||
sequence continuity with observed records. `/v1/audit/reconciliation` exposes
|
||||
the same non-secret current source counts for an authenticated diagnostic read.
|
||||
|
||||
### 9.3 A concrete, actionable-today signal
|
||||
|
||||
This does not require any Kings Guard component: repeated
|
||||
`arg_constraint` or `credential_exfil` deny reasons from the same actor
|
||||
within a short window is a real, current signal. It should trip a
|
||||
tightened rate limit or a temporary lockout for that actor now, using the
|
||||
audit stream and rate limiter that already exist — this is a Fast Local
|
||||
Loop (`NetKingdomImmuneArchitecture.md` §14.1) response that does not need
|
||||
to wait for any future component.
|
||||
tightened rate limit or a temporary lockout for that actor now. The current
|
||||
`DenyEscalationTracker` branches directly on the same decision path that emits
|
||||
the audit event; it does not consume the audit stream. This is a Fast Local Loop
|
||||
(`NetKingdomImmuneArchitecture.md` §14.1) response that does not need to wait
|
||||
for any future component, while the stream remains load-bearing for estate
|
||||
observation rather than for the local lockout.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -235,12 +243,12 @@ to wait for any future component.
|
|||
| Security Genome record | `qonto-assistant` | can ship now |
|
||||
| Audit-stream shape review against Immune Observation contract | `qonto-assistant` | can ship now |
|
||||
| Actor lockout on repeated deny signals | `qonto-assistant` | can ship now |
|
||||
| `key-cape` token verification in place of bearer token | `qonto-assistant` + `key-cape` | needs `key-cape` client integration support |
|
||||
| `finance.qonto.read` resource + live decision call | `qonto-assistant` + `flex-auth` | needs the resource registered in `flex-auth` |
|
||||
| Live tenant-role gate | `qonto-assistant` + `tenant-engine` | needs a `tenant-engine` lookup/cache API call wired in |
|
||||
| `key-cape` token verification in place of bearer token | `qonto-assistant` + `key-cape` | verifier shipped; live enforcement is runtime-configured |
|
||||
| `finance.qonto.read` resource + live decision call | `qonto-assistant` + `flex-auth` | client and policy shipped; live use is runtime-configured |
|
||||
| Live tenant-role gate | `qonto-assistant` + `tenant-engine` | client shipped; live use requires a reachable deployment URL |
|
||||
| Facade / scale-to-zero activator | new component (home TBD — Railiance or a dedicated repo) | design only so far |
|
||||
| I1/I2 isolation placement on `railiance01` | Railiance | needs a placement decision |
|
||||
| Sentinel-mesh / decision-plane consumption of the audit stream | `kings-guard` | does not exist yet — see intake |
|
||||
| Audit-stream observation and completeness acceptance | `kings-guard` | consuming events; cadence/reconciliation acceptance pending |
|
||||
|
||||
Tracked as `QONTO-WP-0004` in this repo, with the `kings-guard`-owned
|
||||
portion tracked as an intake against `KG-WP-0002` (pilot-lane selection).
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ Phase 1 ships:
|
|||
- policy-gated `GET /v1/transactions`
|
||||
- policy-gated `GET /v1/snapshot`
|
||||
- structured audit events without secrets
|
||||
- sequenced audit heartbeat and reconciliation evidence
|
||||
- env-backed or OpenBao-CLI-backed credential loading
|
||||
|
||||
Spend, transfer, card, invoicing, payment-link, and other volume-cost actions
|
||||
|
|
@ -207,6 +208,32 @@ That consumer-side write remains outside this repo.
|
|||
the current dogfood path remains `legacy_api_key` because that is the proven
|
||||
BINKY-WP-0005 header mode.
|
||||
|
||||
## Audit deny-stream reconciliation
|
||||
|
||||
`audit.deny` is a low-volume, load-bearing observation class. Do not infer
|
||||
completeness from a minimum event rate. The service emits `audit.heartbeat` at
|
||||
startup, every `QONTO_AUDIT_HEARTBEAT_INTERVAL_SECONDS` while the process is
|
||||
active (default `86400`), and best-effort at shutdown. Each request event and
|
||||
heartbeat carries `stream_instance_id` plus a monotonic `stream_sequence`.
|
||||
|
||||
For each instance, compare received `audit.allow` / `audit.deny` totals with
|
||||
the heartbeat's `source_transition_counts` and reject sequence gaps. Window
|
||||
counts describe transitions since the prior heartbeat. A quiet deny window has
|
||||
`assertion: nothing-to-report`; a non-quiet one has
|
||||
`assertion: transitions-reported`. Counters are process-local and reset only
|
||||
when `stream_instance_id` changes.
|
||||
|
||||
The authenticated diagnostic view is:
|
||||
|
||||
```text
|
||||
GET /v1/audit/reconciliation
|
||||
```
|
||||
|
||||
It returns only stream identity, timestamps, sequence, and counts. It does not
|
||||
write State Hub, query an observer, include actor/bank data, or increment the
|
||||
stream it describes. The exact contract is declared in
|
||||
`specs/audit-emission-cadence.yaml`.
|
||||
|
||||
## Live authorization gate (flex-auth + tenant-engine)
|
||||
|
||||
Off by default (no `QONTO_FLEX_AUTH_URL` set). When configured, every
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue