Implement AUDIT-WP-0006 honest operational custody.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Postgres now reports custody_class=operational with a cited 30-day
recoverable window. Join ITC-CAP operations.audit at D4, publish the
interface card, and overlay user-engine tenants [*] from Git so an
ExternalSecret refresh cannot shrink it.
This commit is contained in:
tegwick 2026-08-16 00:24:33 +02:00
parent 0a3d05ff1c
commit ded432a63f
25 changed files with 832 additions and 94 deletions

View file

@ -10,7 +10,8 @@ Deployment until:
1. The image digest is pinned (currently `sha256:41493cd5…` from commit `3a7d63e`).
2. Secrets `audit-core-database`, `audit-core-database-migrate`, and
`audit-core-senders` exist.
`audit-core-senders` exist. ConfigMap `audit-core-senders-scope` is
applied (`deploy/senders-scope.yaml`) before the Deployment mounts it.
3. Job `audit-core-migrate` has completed.
```bash

View file

@ -90,11 +90,11 @@ spec:
value: "0.0.0.0"
- name: AUDIT_CORE_HTTP_PORT
value: "8080"
# Refuses to start on anything but archive-class custody, so a
# missing database URL fails loudly instead of silently downgrading
# to the development store.
# Refuses to start on anything but operational (durable Postgres)
# custody. ``archive`` remains an accepted alias for one mixed
# rollout so an old manifest cannot refuse a new image.
- name: AUDIT_CORE_REQUIRE_CUSTODY_CLASS
value: archive
value: operational
# Runtime role cannot CREATE TABLE. Schema changes are a Job
# with the migration lease (deploy/migrate-job.yaml).
- name: AUDIT_CORE_AUTO_MIGRATE
@ -122,6 +122,11 @@ spec:
secretKeyRef:
name: audit-core-senders
key: senders.json
# Non-secret tenant/source scope. Tokens stay in the Secret;
# ExternalSecret refresh cannot shrink user-engine tenants
# below deploy/senders-scope.json.
- name: AUDIT_CORE_SENDERS_SCOPE_PATH
value: /etc/audit-core/senders-scope.json
resources:
requests:
cpu: 50m
@ -144,6 +149,10 @@ spec:
- name: database-credential
mountPath: /etc/audit-core/db
readOnly: true
- name: senders-scope
mountPath: /etc/audit-core/senders-scope.json
subPath: senders-scope.json
readOnly: true
startupProbe:
httpGet: {path: /healthz, port: http}
periodSeconds: 3
@ -175,3 +184,7 @@ spec:
secretName: audit-core-database
# 0440 + fsGroup 10001: 0400 is root-only and the process cannot read it.
defaultMode: 0440
- name: senders-scope
configMap:
name: audit-core-senders-scope
defaultMode: 0444

View file

@ -0,0 +1,9 @@
[
{
"name": "user-engine",
"sources": ["user-engine"],
"tenants": ["*"],
"may_write": true,
"may_read": false
}
]

23
deploy/senders-scope.yaml Normal file
View file

@ -0,0 +1,23 @@
# Non-secret sender scope (AUDIT-WP-0006-T05). Tokens stay in Secret
# audit-core-senders. This ConfigMap is the authority for tenants/sources
# so an ExternalSecret refresh cannot revert user-engine to a single tenant.
# Keep in lockstep with deploy/senders-scope.json.
---
apiVersion: v1
kind: ConfigMap
metadata:
name: audit-core-senders-scope
namespace: audit-core
labels:
app.kubernetes.io/name: audit-core
data:
senders-scope.json: |
[
{
"name": "user-engine",
"sources": ["user-engine"],
"tenants": ["*"],
"may_write": true,
"may_read": false
}
]