docs(capture): make Q4 audit-sink-down posture classification-aware (#49)
Reconsidered Q4: instead of one global degrade-and-warn, the posture now inherits from effective classification (Q1): - confidential + audit-sink-down -> hard-refuse (no buffer; removes the buffer-integrity question for confidential data) - internal/public + audit-sink-down -> degrade-and-warn + durable local buffer + ntfy + reconcile-on-recovery - floor (all tiers): refuse if nothing can record the audit Updated the I5 Gherkin scenarios + obligations row to match. Couples Q4 to the Q1 classification spine -> one coherent sensitivity model.
This commit is contained in:
+37
-18
@@ -47,7 +47,7 @@ beyond the I5 audit log.
|
||||
| **I2 deliberate acceptance** | The *distributed-library* form opens no new acceptance. IF a central relay node is deployed, its cross-harness reach MUST be entered in `infra/docs/security-baseline.md` with Why-accepted / Revisit-if before it ships. |
|
||||
| **I3 GitOps reconcilability** | IF `capture` runs as a deployed service, its manifest lives under `infra/k3s/apps/**` (sovereign source, Flux-reconciled). No untracked runtime. |
|
||||
| **I4 decisions captured** | The distributed-vs-central decision and the intent-named-verb pattern are recorded (ADR + brain). |
|
||||
| **I5 auditability** | Every capture emits a request-level audit record to the alloy/loki substrate: actor/principal, harness, items written, timestamp. |
|
||||
| **I5 auditability** | Every capture emits a request-level audit record (actor/principal, harness, items written, timestamp) to the alloy/loki substrate. **Classification-aware degradation** (§4.4): confidential + sink-down → hard-refuse; internal/public + sink-down → durable local buffer + ntfy + reconcile. Floor: refuse if nothing can record the audit. |
|
||||
|
||||
---
|
||||
|
||||
@@ -147,24 +147,33 @@ Feature: Capture session value uniformly across harnesses
|
||||
Then the would-be receipt is returned
|
||||
And nothing is written anywhere
|
||||
|
||||
# --- I5: auditability is non-optional, but degrades rather than blocks ---
|
||||
Scenario: Capture proceeds under a durable local audit buffer when the central sink is down
|
||||
Given the central audit substrate (loki) cannot be written to
|
||||
# --- I5: auditability is classification-aware (confidential fails closed) ---
|
||||
Scenario: Confidential capture hard-refuses when the central audit sink is down
|
||||
Given the effective classification is "confidential"
|
||||
And the central audit substrate (loki) cannot be written to
|
||||
When capture is invoked
|
||||
Then the capture is refused before any write
|
||||
And the reason names the auditability invariant
|
||||
# Confidential work must be centrally auditable at write time — no buffered exception.
|
||||
|
||||
Scenario: Internal capture degrades to a durable local buffer when the sink is down
|
||||
Given the effective classification is "internal" or "public"
|
||||
And the central audit substrate (loki) cannot be written to
|
||||
When capture is invoked
|
||||
Then the capture proceeds
|
||||
And the audit record is written to a durable LOCAL fallback buffer
|
||||
And an ntfy alert is emitted naming the degraded audit state
|
||||
And the receipt flags that audit was buffered locally, not centrally recorded
|
||||
# Never UN-audited: the write is buffered durably and reconciled on recovery.
|
||||
|
||||
Scenario: Locally buffered audit records reconcile to the central sink on recovery
|
||||
Given audit records were buffered locally during a sink outage
|
||||
Given internal-tier audit records were buffered locally during a sink outage
|
||||
When the central audit substrate becomes reachable again
|
||||
Then the buffered records are replayed to the central sink
|
||||
And the local buffer is cleared only after confirmed central write
|
||||
|
||||
Scenario: Capture refuses if even the local audit buffer cannot be written
|
||||
Given neither the central sink nor the local fallback buffer can be written
|
||||
Scenario: Even internal capture refuses if neither sink nor local buffer can be written
|
||||
Given the effective classification is "internal" or "public"
|
||||
And neither the central sink nor the local fallback buffer can be written
|
||||
When capture is invoked
|
||||
Then the capture is refused
|
||||
And the reason names the auditability invariant
|
||||
@@ -211,13 +220,23 @@ binding design decisions for the build.
|
||||
reach **must be entered in `infra/docs/security-baseline.md`** with Why-accepted / Revisit-if
|
||||
**before it ships** (I2). That ledger entry is v1 work, not a follow-up.
|
||||
|
||||
4. **Audit-sink-down — degrade-and-warn, with a durable local buffer + reconcile-on-recovery.**
|
||||
If the central audit sink (loki) is unreachable, capture **proceeds** rather than blocking
|
||||
(availability), BUT the audit record is written to a **durable local fallback buffer** and an
|
||||
**ntfy alert** fires. Buffered records **reconcile to the central sink on recovery**; the local
|
||||
buffer clears only after confirmed central write. **Floor:** if *neither* the central sink nor the
|
||||
local buffer can be written, capture **refuses** — degrade-and-warn never means *un*-audited.
|
||||
- Rationale: keeps capture available during an observability outage while preserving I5 — the
|
||||
write is always recorded *somewhere durable*, just not centrally until reconciliation. This is
|
||||
the difference between an I5 violation and an I5-compliant degraded mode, and it must be
|
||||
presentable as such to a due-diligence client.
|
||||
4. **Audit-sink-down — classification-aware: confidential fails closed, internal/public degrades.**
|
||||
The posture inherits from the effective classification (decision 1), so there is one coherent
|
||||
sensitivity model rather than a separate availability policy:
|
||||
- **Confidential + central audit sink unreachable → hard-refuse.** No buffer, no proceed.
|
||||
Confidential work must be centrally auditable *at write time*; "buffer and reconcile later"
|
||||
introduces a buffer-integrity question (can a write tamper with its own pending audit record?)
|
||||
that must not exist for confidential data. The simplicity of "refuse" is itself the assurance
|
||||
asset — trivially true, nothing to poke holes in.
|
||||
- **Internal / public + central sink unreachable → degrade-and-warn** with a durable local buffer
|
||||
+ ntfy alert + reconcile-on-recovery (the earlier Q4 design, now scoped to lower tiers). Keeps
|
||||
capture available for your own homelab work during an observability outage; negligible risk
|
||||
since the buffered record is still durable and the data isn't client-confidential.
|
||||
- **Floor (all tiers):** if *nothing* — neither central sink nor (for internal/public) the local
|
||||
buffer — can record the audit, capture **refuses**. No tier writes wholly un-audited.
|
||||
- Rationale: matches assurance cost to data sensitivity, exactly as the I1/sovereignty model
|
||||
does for placement. Presentable to a due-diligence client as "audit posture is
|
||||
classification-aware: confidential fails closed, internal degrades gracefully" — which
|
||||
demonstrates the judgment, not just a binary. Couples Q4 to Q1's classification machinery
|
||||
(being built anyway) and removes the buffer-integrity rabbit hole for the only tier where it
|
||||
mattered.
|
||||
|
||||
Reference in New Issue
Block a user