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:
parent
a0a602976e
commit
d710bf0088
6 changed files with 151 additions and 8 deletions
2
Makefile
2
Makefile
|
|
@ -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"} \
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
21
deploy/keycape-ingress.yaml
Normal file
21
deploy/keycape-ingress.yaml
Normal 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}
|
||||||
84
docs/evidence/2026-09-14-approval-engine-deployment.json
Normal file
84
docs/evidence/2026-09-14-approval-engine-deployment.json
Normal 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."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue