approval-engine/deploy
tegwick be1a388a84 Enforce declared human controls at approval binding
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
2026-09-10 19:26:12 +02:00
..
approval-engine.yaml Pin published approval-engine release candidate 2026-09-06 23:32:19 +02:00
networkpolicies.yaml Implement approval engine production readiness 2026-09-02 00:52:04 +02:00
README.md Enforce declared human controls at approval binding 2026-09-10 19:26:12 +02:00

Deployment gates

The checked-in StatefulSet is a reviewed release input, not evidence of a live deployment. SQLite is intentionally limited to one replica, ReadWriteOnce storage, and an OnDelete update: never start two writers against a copied database.

Image: pinned, published, not rolled out. Both image: references already carry the immutable release digest sha256:73333f5ceb55e48192e3095cb2e2a741cdc6ff0be2f18128301072b4a6b6eb9d (registry tag 0.1.0, OCI index; linux/amd64 manifest sha256:2064d537…), pinned at b51d174. There is no placeholder left to replace. Both references MUST stay identical and MUST stay digests — a tag here would let the init container and the server run different code against one database.

The pinned image predates schema v5. The published artifact recorded in docs/image-scan-2026-09-06.md carries LATEST_SCHEMA_VERSION = 3; this repository is now at 5 (entries.principal_type plus approvals.human_control, see storage-operations.md). The pinned pair is self-consistent — that image migrates to 3 and serves 3 — so nothing is broken by leaving it pinned, but a rollout that must enforce declared human controls or carry approver principal-type evidence requires cutting a new image at step 3 below. The migrate init container then performs the additive upgrade on the existing volume; tests/test_deploy_manifest.py holds this acknowledgement so the drift cannot go quiet.

Gates 1 and 2 below are the outstanding ones; nothing is deployed today.

Before applying:

  1. Register the exact approval-engine audience, caller scopes, and service clients in KeyCape. Confirm the in-cluster JWKS endpoint and configured issuer match the manifest.
  2. Register approval-engine as an audit-core sender, add matching audit-core ingress, and provision approval-engine-audit through the credential owner. The Secret must contain key audit-token; never commit its value.
  3. For a new release only: make image-release (build, scan, push — the scan gate fails the build on any CRITICAL or HIGH, so a failing scan blocks the push), then pin both image: references to the returned registry digest and run make deploy-dry-run. The current digest is already pinned; skip to 4 unless you are cutting a new image.
  4. Take a verified backup. Roll out by deleting the sole pod, then prove readiness, restart persistence, outbox drain, heartbeat, and restore.
  5. Enable a caller namespace only by applying label railiance.io/approval-engine-client=true; JWT scope checks remain the inner boundary.

The audit-core receiver-side registration, cadence findings, and accepted-count reconciliation are tracked by AUDIT-WP-0009-T04/T06/T09 and cannot be closed from this repository.