diff --git a/workplans/AUDIT-WP-0008-tenancy-posture-alignment.md b/workplans/AUDIT-WP-0008-tenancy-posture-alignment.md index 12df94d..f987a0b 100644 --- a/workplans/AUDIT-WP-0008-tenancy-posture-alignment.md +++ b/workplans/AUDIT-WP-0008-tenancy-posture-alignment.md @@ -4,13 +4,14 @@ type: workplan title: "Tenancy posture declaration and read-path enforcement" domain: infotech repo: audit-core -status: proposed +status: ready owner: claude topic_slug: railiance created: "2026-08-17" updated: "2026-08-17" depends_on: - AUDIT-WP-0007 +state_hub_workstream_id: "84c01552-4c65-45fd-bf6c-7daab0ecc9b6" --- # AUDIT-WP-0008 — Tenancy posture declaration and read-path enforcement @@ -87,6 +88,26 @@ Consequences we accept and want stated back: Ask of `rapp-postgres`: amend the ADR-0002 opening context to point at archive rather than at backup retention, so the two statements agree. +**This position is conditional, and the condition is dated.** Routing multi-year +custody to `data.archive` is correct mechanically and unfunded practically: +the capability case records it as unmet, with no booked cost and no procurement +owner in motion. Taking this position without a date would read as "audit-core +is fine", and audit-core is not fine — it is correctly routed and unresourced. + +Escalation: **reviewed 2026-12-31.** If no `data.archive` provision is procured +by then, audit-core reopens placement. §4.5 makes retention a placement trigger, +and P2 — a dedicated cluster with an independent window — becomes the fallback +worth arguing for. P2 still does not deliver WORM, so it is a worse answer than +archive; it is named here so the fallback exists rather than being improvised +under pressure. Recorded now so the review has something to fire against. + +**Also taken now: R1 → R2.** R2 needs only that retention be declared per +dataset rather than inherited silently, and that the erasure horizon be +published. That is an explicit `backupRetentionDays: 30` in the consumer +declaration plus the horizon rendered in our operator surface — a change of +hours, not a future target. Deferring it as a "target" would have been the +framework's own §8.1 failure: a value in a file nobody re-reads. + ### On erasure versus audit (their question 11) Yes to the fact/payload split — retain the fact of an event, encrypt its @@ -119,6 +140,23 @@ Our position: audit-core targets **R2**, not R4. Verified erasure of audit evidence is a design tension we record rather than resolve, and a "R4 everywhere" fleet target must exempt this service explicitly. +**The exemption rests on a legal basis nobody in this estate has confirmed, and +that must be said out loud rather than assumed.** Declining to build erasure +means that if an Article 17 request arrives naming a data subject in the audit +trail, audit-core has no mechanism and must rely on audit evidence being +exempt — legal obligation, or legitimate interest in fraud and security +investigation. Those grounds are ordinary and widely relied on. They are also a +legal conclusion, and this repo is not competent to reach one. Nothing in +INTENT, SCOPE or the capability case records anyone having reached it either. + +This workplan therefore **routes the question rather than absorbing it**: the +position is "we do not build erasure, on a legal basis we have named and not +validated", and the validation is somebody else's to own. An unnamed dependency +here is the difference between a recorded tension and an unnoticed liability — +and an audit service discovering in the middle of a regulator's question that +its exemption was assumed is the specific failure worth spending a paragraph to +avoid. + ## Framework review findings Sent under §20.2 — the framework is validated by whether it can describe us. @@ -126,10 +164,19 @@ Sent under §20.2 — the framework is validated by whether it can describe us. 1. **The E ladder cannot express asymmetric enforcement.** Our write path is E2 and our read path is E1. §4.3 assumes one level per service. This will recur estate-wide, since most services enforce harder on write than on read, and - with no rule everyone will declare their best path. Recommend the vector take - the **minimum across paths**, stated explicitly — the reading consistent with - §6. Per-path declaration (`E: {write: 2, read: 1}`) is the richer option but - invites the same selective quoting downstream. + with no rule everyone will declare their best path. + + Neither obvious fix is right alone. A bare **minimum across paths** is safe + to publish but destroys information: a service at E3-write/E1-read declares + identically to one at E1/E1, though the first is materially further along. + Bare **per-path declaration** (`E: {write: 3, read: 1}`) keeps the signal and + invites exactly the selective quoting §6 exists to stop — "our write path is + E3" is a sentence someone will say. + + Recommend both, with the relationship stated: **declare per path, quote the + minimum.** The posture vector's `E` is the minimum across paths; the per-path + breakdown sits beside it and is where improvement is tracked. One sentence in + §4.3, and it is the only version that keeps honesty and signal at once. 2. **§13.1 collides with the ladder floor.** "A level is claimed only with its evidence artifact present", but §13's table has no artifact for I0, I1, A0, @@ -165,48 +212,100 @@ defect in our code. That is the framework working. contract and it is the right target, but it is blocked behind reaching E2 (§4.3) and needs the `EXPLAIN` comparison first. Declared as target with a recorded gap in T01, not built here. + + **The target carries a date: 2027-03-31.** The framework says services whose + cross-tenant exposure would be a reportable breach SHOULD target E3, and no + service on `platform-pg` fits that description better than this one. Every + quarter spent at E2-with-a-target is a quarter of stated, unclosed risk, and + an undated target is what §8.1 calls documentation rather than control. If the + date slips it slips as a decision, which is the whole point of §12. - **No per-subject encryption is built.** T03 records a position; it does not commit to an implementation. - **No adversarial evidence facility is built here.** §19.3 gave that a home in `whitehat-security`. T05 coordinates, it does not volunteer. +## Sequencing + +**T04 runs before T01.** Task ids are hub-registered and not renumbered; the +execution order is T02 → T04 → T01 → T03 → T05 → T06. + +The reason is narrow. A posture vector published in the repo is a canon-shaped +artifact that outlives the fortnight it takes to fix the read path, and `E1` on +an audit service is a sentence that will be quoted back long after it stopped +being true. Fixing first means the declaration is `E2` on both paths from its +first commit, and the E1 interval never enters the permanent record. + +This is emphatically **not** withholding the finding. T02 goes first precisely +so the correction reaches `rapp-postgres` immediately: their guess of `E2` is +live in canon *now*, and it is wrong now. A message correcting the record is the +fast instrument; a published vector is the durable one. Deferring the durable +artifact by two weeks while the fast one goes out today is sequencing. Delaying +both would be concealment, and §6 would be right to call it that. + ## Tasks ```task id: AUDIT-WP-0008-T01 status: todo priority: high +state_hub_task_id: "65f3109a-fa98-459a-9e59-d7ac211bd2bc" ``` -Publish the posture vector in the repo per Decision 5.1 — `docs/tenancy-posture.yaml`, -declaring `current: I1 A2 E1 P1 R1`, `target: I1 A2 E3 P1 R2`, reviewed date, and a -gap note per axis. The E gap names the read path and cites T04; the R gap names -`data.archive` as the real requirement. Cross-link from `SCOPE.md` and -`docs/interface-card.yaml`. +Publish the posture vector in the repo per Decision 5.1 — `docs/tenancy-posture.yaml`. +**Runs after T04.** Declares `current: I1 A2 E2 P1 R2`, `target: I1 A2 E3 P1 R2`, +reviewed date, and a gap note per axis. Include the per-path E breakdown +(`write: 2, read: 2`) alongside the vector, per finding 1. The E gap names RLS +with its 2027-03-31 date; the R gap names `data.archive` with its 2026-12-31 +review. If T04 has not landed when this is published, declare `E1` and say why — +the vector describes what is true on the day it is written, never what is +expected. Cross-link from `SCOPE.md` and `docs/interface-card.yaml`. + +Fold in the R1 → R2 move: explicit `backupRetentionDays: 30` requested of +`rapp-postgres` for `consumers/audit-core.yaml` (that file is theirs, so this is +a request, not an edit), and the erasure horizon rendered in the operator +surface alongside `recoverable_days`. ```task id: AUDIT-WP-0008-T02 status: todo priority: high +state_hub_task_id: "224a12c0-d646-4c7f-9215-120e1ede4691" ``` -Reply to `rapp-postgres` message `56b77e20-0152-4e09-8f76-99640269063b` with the -retention position: ceiling stays at 365, audit-core declares nothing above the -30-day default, multi-year routes to `data.archive`, service class `batch` -confirmed. Ask for the ADR-0002 context amendment. Mark the message read on send. +Reply to `rapp-postgres` message `56b77e20-0152-4e09-8f76-99640269063b`. +**Runs first.** Carries four things: the retention position (ceiling stays 365, +audit-core declares nothing above the 30-day default, multi-year routes to +`data.archive`) with the **2026-12-31 escalation date and the P2 fallback named**; +the request for an explicit `backupRetentionDays: 30` in `consumers/audit-core.yaml` +to take us to R2; the ADR-0002 context amendment; and `batch` confirmed. + +It also **corrects their posture guess immediately** — `I1 A2 E1 P1 R1`, not +`I2 A3 E2 P1 R1`, with the read-path defect stated plainly and T04 named as the +fix in flight. Their `E2` guess is live in canon and wrong today; the correction +does not wait on T01. Mark the message read on send. ```task id: AUDIT-WP-0008-T03 status: todo priority: high +state_hub_task_id: "0003954e-5033-457f-b9aa-97efdefe9beb" ``` Answer question 11 in `docs/erasure-and-audit.md`: the fact/payload split, the encrypt-then-hash constraint, the confirmation-oracle finding, and the R2-not-R4 target. Record as an audit-core position; send the §4.5 condition to NetKingdom as a framework amendment. +**Name the legal dependency explicitly and route it.** The document must state +that declining to build erasure rests on audit evidence being exempt under legal +obligation or legitimate interest, that this is a legal conclusion, and that no +one in the estate is recorded as having reached it. Route that question to +`the-custodian` for an owner rather than closing it here — audit-core is not +competent to answer it and should not appear to have. The position is publishable +with the dependency open; it is not publishable with the dependency hidden. + ```task id: AUDIT-WP-0008-T04 status: todo priority: high +state_hub_task_id: "e4440db3-6eb0-49f6-9aa9-930db8be9d18" ``` Bind the read path to permitted tenants. `permits_tenant` governs read as it governs write, at the same choke point: tenant-scope `/v1/events`, @@ -219,6 +318,7 @@ and the operator runbook. Raises E to 2 on both paths; update T01's declaration. id: AUDIT-WP-0008-T05 status: todo priority: medium +state_hub_task_id: "30b56b30-e7eb-4873-aa7c-f4a3bf2fcb24" ``` Produce the §13 E2 evidence artifact — adversarial, with a review date: an identity bound to tenant A demonstrably cannot read tenant B. Coordinate with @@ -230,6 +330,7 @@ per §19.3. id: AUDIT-WP-0008-T06 status: todo priority: medium +state_hub_task_id: "ed58ca37-755b-4284-b1ac-2a02baaceb21" ``` Send the five framework review findings to `net-kingdom` (owner of the canon path) and `policy-nexus` (owner of the rendered copy), as a §20.2 review @@ -237,8 +338,13 @@ response rather than as objections. ## Done when -- `docs/tenancy-posture.yaml` declares an accurate vector with targets and gaps. +- `docs/tenancy-posture.yaml` declares an accurate vector with targets, per-path + E breakdown, and dated gaps. - Read path enforces tenant scope, with a test that fails if it regresses. -- `rapp-postgres` has the retention answer and the question 11 answer. +- `rapp-postgres` has the retention answer, the corrected posture guess, and the + question 11 answer. - NetKingdom has the five framework findings. - The §5 worked example for `audit-core` is corrected upstream. +- Two dates are recorded and monitored per §8.1 rather than merely written: + `data.archive` review 2026-12-31, E3 target 2027-03-31. +- The erasure legal question has an owner outside audit-core.