diff --git a/SCOPE.md b/SCOPE.md index 1054634..f35015e 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -38,7 +38,7 @@ audit-core exists to provide the capability described in INTENT.md. (`docs/evidence/chain-head-20260816.json`). - ITC-CAP case: `data/capability/audit-core-operational.json` at D4. `data.archive` is an unmet requirement. -- Tenancy posture: `tenancy.yaml` (NetKingdom Tenancy Posture v0.1 draft-7, +- Tenancy posture: `tenancy.yaml` (NetKingdom Tenancy Posture v0.1 draft-8, Decision 5.4). Declared `I1 A2 E1 P1 R1`, target `E3` and `R2`. E is quoted at 1 although the E2 mechanism is in place on both paths — the §13 E2 artifact is adversarial and does not exist yet (AUDIT-WP-0008-T05). diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index d4fa265..c2e97f0 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -47,7 +47,7 @@ | task | AUDIT-WP-0007-T03 | done | — | workplans/AUDIT-WP-0007-integrity-verification.md | | task | AUDIT-WP-0007-T04 | done | — | workplans/AUDIT-WP-0007-integrity-verification.md | | task | AUDIT-WP-0007-T05 | done | — | workplans/AUDIT-WP-0007-integrity-verification.md | -| task | AUDIT-WP-0008-T01 | todo | — | workplans/AUDIT-WP-0008-tenancy-posture-alignment.md | +| task | AUDIT-WP-0008-T01 | done | — | workplans/AUDIT-WP-0008-tenancy-posture-alignment.md | | task | AUDIT-WP-0008-T02 | done | — | workplans/AUDIT-WP-0008-tenancy-posture-alignment.md | | task | AUDIT-WP-0008-T03 | todo | — | workplans/AUDIT-WP-0008-tenancy-posture-alignment.md | | task | AUDIT-WP-0008-T04 | done | — | workplans/AUDIT-WP-0008-tenancy-posture-alignment.md | diff --git a/docs/interface-card.yaml b/docs/interface-card.yaml index d6b0348..71c8c85 100644 --- a/docs/interface-card.yaml +++ b/docs/interface-card.yaml @@ -92,7 +92,7 @@ consumer_needs: feedback: [] tenancy_posture: declared_at: tenancy.yaml - framework: net-kingdom/canon/standards/tenancy-posture_v0.1.md (draft-7) + framework: net-kingdom/canon/standards/tenancy-posture_v0.1.md (draft-8) current: I1 A2 E1 P1 R1 target: I1 A2 E3 P1 R2 provides_reachable: diff --git a/tenancy.yaml b/tenancy.yaml index 5a90a14..a1c36f8 100644 --- a/tenancy.yaml +++ b/tenancy.yaml @@ -1,22 +1,29 @@ # audit-core tenancy posture # -# Declared per NetKingdom Tenancy Posture v0.1 (draft-7), +# Declared per NetKingdom Tenancy Posture v0.1 (draft-8), # net-kingdom/canon/standards/tenancy-posture_v0.1.md. # Location and schema per Decision 5.4; per-path detail per Decision 5.2; # provider block per Decision 5.5. # # Conformance is accuracy, not altitude (§6). Nothing here is claimed above -# what this repo can evidence today, and two rungs are deliberately declared -# lower than the mechanism in place — see `gap.E` and `gap.R`. +# what this repo can evidence today. E is deliberately declared lower than the +# mechanism in place because the adversarial artifact is still absent. + +schema_version: "0.1" +framework: netkingdom-tenancy-posture +service: audit-core +role: tenant-audit-service tenancy: reviewed: "2026-08-17" + review_due: "2027-02-17" service_class: batch # §8.3.2. Co-resident with latency-critical # tenant-engine on platform-pg; the mixture is # reported by the platform, not hidden. - current: { I: 1, A: 2, E: 1, P: 1, R: 1 } - target: { I: 1, A: 2, E: 3, P: 1, R: 2 } + current: { I: 1, A: 2, E: 1, P: 1, R: 2, V: 0 } + implemented: { E: 2 } + target: { I: 1, A: 2, E: 3, P: 1, R: 2, V: 1 } # §5.2 — declare per path, quote the minimum. The quoted E above is the # minimum across paths. As of AUDIT-WP-0008-T04 both paths carry the same @@ -70,14 +77,6 @@ tenancy: functional indexes built on non-leakproof functions. E4 is unreachable at P1 by the §3.2 coupling and is not a target. - R: >- - R1 today: the platform default 30-day window applies and audit-core has - declared nothing above it. R2 is requested and in flight — audit-core has - asked rapp-postgres to add `backupRetentionDays: 30` to - consumers/audit-core.yaml, making the window declared rather than - inherited. That file is rapp-postgres's, so R2 is not audit-core's to - declare unilaterally. The erasure horizon is published on /readyz as - recoverable_days. R_ceiling: >- R4 is unreachable under the current design and is not a target. Per Decision 4.5.3 — which this repo found — the hash chain commits to a @@ -104,30 +103,46 @@ tenancy: data.archive provision is procured by then, audit-core reopens placement under the §4.5 retention trigger, with P2 as the fallback — a worse answer than archive, named now so it is not improvised later. + V: >- + No exercise establishes restart recovery for the complete audit path. + V1 is the target; replica count or Kubernetes restart policy is not + treated as evidence. - # §5.5 — audit-core provisions operations.audit to senders, so it declares - # what it makes reachable for a consumer's audit trail, not only where it - # sits. A sender's tenant separation inside the trail is audit-core's to - # enforce; the sender cannot reach a level this service does not offer. - provides: - capability: operations.audit - profile: administrative - reachable: - E2: >- - Reachable now. A sender credential is bound to the tenants it may write - for and, if it may read, to the tenants it may read back. - E3: >- - Not reachable yet. Requires rapp-postgres's ADR-0003 GUC contract - applied to the audit_core schema. Targeted 2027-03-31. - E4: >- - Unreachable. One database, one runtime credential, no per-tenant - credential and no per-tenant substrate. A sender needing a structural - guarantee that another tenant cannot reach its audit records cannot get - it here, and should be told so rather than sold E2 in E4's language - (§11.4). - R2: >- - Reachable once the declared window lands; the horizon is already - published on /readyz. - R4: >- - Unreachable by design, per R_ceiling above. A consumer with a verified - erasure obligation over its audit trail cannot discharge it here. +provider: + capability: operations.audit + profile: administrative + axes: + E: + available: 2 + maximum: 3 + conditions: + - "E3 requires rapp-postgres ADR-0003 applied to audit_core and its EXPLAIN probe." + - "E4 is unreachable at P1 with one runtime credential." + evidence: + - "audit_core/ingestion.py" + - "tests/test_ingestion.py" + R: + available: 2 + maximum: 2 + conditions: + - "R4 is unreachable while the integrity chain commits to cleartext hashes." + evidence: + - "rapp-postgres/consumers/audit-core.yaml" + - "audit_core/interface.py" + V: + available: 0 + maximum: 1 + conditions: + - "Exercise restart recovery across audit-core, platform-pg and OpenBao." + +evidence: + A2: + - "audit_core/ingestion.py" + - "tests/test_ingestion.py" + E1: + - "audit_core/postgres_backend.py" + - "tests/test_backend_conformance.py" + P1: "rapp-postgres/docs/evidence/isolation-2026-08-10.md" + R2: + - "rapp-postgres/consumers/audit-core.yaml" + - "tests/test_interface.py" diff --git a/workplans/AUDIT-WP-0008-tenancy-posture-alignment.md b/workplans/AUDIT-WP-0008-tenancy-posture-alignment.md index adb7878..333ca51 100644 --- a/workplans/AUDIT-WP-0008-tenancy-posture-alignment.md +++ b/workplans/AUDIT-WP-0008-tenancy-posture-alignment.md @@ -22,8 +22,18 @@ Answer the `rapp-postgres` review request against NetKingdom Tenancy Posture v0.1, declare an accurate posture vector, and close the one gap the assessment exposed in running code. -Canonical framework: `net-kingdom/canon/standards/tenancy-posture_v0.1.md` -(rendered copy in `policy-nexus/build/tenancy-posture.html`). +Canonical framework: `net-kingdom/canon/standards/tenancy-posture_v0.1.md`, +published by `policy-nexus` at `policy.coulomb.social`. + +> **Framework moved twice under this workplan.** The assessment below was made +> against **draft-5**; the framework is now at **draft-8**. The assessment still +> holds — no finding was reversed — but three things changed underneath it: +> `R` moved 1 → 2 once `rapp-postgres` declared the window, a sixth axis **V +> (availability)** appeared that we had no position on, and the +> implemented-versus-evidenced distinction we used informally for `E` became a +> schema field. `tenancy.yaml` tracks draft-8 and validates clean against +> `net-kingdom/tools/tenancy-posture/validate.py`. T07 and T08 exist because of +> draft-8, not because of the original review. ## Assessment — what the ladders say about us @@ -331,20 +341,24 @@ 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. +Answer question 11 in `docs/erasure-and-audit.md`. -**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. +**Reduced by draft-8.** The framework half is already resolved upstream: the +confirmation-oracle finding landed as **Decision 4.5.3**, and §19.11 is now +marked *framework resolved*, recording that audit-core targets R2 and is +explicitly not a fleet R4 target. There is no amendment left to send. What +remains is audit-core's own position document — the fact/payload split, the +encrypt-then-hash constraint, why the retained cleartext hash defeats key +destruction, and the R2 ceiling — so the reasoning lives in this repo rather +than only in someone else's canon. + +**Route the legal question to `risk-nexus`, not `the-custodian`.** §19.11 ends +"the legal basis for retaining audit facts remains a risk/legal question outside +this framework", and `policy-nexus` INTENT records that judging what an external +rule demands of the estate moved to `risk-nexus` on 2026-08-17. That is the +owner. The position is publishable with the dependency open; it is not +publishable with the dependency hidden. -```task id: AUDIT-WP-0008-T04 status: done priority: high @@ -375,9 +389,65 @@ 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 -response rather than as objections. +Close the review loop with `net-kingdom`. + +**Mostly overtaken.** All five findings were adopted across draft-7 and draft-8, +and the two stale status lines this task was going to flag are already fixed. +Nothing is outstanding as a *finding*. + +What remains is confirmation rather than argument: acknowledge the adoptions, +confirm the §5 worked example `I1 A2 E1 P1 R2 V0` is accurate as written, and +state that `tenancy.yaml` validates clean against +`tools/tenancy-posture/validate.py`. Flag one thing forward — the E line becomes +wrong the day T05 lands, and under Decision 6.1 an *upgrade* has no notification +duty, so the correction is ours to send unprompted or it will simply sit stale. + +```task +id: AUDIT-WP-0008-T07 +status: todo +priority: high +``` +Reach **V1** on the availability axis, new in draft-8. + +We declared `V0` because nothing exercises restart recovery for the complete +audit path. §13's V1 row asks for three things and they are all mechanical: +critical dependencies enumerated, restart/recreate recovery exercised, and the +interruption plus measured recovery time recorded. Decision 4.6.1 makes V +end-to-end, so the enumeration must include `platform-pg` and the credential +path — a receiver that restarts in 5s over a database that takes 90s is not V1 +at 5s. + +The 2026-08-16 reboot walk in the operator runbook is close but is not this: it +observed ~40s of unreadiness after a node reboot, without enumerating the +dependency set or recording a measured recovery time as an exercise. Decision +4.6.1 is explicit that a replica count or a status page is not evidence. + +Write the exercise procedure and the dependency enumeration here; the exercise +itself runs against railiance01 and needs a live window. + +```task +id: AUDIT-WP-0008-T08 +status: todo +priority: high +``` +Disclose the shared-substrate retention coupling in our **provider** block, and +notify `user-engine` under Decision 6.1. + +Two defects in our own declaration, both found by reading draft-8 back against +`tenancy.yaml`: + +- **`provider.R.available: 2` is misleading as written.** It tells a sender it + gets a 30-day horizon. At P1 the horizon is the *instance maximum* across + co-residents (§4.5), so a co-resident declaring longer silently extends what + we hand our senders. Decision 4.5.4 makes exactly this the reason a bare `R2` + minimum is insufficient. We are not a tier, but we are a provider quoting a + number we do not solely control, and the provider block must say so. +- **Our consumers were never told.** Decision 6.1: updating `tenancy.yaml` + without notifying dependants is declaration drift, not a completed change. + `user-engine` is the only sender and has not been told that `E4` and `R4` are + unreachable here, nor that current `E` is 1. This is not a downgrade — it is a + first accurate declaration replacing someone else's generous guess — but the + notification duty is the same and the guess was public. ## Done when @@ -390,4 +460,9 @@ response rather than as objections. - 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. +- The erasure legal question has an owner outside audit-core (`risk-nexus`). +- `tenancy.yaml` validates clean against the normative validator, and keeps + doing so as the framework revises. +- V1 is evidenced by an exercise, not asserted from a restart policy (T07). +- The provider block discloses what it does not solely control, and every + consumer of `operations.audit` has been told what it declares (T08).