Parent:#49 · Spec:specs/capture-bdd-spec.md §2 (I5), §4.4 · Depends on:#50 (classification), #53 (the adapter it instruments) · Independent of: #49f (relay)
Scope
The I5 audit path with Q4 classification-aware degradation. Needed by any capture deployment, including direct-REST/library-only — so it's split from the relay (#49f) and can land before it. After this, direct-REST capture (CLI/Agentsquad) is fully I5-compliant.
What to build (per §4.4, classification-aware)
AuditSink impl → alloy/loki. Every capture emits a request-level audit record: actor/principal, server-derived harness origin, effective classification, items written (ids/numbers), timestamp, outcome.
Classification-aware degradation:
Confidential + central sink unreachable → hard-refuse the capture (no buffer, no proceed). Confidential must be centrally auditable at write time.
Internal/public + central sink unreachable → degrade-and-warn: write the audit record to a durable local buffer, fire an ntfy alert, flag the receipt as locally-buffered.
Floor (all tiers): if neither central sink nor (for internal/public) the local buffer can be written → refuse. No tier writes wholly un-audited.
Reconciliation: buffered (internal/public) records replay to the central sink on recovery; local buffer cleared only after confirmed central write.
Buffer durability: the local buffer survives process restart (file/embedded-store, not in-memory).
Acceptance criteria
AuditSink writes request-level records to loki with the full field set
Confidential + sink-down → capture refused (hard, no buffer)
Couples to #50's classification (the degradation tier is the effective classification) and instruments #53's adapter. The buffer/reconcile machinery is internal/public-tier only — confidential is a plain refuse, which is why the build is smaller than a global-buffer design. Gherkin scenarios this makes live: the entire I5 classification-aware audit block.
**Parent:** #49 · **Spec:** `specs/capture-bdd-spec.md` §2 (I5), §4.4 · **Depends on:** #50 (classification), #53 (the adapter it instruments) · **Independent of:** #49f (relay)
## Scope
The I5 audit path with Q4 classification-aware degradation. Needed by **any** capture deployment, including direct-REST/library-only — so it's split from the relay (#49f) and can land before it. After this, direct-REST capture (CLI/Agentsquad) is fully I5-compliant.
## What to build (per §4.4, classification-aware)
1. **`AuditSink` impl → alloy/loki.** Every capture emits a request-level audit record: actor/principal, server-derived harness origin, effective classification, items written (ids/numbers), timestamp, outcome.
2. **Classification-aware degradation:**
- **Confidential + central sink unreachable → hard-refuse** the capture (no buffer, no proceed). Confidential must be centrally auditable at write time.
- **Internal/public + central sink unreachable → degrade-and-warn:** write the audit record to a **durable local buffer**, fire an **ntfy alert**, flag the receipt as locally-buffered.
- **Floor (all tiers):** if neither central sink nor (for internal/public) the local buffer can be written → refuse. No tier writes wholly un-audited.
3. **Reconciliation:** buffered (internal/public) records replay to the central sink on recovery; local buffer cleared only after confirmed central write.
4. **Buffer durability:** the local buffer survives process restart (file/embedded-store, not in-memory).
## Acceptance criteria
- [ ] `AuditSink` writes request-level records to loki with the full field set
- [ ] Confidential + sink-down → capture refused (hard, no buffer)
- [ ] Internal/public + sink-down → proceeds, audit buffered locally, ntfy fired, receipt flagged
- [ ] Floor: nothing-can-record → refuse (all tiers)
- [ ] Reconciliation replays buffered records on recovery; buffer cleared only after confirmed central write
- [ ] Buffer is durable across restart
- [ ] Tests: confidential-sink-down-refused, internal-sink-down-buffered, reconcile-on-recovery, floor-refuse, buffer-survives-restart
- [ ] `task check` green
## Notes
Couples to #50's classification (the degradation tier is the effective classification) and instruments #53's adapter. The buffer/reconcile machinery is **internal/public-tier only** — confidential is a plain refuse, which is why the build is smaller than a global-buffer design. Gherkin scenarios this makes live: the entire I5 classification-aware audit block.
Two-phase port (the load-bearing design): Reserve(level) runs before any write and refuses (confidential+down, or floor) / grants central / grants buffered; Record(entry, outcome) runs after. Makes "refuse before any write" literally true even though the record lists what landed.
audit.DegradingSink (§4.4): central up → central; down+confidential → refuse (no buffer); down+internal/public+buffer-writable → buffer+ntfy+receipt flag; down+buffer-unwritable → floor refuse.
Components:FileBuffer (durable JSONL, survives restart, confirm-then-clear per record), LokiCentral (/ready + /loki/api/v1/push), NtfyNotifier (token never logged), Reconcile/StartReconcile (replay on recovery, failed push keeps record buffered). SlogSink → two-phase, default when no loki.
All ACs met (incl. confidential-refuse, internal-buffer, floor-refuse, reconcile, survives-restart); task check green.
⚠️ For #55 (k3s manifest, I3): the durable buffer is a file at <brain>/.audit-buffer/capture.jsonl. In k8s it must be on a persistent volume, else "durable across restart" fails on pod reschedule. Flag for the relay manifest.
Milestone: #50–#54 complete capture for direct-REST harnesses, fully I1+I5-compliant. Only #55 remains (central relay + I2 security-baseline ledger — heaviest security weight; ledger must merge before relay ships).
## Implemented — PR #60 (open, base main)
**PR:** https://git.d-ma.be/mathias/hyperguild/pulls/60 · branches from `main` (#53 merged).
**Two-phase port** (the load-bearing design): `Reserve(level)` runs before any write and refuses (confidential+down, or floor) / grants central / grants buffered; `Record(entry, outcome)` runs after. Makes "refuse before any write" literally true even though the record lists what landed.
**`audit.DegradingSink` (§4.4):** central up → central; down+confidential → refuse (no buffer); down+internal/public+buffer-writable → buffer+ntfy+receipt flag; down+buffer-unwritable → floor refuse.
**Components:** `FileBuffer` (durable JSONL, survives restart, confirm-then-clear per record), `LokiCentral` (/ready + /loki/api/v1/push), `NtfyNotifier` (token never logged), `Reconcile`/`StartReconcile` (replay on recovery, failed push keeps record buffered). `SlogSink` → two-phase, default when no loki.
**Wiring:** `buildAuditSink` picks DegradingSink (loki+buffer+ntfy+reconcile) when `BRAIN_LOKI_URL` set, else slog. `ErrAuditUnavailable` → HTTP 503. Env: `BRAIN_LOKI_URL`, `BRAIN_NTFY_URL`, `BRAIN_NTFY_TOKEN`, `BRAIN_AUDIT_RECONCILE_INTERVAL`.
All ACs met (incl. confidential-refuse, internal-buffer, floor-refuse, reconcile, survives-restart); `task check` green.
**⚠️ For #55 (k3s manifest, I3):** the durable buffer is a file at `<brain>/.audit-buffer/capture.jsonl`. In k8s it **must be on a persistent volume**, else "durable across restart" fails on pod reschedule. Flag for the relay manifest.
Brain: `wiki/hyperguild/decisions/two-phase-reserve-record-audit-gate`.
**Milestone:** #50–#54 complete capture for direct-REST harnesses, fully I1+I5-compliant. Only **#55** remains (central relay + I2 security-baseline ledger — heaviest security weight; ledger must merge before relay ships).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Parent: #49 · Spec:
specs/capture-bdd-spec.md§2 (I5), §4.4 · Depends on: #50 (classification), #53 (the adapter it instruments) · Independent of: #49f (relay)Scope
The I5 audit path with Q4 classification-aware degradation. Needed by any capture deployment, including direct-REST/library-only — so it's split from the relay (#49f) and can land before it. After this, direct-REST capture (CLI/Agentsquad) is fully I5-compliant.
What to build (per §4.4, classification-aware)
AuditSinkimpl → alloy/loki. Every capture emits a request-level audit record: actor/principal, server-derived harness origin, effective classification, items written (ids/numbers), timestamp, outcome.Acceptance criteria
AuditSinkwrites request-level records to loki with the full field settask checkgreenNotes
Couples to #50's classification (the degradation tier is the effective classification) and instruments #53's adapter. The buffer/reconcile machinery is internal/public-tier only — confidential is a plain refuse, which is why the build is smaller than a global-buffer design. Gherkin scenarios this makes live: the entire I5 classification-aware audit block.
Implemented — PR #60 (open, base main)
PR: #60 · branches from
main(#53 merged).Two-phase port (the load-bearing design):
Reserve(level)runs before any write and refuses (confidential+down, or floor) / grants central / grants buffered;Record(entry, outcome)runs after. Makes "refuse before any write" literally true even though the record lists what landed.audit.DegradingSink(§4.4): central up → central; down+confidential → refuse (no buffer); down+internal/public+buffer-writable → buffer+ntfy+receipt flag; down+buffer-unwritable → floor refuse.Components:
FileBuffer(durable JSONL, survives restart, confirm-then-clear per record),LokiCentral(/ready + /loki/api/v1/push),NtfyNotifier(token never logged),Reconcile/StartReconcile(replay on recovery, failed push keeps record buffered).SlogSink→ two-phase, default when no loki.Wiring:
buildAuditSinkpicks DegradingSink (loki+buffer+ntfy+reconcile) whenBRAIN_LOKI_URLset, else slog.ErrAuditUnavailable→ HTTP 503. Env:BRAIN_LOKI_URL,BRAIN_NTFY_URL,BRAIN_NTFY_TOKEN,BRAIN_AUDIT_RECONCILE_INTERVAL.All ACs met (incl. confidential-refuse, internal-buffer, floor-refuse, reconcile, survives-restart);
task checkgreen.⚠️ For #55 (k3s manifest, I3): the durable buffer is a file at
<brain>/.audit-buffer/capture.jsonl. In k8s it must be on a persistent volume, else "durable across restart" fails on pod reschedule. Flag for the relay manifest.Brain:
wiki/hyperguild/decisions/two-phase-reserve-record-audit-gate.Milestone: #50–#54 complete capture for direct-REST harnesses, fully I1+I5-compliant. Only #55 remains (central relay + I2 security-baseline ledger — heaviest security weight; ledger must merge before relay ships).