Integrate verified State Hub dispatcher into lifecycle release

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a10780-6d41-7952-9718-dfbc1f41236f
This commit is contained in:
tegwick 2026-10-04 22:44:01 +02:00
parent 4898c9b764
commit c084600082
10 changed files with 361 additions and 16 deletions

View file

@ -15,7 +15,8 @@ humans and system:serviceaccount:state-hub:state-hub. **Not deployed.**
sibling JSON registries. The policy YAML is Helm-rendered from Access Engine's
FLEX-WP-0033 candidate values; it is a review artifact, not a second deployment
owner. Existing Helm releases must be updated through their owning workflow.
All 25 proposed objects passed Kubernetes server dry-run against railiance01.
The original 25 objects and the three dispatcher objects passed Kubernetes
server dry-run against railiance01.
No Secret is included. Dry-run does not test reachability, credential delivery,
policy caller identity, backup restoration or a human disposition.
@ -46,10 +47,11 @@ contract; neither is presented as a newly enforced control.
under its separate live go, with exact identity/path negative proofs and
mounted-file refresh/rotation evidence. Only the audit ExternalSecret was
present in the live namespace at inspection.
2. INFD-WP-0005-T08: State Hub owner supplies production forwarding and separate
lifecycle/intake audience token projections. Its current dev fixture is not
an activated forwarder. Confirm notification receiver reachability; keep
resolution write-back disabled until its owner admission.
2. INFD-WP-0005-T08 / STATE-WP-0096: the private dispatcher and separate
lifecycle/intake token projections are prepared in dispatcher.candidate.json.
Deploy after intake prerequisites. Explicit enqueue requires trusted workload
execution; there is no new public delegate endpoint. Confirm notification
receiver reachability; keep resolution write-back disabled.
3. Refresh live image/config pins and unresolved-entry counts; complete the
backup/restore steps below; obtain explicit approval of this concrete rollout.
INFD-WP-0007-T07 and CCR-2026-0032 require this distinct go.
@ -98,3 +100,22 @@ broader browser packet/binding profiles remain explicit workplan tasks.
The final Access Engine pin is the Forgejo image built from commit d2d0b6f
(run https://forgejo.coulomb.social/coulomb/access-engine/actions/runs/316).
It supersedes the locally built candidate for the proposed deployment.
## Private State Hub dispatcher continuation
The dispatcher is a separate Recreate workload with 5m CPU / 24Mi memory requests,
one private 1Gi PVC, no Service/Ingress, and the existing state-hub service account.
Its only egress is DNS and the exact informed-decision peer on 8081. Projected
lifecycle and intake tokens each expire after 600 seconds. The dispatcher reads
only the intake token and never binds. Its pod omits app=state-hub, so it is not
selected by the existing Hub API Service.
Image: forgejo.coulomb.social/coulomb/state-hub-memo-dispatch@sha256:410fb6b6c9fd7a7ee0b63d727fd3249fb3ae13943421d6a5ce88b7ad2520bd2f
Source: State Hub 4a59b23, built by the dedicated Forgejo workflow.
Source integration proves queue/restart/lost-reply handling (one create, replayed
receipt), subsequent human acceptance and independent audit delivery. All identity
and credential inputs in that test are synthetic. Native acceptance remains open.
There is no new service-image build for this continuation. The service suite now
passes 663 tests (1 skipped); State Hub forwarding/notification tests pass 28.

View file

@ -0,0 +1,214 @@
{
"apiVersion": "v1",
"kind": "List",
"items": [
{
"apiVersion": "v1",
"kind": "PersistentVolumeClaim",
"metadata": {
"name": "state-hub-memo-dispatch",
"namespace": "state-hub",
"annotations": {
"state-hub.coulomb.social/admission": "proposal; STATE-WP-0096 / INFD-WP-0007 approved rollout required"
}
},
"spec": {
"accessModes": [
"ReadWriteOnce"
],
"storageClassName": "local-path",
"resources": {
"requests": {
"storage": "1Gi"
}
}
}
},
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "state-hub-memo-dispatch",
"namespace": "state-hub",
"annotations": {
"state-hub.coulomb.social/admission": "proposal; STATE-WP-0096 / INFD-WP-0007 approved rollout required"
}
},
"spec": {
"replicas": 1,
"strategy": {
"type": "Recreate"
},
"selector": {
"matchLabels": {
"app.kubernetes.io/name": "state-hub",
"app.kubernetes.io/component": "memo-dispatch"
}
},
"template": {
"metadata": {
"labels": {
"app.kubernetes.io/name": "state-hub",
"app.kubernetes.io/component": "memo-dispatch"
}
},
"spec": {
"serviceAccountName": "state-hub",
"automountServiceAccountToken": false,
"terminationGracePeriodSeconds": 15,
"securityContext": {
"runAsNonRoot": true,
"runAsUser": 10001,
"runAsGroup": 10001,
"fsGroup": 10001,
"fsGroupChangePolicy": "OnRootMismatch",
"seccompProfile": {
"type": "RuntimeDefault"
}
},
"containers": [
{
"name": "dispatcher",
"image": "forgejo.coulomb.social/coulomb/state-hub-memo-dispatch@sha256:410fb6b6c9fd7a7ee0b63d727fd3249fb3ae13943421d6a5ce88b7ad2520bd2f",
"securityContext": {
"allowPrivilegeEscalation": false,
"readOnlyRootFilesystem": true,
"capabilities": {
"drop": [
"ALL"
]
}
},
"resources": {
"requests": {
"cpu": "5m",
"memory": "24Mi"
},
"limits": {
"cpu": "100m",
"memory": "96Mi"
}
},
"volumeMounts": [
{
"name": "queue",
"mountPath": "/data"
},
{
"name": "intake-caller",
"mountPath": "/var/run/secrets/state-hub/informed-decision",
"readOnly": true
}
]
}
],
"volumes": [
{
"name": "queue",
"persistentVolumeClaim": {
"claimName": "state-hub-memo-dispatch"
}
},
{
"name": "intake-caller",
"projected": {
"defaultMode": 288,
"sources": [
{
"serviceAccountToken": {
"audience": "informed-decision-intake",
"expirationSeconds": 600,
"path": "intake-token"
}
},
{
"serviceAccountToken": {
"audience": "informed-decision-lifecycle",
"expirationSeconds": 600,
"path": "lifecycle-token"
}
}
]
}
}
]
}
}
}
},
{
"apiVersion": "networking.k8s.io/v1",
"kind": "NetworkPolicy",
"metadata": {
"name": "state-hub-memo-dispatch",
"namespace": "state-hub",
"annotations": {
"state-hub.coulomb.social/admission": "proposal; STATE-WP-0096 / INFD-WP-0007 approved rollout required"
}
},
"spec": {
"podSelector": {
"matchLabels": {
"app.kubernetes.io/name": "state-hub",
"app.kubernetes.io/component": "memo-dispatch"
}
},
"policyTypes": [
"Ingress",
"Egress"
],
"ingress": [],
"egress": [
{
"to": [
{
"namespaceSelector": {
"matchLabels": {
"kubernetes.io/metadata.name": "kube-system"
}
},
"podSelector": {
"matchLabels": {
"k8s-app": "kube-dns"
}
}
}
],
"ports": [
{
"protocol": "UDP",
"port": 53
},
{
"protocol": "TCP",
"port": 53
}
]
},
{
"to": [
{
"namespaceSelector": {
"matchLabels": {
"kubernetes.io/metadata.name": "informed-decision"
}
},
"podSelector": {
"matchLabels": {
"app.kubernetes.io/name": "informed-decision"
}
}
}
],
"ports": [
{
"protocol": "TCP",
"port": 8081
}
]
}
]
}
}
]
}

View file

@ -0,0 +1,16 @@
{
"status": "passed",
"image_id": "sha256:410fb6b6c9fd7a7ee0b63d727fd3249fb3ae13943421d6a5ce88b7ad2520bd2f",
"checks": [
"non-root read-only worker starts without external network",
"explicit enqueue persists a lifecycle envelope without inventing a Hub relationship",
"SIGTERM stops without forced kill",
"restart retains the exact pending envelope; missing custody does not drop it",
"private queue and database modes enforced"
],
"fixture_only": true,
"external_network": "none",
"native_identity_proven": false,
"envelope_sha256": "ce0b8e1c6e861fde2da584363b695932bd0c74f923d1688211cc8b3d9a2f8c2e",
"cleanup_complete": true
}

View file

@ -0,0 +1,9 @@
{
"image": "forgejo.coulomb.social/coulomb/state-hub-memo-dispatch@sha256:410fb6b6c9fd7a7ee0b63d727fd3249fb3ae13943421d6a5ce88b7ad2520bd2f",
"source_commit": "4a59b23",
"scanner": "Trivy 0.74.0",
"created_at": "2026-10-04T20:38:44.831801093Z",
"high": 0,
"critical": 0,
"scope": "Image vulnerabilities in the scanner database at scan time; not universal security attestation."
}

View file

@ -3,11 +3,12 @@
"status": "prepared-not-deployed",
"server_dry_run": {
"cluster": "railiance01",
"objects": 25,
"objects": 28,
"passed": true
},
"sha256": {
"deploy/lifecycle/admission.candidate.json": "e3054aebb2338146b912121198eba0232973574b9f045710abc802e524fe43f6",
"deploy/lifecycle/dispatcher.candidate.json": "e64bdaabfc307caf5f65894ce7e37fd66abaaf9328fd35cfd9103e14bbf739c8",
"deploy/lifecycle/intake-submitters.json": "58ba5ef87ecfd40bc7f0eaf2beb5e50f0c1f0e47315e79dbfa9e06411a17eba9",
"deploy/lifecycle/lifecycle-callers.json": "0b66382a64bdf81ee75d14a70ba3b19290084922a1e0bc8a873af0f039dc7041",
"deploy/lifecycle/policy.candidate.yaml": "850d96aa7807b0e81e28278d380543770012a006b5dc3ebb06a5a8f5fcb43c4a",
@ -15,5 +16,12 @@
"deploy/lifecycle/review-groups.json": "1d61d2b82230981130936651a7fc4230e06ecaa51416a8a1be2bb2eb6ec4c353",
"deploy/lifecycle/review-policy.candidate.yaml": "1f680eb1184c4a26ae2638b7e0829e5df42237ee09550e7554a3e965edbc8674",
"deploy/lifecycle/service.candidate.json": "bf36ecb0160bf893a60fe059a3baf27a63668e2bb7b9f17c9582d6de8e842545"
},
"dispatcher_verification": {
"owner_tests_passed": 28,
"cross_service_tests_passed": 663,
"cross_service_tests_skipped": 1,
"installed_container_checks": 5,
"high_critical_findings": 0
}
}

View file

@ -86,3 +86,15 @@ No repeated design decision is needed for this accepted boundary. The remaining
human action is separate founder approval of the final concrete live packet and
named verification/abort attendance, after these technical gaps are closed.
No live apply, credential read, rotation, issuer mutation or deployment occurred.
## Runtime acceptance update — 2026-10-04
The private listener, CA-verified TokenReview, rotating file-backed requester,
attribute consumer and durable State Hub dispatch are now composed and tested.
The service image remains the scanned 2768bd1 candidate in deploy/lifecycle;
State Hub worker is the CI-built 410fb6b candidate. This closes the source/runtime
composition gap only. The live CCR remains proposed; its current SHA256 is
ab4cd7c15192ebe98e17b0c40a188ae67616c63cc796d9b807adba7c39f2edb3.
No new design decision is requested. Exact live delivery approval, attended
OpenBao login, native custody/rotation proof and cutover acceptance remain.

View file

@ -56,11 +56,16 @@ live Approval Engine object, checks its binding pin, requester informed-decision
and human_control=true before the PDP call. Store guards recheck retained intake
metadata at the mutation boundary. A valid-looking UUID/digest alone is not proof.
State Hub forwarding currently exists as a dev fixture, not a production worker.
The selected identity still needs projected lifecycle/intake tokens and an owner
runtime integration. State Hub notifications/write-back require admitted receiver
routes; `INFD_HUB_WRITEBACK` stays off in this candidate. Pending rows remain
explicit. These are live INFD-WP-0005 dependencies, not claimed release evidence.
State Hub now has a private explicitly queued dispatcher (STATE-WP-0096), with a
separate standard-library-only image and PVC. It uses the selected existing
service account and distinct projected lifecycle/intake tokens; its transport
reads only the intake token. The Hub public API does not gain a credentialed
forwarding route. Queueing is trusted workload execution. New review envelopes
are data operations and do not change deployments. The exact lifecycle snapshot
id/key is preserved without inventing a Hub relationship when serves is absent.
Lost replies replay the same envelope; native-create uncertainty blocks the row.
Native deployment/acceptance still remains open. Notification/write-back receiver
admission is unchanged; INFD_HUB_WRITEBACK remains off.
The image migrates schema 2 to schema 5 on open. Existing records, presentations,
dispositions, submissions and outbox evidence must survive. Rollback to the old
@ -76,7 +81,8 @@ must be reconciled, never retried or discarded to make rollback convenient.
evidence remain with RPF-WP-0051.
- FLEX-WP-0033: publish scanned policy artifact; approve exact policy/RBAC/network
objects and native caller/subject allow/refusal tests.
- INFD-WP-0005: production State Hub forwarding/receiver and token projections.
- INFD-WP-0005 / STATE-WP-0096: activate and verify the prepared private State Hub
dispatcher and its projected tokens; retain gated resolution write-back.
- INFD-WP-0007-T06/T07: backup/restore, exact rollout approval, native human review
and independent audit receipts across two decisions and a revision with unchanged
release/configuration pins. Browser packet authoring and broader binding profiles

View file

@ -164,13 +164,26 @@ from test_approval_component import component, signing_key
from test_durable_component import receiver
def test_actual_engine_human_entry_and_independent_audit_link_to_source_revision(handoff, component, receiver):
@pytest.mark.parametrize("via_dispatch", [False, True])
def test_actual_engine_human_entry_and_independent_audit_link_to_source_revision(handoff, component, receiver, tmp_path, via_dispatch):
import jwt
from informed_decision.intake_approval import IntakeApprovalClient
from informed_decision.approval_http import ApprovalHTTPClient
from informed_decision.review import ReviewController
from informed_decision.runtime import AuditPump
app, actor, memo, options, intake, calls = handoff
if via_dispatch:
import os, sys
source = os.environ.get('INFD_STATE_HUB_SOURCE')
if not source:
pytest.skip('State Hub source required for private dispatcher integration')
sys.path.insert(0, source)
from api.services.memo_dispatch import Dispatcher
options = {**options, 'memo_class': 'decision-record'}
grants = json.loads(intake.submitters_path.read_text())
for row in grants.values():
row['memo_classes'] = ['decision-record']
intake.submitters_path.write_text(json.dumps(grants))
client, engine, transport, human, signing = component
groups = json.loads(intake.groups_path.read_text())
groups[options['review_group']]['subject'] = human.subject
@ -183,8 +196,31 @@ def test_actual_engine_human_entry_and_independent_audit_link_to_source_revision
summary='Responded to research', key='revise')
app.publish(actor, memo.id, 2, audience={'subjects': [human.subject], 'groups': []}, key='publish-second')
receipt = app.request_review(actor, memo.id, 2, options=options, key='review-second')
status, loaded = request(intake, receipt['envelope'])
assert status == 201, loaded
if via_dispatch:
attempts = []
now = [time.time()]
def send(method, url, body, headers):
assert headers['Authorization'] == 'Bearer projected-token'
result = request(intake, body)
attempts.append(result[0])
if len(attempts) == 1:
raise OSError('lost reply after native creation')
return result
worker = Dispatcher(tmp_path / 'private-dispatch', send=send,
read_token=lambda: 'projected-token', clock=lambda: now[0])
assert worker.enqueue(receipt['envelope']).status == 'pending'
assert not attempts
assert worker.once().status == 'pending'
now[0] += 10
worker = Dispatcher(tmp_path / 'private-dispatch', send=send,
read_token=lambda: 'projected-token', clock=lambda: now[0])
forwarded = worker.once()
assert forwarded.status == 'delivered' and attempts == [201, 200]
loaded = forwarded.receipt
assert worker.once() is None
else:
status, loaded = request(intake, receipt['envelope'])
assert status == 201, loaded
controller = ReviewController(app.repository.store, policy(),
lambda session: ApprovalHTTPClient('https://approval.test', session, transport=transport), intake_attributes=True)
page = controller.open(human, receipt['review_memo_id'])

View file

@ -167,7 +167,7 @@ id: INFD-WP-0005-T08
status: wait
priority: medium
blocked_on: message-from:state-hub
blocking_reason: "Founder selected system:serviceaccount:state-hub:state-hub; production token projection and runtime forwarding remain, alongside T05/T06"
blocking_reason: "STATE-WP-0096 implements private queue/runtime and projected audience proposals; native forwarding acceptance waits on requester custody and approved rollout"
state_hub_task_id: "ce2b36b4-ef82-53b7-aee7-e0063855370b"
```
@ -271,3 +271,14 @@ registries and live runtime remain unchanged. T05/T06 remain waiting on native
custody and policy deployment; T08 still needs the State Hub production forwarder,
not merely its dev fixture. See docs/specs/LifecycleProductionRuntime.md and
FLEX-WP-0033 for exact boundaries.
## Private State Hub runtime delivered in source — 2026-10-04
STATE-WP-0096 adds the private explicit queue dispatcher, immutable CI image,
separate lifecycle/intake token projections, and isolated PVC/network proposal.
Cross-service proof covers retained lifecycle envelope forwarding, lost reply
following one native approval create, restart, replay receipt, human acceptance
and Audit Core delivery. 663 service tests pass (1 skipped), including this path;
28 State Hub dispatch/contract/notification tests pass. T08 remains waiting only
for approved native delivery/acceptance, not implementation of its runtime.

View file

@ -274,3 +274,15 @@ CCR-2026-0032 / RPF-WP-0051 owns requester delivery; INFD-WP-0005-T08 owns State
Hub forwarding/token projections. The exact chosen identity is no longer an open
question. Rollback must retain new evidence and must not run a schema-2 reader
on the migrated schema-5 database.
## Private dispatcher production continuation — 2026-10-04
STATE-WP-0096 now supplies the prepared private State Hub worker and audience
projections. No credentialed forwarding route is exposed through the unauthenticated
Hub API. Fixed a consumer contract mismatch: immutable lifecycle review envelopes
may omit serves and retain their review-UUID idempotency key. Integration proves
lost-reply/restart recovery without duplicate native approval, followed by human
review and independent audit. Validation: 663 passed, 1 skipped; 28 Hub tests.
The exact three worker objects are added to deploy/lifecycle. Native credentials,
rollout approval and human acceptance remain the next gates; no live mutation.