# audit-core tenancy posture # # 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. E2 is backed by a bounded adversarial # target run; its scope and freshness limit are recorded below. schema_version: "0.1" framework: netkingdom-tenancy-posture service: audit-core role: tenant-audit-service tenancy: reviewed: "2026-08-22" review_due: "2026-08-23" 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: 2, P: 1, R: 2, V: 0 } 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 # mechanism. The quoted level is now 2 because the adversarial target artifact # required by §13.2 exists for this revision. paths: E: write: 2 # Sender credential bound to the sources and tenants it may # claim; checked at one choke point (audit_core/ingestion.py). read: 2 # Event reads filtered to permitted tenants; surfaces with no # tenant key require full scope. Cross-tenant fetch returns # 404, not 403, so the surface is not an existence oracle. placement_exceptions: [] # No tenant is on dedicated substrate. All # tenants share database audit_core on # platform-pg. gap: I: >- I1 is accurate and is not currently a target. Senders authenticate with bearer tokens and the tenant arrives in the request body, checked against an allowlist bound to the credential (§4.1 places request-supplied tenant identifiers at I1 however canonical the string). The allowlist is a real control, but it is an A-axis control; it does not make the identity verified. Moving to I2 means senders carrying a verified token with a tenant claim, which is a change to every sender and not audit-core's to make alone. Recorded as accurate, not as ambition. A: >- A2 is accurate. Authorization is a single local boundary — permitted sources, permitted tenants, may_write, may_read — bound once and centrally. No flex-auth delegation. A3 is not a near-term target: flex-auth is itself at A0 on its own self-report (/v1/check authenticates no caller), so delegating to it today would lower this service's assurance, not raise it. E: >- E2 is implemented on both paths by AUDIT-WP-0008-T04 and evidenced by the bounded whitehat-security target run WH-ENG-20260822-AUDIT-E2-03. An ordinary tenant-A identity could not fetch tenant B's event by id, observe tenant B's correlation fixture, or append as tenant B across ten attempted operations. The run ended 2026-08-22T22:10:25Z with no limitations; receipt-bound cleanup removed both temporary identities, both exact KV paths, all projection resources and the runner. This says only that the attempted attacks did not work, not that the boundary always holds. The 24-hour facility baseline makes review or replacement due at 2026-08-23T22:10:25Z, and relevant boundary changes require a pre-promotion run. E_target: >- E3 (row-level security per rapp-postgres ADR-0003) targeted 2027-03-31. Blocked behind the E2 artifact — §4.3 requires E2 evidence alongside any E3 claim — and needs the EXPLAIN comparison first, since RLS disables functional indexes built on non-leakproof functions. E4 is unreachable at P1 by the §3.2 coupling and is not a target. 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 SHA-256 of the cleartext record, which survives key destruction as a confirmation oracle over low-entropy fields. A fleet R4 target must exempt this service explicitly. See docs/erasure-and-audit.md (AUDIT-WP-0008-T03). credentials: >- Stated gap against Decision 9.2 rather than a silent exclusion. Database credentials comply with 9.1: dynamic leases re-read from a mounted Secret at connection time, no restart on rotation. Ingest credentials do not: they are static long-lived bearer tokens, rotated overlap-first by publishing a replacement alongside the incumbent and then dropping the predecessor. audit-core raised this omission during review and is not exempting itself from the rule it asked for. No dated remedy yet; leasing consumer-facing credentials needs a broker path that does not exist. retention_placement: >- audit-core's INTENT wants unbounded WORM archive. That is data.archive in ITC-CAP terms, recorded as an unmet requirement in data/capability/audit-core-operational.json, and it is deliberately not a backup-window question: a 30-day WAL window is not an archive, is not searchable, and restores instance-wide. Reviewed 2026-12-31. If no 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. Dependencies enumerated and the exercise specified in docs/availability-recovery.md (AUDIT-WP-0008-T07); the exercise itself awaits a live window on railiance01. V2 is not reachable at P1 as built: platform-pg runs instances 1, and Decision 4.6.1 makes V the minimum across synchronous providers, so V1 is the ceiling here rather than the next step. 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" - "docs/evidence/AUDIT-WP-0008-T05-whitehat-e2-03-pass-2026-08-22.md" R: available: 2 maximum: 2 conditions: - "R4 is unreachable while the integrity chain commits to cleartext hashes." - >- The 30-day horizon is declared but NOT solely ours to hold. audit-core is at P1, and on shared substrate the erasure horizon is the instance maximum across co-residents (§4.5), not the value this consumer declares. A co-resident on platform-pg declaring a longer window extends what a sender's audit records remain recoverable for, without any change here and without notice to the sender. Decision 4.5.4 names this as why a bare R2 is insufficient for a retention promise; we are not a tier, but we are a provider quoting a number we do not control alone, and a sender relying on 30 days should read it as a floor on our side and a platform-derived maximum in practice. - >- A sender needing a horizon it can actually rely on needs audit-core at P2. That is the §4.5 retention trigger and it is recorded in gap.retention_placement with a 2026-12-31 review. 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" E2: - "docs/evidence/AUDIT-WP-0008-T05-whitehat-e2-03-pass-2026-08-22.md" - "docs/evidence/AUDIT-WP-0008-T05-whitehat-e2-03-final-report.json" P1: "rapp-postgres/docs/evidence/isolation-2026-08-10.md" R2: - "rapp-postgres/consumers/audit-core.yaml" - "tests/test_interface.py"