Deploy approval service and verify native persistence and audit

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a09cbb-87c6-7900-a145-4ce53ba9f1a6
This commit is contained in:
tegwick 2026-09-14 01:08:50 +02:00
parent a0a602976e
commit d710bf0088
6 changed files with 151 additions and 8 deletions

View file

@ -23,7 +23,7 @@ image-release: image-build image-scan ## Build, scan, then push. Push only runs
@docker inspect --format '{{index .RepoDigests 0}}' $(IMAGE):$(VERSION) @docker inspect --format '{{index .RepoDigests 0}}' $(IMAGE):$(VERSION)
deploy-dry-run: ## Validate Kubernetes manifests without applying them deploy-dry-run: ## Validate Kubernetes manifests without applying them
kubectl apply --dry-run=client -f deploy/approval-engine.yaml -f deploy/networkpolicies.yaml kubectl apply --dry-run=client -f deploy/approval-engine.yaml -f deploy/networkpolicies.yaml -f deploy/keycape-ingress.yaml
help: ## Show this help help: ## Show this help
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} \ @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} \

View file

@ -1,11 +1,11 @@
# Deployment gates # Deployment gates
The checked-in StatefulSet is a reviewed release input, not evidence of a live The StatefulSet is deployed as of 2026-09-14. The metadata receipt is
deployment. SQLite is intentionally limited to one replica, `ReadWriteOnce` `docs/evidence/2026-09-14-approval-engine-deployment.json`. SQLite is intentionally limited to one replica, `ReadWriteOnce`
storage, and an `OnDelete` update: never start two writers against a copied storage, and an `OnDelete` update: never start two writers against a copied
database. database.
**Image: pinned, published, not rolled out.** Both `image:` references carry **Image: pinned, published and deployed.** Both `image:` references carry
`sha256:251941a5cb2724b57cc32cff6b693b1ab0be695bee4f56f02d51961189c0fa49` `sha256:251941a5cb2724b57cc32cff6b693b1ab0be695bee4f56f02d51961189c0fa49`
(registry tag `0.1.0-hfact-be1a388`, source `be1a388`). Both references MUST stay (registry tag `0.1.0-hfact-be1a388`, source `be1a388`). Both references MUST stay
identical immutable digests: migration and server share one database. identical immutable digests: migration and server share one database.
@ -20,9 +20,29 @@ This is artifact evidence; native identity, audit delivery and production restor
remain deployment gates. Never roll the old v3 image over a v5 database; use the remain deployment gates. Never roll the old v3 image over a v5 database; use the
matching verified pre-migration backup for rollback. matching verified pre-migration backup for rollback.
Gates 1 and 2 below are the outstanding ones; nothing is deployed today. The existing KeyCape consumer registration and CCR-2026-0021 audit projection
were already verified by their owners. Live inspection confirmed the projection
Ready, exact key name, receiver c82e0442 and existing producer ingress; custody
was reused without reading or rotating its value. Added the exact namespace AND
pod-label ingress to KeyCape for JWKS (`deploy/keycape-ingress.yaml`).
Before applying: The pod requests 25m CPU/64Mi with unchanged 500m/256Mi limits: the node had
3975m of its 4000m CPU reserved, so the prior 50m request could not schedule.
Initial native observation was 1m CPU/20Mi. No other workload was resized.
Kubernetes token automount is disabled; the service uses no Kubernetes API.
The live service passed schema-v5 integrity, current JWKS reachability, anonymous
and invalid-bearer 401, first heartbeat delivery with zero pending outbox,
pod replacement with unchanged persisted outbox, verified online backup and an
isolated restore opened by the actual engine. An independent 0600 backup copy
was inspected locally. No live database was overwritten. The initial backup
copies are test evidence, not a long-term backup schedule or disaster-recovery
acceptance. Capacity and independent retention remain operational follow-up.
The real PEP adoption is still T05: CCR-2026-0019's operator group/reader,
requesting identity and real human approval are not supplied by deployment.
For a future reinstall or release:
1. Register the exact `approval-engine` audience, caller scopes, and service 1. Register the exact `approval-engine` audience, caller scopes, and service
clients in KeyCape. Confirm the in-cluster JWKS endpoint and configured clients in KeyCape. Confirm the in-cluster JWKS endpoint and configured

View file

@ -35,6 +35,7 @@ spec:
labels: labels:
app.kubernetes.io/name: approval-engine app.kubernetes.io/name: approval-engine
spec: spec:
automountServiceAccountToken: false
securityContext: securityContext:
runAsNonRoot: true runAsNonRoot: true
runAsUser: 10001 runAsUser: 10001
@ -78,7 +79,7 @@ spec:
ports: ports:
- {name: http, containerPort: 8080} - {name: http, containerPort: 8080}
resources: resources:
requests: {cpu: 50m, memory: 64Mi} requests: {cpu: 25m, memory: 64Mi}
limits: {cpu: 500m, memory: 256Mi} limits: {cpu: 500m, memory: 256Mi}
securityContext: securityContext:
allowPrivilegeEscalation: false allowPrivilegeEscalation: false

View file

@ -0,0 +1,21 @@
# APPROVAL-WP-0002-T03 / SECRETS-WP-0010-T03: exact verifier JWKS peer.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-approval-engine-to-keycape
namespace: sso
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: keycape
policyTypes: [Ingress]
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: approval-engine
podSelector:
matchLabels:
app.kubernetes.io/name: approval-engine
ports:
- {protocol: TCP, port: 8080}

View file

@ -0,0 +1,84 @@
{
"storage": {
"schema_version": 5,
"expected_schema_version": 5,
"schema_current": true,
"persistent": true,
"required_tables": {
"approvals": true,
"entries": true,
"outbox": true
},
"integrity": [
"ok"
],
"foreign_key_violations": 0,
"ok": true
},
"outbox": {
"total": 1,
"pending": 0,
"max_age_seconds": 0,
"attempts": 1,
"failed_pending": 0,
"counts": {
"issuance": 0,
"use": 0,
"supersession": 0,
"revocation": 0,
"heartbeat": 1
}
},
"restored": {
"schema_version": 5,
"expected_schema_version": 5,
"schema_current": true,
"persistent": true,
"required_tables": {
"approvals": true,
"entries": true,
"outbox": true
},
"integrity": [
"ok"
],
"foreign_key_violations": 0,
"ok": true
},
"readiness": 200,
"anonymous_refusal": 401,
"invalid_bearer_refusal": 401,
"jwks_reachable": true,
"timestamp": "2026-09-13T23:06:37.566144+00:00",
"status": "deployed_and_restart_restore_verified",
"cluster_uid": "a553c742-0115-43d4-99a4-a5ca56fe0786",
"pod_uid_before_restart": "95215659-3b17-4ccf-ab8c-98418439cdbe",
"pod_uid_after_restart": "7b8c05d9-f1d5-403d-abb3-2250bff9db5e",
"image": "forgejo.coulomb.social/coulomb/approval-engine@sha256:251941a5cb2724b57cc32cff6b693b1ab0be695bee4f56f02d51961189c0fa49",
"resources": {
"limits": {
"cpu": "500m",
"memory": "256Mi"
},
"requests": {
"cpu": "25m",
"memory": "64Mi"
}
},
"caller_token_automount": false,
"tests": {
"repository": 156,
"changed_manifest": 6
},
"backup": {
"persistent_path": "/data/t03-initial-backup-20260914.sqlite",
"independent_local_copy": "/tmp/t03-approval-backup/restored.sqlite",
"restore_exercise": "isolated copy opened with actual engine; live database was not overwritten",
"retention": "initial test backups only; long-term backup schedule/retention not established"
},
"limitations": [
"No real approval created, human entry supplied, or credential consumed.",
"Consumer client-side reader and requesting identity remain unadmitted.",
"Heartbeat acknowledgment/outbox persistence proved; independent audit archive retrieval remains Audit Core owner work."
]
}

View file

@ -342,11 +342,28 @@ retry-attempt state, and migration/backup/atomicity tests are in place.
```task ```task
id: APPROVAL-WP-0002-T03 id: APPROVAL-WP-0002-T03
status: wait status: done
priority: high priority: high
state_hub_task_id: "f0aa2e6d-19e6-5b43-886c-efa4e3de5f22" state_hub_task_id: "f0aa2e6d-19e6-5b43-886c-efa4e3de5f22"
``` ```
**Completed 2026-09-14 during SECRETS-WP-0010-T03.** Existing service registration
and audit custody had already passed owner acceptance; their stale absence was
not carried forward. Deployed the pinned schema-v5 image with the existing ESO
projection. Added exact KeyCape JWKS ingress, disabled unused SA token mounting,
and set a 25m CPU request to fit the node's measured remaining reservation
(500m limit unchanged; observed 1m CPU/20Mi). All 156 tests and six changed-manifest
checks pass. Native ready/schema/integrity/JWKS, anonymous/invalid-bearer refusal,
heartbeat acknowledgment and zero-pending outbox pass. Replaced the pod and
verified persistence; made an online 0600 backup, independently copied/checked
it and opened an isolated restore with the actual engine. Live DB not overwritten.
Receipt: `docs/evidence/2026-09-14-approval-engine-deployment.json`. T05 retains
the real PEP claim/check/consume and human/caller admission. Long-term backup
retention and independent receiver archive evidence remain operational/owner
follow-up, not implied by this initial restore exercise.
Add the governed image/deployment surface, health and readiness behavior, Add the governed image/deployment surface, health and readiness behavior,
resource bounds, and fail-closed caller configuration. A local WSGI development resource bounds, and fail-closed caller configuration. A local WSGI development
server is not production evidence. server is not production evidence.