Accept now extends a single-schema chain. Verify walks it; a rewritten
payload_hash is a break. Tamper evidence is that detector plus an
external chain-head attestation, not WORM.
Live receiver now reports custody_class=operational with a cited
30-day recoverable window (sha256:05fe1c06). Next workplan is
integrity verification so tamper_evidence can become honest.
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.
Logical dump of live audit_core restored locally in 4.1s; counts and
sample triples match the rapp-postgres drill. Retention is platform
backup, not an audit-core deletion window. Workplan finished.
SET ROLE during migrate so tables are not owned by a disposable lease.
Current image sha256:aec5575. T02/T03 recorded done after live rotation
and rollout undo. T05 remote run is partial (port-forward dropped).
Database leases come from ClusterSecretStore openbao-audit-core-database
(one extract, one lease). Senders are minted in-cluster. Secret mounts
use fsGroup 10001. Schema 0001-0004 applied after dropping the isolation
stub events table. /readyz reports custody_class=archive.
VaultDynamicSecret pulls database/creds/* so a rotating lease is not frozen
into KV. Runtime sets AUDIT_CORE_AUTO_MIGRATE=0; schema is a Job with the
migration lease. Image base is digest-pinned. Namespace and NetworkPolicies
are on the cluster; Deployment waits for the attended OpenBao ESO token.
AUDIT-WP-0005-T02 (progress). rapp-postgres has landed platform-pg with the
audit_core database, roles, and dynamic credential provisioning, so
audit-core's side is now built against it.
In-cluster delivery is a mounted directory rather than environment variables.
A dynamic lease rotates while the pod runs and an env var is fixed at process
start, so env delivery would force a restart on every rotation - and every
restart is a delivery gap, which is what this task forbids.
CredentialDirectory is re-read on every connection attempt via psycopg_pool's
callable kwargs, so a rotated lease takes effect with no restart. Rotation is
logged by password fingerprint, never by value.
deploy/externalsecrets.yaml follows the ClusterSecretStore -> ExternalSecret ->
Secret pattern already used by activity-core and rapp-qonto, at a 15m refresh
rather than the default 1h since the interval bounds how long a revoked lease
can stay mounted. All manifests validated --dry-run=server --validate=strict.
The rotation test was initially vacuous: it passed against a deliberately naive
implementation that read credentials once at startup, because pooled sessions
stay authenticated after a password change and nothing forced a reconnect. It
now terminates the role's sessions first, and is verified to fail against the
naive implementation and pass against the real one. Tests 82 -> 84.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AUDIT-WP-0005-T03 (progress). Manifests validated --dry-run=server
--validate=strict against railiance01; not applied, since deployment is gated
on RAPP-POSTGRES-WP-0002 and T02 credentials. Nothing here mutates the cluster.
Conventions read off the deployed user-engine workload rather than invented:
digest-pinned image from forgejo.coulomb.social, runAsNonRoot with
RuntimeDefault seccomp, no privilege escalation, all capabilities dropped,
readOnlyRootFilesystem, probes on a named http port, same resource envelope.
The namespace carries railiance.io/postgres-client: platform-pg, which is what
platform-pg-consumer-ingress in rapp-postgres admits; without that label the
pod cannot reach the database at all.
NetworkPolicies default-deny both directions, then permit ingress from the
user-engine namespace only, a separately labelled operator read path, and
egress to PostgreSQL in databases plus DNS.
Three decisions worth naming. Liveness is /healthz while readiness is /readyz,
so a database outage drops the pod from the Service rather than restarting it
in a loop. readOnlyRootFilesystem enforces the empty-filesystem property rather
than trusting it, so the SQLite fallback physically cannot accumulate audit
records on ephemeral storage. AUDIT_CORE_REQUIRE_CUSTODY_CLASS=archive makes a
missing database URL a startup failure instead of a silent downgrade to the
development store.
Counters deferred from WP-0004-T06 are exposed as JSON at /v1/stats behind the
read privilege, not as Prometheus exposition format: the cluster runs no
Prometheus, no ServiceMonitor CRD and no other scrape target, so an exposition
endpoint would target a scrape path that does not exist. Usable with curl now
and a small step from /metrics later.
Tests 77 -> 80.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>