185 lines
11 KiB
Markdown
185 lines
11 KiB
Markdown
|
|
# Review service deployment candidate
|
||
|
|
|
||
|
|
`INFD-WP-0001-T08` owns this packet. It prepares the existing
|
||
|
|
`decisions.coulomb.social` origin for the review service. **No image publication,
|
||
|
|
production configuration, credential provisioning or cluster apply is performed
|
||
|
|
by the build, renderer or tests.** The origin is still the Railiance Apps nginx
|
||
|
|
placeholder. Native policy, custody, registration and service admission remain
|
||
|
|
required before cutover.
|
||
|
|
|
||
|
|
## Artifact and local proof
|
||
|
|
|
||
|
|
```sh
|
||
|
|
make image-build IMAGE=informed-decision:local
|
||
|
|
python tools/smoke_container.py --image informed-decision:local --receipt /tmp/infd-container-proof.json
|
||
|
|
```
|
||
|
|
|
||
|
|
`Containerfile` uses the digest-pinned Alpine base already used by Approval
|
||
|
|
Engine, installs the hashed `requirements.lock`, then installs the built wheel
|
||
|
|
without dependency resolution. The image contains no pip or build installer.
|
||
|
|
Its user/group is 10001; runtime deployments must use a read-only root filesystem
|
||
|
|
and drop capabilities. Rebuild the wheel before building the image. Refresh
|
||
|
|
dependency pins deliberately with `uv pip compile pyproject.toml --python-version
|
||
|
|
3.12 --generate-hashes --output-file requirements.lock`, then repeat checks and
|
||
|
|
the image scan. A successful local build is not a published registry digest.
|
||
|
|
|
||
|
|
The dated evidence pins the exact wheel/image/runtime-file hashes and scanner.
|
||
|
|
The local image passed 11 container checks and a HIGH/CRITICAL Trivy scan with
|
||
|
|
zero findings. The scanner also emitted an Alpine lifecycle-list warning; the
|
||
|
|
receipt retains it. This scan is evidence about the selected severities and
|
||
|
|
database at that time, not a general security attestation.
|
||
|
|
|
||
|
|
The smoke harness creates and removes its own labeled containers/volumes. It
|
||
|
|
uses `--network none`, publishes no port and mounts only synthetic configuration
|
||
|
|
and credentials. It tests the installed entrypoint, private file ownership,
|
||
|
|
anonymous refusal, unhealthy audit readiness, exclusion of a second writer,
|
||
|
|
consistent backup, restart, and restore onto another volume. A synthetic
|
||
|
|
unresolved submission and its undelivered evidence survive unchanged. No native
|
||
|
|
issuer, PDP, Approval Engine or Audit Core is contacted. Actual component/browser
|
||
|
|
contracts have separate receipts from the previous integration milestone.
|
||
|
|
|
||
|
|
The lifecycle check caught the initial PID-1 process requiring Docker's forced
|
||
|
|
kill (exit 137). The container now handles SIGTERM through Waitress shutdown and
|
||
|
|
the audit pump's stop path; the final image exits cleanly within the tested
|
||
|
|
15-second window. Kubernetes allows 45 seconds. Forced termination still cannot
|
||
|
|
justify retrying an unresolved approval entry.
|
||
|
|
|
||
|
|
## Private storage and projected configuration
|
||
|
|
|
||
|
|
The deployment uses a single replica with **Recreate**, a ReadWriteOnce PVC and
|
||
|
|
an OS lock held for the life of the serving process. Do not add replicas, a
|
||
|
|
rolling surge or another writer: sessions are process-local and this store is a
|
||
|
|
local-filesystem design. The lock rejects accidental concurrent service starts;
|
||
|
|
it is not an admission of network-filesystem locking semantics.
|
||
|
|
|
||
|
|
Kubernetes projected ConfigMaps are root-owned and symlinked. The container
|
||
|
|
entrypoint reads at most 16 KiB from `/configuration/runtime.json`, requires
|
||
|
|
`evidence_db=/data/private/review.sqlite`, and copies the configuration into
|
||
|
|
owned ephemeral storage at `/run/informed-decision/private/runtime.json` (0600).
|
||
|
|
It creates `/data/private` as the process user with mode 0700 and refuses unsafe
|
||
|
|
existing ownership/modes. It does not silently chmod or take over existing data.
|
||
|
|
The evidence database remains 0600. Credential callbacks read rotating projected
|
||
|
|
files directly; bearer tokens are never copied into SQLite or the config snapshot.
|
||
|
|
|
||
|
|
The pod uses fsGroup 10001 with `OnRootMismatch`. Its storage driver must make
|
||
|
|
the volume root writable by that group while preserving the private child's
|
||
|
|
ownership/modes on later mounts. A restored volume whose root triggers a
|
||
|
|
recursive permission rewrite needs owner repair before admission, not relaxed
|
||
|
|
store checks. The local-path class exists on Railiance; its backup/retention and
|
||
|
|
restore admission are still the platform owner's work.
|
||
|
|
|
||
|
|
The normal workstation entrypoint continues listening on 127.0.0.1. The container
|
||
|
|
entrypoint explicitly selects 0.0.0.0; the public issuer/callback remain fixed,
|
||
|
|
and Host/forwarded headers cannot alter them.
|
||
|
|
|
||
|
|
## Render the review packet
|
||
|
|
|
||
|
|
Copy `admission.example.json` outside Git and replace its intentionally invalid
|
||
|
|
placeholders with the published image manifest digest, admitted PDP endpoint and
|
||
|
|
package/version/digest, exact PDP pod label, observed/admitted issuer IPs and
|
||
|
|
storage class. The renderer accepts no inline token or broad egress CIDR.
|
||
|
|
|
||
|
|
```sh
|
||
|
|
python tools/render_deployment.py --input /operator/review-inputs.json --output /tmp/infd-candidate.json
|
||
|
|
kubectl --kubeconfig /operator/railiance-kubeconfig apply --dry-run=server -f /tmp/infd-candidate.json
|
||
|
|
```
|
||
|
|
|
||
|
|
Rendering and server dry-run do not admit the values supplied. The renderer
|
||
|
|
produces eight review objects: service account, PVC, immutable configuration,
|
||
|
|
Recreate Deployment, Service, the review pod's NetworkPolicy, and two exact
|
||
|
|
counterparty ingress proposals. It creates no Secret, RBAC grant, Namespace,
|
||
|
|
Ingress or policy assignment. The service retains the existing name/port and
|
||
|
|
selects the review component. The Deployment keeps the existing immutable
|
||
|
|
selector so the same origin can be replaced under a reviewed cutover.
|
||
|
|
|
||
|
|
The incoming Traefik peer is namespace AND pod label. Egress permits DNS,
|
||
|
|
exact Approval/Audit/PDP peers, exact public KeyCape addresses on 443, and the
|
||
|
|
observed Traefik websecure peer on 8443 for enforcement after Service DNAT.
|
||
|
|
Railiance metadata on 2026-09-11 confirmed the Traefik label/8443 port and
|
||
|
|
`kc.coulomb.social -> 92.205.62.239`. These observations do not prove the CNI
|
||
|
|
path; test the actual issuer token/JWKS reachability after admission. The shared
|
||
|
|
HTTPS router is an L3/L4 boundary, not a hostname authorization policy; the
|
||
|
|
client itself pins the issuer and refuses redirects.
|
||
|
|
|
||
|
|
Approval Engine's existing caller policy permits Secrets Engine or namespaces
|
||
|
|
carrying its client label. That does not admit this review pod. The packet adds
|
||
|
|
an **owner-reviewed proposal** ANDing namespace, app label and component rather
|
||
|
|
than labelling the whole namespace as an approved caller. Flex Auth receives a
|
||
|
|
similarly exact peer proposal. Audit Core already owns its prepared Informed
|
||
|
|
Decision ingress under `AUDIT-WP-0009-T11`; do not create a competing copy.
|
||
|
|
|
||
|
|
Seven of eight objects passed server dry-run in their actual namespaces. The
|
||
|
|
Approval Engine peer failed because namespace `approval-engine` is absent.
|
||
|
|
The identical policy shape passed with only its metadata namespace/name mapped
|
||
|
|
to an existing namespace for schema validation. That does not close the missing
|
||
|
|
owner deployment. Schema checks used fixture policy pins and an unpublished image
|
||
|
|
reference; their rendered output is not an admitted production configuration.
|
||
|
|
|
||
|
|
## Serving health and acceptance readiness
|
||
|
|
|
||
|
|
Startup, Kubernetes readiness and liveness use `/healthz`. An audit dependency
|
||
|
|
outage must not restart the writer or remove the service's refusal/recovery
|
||
|
|
pages from the origin. `/readyz` separately reports recent audit delivery health,
|
||
|
|
and the application refuses accept while it is unhealthy. Monitor `/readyz`
|
||
|
|
and pending/blocked outbox counts explicitly; Kubernetes `Ready=True` alone is
|
||
|
|
not deployment acceptance or native policy proof. A healthy process can still
|
||
|
|
refuse every action when policy/custody has not been admitted.
|
||
|
|
|
||
|
|
## Native admission and cutover
|
||
|
|
|
||
|
|
Use the existing owners and records:
|
||
|
|
|
||
|
|
1. Flex Auth/Informed Decision admit the exact request package, assignments and
|
||
|
|
caller `informed-decision=system:serviceaccount:informed-decision:review`.
|
||
|
|
The projected token's audience is `flex-auth`; no browser token substitutes.
|
||
|
|
The service account spelling is a candidate until this return is recorded.
|
||
|
|
2. Platform/Audit Core complete `AUDIT-WP-0009-T11`: the candidate expects
|
||
|
|
`informed-decision-audit` with key `token`, projected read-only. Its sender is
|
||
|
|
exact `informed-decision`, tenant `tenant:platform`, write-only, load-bearing,
|
||
|
|
redact. Run `warden route find/show` before any credential request. Rendering
|
||
|
|
the Secret reference provisions nothing and confirms no custody name.
|
||
|
|
3. Approval Engine admits its namespace/service and the exact review caller;
|
||
|
|
KeyCape rolls out the already published browser registration. Railiance Apps
|
||
|
|
admits image publication/cutover against its existing origin. Complete
|
||
|
|
private-state backup/restore, monitoring and rollback ownership.
|
||
|
|
4. Capture the current Deployment and Service configuration, close new review
|
||
|
|
traffic for cutover, and retain any existing source-held evidence. Only then
|
||
|
|
apply the admitted packet. Its Recreate rollout intentionally incurs downtime.
|
||
|
|
5. Check both process health and application readiness, policy caller/subject
|
||
|
|
allow/refusal cases, genuine human login and a declared human-control approval.
|
||
|
|
Retrieve the independent audit receipt and the original presentation. Recheck
|
||
|
|
the callback's TLS/redirect/cookie behavior and source custody after restart.
|
||
|
|
|
||
|
|
The image publication, workload/policy/custody admission and rollout remain
|
||
|
|
distinct. No signed decision attribution, native approval or factory admission
|
||
|
|
is inferred from this packet. `FLEX-WP-0024` and the commitment-only custody
|
||
|
|
limitations remain as recorded in the consumer contract.
|
||
|
|
|
||
|
|
## Backup, restore and rollback
|
||
|
|
|
||
|
|
The installed `informed-decision-admin` supports local custody operations only:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
informed-decision-admin inspect --db /data/private/review.sqlite
|
||
|
|
informed-decision-admin backup --db /data/private/review.sqlite --output /backup/private/review.sqlite
|
||
|
|
```
|
||
|
|
|
||
|
|
The backup parent must already be owned/private 0700; the destination must not
|
||
|
|
exist. Backup uses SQLite's consistent backup API while the service may be live.
|
||
|
|
Do not copy a running SQLite file while ignoring WAL. Inspection prints counts
|
||
|
|
and schema version, not memo content or credentials. These are privileged
|
||
|
|
operator custody commands, not browser endpoints. An independent backup copy,
|
||
|
|
retention, access controls and an actual platform restoration drill remain owed.
|
||
|
|
|
||
|
|
For restore, stop the serving writer, restore to a separately prepared private
|
||
|
|
volume, verify the immutable content and unresolved submission state, then
|
||
|
|
mount that volume as `/data`. A restart requires a new human login. Never reset
|
||
|
|
an unresolved intent or POST again to manufacture a presentation correlation.
|
||
|
|
Do not roll schema v2 back to an older reader against the same live database.
|
||
|
|
|
||
|
|
For a failed initial cutover, stop the new writer, retain the PVC and restore
|
||
|
|
Railiance Apps' **Deployment and Service** from the prior origin manifest.
|
||
|
|
Restoring only the Deployment is insufficient: the candidate Service selector
|
||
|
|
also names the review component. Returning to the placeholder is a service
|
||
|
|
rollback, not deletion or reconciliation of any approval/evidence already held.
|