Compare commits

...
Sign in to create a new pull request.

87 commits

Author SHA1 Message Date
4e2809cb1e Close verified Coulomb editor release delivery at revision 21
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-11-01 00:41:57 +01:00
0c72b3bec8 Link editor release execution task to State Hub
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 22:17:56 +01:00
979b7819df Prepare attended Coulomb editor attachment visibility release
Some checks are pending
CI Smoke / host-smoke (push) Waiting to run
CI Smoke / container-smoke (push) Waiting to run
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 22:16:46 +01:00
b39910e330 Reconcile verified Coulomb revision 20 and close release delivery
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 21:29:42 +01:00
a15cc888d2 Route content release wait to its exact execution decision
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 19:44:16 +01:00
dc927d7751 Prepare verified combined Coulomb attachment and content release
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 19:42:01 +01:00
c733e86b0f Answer NK-WP-0056-T02 owner request: rapp-bounded-executor intake
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Owner answer for who creates the rapp-bounded-executor repository and
which Railiance rapp conventions apply. Proposal only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 994874@bnt-lap001
Assistant-Session: 6017c416-d619-47eb-bd1a-f670a02d7d87
2026-10-31 14:19:03 +01:00
9c74884641 Prepare guarded content release after inactive backup installation
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 13:20:46 +01:00
75343cfec1 Record deployment-owner task identity for content recovery release
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 13:00:13 +01:00
d3c46421b8 Track release delivery for the content recovery correction
Some checks are pending
CI Smoke / host-smoke (push) Waiting to run
CI Smoke / container-smoke (push) Waiting to run
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 12:58:59 +01:00
169dd3e074 Record State Hub identity for release custody correction
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 12:46:11 +01:00
904dcb3ce3 Keep future Coulomb release evidence directories private
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 12:44:59 +01:00
d795120f98 Reconcile verified Coulomb revision 19 and complete image delivery
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 0s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 08:35:54 +01:00
a70e3b2e9f Record published identity export release handoff
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 04:52:12 +01:00
84a5aa0c92 Prepare supervised Coulomb identity and export image release
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 04:47:32 +01:00
8434134391 Record founder-verified Coulomb storage release revision 18
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 01:33:51 +01:00
bf77257d52 FORGE-WP-ADHOC-2026-10-27: admit the filename-digest receive scanner
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
The Forgejo template and init guard now include the 20261027 adapter beside the two older admitted binaries.

Assistant: grok
Assistant-Model: grok-4.7
Assistant-Session: 01a17f2b-8cc3-76f0-9bce-3b3444b9a5e9
2026-10-27 22:45:21 +01:00
0a1aba9d62 Declare the forgejo release unit.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Claims Deployment forgejo/forge and Deployment forgejo/forgejo-runner so rapp-forgejo is no longer the declaration authority.

Assistant: grok
Assistant-Session: 01a1609f-72b8-7222-9dff-fae300115e42
2026-10-22 00:56:07 +02:00
cd6cd38356 Wave-2 right-sizing applied 2026-10-22 (CUST-WP-0071)
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 276069@bnt-lap001
Assistant-Session: 2e192b3d-b3e8-4403-94d4-d55a2c8b476c
2026-10-22 00:52:51 +02:00
9ecb123451 RAPPS-WP-0018-T01: record wave-2 preparation (not applied)
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 276069@bnt-lap001
Assistant-Session: 2e192b3d-b3e8-4403-94d4-d55a2c8b476c
2026-10-22 00:06:57 +02:00
b7fb7b226f Wave-2 right-sizing and inter-hub removal (RAPPS-WP-0018, CSOC-WP-0017, REUSE-WP-0026)
- coulomb-social: requests 100m/256Mi -> 15m/176Mi (limits unchanged)
- reuse-surface: main 100m/256Mi -> 5m/64Mi, landing 10m/32Mi -> 5m/16Mi
- vergabe-demo-company: memory request 256Mi -> 512Mi, CPU 60m kept
- inter-hub (retired, CORE-WP-0007): delete chart, values, Make targets,
  smoke script and production deploy workflow; release to be uninstalled

Full renders vs live: resources only, except the vergabe release-unit label
from 64530e1 not yet on its Service and PVCs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 276069@bnt-lap001
Assistant-Session: 2e192b3d-b3e8-4403-94d4-d55a2c8b476c
2026-10-22 00:03:48 +02:00
1dd7387cbc RAPPS-WP-0019 finished; forge runner memory limit
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 276069@bnt-lap001
Assistant-Session: 2e192b3d-b3e8-4403-94d4-d55a2c8b476c
2026-10-21 23:46:40 +02:00
db0ade1cf3 RAPPS-WP-0019 stage C: consumers on forge-http; remove compatibility alias
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 276069@bnt-lap001
Assistant-Session: 2e192b3d-b3e8-4403-94d4-d55a2c8b476c
2026-10-21 23:38:03 +02:00
86c255a932 RAPPS-WP-0019: stage A and B applied; forge objects renamed
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 276069@bnt-lap001
Assistant-Session: 2e192b3d-b3e8-4403-94d4-d55a2c8b476c
2026-10-21 23:31:40 +02:00
5d2c0d8564 RAPPS-WP-0019 stage B: rename forge objects (forgejo-gitea-* -> forge-*)
Local until the attended cutover; see the workplan for the scale-down order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 276069@bnt-lap001
Assistant-Session: 2e192b3d-b3e8-4403-94d4-d55a2c8b476c
2026-10-21 23:14:46 +02:00
ce1c56dcb3 RAPPS-WP-0019: plan product-neutral forge object names
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 276069@bnt-lap001
Assistant-Session: 2e192b3d-b3e8-4403-94d4-d55a2c8b476c
2026-10-21 23:14:42 +02:00
64530e19e2 Declare the Vergabe release unit.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
The chart label is vergabe, the claim is the live Deployment in vergabe-demo-company, and the image pin is the digest that Deployment runs. The shared database is a reference, not a workload claim.

RELREG-WP-0002-T01

Assistant: grok
Assistant-Session: 01a15f9f-3d77-7d21-911b-d93c86f9d14f
2026-10-21 22:23:50 +02:00
e9d2b5f39a chore(consistency): write back State Hub ids
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 276069@bnt-lap001
Assistant-Session: 2e192b3d-b3e8-4403-94d4-d55a2c8b476c
2026-10-21 21:14:40 +02:00
4a1d6539bf FORGE-WP-0011: Forgejo and runner resource requests (CUST-WP-0071 wave 1)
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 276069@bnt-lap001
Assistant-Session: 2e192b3d-b3e8-4403-94d4-d55a2c8b476c
2026-10-21 20:54:00 +02:00
47b3a4a5ca chore(consistency): write back State Hub ids
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 8340@bnt-lap001
Assistant-Session: 2e192b3d-b3e8-4403-94d4-d55a2c8b476c
2026-10-21 12:30:30 +02:00
10465cb39c RAPPS-WP-0018: wave-2 resource right-sizing receiving workplan
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Receiving workplan for the 2026-10-21 node allocation review
(RESOURCE-WP-0008-T03 / CUST-WP-0071-T03). Wave-2 apply is human-gated;
no manifest changes. WORK-RECORDS.md regenerated from staged sources.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 8340@bnt-lap001
Assistant-Session: 2e192b3d-b3e8-4403-94d4-d55a2c8b476c
2026-10-21 08:48:25 +02:00
1af6d828f5 RAPPS-WP-0014-T04: record isolated target-hardware Vergabe replay
Throwaway namespace on railiance01 ran the deployed digest (3 workers,
60m/1000m CPU, 256Mi/1Gi) against fresh PostgreSQL 16 with the phased
synthetic profile: ordinary p95 0.192s, transfer p95 0.076s, zero errors.
Raw cgroup/node/Prometheus samples, manifests, driver and a bounded
recommendation retained; namespace torn down; T03 unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 8340@bnt-lap001
Assistant-Session: 2e192b3d-b3e8-4403-94d4-d55a2c8b476c
2026-10-21 08:38:12 +02:00
3851865c87 Record verified approved Policy Nexus deployment
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a15374-f59f-7a31-86d2-d23b5d2ed181
2026-10-19 17:55:35 +02:00
170e9b702c plan: receive exact-release and contention sizing evidence
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a15374-f59f-7a31-86d2-d23b5d2ed181
2026-10-19 15:39:36 +02:00
3be92daf74 Approve pinned Policy Nexus guardrail release
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a15374-f59f-7a31-86d2-d23b5d2ed181
2026-10-19 15:29:55 +02:00
0e1d2b3cfe chore: keep generated orientation brief local
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a15374-f59f-7a31-86d2-d23b5d2ed181
2026-10-19 13:50:32 +02:00
a6fe0aba0f build: adopt staged work-record index generation
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a15374-f59f-7a31-86d2-d23b5d2ed181
2026-10-19 13:23:12 +02:00
4d6a8dde21 Declare approved Forgejo public PKCE development sign-in profile
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a15302-b965-7d43-a9d8-42710914b24e
2026-10-19 10:35:14 +02:00
2fdc198b2b Declare scanner rollout admitted by cb9a20f9-5f8d-411f-b034-b15856c0efb5
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 6s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a14bfc-9643-7872-aa12-98326dcf25e7
2026-10-18 17:04:45 +02:00
ab3cf5cdc5 Correct composed validation scope and retain the initial fixture skip
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a14bfc-9643-7872-aa12-98326dcf25e7
2026-10-18 12:21:03 +02:00
a927ec8cc8 Join Forgejo callback identity to the issuer-owned current access grant
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a14bfc-9643-7872-aa12-98326dcf25e7
2026-10-18 12:17:36 +02:00
308e83fcfb Verify Forgejo PostgreSQL operations and preserve tracing hook context
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a14bfc-9643-7872-aa12-98326dcf25e7
2026-10-18 03:21:56 +02:00
3fb031a47f Compose Forgejo private-input and authenticated decision build candidate
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a14bfc-9643-7872-aa12-98326dcf25e7
2026-10-18 02:21:33 +02:00
c29408ae67 Add pinned Forgejo release build path and track operational integration
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a1468b-63fa-7710-85dc-8f2e16b3ebfa
2026-10-18 00:06:47 +02:00
944b30ad63 RAPPS-WP-0016: correct T02 role/policy naming, point T05 at 1.3 review
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
T02 named the OpenBao policy as the role; the Kubernetes-auth role is
external-secrets-keycape-forgejo-forgejo, carrying only policy
workload-kv-read-keycape-forgejo-forgejo. T05's blocking reason now
points at the Apps 1.3 owner-review record instead of the settled 1.2
option decision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1045697@bnt-lap001
Assistant-Session: f16df812-4a57-4823-a2da-73c69e3bb4d1
2026-10-17 12:12:52 +02:00
0da83472c4 Default railiance01 kubeconfig to config-railiance01
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 5s
config-hosteurope names port 16443, but the k3s-api-railiance01
ops-bridge tunnel listens on 16444, so every default target failed
with connection refused. Switch Makefile, tools and docs to
~/.kube/config-railiance01 and fix the restore recipe to rewrite the
API address to the tunnel port. The node-side hostPath in STATE.md is
unrelated and unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1031639@bnt-lap001
Assistant-Session: e5073fee-f4ae-4fc3-a75a-6c1fc83e0576
2026-10-17 11:46:40 +02:00
74213d80f0 Close RAPPS-WP-0016-T01: break-glass admin sealed and verified
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
forgejo_admin password lives only in OpenBao
platform/workloads/forgejo/break-glass-admin (v1); founder-run local
sign-in passed (HTTP 200, site admin). Value-free evidence committed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1031639@bnt-lap001
Assistant-Session: e5073fee-f4ae-4fc3-a75a-6c1fc83e0576
2026-10-17 11:41:51 +02:00
fe5cabecd6 Add Forgejo break-glass seal/verify tooling and recovery runbook
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
RAPPS-WP-0016-T01: founder-run targets rotate the forgejo_admin password
into OpenBao (platform/workloads/forgejo/break-glass-admin) and record a
value-free sign-in. T01 stays in progress until the founder runs them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1031639@bnt-lap001
Assistant-Session: e5073fee-f4ae-4fc3-a75a-6c1fc83e0576
2026-10-17 11:32:09 +02:00
fbe012789e Write back hub IDs for RAPPS-WP-0016-T05
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1031639@bnt-lap001
Assistant-Session: e5073fee-f4ae-4fc3-a75a-6c1fc83e0576
2026-10-17 11:26:05 +02:00
9b0999fc37 Record Apps owner review for NK-WP-0048-T16 and plan consumer seams
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Needs-change verdict on deliver-consumer-secret and hand-over adapters
(net-kingdom ff8f597); option C preferred. WP-0016 T02 moves to the
platform/workloads/keycape/forgejo custody path; new T05 for the real
Apps owner seams.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1031639@bnt-lap001
Assistant-Session: e5073fee-f4ae-4fc3-a75a-6c1fc83e0576
2026-10-17 11:25:11 +02:00
a1ef3fe77a Close verified reuse reader rollout and hosted discovery
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a137ca-5c77-7a71-b9ae-23ad9b0b5684
2026-10-15 05:49:19 +02:00
2b9f030c24 Declare approved scoped Forgejo reader delivery to reuse hub
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 0s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a137ca-5c77-7a71-b9ae-23ad9b0b5684
2026-10-15 05:11:08 +02:00
91f828b3c1 Declare approved hardened reuse image and exact source routing
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a137ca-5c77-7a71-b9ae-23ad9b0b5684
2026-10-15 02:15:36 +02:00
ea2d4632a1 Prepare exact reader delivery recovery and native runtime proof
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a137ca-5c77-7a71-b9ae-23ad9b0b5684
2026-10-15 02:01:53 +02:00
05f654e20f Record canonical UUID bindings for reuse reader preparation
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a137ca-5c77-7a71-b9ae-23ad9b0b5684
2026-10-15 01:47:29 +02:00
fb7d56d3dc Prepare scoped reuse reader release with qualified content digests
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a137ca-5c77-7a71-b9ae-23ad9b0b5684
2026-10-15 01:47:29 +02:00
72bcad86d7 docs(reuse): establish reviewed NetKingdom security capabilities
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a137ca-5c77-7a71-b9ae-23ad9b0b5684
2026-10-14 06:41:38 +02:00
706461b845 security: restrict runner Docker API to pod loopback
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 27s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a1346b-72a4-7d40-b8bd-8b8ca346cfa2
2026-10-13 18:41:10 +02:00
50b66c4e44 security: restore pinned scanner custody before Forgejo startup
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a1346b-72a4-7d40-b8bd-8b8ca346cfa2
2026-10-13 14:23:00 +02:00
390a30a5c1 security: persist Forgejo receive hook inheritance for new repositories
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a1346b-72a4-7d40-b8bd-8b8ca346cfa2
2026-10-13 13:59:43 +02:00
custodian-sync
c94b301c2a chore(consistency): renormalize lifecycle state [auto]
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Updated by fix-consistency on 2026-10-11:
  - workplan status: ready → active

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
2026-10-11 08:58:12 +02:00
d349815195 Record source-only Forgejo reference migration decision
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
2026-10-11 08:56:45 +02:00
367f01cb4b Prepare reviewed Forgejo consumer ESO scope and truthful recovery records
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
2026-10-11 04:09:45 +02:00
c4359794e8 Reconcile verified Policy Nexus release nine to approved source pins
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
2026-10-11 03:27:48 +02:00
custodian-sync
602c9dcc1c chore(consistency): sync task status from DB [auto]
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Updated by fix-consistency on 2026-10-11:
  - update .custodian-brief.md for railiance-apps

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
2026-10-11 03:12:44 +02:00
6953a23e62 Prepare unapproved exact Policy Nexus image and provenance pins
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Has been cancelled
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
2026-10-11 03:12:03 +02:00
b6be18b727 chore(consistency): refresh the work-record index
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 5457@bnt-lap001
Assistant-Session: 792bf84a-3783-4f5d-a811-cded61dd2012
2026-10-08 19:13:32 +02:00
custodian-sync
21022a6e1c chore(consistency): sync task status from DB [auto]
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Updated by fix-consistency on 2026-10-08:
  - update .custodian-brief.md for railiance-apps

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 5457@bnt-lap001
Assistant-Session: 792bf84a-3783-4f5d-a811-cded61dd2012
2026-10-08 19:13:02 +02:00
7ea9e1c911 Add RAPPS-WP-0016: Forgejo sign-in through NetKingdom
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
ORGREF-WP-0002-T05; founder-approved 2026-10-08. Break-glass first, no
visibility change, depends on key-cape KEY-WP-0038.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 5457@bnt-lap001
Assistant-Session: 792bf84a-3783-4f5d-a811-cded61dd2012
2026-10-08 19:12:45 +02:00
815ba1f70c Record verified Coulomb revision 16 deployment and paired recovery
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a119cf-56d0-7fa3-840b-eb8732e1db0b
2026-10-08 09:38:11 +02:00
2202e690cc Pin Coulomb sharing and portability release dd83c90
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a119cf-56d0-7fa3-840b-eb8732e1db0b
2026-10-08 09:26:40 +02:00
de2457cab2 Record Spaces-list cover revision15 rollout receipt
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a11538-05ea-7b63-b3c7-a2df5f38898d
2026-10-07 16:01:22 +02:00
f5fc9eaa15 Pin Spaces-list Visual correction after founder review
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 11s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a11538-05ea-7b63-b3c7-a2df5f38898d
2026-10-07 15:10:02 +02:00
7fb7a1c389 Record private Visual revision14 deployment receipt
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a11538-05ea-7b63-b3c7-a2df5f38898d
2026-10-07 13:44:02 +02:00
2e95944ba3 Pin approved private Visual release for coulomb-social
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a11538-05ea-7b63-b3c7-a2df5f38898d
2026-10-07 12:43:40 +02:00
4a807932fa Record revision13 protocol naming verification
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a10e2d-a5ef-7e92-b03d-b1694e915680
2026-10-07 02:13:00 +02:00
22c5695a21 Pin tested protocol naming corrective release
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 6s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a10e2d-a5ef-7e92-b03d-b1694e915680
2026-10-07 02:08:00 +02:00
bca75d70a0 Record revision12 clipboard and content preservation proof
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a10e2d-a5ef-7e92-b03d-b1694e915680
2026-10-07 01:53:29 +02:00
9499c39676 Pin full-protocol clipboard corrective release
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a10e2d-a5ef-7e92-b03d-b1694e915680
2026-10-07 00:29:16 +02:00
5f6244ec62 docs: record verified real ReichelAg content import
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a10e2d-a5ef-7e92-b03d-b1694e915680
2026-10-06 22:54:35 +02:00
f42f8024c0 docs: record paired recovery before first ReichelAg transfer
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a10e2d-a5ef-7e92-b03d-b1694e915680
2026-10-06 22:11:42 +02:00
62dc523d7e docs: record user workspace acceptance after login repair
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a10e2d-a5ef-7e92-b03d-b1694e915680
2026-10-06 20:54:42 +02:00
ae2885728a docs: record canonical user service correction deployment
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a10e2d-a5ef-7e92-b03d-b1694e915680
2026-10-06 20:52:28 +02:00
3f8276e0ea fix(coulomb): use canonical user service during personal login
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a10e2d-a5ef-7e92-b03d-b1694e915680
2026-10-06 20:50:14 +02:00
8a57e37ded Refresh work-record index after Coulomb deployment
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a10e2d-a5ef-7e92-b03d-b1694e915680
2026-10-06 17:03:39 +02:00
custodian-sync
df7b69ff3b chore(consistency): sync task status from DB [auto]
Some checks are pending
CI Smoke / host-smoke (push) Waiting to run
CI Smoke / container-smoke (push) Waiting to run
Updated by fix-consistency on 2026-10-06:
  - update .custodian-brief.md for railiance-apps

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a10e2d-a5ef-7e92-b03d-b1694e915680
2026-10-06 17:03:10 +02:00
f713aa90ef Record approved deployment, backup and personal login blocker
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a10e2d-a5ef-7e92-b03d-b1694e915680
2026-10-06 17:02:15 +02:00
190 changed files with 20078 additions and 599 deletions

View file

@ -1,7 +1,7 @@
## Stack
- **Language:** Helm values + Kubernetes manifests, Bash Make targets
- **Key deps:** vergabe-teilnahme (Django) and inter-hub releases, shared apps-pg cnpg cluster, SOPS/age; runs against the S2 cluster on railiance01
- **Key deps:** vergabe-teilnahme (Django), coulomb-social and reuse-surface releases (inter-hub retired and removed), shared apps-pg cnpg cluster, SOPS/age; runs against the S2 cluster on railiance01
## Dev Commands
@ -13,7 +13,7 @@ make vergabe-dry-run # helm template render, no apply
make vergabe-status # pod / svc / ingress / cert state (read-only)
make vergabe-deploy # production-touching
make vergabe-migrate | vergabe-seed # production-touching (Django management)
make check-inter-hub-image-tag # gate: explicit image tag for prod deploys
make check-vergabe-image-tag # gate: explicit image tag for prod deploys
```
`*-deploy` / `*-migrate` / `*-seed` change production workloads — operator

View file

@ -1,35 +0,0 @@
<!-- custodian-brief: generated by fix-consistency — do not edit manually -->
# Custodian Brief — railiance-apps
**Domain:** financials
**Last synced:** 2026-10-06 14:44 UTC
**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)*
## Active Workstreams
### Coulomb-social persistent personal workspace release
Progress: 2/4 done | workplan_id: `79abacf8-361e-51ab-829d-40dec8e142d1`
**Open tasks:**
- ! T03 — Approved upgrade and recovery point `2ce05439`
- ! T04 — Personal sign-in and content acceptance `8bc4962a`
### Deploy and recover the first invited Vergabe company pilot
Progress: 2/3 done | workplan_id: `c7fdaa7e-cab8-5d1d-86c2-f1aad7927c57`
**Open tasks:**
- ! Demonstrate restart, isolated restore, rollback and operating ownership `dd069c6d`
*(wait: Await populated two-user/document workflow, coherent PostgreSQL/media/issue-state off-host recovery point, isolated restored workflow, rollback evidence and support ownership. September 25 proves database restore and restart only.)*
## Inbox Hygiene
**Stale unread:** 2 message(s) older than 3 day(s) — triage at session start.
**Missing thread_id:** 2 unread message(s) lack supersession chains.
---
## MCP Orientation (when available)
If the state-hub MCP server is reachable, call:
`get_domain_summary("financials")`
This provides richer cross-domain context.
If the MCP call fails, use this file as your orientation source.

View file

@ -0,0 +1 @@
{"version":1,"repo_slug":"railiance-apps"}

View file

@ -0,0 +1,8 @@
# Explicit opt-in recipe; no environment files or external make includes.
CUSTODIAN_SOURCE ?= /home/worsch/the-custodian
GENERATED_REPO_railiance-apps ?= railiance-apps
.PHONY: generated-work-records generated-work-records-check
generated-work-records:
python3 "$(CUSTODIAN_SOURCE)/scripts/generate_staged_work_records.py" --repo . --repo-slug "$(GENERATED_REPO_railiance-apps)"
generated-work-records-check:
python3 "$(CUSTODIAN_SOURCE)/scripts/generate_staged_work_records.py" --repo . --repo-slug "$(GENERATED_REPO_railiance-apps)" --check

View file

@ -1,50 +0,0 @@
name: Inter-Hub production deploy
on:
workflow_dispatch:
inputs:
image_tag:
description: "Immutable inter-hub image tag to deploy, for example 5101eb5"
required: true
confirm:
description: "Type deploy-inter-hub-production to confirm Railiance01 production deploy"
required: true
jobs:
deploy:
runs-on: [self-hosted, haskelseed, linux_amd64]
timeout-minutes: 20
env:
INTER_HUB_IMAGE_TAG: ${{ inputs.image_tag }}
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Validate dispatch inputs
env:
CONFIRM: ${{ inputs.confirm }}
run: |
set -euo pipefail
if [ "$CONFIRM" != "deploy-inter-hub-production" ]; then
echo "Refusing deploy: confirmation text did not match." >&2
exit 1
fi
if ! [[ "$INTER_HUB_IMAGE_TAG" =~ ^[A-Za-z0-9._-]+$ ]]; then
echo "Refusing deploy: image_tag contains unsupported characters." >&2
exit 1
fi
- name: Verify image manifest exists
run: make check-inter-hub-image
- name: Helm server dry-run
run: make inter-hub-server-dry-run
- name: Deploy Inter-Hub
run: make inter-hub-deploy
- name: Show release status
run: make inter-hub-status
- name: Run public smoke checks
run: make inter-hub-smoke

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
# Local State Hub orientation snapshot; authored work records remain tracked.
/.custodian-brief.md

110
Makefile
View file

@ -24,14 +24,6 @@ COULOMB_SOCIAL_IMAGE_REPOSITORY ?= forgejo.coulomb.social/coulomb/coulomb-social
COULOMB_SOCIAL_INGRESS ?= manifests/coulomb-social-ingress.yaml
COULOMB_SOCIAL_ENV_SECRET ?= coulomb-social-env
INTER_HUB_RELEASE ?= inter-hub
INTER_HUB_NAMESPACE ?= inter-hub
INTER_HUB_CHART ?= charts/inter-hub
INTER_HUB_VALUES ?= helm/inter-hub-values.yaml
INTER_HUB_IMAGE_REPOSITORY ?= forgejo.coulomb.social/coulomb/inter-hub
INTER_HUB_IMAGE_TAG ?=
INTER_HUB_BASE_URL ?= https://hub.coulomb.social
REUSE_RELEASE ?= reuse
REUSE_NAMESPACE ?= reuse
REUSE_CHART ?= charts/reuse-surface
@ -50,9 +42,8 @@ POLICY_NEXUS_SOURCE_SET_DIGEST ?= $(shell python3 -c 'import json; print(json.lo
POLICY_NEXUS_REVISION ?=
COULOMBCORE_KUBECONFIG ?= $(HOME)/.kube/config
RAILIANCE01_KUBECONFIG ?= $(HOME)/.kube/config-hosteurope
RAILIANCE01_KUBECONFIG ?= $(HOME)/.kube/config-railiance01
PRODUCTION_KUBECONFIG ?= $(COULOMBCORE_KUBECONFIG)
INTER_HUB_KUBECONFIG ?= $(RAILIANCE01_KUBECONFIG)
REUSE_KUBECONFIG ?= $(RAILIANCE01_KUBECONFIG)
FORGEJO_KUBECONFIG ?= $(RAILIANCE01_KUBECONFIG)
FORGEJO_RELEASE ?= forgejo
@ -63,6 +54,7 @@ FORGEJO_CHART_VERSION ?= 12.5.0
FORGEJO_VALUES ?= helm/forgejo-values.yaml
FORGEJO_REGISTRY_VALUES ?= helm/forgejo-registry-values.yaml
FORGEJO_SECRETS ?= helm/forgejo-secrets.sops.yaml
FORGEJO_SECRET_SCAN_TEMPLATE ?= manifests/forgejo-secret-scan-template.yaml
FORGEJO_INGRESS ?= manifests/forgejo-ingress.yaml
FORGEJO_SSH_NODEPORT ?= manifests/forgejo-ssh-nodeport.yaml
FORGEJO_BASE_URL ?= https://forgejo.coulomb.social
@ -74,8 +66,6 @@ FORGEJO_MAILER_EXTERNAL_SECRET ?= manifests/forgejo-mailer-externalsecret.yaml
REUSE_RUNTIME_EXTERNAL_SECRET ?= manifests/reuse-surface-runtime-externalsecret.yaml
PLATFORM_REPO ?= $(HOME)/railiance-platform
ESO_CHART_VERSION ?= 0.16.1
INTER_HUB_IMAGE_REF = $(INTER_HUB_IMAGE_REPOSITORY):$(INTER_HUB_IMAGE_TAG)
INTER_HUB_IMAGE_SET_ARG = $(if $(strip $(INTER_HUB_IMAGE_TAG)),--set image.tag=$(INTER_HUB_IMAGE_TAG),)
CORE_HUB_RELEASE ?= core-hub
CORE_HUB_NAMESPACE ?= core-hub
@ -138,16 +128,6 @@ check-coulombcore-kubeconfig: ## Verify CoulombCore kubeconfig and k3s API tunne
check-production-kubeconfig: check-coulombcore-kubeconfig ## Verify production app cluster API (CoulombCore)
check-inter-hub-image-tag: ## Require an explicit inter-hub image tag for production deploys
@test -n "$(INTER_HUB_IMAGE_TAG)" || { \
echo "Set INTER_HUB_IMAGE_TAG=<sha> for inter-hub production deploys." >&2; \
echo "Example: INTER_HUB_IMAGE_TAG=91037a4 make inter-hub-deploy" >&2; \
exit 1; \
}
check-inter-hub-image: check-inter-hub-image-tag ## Verify the inter-hub OCI image tag exists before deploy
tools/check-oci-image.sh "$(INTER_HUB_IMAGE_REF)"
check-core-hub-image-tag: ## Require an explicit core-hub image tag for production deploys
@test -n "$(CORE_HUB_IMAGE_TAG)" || { \
echo "Set CORE_HUB_IMAGE_TAG=<sha> for core-hub production deploys." >&2; \
@ -314,43 +294,6 @@ coulomb-social-env-secret: ## Create/patch coulomb-social-env (SECRET_KEY, DATAB
coulomb-social-env-secret-dry-run: ## Plan coulomb-social-env keys without applying
@bash tools/create-coulomb-social-env-secret.sh --dry-run $(COULOMB_SOCIAL_ENV_SECRET_ARGS)
##@ Inter-Hub
inter-hub-render-baseline: ## Render checked-in inter-hub values for chart validation only
helm template $(INTER_HUB_RELEASE) $(INTER_HUB_CHART) \
--namespace $(INTER_HUB_NAMESPACE) \
-f $(INTER_HUB_VALUES)
inter-hub-dry-run: check-railiance01-kubeconfig check-inter-hub-image-tag ## helm template render with an explicit production image tag
KUBECONFIG="$(INTER_HUB_KUBECONFIG)" helm template $(INTER_HUB_RELEASE) $(INTER_HUB_CHART) \
--namespace $(INTER_HUB_NAMESPACE) \
-f $(INTER_HUB_VALUES) $(INTER_HUB_IMAGE_SET_ARG)
inter-hub-server-dry-run: check-railiance01-kubeconfig check-inter-hub-image ## Helm server dry-run inter-hub upgrade on Railiance01
KUBECONFIG="$(INTER_HUB_KUBECONFIG)" helm upgrade --install $(INTER_HUB_RELEASE) $(INTER_HUB_CHART) \
--namespace $(INTER_HUB_NAMESPACE) --create-namespace \
-f $(INTER_HUB_VALUES) $(INTER_HUB_IMAGE_SET_ARG) --dry-run=server --timeout 5m
inter-hub-deploy: check-railiance01-kubeconfig check-inter-hub-image ## Deploy / upgrade inter-hub Helm release on Railiance01
KUBECONFIG="$(INTER_HUB_KUBECONFIG)" helm upgrade --install $(INTER_HUB_RELEASE) $(INTER_HUB_CHART) \
--namespace $(INTER_HUB_NAMESPACE) --create-namespace \
-f $(INTER_HUB_VALUES) $(INTER_HUB_IMAGE_SET_ARG) --wait --timeout 5m
inter-hub-status: check-railiance01-kubeconfig ## Show inter-hub pod / svc / ingress / cert state on Railiance01
KUBECONFIG="$(INTER_HUB_KUBECONFIG)" kubectl get pods,svc,ingress,certificate -n $(INTER_HUB_NAMESPACE) -l app=$(INTER_HUB_RELEASE) --ignore-not-found
inter-hub-release-info: check-railiance01-kubeconfig ## Show inter-hub Helm history, values, deployment, and pods
KUBECONFIG="$(INTER_HUB_KUBECONFIG)" helm history $(INTER_HUB_RELEASE) -n $(INTER_HUB_NAMESPACE)
KUBECONFIG="$(INTER_HUB_KUBECONFIG)" helm get values $(INTER_HUB_RELEASE) -n $(INTER_HUB_NAMESPACE)
KUBECONFIG="$(INTER_HUB_KUBECONFIG)" kubectl describe deploy/$(INTER_HUB_RELEASE) -n $(INTER_HUB_NAMESPACE)
KUBECONFIG="$(INTER_HUB_KUBECONFIG)" kubectl get pods -n $(INTER_HUB_NAMESPACE) -o wide
inter-hub-smoke: ## Verify public inter-hub v2 route and OpenAPI surface after rollout
INTER_HUB_BASE_URL="$(INTER_HUB_BASE_URL)" tools/inter-hub-smoke.sh
inter-hub-logs: check-railiance01-kubeconfig ## Tail inter-hub app logs from Railiance01
KUBECONFIG="$(INTER_HUB_KUBECONFIG)" kubectl logs -n $(INTER_HUB_NAMESPACE) -l app=$(INTER_HUB_RELEASE) -f --tail=50
##@ Core Hub
core-hub-helm-adopt: check-production-kubeconfig ## Annotate live Core Hub resources for Helm ownership
@ -469,7 +412,10 @@ forgejo-server-dry-run: check-railiance01-kubeconfig ## Helm server dry-run Forg
-f <(KUBECONFIG="$(FORGEJO_KUBECONFIG)" tools/forgejo-helm-secrets.sh) \
--dry-run=server --timeout 5m
forgejo-deploy: check-railiance01-kubeconfig forgejo-mailer-es-deploy ## Deploy / upgrade Forgejo on railiance01
forgejo-secret-scan-template-deploy: check-railiance01-kubeconfig ## Install the immutable, non-secret receive-hook template
KUBECONFIG="$(FORGEJO_KUBECONFIG)" kubectl apply --server-side -f $(FORGEJO_SECRET_SCAN_TEMPLATE)
forgejo-deploy: check-railiance01-kubeconfig forgejo-mailer-es-deploy forgejo-secret-scan-template-deploy ## Deploy / upgrade Forgejo on railiance01
@test -n "$$(KUBECONFIG="$(FORGEJO_KUBECONFIG)" kubectl get secret forgejo-mailer -n $(FORGEJO_NAMESPACE) -o name 2>/dev/null)" || { \
echo "Missing secret forgejo-mailer in $(FORGEJO_NAMESPACE)." >&2; \
echo "Ensure ExternalSecret is Ready and OpenBao role external-secrets-forgejo is configured." >&2; \
@ -510,6 +456,12 @@ forgejo-verify: check-railiance01-kubeconfig ## T05 acceptance: smoke, TLS, DB,
forgejo-operator-bootstrap: ## Idempotent operator user + SSH key bootstrap (no secrets in Git)
FORGEJO_BASE_URL="$(FORGEJO_BASE_URL)" tools/forgejo-operator-bootstrap.sh
forgejo-breakglass-seal: check-railiance01-kubeconfig ## Founder: rotate break-glass admin password into OpenBao (interactive, production-touching)
FORGEJO_KUBECONFIG="$(FORGEJO_KUBECONFIG)" FORGEJO_NAMESPACE="$(FORGEJO_NAMESPACE)" tools/forgejo-breakglass-seal.sh
forgejo-breakglass-verify: ## Founder: verify one break-glass sign-in and write value-free evidence
FORGEJO_BASE_URL="$(FORGEJO_BASE_URL)" tools/forgejo-breakglass-verify.sh
forgejo-runner-registration-sops-bootstrap: check-railiance01-kubeconfig ## Capture cluster runner token into SOPS (operator age key)
KUBECONFIG="$(FORGEJO_KUBECONFIG)" tools/forgejo-runner-registration-sops-bootstrap.sh
@ -642,4 +594,40 @@ help: ## Show this help
/^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 } \
/^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) }' $(MAKEFILE_LIST)
.PHONY: check-tools check-sops k8s-server-dry-run apps-pg-status apps-pg-restore-drill check-railiance01-kubeconfig check-inter-hub-image-tag check-inter-hub-image check-vergabe-image-tag check-vergabe-image vergabe-dry-run vergabe-deploy vergabe-ingress-deploy vergabe-status vergabe-migrate vergabe-seed vergabe-superuser vergabe-logs vergabe-db-url-secret check-coulomb-social-image-tag coulomb-social-dry-run coulomb-social-deploy coulomb-social-ingress-deploy coulomb-social-status coulomb-social-logs coulomb-social-env-secret coulomb-social-env-secret-dry-run eso-deploy forgejo-openbao-eso-token-apply forgejo-openbao-store-deploy forgejo-mailer-es-deploy forgejo-mailer-es-status forgejo-dry-run forgejo-server-dry-run forgejo-deploy forgejo-ingress-deploy forgejo-ssh-nodeport-deploy forgejo-status forgejo-smoke forgejo-npm-smoke forgejo-verify forgejo-operator-bootstrap forgejo-runner-registration-sops-bootstrap forgejo-secrets-check forgejo-logs forgejo-runner-registration-deploy forgejo-runner-deploy forgejo-runner-status forgejo-runner-logs inter-hub-render-baseline inter-hub-dry-run inter-hub-server-dry-run inter-hub-deploy inter-hub-status inter-hub-release-info inter-hub-smoke inter-hub-logs reuse-dry-run reuse-deploy reuse-status reuse-smoke reuse-logs reuse-forgejo-webhook reuse-openbao-eso-token-apply reuse-openbao-store-deploy reuse-runtime-es-deploy reuse-runtime-es-status reuse-webhook-smoke policy-nexus-check-binding policy-nexus-check-image policy-nexus-render policy-nexus-server-dry-run policy-nexus-deploy policy-nexus-status policy-nexus-smoke policy-nexus-rollback help
.PHONY: check-tools check-sops k8s-server-dry-run apps-pg-status apps-pg-restore-drill check-railiance01-kubeconfig check-vergabe-image-tag check-vergabe-image vergabe-dry-run vergabe-deploy vergabe-ingress-deploy vergabe-status vergabe-migrate vergabe-seed vergabe-superuser vergabe-logs vergabe-db-url-secret check-coulomb-social-image-tag coulomb-social-dry-run coulomb-social-deploy coulomb-social-ingress-deploy coulomb-social-status coulomb-social-logs coulomb-social-env-secret coulomb-social-env-secret-dry-run eso-deploy forgejo-openbao-eso-token-apply forgejo-openbao-store-deploy forgejo-mailer-es-deploy forgejo-mailer-es-status forgejo-dry-run forgejo-server-dry-run forgejo-deploy forgejo-ingress-deploy forgejo-ssh-nodeport-deploy forgejo-status forgejo-smoke forgejo-npm-smoke forgejo-verify forgejo-operator-bootstrap forgejo-runner-registration-sops-bootstrap forgejo-secrets-check forgejo-logs forgejo-runner-registration-deploy forgejo-runner-deploy forgejo-runner-status forgejo-runner-logs reuse-dry-run reuse-deploy reuse-status reuse-smoke reuse-logs reuse-forgejo-webhook reuse-openbao-eso-token-apply reuse-openbao-store-deploy reuse-runtime-es-deploy reuse-runtime-es-status reuse-webhook-smoke policy-nexus-check-binding policy-nexus-check-image policy-nexus-render policy-nexus-server-dry-run policy-nexus-deploy policy-nexus-status policy-nexus-smoke policy-nexus-rollback help
.PHONY: forgejo-secret-scan-template-deploy
##@ Isolated Forgejo source release preparation (no deployment/publication)
FORGEJO_BUILD_WORKSPACE ?= /tmp/railiance-apps-forgejo-release
FORGEJO_SOURCE_ARCHIVE ?=
FORGEJO_GO_ARCHIVE ?=
FORGEJO_NODE_ARCHIVE ?=
FORGEJO_BUILD_GOMODCACHE ?=
FORGEJO_BUILD_GOCACHE_SEED ?=
FORGEJO_BUILD_NPM_CACHE_SEED ?=
.PHONY: forgejo-release-prepare forgejo-release-acquire-npm forgejo-release-build forgejo-release-verify forgejo-release-container
forgejo-release-prepare: ## Verify archives/patch/locks and prepare a new isolated workspace
/usr/bin/python3 -I tools/forgejo-release-build.py prepare --workspace "$(FORGEJO_BUILD_WORKSPACE)" --source-archive "$(FORGEJO_SOURCE_ARCHIVE)" --go-archive "$(FORGEJO_GO_ARCHIVE)" --node-archive "$(FORGEJO_NODE_ARCHIVE)" --module-cache "$(FORGEJO_BUILD_GOMODCACHE)" $(if $(FORGEJO_BUILD_GOCACHE_SEED),--go-cache-seed "$(FORGEJO_BUILD_GOCACHE_SEED)") $(if $(FORGEJO_BUILD_NPM_CACHE_SEED),--npm-cache-seed "$(FORGEJO_BUILD_NPM_CACHE_SEED)")
forgejo-release-acquire-npm: ## Explicit public registry dependency acquisition; ignores lifecycle scripts
/usr/bin/python3 -I tools/forgejo-release-build.py acquire-npm --workspace "$(FORGEJO_BUILD_WORKSPACE)"
forgejo-release-build: ## Offline full frontend, bindata and static backend via upstream targets
/usr/bin/python3 -I tools/forgejo-release-build.py build --workspace "$(FORGEJO_BUILD_WORKSPACE)"
forgejo-release-verify: ## Verify all embedded assets, static executable and new CLI shape offline
/usr/bin/python3 -I tools/forgejo-release-build.py verify --workspace "$(FORGEJO_BUILD_WORKSPACE)"
forgejo-release-container: ## Build/verify a deterministic local rootless OCI archive; never publish or deploy
/usr/bin/python3 -I tools/forgejo-release-build.py container --workspace "$(FORGEJO_BUILD_WORKSPACE)"
# CUST-WP-0079-T05: staged-source generation; hook uses the isolated recipe.
.PHONY: generated-work-records generated-work-records-check
generated-work-records:
$(MAKE) --no-print-directory -f .generated-work-records.mk generated-work-records
generated-work-records-check:
$(MAKE) --no-print-directory -f .generated-work-records.mk generated-work-records-check

View file

@ -270,3 +270,36 @@ keywords: [operator, runbook, sops, cnpg, dry-run, smoke-test, deployment]
9. Run `make check-tools` before deploy work. Run
`SOPS_SENTINEL=<encrypted-file> make check-sops` when app release work
touches encrypted SOPS files.
## Generated work-record index
This repository opts into staged-source WORK-RECORDS.md generation through
`.generated-work-records.json` and the isolated `.generated-work-records.mk`
recipe. `make generated-work-records` reuses the State Hub renderer through
the Custodian adapter; `make generated-work-records-check` verifies byte-exact
agreement with staged Markdown. The pre-commit hook uses the isolated recipe,
preserves unstaged index edits by refusing them, and retains the generated-only
commit guard and existing security checks. It does not contact the hub, alter
workplan statuses or grant deployment authority. This consumes shared tooling;
it does not make this repository the provider of the generator capability.
## Local orientation brief policy — 2026-10-19
`.custodian-brief.md` is a local, ignored State Hub orientation snapshot. Its
existing bytes remain available for offline session startup; refreshing it does
not require a Git commit. Authored workplans and the generated WORK-RECORDS index
retain their existing source and staged-generation contracts. No runtime or
credential change follows from this repository hygiene policy.
## Synthetic pilot sizing follow-up
Application-owner local evidence and configurable workloads live in
`vergabe-teilnahme/docs/evidence/2026-10-19-synthetic-sizing/` and that repository's
`config/synthetic-pilot*.json`. RAPPS-WP-0014-T04 owns fresh exact release/hardware
metadata and a co-timed isolated replay/shared-contention assessment feeding
CUST-WP-0071-T02 and RESOURCE-WP-0008-T02. Routine read-only capture/preparation is
authorized; no benchmark writes to customer datasets or unreviewed deployment
changes follow from the local synthetic result. The existing T03 human/recovery
gate remains separate and unchanged.

View file

@ -44,7 +44,7 @@ export KUBECONFIG=~/.kube/config
make cnpg-backup-status
# railiance01 activity
export KUBECONFIG=~/.kube/config-hosteurope
export KUBECONFIG=~/.kube/config-railiance01
kubectl -n activity-core get externalsecret actcore-backup-offsite
# inventory should list Daily CNPG Option A Backup enabled
```

View file

@ -11,8 +11,12 @@
| workplan | RAPPS-WP-0001 | finished | — | workplans/RAPPS-WP-0001-cnpg-backup-scheduledbackup-coverage.md |
| workplan | RAPPS-WP-0002 | finished | — | workplans/RAPPS-WP-0002-railiance01-activity-core-backup-automation.md |
| workplan | RAPPS-WP-0003 | finished | — | workplans/RAPPS-WP-0003-policy-nexus-production-binding.md |
| workplan | RAPPS-WP-0014 | blocked | — | workplans/RAPPS-WP-0014-vergabe-invited-pilot.md |
| workplan | RAPPS-WP-0014 | active | — | workplans/RAPPS-WP-0014-vergabe-invited-pilot.md |
| workplan | RAPPS-WP-0015 | active | — | workplans/RAPPS-WP-0015-coulomb-personal-content-release.md |
| workplan | RAPPS-WP-0016 | active | — | workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md |
| workplan | RAPPS-WP-0017 | finished | — | workplans/RAPPS-WP-0017-reuse-private-reader-release.md |
| workplan | RAPPS-WP-0018 | finished | — | workplans/RAPPS-WP-0018-wave2-resource-right-sizing.md |
| workplan | RAPPS-WP-0019 | finished | — | workplans/RAPPS-WP-0019-product-neutral-forge-object-names.md |
| task | RAPPS-WP-0001-T01 | done | — | workplans/RAPPS-WP-0001-cnpg-backup-scheduledbackup-coverage.md |
| task | RAPPS-WP-0001-T02 | done | — | workplans/RAPPS-WP-0001-cnpg-backup-scheduledbackup-coverage.md |
| task | RAPPS-WP-0001-T03 | done | — | workplans/RAPPS-WP-0001-cnpg-backup-scheduledbackup-coverage.md |
@ -32,7 +36,30 @@
| task | RAPPS-WP-0014-T01 | done | — | workplans/RAPPS-WP-0014-vergabe-invited-pilot.md |
| task | RAPPS-WP-0014-T02 | done | — | workplans/RAPPS-WP-0014-vergabe-invited-pilot.md |
| task | RAPPS-WP-0014-T03 | wait | — | workplans/RAPPS-WP-0014-vergabe-invited-pilot.md |
| task | RAPPS-WP-0014-T04 | done | — | workplans/RAPPS-WP-0014-vergabe-invited-pilot.md |
| task | RAPPS-WP-0015-T01 | done | — | workplans/RAPPS-WP-0015-coulomb-personal-content-release.md |
| task | RAPPS-WP-0015-T02 | done | — | workplans/RAPPS-WP-0015-coulomb-personal-content-release.md |
| task | RAPPS-WP-0015-T03 | wait | — | workplans/RAPPS-WP-0015-coulomb-personal-content-release.md |
| task | RAPPS-WP-0015-T03 | done | — | workplans/RAPPS-WP-0015-coulomb-personal-content-release.md |
| task | RAPPS-WP-0015-T04 | wait | — | workplans/RAPPS-WP-0015-coulomb-personal-content-release.md |
| task | RAPPS-WP-0015-T05 | done | — | workplans/RAPPS-WP-0015-coulomb-personal-content-release.md |
| task | RAPPS-WP-0015-T06 | done | — | workplans/RAPPS-WP-0015-coulomb-personal-content-release.md |
| task | RAPPS-WP-0015-T07 | done | — | workplans/RAPPS-WP-0015-coulomb-personal-content-release.md |
| task | RAPPS-WP-0015-T08 | done | — | workplans/RAPPS-WP-0015-coulomb-personal-content-release.md |
| task | RAPPS-WP-0016-T01 | done | — | workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md |
| task | RAPPS-WP-0016-T02 | wait | — | workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md |
| task | RAPPS-WP-0016-T03 | wait | — | workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md |
| task | RAPPS-WP-0016-T04 | wait | — | workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md |
| task | RAPPS-WP-0016-T05 | wait | — | workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md |
| task | RAPPS-WP-0016-T06 | done | — | workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md |
| task | RAPPS-WP-0016-T07 | progress | — | workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md |
| task | RAPPS-WP-0016-T08 | progress | — | workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md |
| task | RAPPS-WP-0016-T09 | progress | — | workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md |
| task | RAPPS-WP-0017-T01 | done | — | workplans/RAPPS-WP-0017-reuse-private-reader-release.md |
| task | RAPPS-WP-0017-T02 | done | — | workplans/RAPPS-WP-0017-reuse-private-reader-release.md |
| task | RAPPS-WP-0017-T03 | done | — | workplans/RAPPS-WP-0017-reuse-private-reader-release.md |
| task | RAPPS-WP-0018-T01 | done | — | workplans/RAPPS-WP-0018-wave2-resource-right-sizing.md |
| task | RAPPS-WP-0019-T01 | done | — | workplans/RAPPS-WP-0019-product-neutral-forge-object-names.md |
| task | RAPPS-WP-0019-T02 | done | — | workplans/RAPPS-WP-0019-product-neutral-forge-object-names.md |
| task | RAPPS-WP-0019-T03 | done | — | workplans/RAPPS-WP-0019-product-neutral-forge-object-names.md |
| task | RAPPS-WP-0019-T04 | done | — | workplans/RAPPS-WP-0019-product-neutral-forge-object-names.md |
| task | RAPPS-WP-0019-T05 | done | — | workplans/RAPPS-WP-0019-product-neutral-forge-object-names.md |

View file

@ -0,0 +1,20 @@
{
"schema_version": "policy-nexus-production-binding/v1",
"status": "prepared-unapproved",
"approved_on": null,
"rapp_repo": "rapp-policy-nexus",
"reef": "reef-railiance",
"rail": "rail-kubernetes",
"namespace": "policy-nexus",
"release": "policy-nexus",
"hostname": "policy.coulomb.social",
"image_repository": "forgejo.coulomb.social/coulomb/policy-nexus",
"image_digest": "sha256:4e0db6a57eb00f577159aa6ee10f910dd67b03fd0541c6ce849f5426fdb99d06",
"publication_manifest_digest": "ba53cc4900e6bb9ab8aa4bac4e58cb5fe7a03d656bafb3f9cb3d9c8ebff7843a",
"source_inventory_digest": "40d9e8852fe4c9e75b71f371d0fdae38664b068e7498711b0ac5671d0f41efb5",
"source_set_digest": "15bda2e332285afc862df8974cf29de403737df5073fa362adfc6d203cf2f256",
"residual_risk_owner": "the-custodian",
"proposal_id": "POLICY-PUB-20261011-SECURITY-LAYER-V08-R1",
"publication_source_revision": "b64a0c52ec1ea8d1efa99dd7f870e580d7c60df8",
"source_lock_digest": "99c695f46a54ead4cfe8f560f3648bf4db19404fe198b156c740115c97d3ed40"
}

View file

@ -1,7 +1,7 @@
{
"schema_version": "policy-nexus-production-binding/v1",
"status": "release-approved",
"approved_on": "2026-09-01",
"approved_on": "2026-10-19",
"rapp_repo": "rapp-policy-nexus",
"reef": "reef-railiance",
"rail": "rail-kubernetes",
@ -9,9 +9,15 @@
"release": "policy-nexus",
"hostname": "policy.coulomb.social",
"image_repository": "forgejo.coulomb.social/coulomb/policy-nexus",
"image_digest": "sha256:23df0ab3dc80d2a92b8058eeb572c222ceb05d17434f173a24142b2450ab3cea",
"publication_manifest_digest": "09f9f7edcc46e31d4b3f73b62d16fdf17808ae1f893f312242b7b9e2037319e0",
"source_inventory_digest": "9658803bc79d19cef1a9c842d1a8c161f1998d59ce6e4bc4df3c88aba2f4c9c4",
"source_set_digest": "03f04902a3559099433795453829d75470aa1490dd243b42e7703b6eab87772f",
"residual_risk_owner": "the-custodian"
"image_digest": "sha256:214b122c3d175563f114552fa1826dac9642433be019c736c3265d0a3ef379f1",
"publication_manifest_digest": "8c74b4e414f7760429600b7ba0b3627fcfe438417bfe6695f55acfa6905d352e",
"source_inventory_digest": "e84453b6b411a7d66d2b5efbf083cd0033441d6b12983326442933da87bf61fd",
"source_set_digest": "b00535063c56ede08f0a292b301a47add18bf70f81904bf3f434c8faa5ebe682",
"residual_risk_owner": "the-custodian",
"proposal_id": "POLICY-PUB-20261019-BUILD-MODE-GUARDRAIL-R1",
"publication_source_revision": "9fabcce1cb4c83165bcaba8be58d76788b4d87a1",
"source_lock_digest": "c150c6c0745441b4b69884ecc6cf55e97db26080fcf4c2224775b1f755580529",
"approval_ref": "conversation:call_5157a09329d34746a3fdf857d5a47b8d:item0",
"verified_helm_revision": 10,
"native_verification_ref": "docs/evidence/2026-10-19-build-mode-publication/receipt.json"
}

172
build/forgejo/README.md Normal file
View file

@ -0,0 +1,172 @@
# Forgejo release preparation (build only)
The Apps Makefile targets package the digest-bound 49-path Forgejo 11.0.3
source candidate in `forgejo-security-extension.patch`. It combines the original
three-group any-of gate, private secret-file input, private source-operation DB
primitives and the producer-owned Access Engine client in the OIDC callback.
Coordinator source tests and complete release checks are recorded separately;
independent owner review and operational admission of this revision remain open. They never deploy, push, change default values or choose
an operational image. Third-party application source is expanded only into a new
`/tmp` workspace; this repository retains a release input lock and exact reviewed
patch, not a second source-code tree. Upstream source ownership and publication
remain separate owner work.
Supply explicit public input archives/cache paths to `make forgejo-release-prepare`:
`FORGEJO_SOURCE_ARCHIVE`, `FORGEJO_GO_ARCHIVE`, `FORGEJO_NODE_ARCHIVE`,
`FORGEJO_BUILD_GOMODCACHE`, optionally `FORGEJO_BUILD_GOCACHE_SEED` and
`FORGEJO_BUILD_NPM_CACHE_SEED`. Every archive, patch, source output, dependency
lock and selected host compiler tool is digest-bound by `inputs.lock.json`.
Go's official checksums and Node's official SHASUMS must be independently checked
when acquiring those public archives. The source archive is official Forgejo
v11.0.3; archive PAX commit b81aa4d0807cc1e2d6a2fbafd0b75c46cc0ae237.
The workspace must not exist. Existing trials are preserved, never restarted
because an observer timed out. The recipe's `process.json` records the exact
active child PID, phase and command, then completion; logs and receipts stay in
the workspace. Do not run two phases against the same workspace concurrently.
`make forgejo-release-acquire-npm` explicitly acquires only public registry
packages, with fresh HOME/config paths, integrity-bound lockfile and lifecycle
scripts disabled. With the cache ready, `make forgejo-release-build` reruns
`npm ci --offline --ignore-scripts`, Go module verification, upstream frontend
compilation and upstream generation/static backend targets inside a network
namespace without connectivity. It uses the exact Go/Node distributions, local
compiler inputs, `bindata timetzdata sqlite sqlite_unlock_notify` plus upstream
static target's `netgo osusergo`, explicit Make GOFLAGS for trimpath/buildvcs and
fixed file/directory timestamps. Build tags retain the upstream rootless build
profile. `make forgejo-release-verify` compares every embedded public/options/
template file to actual generated source bytes, checks the extension CLI and
checks the ELF has no dynamic interpreter. These are local artifact checks,
not application login, DB, native rollout or architecture acceptance.
For a **local container build** only, first acquire the locked public amd64
base with an empty Docker client config and the explicit local socket. Then
`make forgejo-release-container` uses that exact digest as a build input and a
minimal context containing only the verified executable and generated Dockerfile.
It directly exports a local OCI archive with `rewrite-timestamp=true`, no-cache,
no image load or push. SOURCE_DATE_EPOCH alone does not fix COPY parent-directory
timestamps. The image epoch is max(source epoch,ceil immutable base created
instant), so the original base layers remain exact while new metadata is stable.
The recipe verifies every OCI blob, original base layer prefix, rootless runtime
configuration, provenance labels and exact final executable. Two no-cache exports
must reproduce the frozen output image and archive digests before claiming
reproducibility. The actual local proof receipts are in the accompanying source
proposal packet, not an admission of the resulting artifact.
There is no deployment tag, registry publication, RUN directive, privileged
container or Docker host/secret mount. Public base acquisition and a standard
local Docker/buildx daemon supporting OCI export/layer timestamp rewriting are
explicit prerequisites. An unavailable feature is a retained blocker; do not
switch to a privileged builder or claim a successful image from a backend-only
binary. Existing contexts/output archives refuse overwrite. Reconcile an actual
failed export explicitly, retaining original metadata/logs/artifacts; observation
timeouts never justify another build. Older prototype load/save behavior and
its config-versus-manifest-ID issue are retained in the source trial history,
not shipped as a production continuation.
The static host toolchain is pinned for this Linux amd64 review estate. Broader
cross-host reproducibility or a different architecture/compiler/libc is a new
input revision, not an implicit fallback. The glibc static-linker's optional
SQLite `dlopen` diagnostic is retained; no SQLite-extension runtime compatibility
or production platform admission is inferred from ELF inspection or asset checks.
An accepted owner image/source/backend, authenticated access-engine decision,
private secret-input boundary, type/case/descriptor re-freeze, current operation/
inverse sessions, durable journal and application/Helm writer exclusion are
separate gates. Helm omission/rollback does not remove persisted OAuth source.
The existing case gate is compatibility code, not a single-PDP exception.
## KeyCape identity and callback admission
The callback independently verifies the ID token against owner-admitted public
keys, algorithms, issuer, audience and token type. Public trust is loaded from
`FORGEJO_ACCESS_ENGINE_ADMISSION_FILE`; only an absolute private file reference
is used for the PDP caller token. No admission file or operational keys are
supplied by this build. New fields `tenant_source_claim` and
`max_authentication_age` are mandatory. The KeyCape v0.3 profile uses
`assurance_claim: assurance` and `tenant_source_claim: tenant_source`. Maximum
identity age and maximum authentication age are separate positive durations
chosen by the owner, not implicit defaults.
The signed assurance object preserves `level`, `methods`, boolean `mfa`,
`source: key-cape` and integer `at`. Its authentication time must not follow
issuance or the current time, and must satisfy the admitted authentication-age
bound. Identity validity ends at the earliest token expiration, issued-at age
bound or authentication-age bound. Directory, registration and default tenant
provenance remain distinct in the request. Mutable UserInfo cannot replace
these authenticated facts. Current UserInfo subject and the Goth subject must
match the independently verified token; the three required group alternatives
remain policy input.
The parser also represents valid lower-assurance facts. Application policy
continues to decide authorization. The Check client and unchanged canonical
signature bytes are a generated source slice of Access Engine ca0cb757; Forgejo
adds no policy evaluator. Exact caller/policy/registry/signer/request/lifetime
pins, an authenticated HTTPS connection and a current ALLOW are required.
Synthetic callback fixtures test ALLOW, DENY and adverse cases, including an ID
token serialized by the actual pinned KeyCape handler. Those fixtures do not
prove an actual browser, factor, custody or native rollout.
The current integration covers this guarded OIDC callback. Ongoing application
HTTP/API/Git/SSH paths, current operation/inverse sessions, durable private
evidence and retention, deployed PostgreSQL/custody acceptance and exclusion of all release
writers remain open in RAPPS-WP-0016-T08/T09 and NK-WP-0048-T43. An old image
approval or source test result cannot admit this candidate.
## PostgreSQL operation-source verification
The source primitive has now run on a disposable PostgreSQL 16.14 instance using
the actual Forgejo engine, model schemas and OAuth configuration types. Tests
cover default, explicit-public and named schemas, exact source restoration, drift
and linked-account refusals, trigger rollback, uncooperative SQL writer exclusion
and concurrent preparation. Independent client processes and an actual server
restart verify prepared/applied/inverse journal recovery. The test-only
`source_operation_postgres` tag selects the integration tests; no runtime route,
credential, session or authority callback is added. The companion Custodian
`forgejo-postgres-source-r1` packet holds the isolated fixture, commands, source
pins, counterexamples and original failed attempts.
Actual PostgreSQL diagnostics exposed a tracing-hook composition crash. Tracing
now publishes its task context to the shared XORM hook context so later hooks
retain it. Ordinary and private SQLite traces and actual PostgreSQL failure
queries exercise this path. Controlled copies without the table lock or private
query marker fail their respective regressions. These are source/database
mechanics checks; owner admission, current authenticated operation/inverse
sessions, runtime/backup custody, retention, every release writer and native
deployed-database acceptance remain separate obligations. The previous frozen
release is historical and does not contain this context correction.
## Current-token callback source continuation
The 49-path candidate additionally vendors the unchanged, producer-generated
KeyCape current-token adapter from `08c083ba`; no consumer token registry or
handwritten copy is introduced. `current_token` (explicit HTTPS issuer, resource
audience, owning client, CA/JWKS/private-client-file references and maximum token
age) and `application_tenant` are mandatory in the independently admitted
`FORGEJO_ACCESS_ENGINE_ADMISSION_FILE` for this guarded callback profile. A signed
caller cannot select the application's tenant. No operational configuration or
credential is supplied by the build.
The callback requires the online KeyCape human ID token's exact RS256 `at_hash`
access-grant binding before private caller use. Access-only `jti`/`scope` cannot
substitute for an ID token. It joins independently verified subject/tenant/type,
issuance, assurance/authentication time and tenant provenance to a fresh issuer
observation before the PDP, then checks current status again after the decision.
Source tests refuse mixed, revoked, suspended, stale, malformed and unavailable
inputs; actual HTTPS issuer PKCE/discovery/UserInfo/status/revocation and real
Forgejo provider/callback run in five isolated consumer processes. MFA session
seeds and PDP admissions are synthetic. Complete release build/asset/OCI evidence
for the preceding patch remains historical; this revision requires its own build
and independent owner/package/type/custody admission before operational use.
The user selected current KeyCape checks on every ongoing API/Git/SSH operation
for NetKingdom-linked accounts, while retaining the separate local break-glass
path. That is an implementation requirement under RAPPS16-T09/NK48-T43, not a
claim that the callback implements those routes or a native deployment grant.
Ongoing resource/action/current-group decisions, protected operation/inverse and
all release writers/journal custody remain acceptance work.
Final composed source validation passes 116 test/subtest results without skips.
The initial 114-pass run skipped its private file-replacement check because a
reused fixture was exhausted; the runner now creates fresh private/read-only
aliases. That initial skip is retained and is not PostgreSQL acceptance.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,244 @@
{
"schema": "railiance-apps.forgejo-release-inputs.v1",
"forgejo_version": "11.0.3",
"source_archive_sha256": "1ddd8c926ca07d40436897971458ace7dcf1d3496a0813ab9e0d869edef2365c",
"source_commit": "b81aa4d0807cc1e2d6a2fbafd0b75c46cc0ae237",
"patch_sha256": "9fe3d7baa648c9347964a177befab5070e00151d1ec5ddff16c41890db84e62a",
"go_archive_sha256": "3333f6ea53afa971e9078895eaa4ac7204a8c6b5c68c10e6bc9a33e8e391bdd8",
"go_version": "1.24.3",
"node_archive_sha256": "dafe2e8f82cb97de1bd10db9e2ec4c07bbf53389b0799b1e095a918951e78fd4",
"node_version": "22.15.0",
"source_date_epoch": 1752105237,
"release_version": "11.0.3+cust80.9fe3d7ba",
"tags": "bindata timetzdata sqlite sqlite_unlock_notify",
"runtime_base": "code.forgejo.org/forgejo/forgejo@sha256:41649dff5311664097d29b4d80620403f0b27e7c0eee0b8af79b132a37f3807b",
"runtime_index_digest": "sha256:691657c7e64af7730a2e885e6d022cf16ae60724931c5392bd61838afadcaeba",
"platform": "linux/amd64",
"reviewed_outputs": [
{
"path": "cmd/admin_auth_oauth.go",
"sha256": "d718d817c46f6393488a9c5a894b7535640cd0ec1a1ae3f72f8411a2bbec3b38"
},
{
"path": "cmd/admin_auth_oauth_secret_linux.go",
"sha256": "6741961f1df67c1afbf34c796e549425f0e04a0d0678c6eedb05f66d4754c64b"
},
{
"path": "cmd/admin_auth_oauth_secret_linux_test.go",
"sha256": "b28c2369dfab9bf2ca48e707e01b4394a36c8be5490cfe5bb658017bb626c4b2"
},
{
"path": "cmd/admin_auth_oauth_secret_other.go",
"sha256": "54056c989a4757e73db0252130e7c16bf16f5ec8eaaca2468f019ba2ef5c532d"
},
{
"path": "go.mod",
"sha256": "11fe525ccb3f099aa281738b86097bbd587a99b4640ddae8dd04dd7d046c49f0"
},
{
"path": "models/auth/source_operation.go",
"sha256": "ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31"
},
{
"path": "models/auth/source_operation_internal_test.go",
"sha256": "af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb"
},
{
"path": "models/auth/source_operation_postgres_export_test.go",
"sha256": "b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d"
},
{
"path": "models/auth/source_operation_postgres_test.go",
"sha256": "4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe"
},
{
"path": "models/auth/source_operation_process_test.go",
"sha256": "40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd"
},
{
"path": "models/auth/source_operation_test.go",
"sha256": "77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428"
},
{
"path": "models/db/context.go",
"sha256": "d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7"
},
{
"path": "models/db/engine.go",
"sha256": "d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8"
},
{
"path": "models/db/sensitive_sql.go",
"sha256": "06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584"
},
{
"path": "models/db/sensitive_sql_logging_test.go",
"sha256": "d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7"
},
{
"path": "models/db/sensitive_sql_test.go",
"sha256": "de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b"
},
{
"path": "models/db/tracing_context_test.go",
"sha256": "612b60c67a2be7e2da9173b95d5ef5abd7b9e1e377360828e39a622610bba070"
},
{
"path": "routers/web/admin/auths.go",
"sha256": "3498229e06fb9ff52396bcb5e68c0cc2d2ad24e6494533d8717d7c833f3e2923"
},
{
"path": "routers/web/auth/oauth.go",
"sha256": "58bff778e0dc17a521706289bf45c3e0d76a759a0874f76c379b3d1ddb419b17"
},
{
"path": "services/auth/source/oauth2/access_engine.go",
"sha256": "d16330043825d0284253e65d28bda2de4edbc633e4887d8c3618a106ee251daa"
},
{
"path": "services/auth/source/oauth2/access_engine_assurance.go",
"sha256": "42de751b1e9a243301d566260954813b770558ecb449639acaeab307fe788642"
},
{
"path": "services/auth/source/oauth2/access_engine_assurance_test.go",
"sha256": "44e7459f6c1106a264093c19d61d01a8365f6ad22682906552d832ba39d61dd3"
},
{
"path": "services/auth/source/oauth2/access_engine_azp_test.go",
"sha256": "5f94f43ade9a79b6b7ef529c80115a8c32acfece425dc8ed3f24e6ad839fc0ce"
},
{
"path": "services/auth/source/oauth2/access_engine_callback_test.go",
"sha256": "1cbe378cc20149be7b354484ab3297916733564f03a2441cd276f9039d043580"
},
{
"path": "services/auth/source/oauth2/access_engine_current_token.go",
"sha256": "4a2e484ece7cf74686173d5e85c127643287570c2298af135ddfdc7c450b18d8"
},
{
"path": "services/auth/source/oauth2/access_engine_current_token_test.go",
"sha256": "58f799c08a7b4638780abc511bd1a7359bd5e691c7805ff54582d02d2f42cae1"
},
{
"path": "services/auth/source/oauth2/access_engine_purpose_test.go",
"sha256": "533dc3153fcf9bfcf7bfc08712c057092c7b3892882f217fb8f839fdbdd99939"
},
{
"path": "services/auth/source/oauth2/access_engine_test.go",
"sha256": "9112a142983c0a58c5d40344b58a20fa2c1bdbc7a79b5c586796362839a43b48"
},
{
"path": "services/auth/source/oauth2/current_issuer_fixture_test.go",
"sha256": "24fb430e72848326c5d9ef42eab5028cf915dd601f43fb06010b32b568908781"
},
{
"path": "services/auth/source/oauth2/providers_openid.go",
"sha256": "5dd9b906cce434c774968d57fcc790cb678147345fea4d2e58477ec6eed70bcf"
},
{
"path": "services/auth/source/oauth2/required_claim_values.go",
"sha256": "29f16d07464bbe12e3f65616b6c1eb60561d04cd8ff89ecbfb56f93dbeaf1cda"
},
{
"path": "services/auth/source/oauth2/required_claim_values_test.go",
"sha256": "54a8bd97dc51c524a9adf0c6aefb9a19fa152d234b3bb929bf1a90932662d3d0"
},
{
"path": "services/auth/source/oauth2/source.go",
"sha256": "44e99883c2ec479427b3f72c73353fe33936eed2e5c962b2fa40803576aa5642"
},
{
"path": "services/auth/source/oauth2/source_callout.go",
"sha256": "6abba69df97afc62467f0328eaa5a6383cfb4900202a081dec4b84a21b37349c"
},
{
"path": "third_party/access-engine-pep/LICENSE",
"sha256": "ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6"
},
{
"path": "third_party/access-engine-pep/go.mod",
"sha256": "fea700810e47c1ba5638b86a168f8531c083a6e1222ca80bd2a8947b2ea6f2c3"
},
{
"path": "third_party/access-engine-pep/internal/sign/sign.go",
"sha256": "0df9515922e90775ebbfac798361dcafb20da8a4213796b44e246b8a92046cde"
},
{
"path": "third_party/access-engine-pep/pkg/api/canonical.go",
"sha256": "a122a2e8cd524333b13c5939c7a3685e41efbe47497a64e0c7297c92b7f10d19"
},
{
"path": "third_party/access-engine-pep/pkg/api/caring.go",
"sha256": "1907156f0f889e7b9e9c14970cf7a86a20d00478a496efaf92c7231f27fec5e3"
},
{
"path": "third_party/access-engine-pep/pkg/api/resource_manifest.go",
"sha256": "f0fa4080287d389ed48c2b1d6af96cc62e57ef72c902333f677f964420be133f"
},
{
"path": "third_party/access-engine-pep/pkg/decisionpep/client.go",
"sha256": "0d57f6ddaca5c3f81ce426c0e8b14ba14ea5962096d3be5909aa456eef19eeba"
},
{
"path": "third_party/access-engine-pep/pkg/decisionpep/client_test.go",
"sha256": "1a6be3485f580f95a1f1406fb76e525f2c4bf02fe04a424cfe0095bb728e3f62"
},
{
"path": "third_party/access-engine-pep/producer-source.json",
"sha256": "c1d83e3d79f9860ffaa61ec19a6b3415e914d3058b11b07cdd05a7d1182cdceb"
},
{
"path": "third_party/access-engine-pep/testsupport/signfixture/sign.go",
"sha256": "fe4aa86f444dc20145aefbaca35b24f046264b2c5c8b4e3b057a4496b77a4d29"
},
{
"path": "third_party/keycape-tokenstatus/LICENSE",
"sha256": "ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6"
},
{
"path": "third_party/keycape-tokenstatus/go.mod",
"sha256": "e8b028e1002e63a76dba78a4abbd67882307ad198e4783bad631652061aa0f54"
},
{
"path": "third_party/keycape-tokenstatus/internal/jose/jose.go",
"sha256": "a6de154cd81e34a521b8aac5dce6b45a3cfb015681ed16e2beb8f2a24c72c00d"
},
{
"path": "third_party/keycape-tokenstatus/pkg/tokenstatus/client.go",
"sha256": "f34626a85d67590b9c61474f19ce1021f8afd17093f8f522b3f98e9cef34e575"
},
{
"path": "third_party/keycape-tokenstatus/producer-source.json",
"sha256": "3bd9b25b912e7c17b331997ca5fee9da8a7c314f336e09e3c116885954799b73"
}
],
"lockfiles": {
"go.mod": "11fe525ccb3f099aa281738b86097bbd587a99b4640ddae8dd04dd7d046c49f0",
"go.sum": "19c305c7e68de9d3f1bc0dda2de09c5d38597f1ee7fa411c7b36b92467ae1eaf",
"package.json": "ab5c54ec7077501b50acf3ab5f1fff4f63a51abe99e720b8ecdc6cc6c78f39fc",
"package-lock.json": "fdd5c2f891311be7c01b1907b418c243ca1f0f951fd4b4ee5b5e931c43493657"
},
"host_tools": {
"/usr/bin/make": "d78b8f1d099fbcfb6f2f49ab87223b9b68fb3956642f92d6ec6de812e8afa965",
"/usr/bin/gcc": "1b99826121ae6682a634e5efe09bd3e3df58ce58e0b28f849114ab5b89139c26",
"/usr/bin/ld": "e46a122cc3a29feba3b1d0fc2a274b6e46538be016ebe381370c0801bb98b0b8",
"/usr/bin/ar": "534681ac11c18868cfc4fdf98770aa0ba8973eedc90c231e94e6ba96e1a04f27",
"/usr/bin/bwrap": "e318903862396f96de3df57264e0158682b952fd3fb53ac23d876413e7b30f71",
"/usr/lib/x86_64-linux-gnu/libc.a": "a40c7eae7581f977b65eddb81cbc799213a695749e039d966db55bdee4ac9621",
"/usr/lib/x86_64-linux-gnu/libm.a": "420cd3c406f6f8c12deb3d1291146e9c091ee015a7c39eaf2237f01e80d6f0ae"
},
"runtime_base_config_sha256": "bc5a3df111729582faadebd01d126ba769e2b65d0c62f306f6bc79f4e562452b",
"patch_file": "forgejo-security-extension.patch",
"candidate_review_status": "current-token callback source tested; complete release rebuild and independent owner review pending",
"producer_source_commit": "ca0cb75729a63c9288b7333382d0fa3eb1e8c9a3",
"optional_source_tests": {
"source_operation_postgres": {
"database_major": 16,
"synthetic_fixture_image": "postgres@sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777",
"source_only": true,
"production_connection_or_credentials": false
}
},
"identity_client_source_revision": "git:08c083bae9ff35fbaf028c36d72a95b57d2541b7",
"identity_issuer_source_revision": "git:e2c5a947829568baf837543a6708fd0c3f6a863b"
}

View file

@ -0,0 +1,484 @@
--- a/cmd/admin_auth_oauth.go
+++ b/cmd/admin_auth_oauth.go
@@ -94,6 +94,10 @@
Name: "required-claim-value",
Value: "",
Usage: "Claim value that has to be set to allow users to login with this source",
+ },
+ &cli.StringSliceFlag{
+ Name: "required-claim-values",
+ Usage: "Require any one exact array value from current OpenID Connect UserInfo (cannot combine with required-claim-value)",
},
&cli.StringFlag{
Name: "group-claim-name",
@@ -160,6 +164,7 @@
Scopes: c.StringSlice("scopes"),
RequiredClaimName: c.String("required-claim-name"),
RequiredClaimValue: c.String("required-claim-value"),
+ RequiredClaimValues: c.StringSlice("required-claim-values"),
GroupClaimName: c.String("group-claim-name"),
AdminGroup: c.String("admin-group"),
RestrictedGroup: c.String("restricted-group"),
@@ -177,6 +182,12 @@
}
config := parseOAuth2Config(c)
+ if err := oauth2.ValidateRequiredClaimValues(config.RequiredClaimName, config.RequiredClaimValue, config.RequiredClaimValues); err != nil {
+ return err
+ }
+ if config.RequiredClaimValues != nil && config.Provider != "openidConnect" {
+ return errors.New("required-claim-values requires openidConnect")
+ }
if config.Provider == "openidConnect" {
discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL)
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
@@ -246,6 +257,10 @@
oAuth2Config.RequiredClaimValue = c.String("required-claim-value")
}
+ if c.IsSet("required-claim-values") {
+ oAuth2Config.RequiredClaimValues = c.StringSlice("required-claim-values")
+ }
+
if c.IsSet("group-claim-name") {
oAuth2Config.GroupClaimName = c.String("group-claim-name")
}
@@ -293,6 +308,12 @@
}
oAuth2Config.CustomURLMapping = customURLMapping
+ if err := oauth2.ValidateRequiredClaimValues(oAuth2Config.RequiredClaimName, oAuth2Config.RequiredClaimValue, oAuth2Config.RequiredClaimValues); err != nil {
+ return err
+ }
+ if oAuth2Config.RequiredClaimValues != nil && oAuth2Config.Provider != "openidConnect" {
+ return errors.New("required-claim-values requires openidConnect")
+ }
source.Cfg = oAuth2Config
return auth_model.UpdateSource(ctx, source)
--- a/routers/web/admin/auths.go
+++ b/routers/web/admin/auths.go
@@ -322,6 +322,13 @@
ctx.Data["Source"] = source
ctx.Data["HasTLS"] = source.HasTLS()
+ // The legacy form has no representation for the opt-in current-UserInfo gate.
+ // Refuse rather than silently dropping it during source reconstruction.
+ if current, ok := source.Cfg.(*oauth2.Source); ok && current.RequiredClaimValues != nil {
+ ctx.RenderWithErr("This OAuth source uses required-claim-values; update it through the reviewed CLI.", tplAuthEdit, form)
+ return
+ }
+
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplAuthEdit)
return
--- a/routers/web/auth/oauth.go
+++ b/routers/web/auth/oauth.go
@@ -1427,7 +1427,11 @@
return goth.User{}, err
}
- if oauth2Source.RequiredClaimName != "" {
+ if oauth2Source.RequiredClaimValues != nil {
+ if oauth2Source.Provider != "openidConnect" || !oauth2.MatchRequiredClaimValues(gothUser.RawData, oauth2Source.RequiredClaimName, oauth2Source.RequiredClaimValue, oauth2Source.RequiredClaimValues) {
+ return goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID}
+ }
+ } else if oauth2Source.RequiredClaimName != "" {
claimInterface, has := gothUser.RawData[oauth2Source.RequiredClaimName]
if !has {
return goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID}
--- a/services/auth/source/oauth2/providers_openid.go
+++ b/services/auth/source/oauth2/providers_openid.go
@@ -4,6 +4,7 @@
package oauth2
import (
+ "errors"
"html/template"
"forgejo.org/modules/log"
@@ -43,6 +44,22 @@
if err != nil {
log.Warn("Failed to create OpenID Connect Provider with name '%s' with url '%s': %v", providerName, source.OpenIDConnectAutoDiscoveryURL, err)
}
+ if err != nil {
+ return nil, err
+ }
+ if source.RequiredClaimValues != nil {
+ if err := ValidateRequiredClaimValues(source.RequiredClaimName, source.RequiredClaimValue, source.RequiredClaimValues); err != nil {
+ return nil, err
+ }
+ if provider.OpenIDConfig == nil || provider.SkipUserInfoRequest {
+ return nil, errors.New("required-claim-values needs current UserInfo")
+ }
+ guarded, err := RequireCurrentUserInfoClaim(provider.Client(), provider.OpenIDConfig.UserInfoEndpoint, source.RequiredClaimName)
+ if err != nil {
+ return nil, err
+ }
+ provider.HTTPClient = guarded
+ }
return provider, err
}
--- /dev/null
+++ b/services/auth/source/oauth2/required_claim_values.go
@@ -0,0 +1,202 @@
+// Copyright 2026 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package oauth2
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "io"
+ "net/http"
+ "net/url"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+const maxClaimValues = 1024
+const maxUserInfoBytes = 1 << 20
+
+// ValidateRequiredClaimValues validates only the opt-in list extension. A nil
+// list retains every legacy single-value behavior. An explicitly empty list
+// fails closed rather than turning a configured gate into claim-presence-only.
+func ValidateRequiredClaimValues(name, legacy string, values []string) error {
+ if values == nil {
+ return nil
+ }
+ if !validClaimLiteral(name) || legacy != "" || len(values) == 0 || len(values) > 64 {
+ return errors.New("invalid or conflicting required-claim-values configuration")
+ }
+ seen := map[string]bool{}
+ for _, value := range values {
+ if !validClaimLiteral(value) || seen[value] {
+ return errors.New("invalid required-claim-values literal")
+ }
+ seen[value] = true
+ }
+ return nil
+}
+
+func validClaimLiteral(value string) bool {
+ return value != "" && len(value) <= 256 && utf8.ValidString(value) &&
+ value == strings.TrimSpace(value) && !strings.ContainsAny(value, "\x00\r\n,") &&
+ !strings.ContainsFunc(value, func(r rune) bool { return unicode.IsControl(r) || r == utf8.RuneError })
+}
+
+// strictClaimValues accepts only a well-formed claim array. No scalar parsing,
+// coercion, regex or prefix matching is introduced into the new gate.
+func strictClaimValues(raw any) ([]string, bool) {
+ var values []string
+ switch v := raw.(type) {
+ case []string:
+ values = v
+ case []any:
+ values = make([]string, len(v))
+ for i, value := range v {
+ s, ok := value.(string)
+ if !ok {
+ return nil, false
+ }
+ values[i] = s
+ }
+ default:
+ return nil, false
+ }
+ if len(values) > maxClaimValues {
+ return nil, false
+ }
+ for _, value := range values {
+ if !validClaimLiteral(value) {
+ return nil, false
+ }
+ }
+ return values, true
+}
+
+// MatchRequiredClaimValues evaluates the new consumer gate against current
+// authenticated provider data; it does not mutate or manufacture issuer claims.
+func MatchRequiredClaimValues(claims map[string]any, name, legacy string, required []string) bool {
+ if required == nil || ValidateRequiredClaimValues(name, legacy, required) != nil {
+ return false
+ }
+ raw, present := claims[name]
+ if !present {
+ return false
+ }
+ values, ok := strictClaimValues(raw)
+ if !ok {
+ return false
+ }
+ for _, value := range values {
+ for _, wanted := range required {
+ if value == wanted {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// RequireCurrentUserInfoClaim preserves the existing HTTP client settings and
+// adds a strict response guard for this opt-in source only. A current UserInfo
+// array must overwrite any stale ID-token claim. Missing/invalid data and HTTP
+// failures cannot fall back to the ID-token map in Goth's merging code.
+func RequireCurrentUserInfoClaim(client *http.Client, endpoint, claim string) (*http.Client, error) {
+ u, err := url.Parse(endpoint)
+ if err != nil || u.Scheme != "https" || u.Host == "" || u.User != nil || u.Fragment != "" || !validClaimLiteral(claim) {
+ return nil, errors.New("required-claim-values needs an HTTPS UserInfo endpoint")
+ }
+ if client == nil {
+ client = http.DefaultClient
+ }
+ result := *client
+ base := result.Transport
+ if base == nil {
+ base = http.DefaultTransport
+ }
+ result.Transport = &currentClaimTransport{base: base, endpoint: endpoint, claim: claim}
+ // Redirects could evade endpoint matching or send a bearer to another origin.
+ // They are unsupported for the new guarded provider; legacy clients are intact.
+ result.CheckRedirect = func(*http.Request, []*http.Request) error {
+ return errors.New("guarded OAuth redirects are not supported")
+ }
+ return &result, nil
+}
+
+type currentClaimTransport struct {
+ base http.RoundTripper
+ endpoint, claim string
+}
+
+func (t *currentClaimTransport) RoundTrip(req *http.Request) (*http.Response, error) {
+ response, err := t.base.RoundTrip(req)
+ if req.URL.String() != t.endpoint {
+ return response, err
+ }
+ if err != nil {
+ if response != nil && response.Body != nil {
+ response.Body.Close()
+ }
+ return nil, errors.New("current UserInfo lookup failed")
+ }
+ if response == nil || response.Body == nil {
+ return nil, errors.New("current UserInfo response missing")
+ }
+ defer response.Body.Close()
+ if req.Method != http.MethodGet || response.StatusCode != http.StatusOK {
+ return nil, errors.New("current UserInfo refused")
+ }
+ body, err := io.ReadAll(io.LimitReader(response.Body, maxUserInfoBytes+1))
+ if err != nil || len(body) > maxUserInfoBytes || !utf8.Valid(body) {
+ return nil, errors.New("current UserInfo response invalid")
+ }
+ // Reject duplicate top-level keys before Unmarshal can hide a conflicting
+ // groups or subject claim. json.RawMessage permits unrelated normal claims.
+ decoder := json.NewDecoder(bytes.NewReader(body))
+ token, err := decoder.Token()
+ if err != nil || token != json.Delim('{') {
+ return nil, errors.New("current UserInfo object required")
+ }
+ claims := map[string]json.RawMessage{}
+ for decoder.More() {
+ keyToken, err := decoder.Token()
+ if err != nil {
+ return nil, errors.New("current UserInfo key invalid")
+ }
+ key, ok := keyToken.(string)
+ if !ok {
+ return nil, errors.New("current UserInfo key invalid")
+ }
+ if _, duplicate := claims[key]; duplicate {
+ return nil, errors.New("current UserInfo duplicate claim")
+ }
+ var value json.RawMessage
+ if decoder.Decode(&value) != nil {
+ return nil, errors.New("current UserInfo value invalid")
+ }
+ claims[key] = value
+ }
+ if token, err = decoder.Token(); err != nil || token != json.Delim('}') {
+ return nil, errors.New("current UserInfo object invalid")
+ }
+ var trailing any
+ if decoder.Decode(&trailing) != io.EOF {
+ return nil, errors.New("current UserInfo trailing data")
+ }
+ raw, present := claims[t.claim]
+ if !present {
+ return nil, errors.New("current UserInfo required claim missing")
+ }
+ var value any
+ if json.Unmarshal(raw, &value) != nil {
+ return nil, errors.New("current UserInfo required claim invalid")
+ }
+ if _, ok := strictClaimValues(value); !ok {
+ return nil, errors.New("current UserInfo required claim array invalid")
+ }
+ // Return the original public response bytes to Goth. Subject equality and
+ // audience/issuer checks remain in its existing authenticated provider path.
+ response.Body = io.NopCloser(bytes.NewReader(body))
+ return response, nil
+}
--- /dev/null
+++ b/services/auth/source/oauth2/required_claim_values_test.go
@@ -0,0 +1,144 @@
+// Copyright 2026 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+package oauth2
+
+import (
+ "errors"
+ "io"
+ "net/http"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+var caseValues = []string{"forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"}
+
+type roundTripFunc func(*http.Request) (*http.Response, error)
+
+func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }
+
+func mockUserInfo(body string, status int) *http.Client {
+ return &http.Client{Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
+ return &http.Response{StatusCode: status, Header: http.Header{}, Body: io.NopCloser(strings.NewReader(body)), Request: r}, nil
+ })}
+}
+
+func TestRequiredClaimExactAnyOfAndNoMutation(t *testing.T) {
+ for _, group := range caseValues {
+ groups := []any{group, "unrelated"}
+ claims := map[string]any{"groups": groups, "other": "preserved"}
+ if !MatchRequiredClaimValues(claims, "groups", "", caseValues) {
+ t.Fatal("individual allowed group rejected")
+ }
+ if !reflect.DeepEqual(claims, map[string]any{"groups": groups, "other": "preserved"}) {
+ t.Fatal("claims mutated")
+ }
+ }
+ if !MatchRequiredClaimValues(map[string]any{"groups": append([]string(nil), caseValues...)}, "groups", "", caseValues) {
+ t.Fatal("multiple allowed groups rejected")
+ }
+ for _, raw := range []any{nil, true, 1, "forge-admins", "forge-admins,forge-coulomb-owners", []any{"forge-admins", 1}, []any{"forge-admins", nil}, []string{}, []any{}, []string{"forge-admins-lookalike"}, []string{" Forge-admins"}, []string{"forge-admins\n"}, []string{"Forge-admins"}, []string{"forge-admins,other"}, []string{"forge-admins", ""}} {
+ if MatchRequiredClaimValues(map[string]any{"groups": raw}, "groups", "", caseValues) {
+ t.Fatal("malformed/nonmatching claim accepted")
+ }
+ }
+ if MatchRequiredClaimValues(map[string]any{}, "groups", "", caseValues) {
+ t.Fatal("absent claim accepted")
+ }
+ if MatchRequiredClaimValues(map[string]any{"unrelated": []string{"forge-admins"}}, "groups", "", caseValues) {
+ t.Fatal("unrelated claim used")
+ }
+}
+
+func TestRequiredClaimConfigurationRefusalsAndLegacyIsolation(t *testing.T) {
+ if ValidateRequiredClaimValues("", "old arbitrary literal", nil) != nil {
+ t.Fatal("legacy config changed")
+ }
+ for _, cfg := range []struct {
+ name, legacy string
+ values []string
+ }{
+ {"groups", "", []string{}}, {"groups", "", []string{""}}, {"groups", "old", caseValues}, {"", "", caseValues},
+ {"groups", "", []string{"a", "a"}}, {"groups", "", []string{"a,b"}}, {"groups", "", []string{" a"}},
+ } {
+ if ValidateRequiredClaimValues(cfg.name, cfg.legacy, cfg.values) == nil {
+ t.Fatal("invalid config accepted")
+ }
+ }
+ if ValidateRequiredClaimValues("groups", "", caseValues) != nil {
+ t.Fatal("case config invalid")
+ }
+}
+
+func TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable(t *testing.T) {
+ for _, body := range []string{
+ `{}`, `{"sub":"alice"}`, `{"groups":null}`, `{"groups":true}`, `{"groups":"forge-admins"}`,
+ `{"groups":["forge-admins",null]}`, `{"groups":["forge-admins",1]}`,
+ `{"groups":["forge-admins"],"groups":[]}`, `{"groups":[]} {}`, `[]`, `{"groups":[]`,
+ `{"groups":["forge-admins",""]}`, strings.Repeat(" ", maxUserInfoBytes+1),
+ } {
+ client, err := RequireCurrentUserInfoClaim(mockUserInfo(body, 200), "https://issuer.example/userinfo", "groups")
+ if err != nil {
+ t.Fatal(err)
+ }
+ response, err := client.Get("https://issuer.example/userinfo")
+ if err == nil {
+ response.Body.Close()
+ t.Fatal("invalid current claim did not fail closed")
+ }
+ }
+ client, _ := RequireCurrentUserInfoClaim(mockUserInfo(`{"groups":[]}`, 503), "https://issuer.example/userinfo", "groups")
+ if response, err := client.Get("https://issuer.example/userinfo"); err == nil {
+ response.Body.Close()
+ t.Fatal("non200 accepted")
+ }
+ broken := &http.Client{Transport: roundTripFunc(func(*http.Request) (*http.Response, error) { return nil, errors.New("private backend error") })}
+ client, _ = RequireCurrentUserInfoClaim(broken, "https://issuer.example/userinfo", "groups")
+ if _, err := client.Get("https://issuer.example/userinfo"); err == nil || strings.Contains(err.Error(), "private backend") {
+ t.Fatal("lookup failure not contained")
+ }
+}
+
+func TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval(t *testing.T) {
+ for _, body := range []string{`{"sub":"alice","groups":[]}`, `{"sub":"alice","groups":["forge-coulomb-readers"]}`} {
+ client, err := RequireCurrentUserInfoClaim(mockUserInfo(body, 200), "https://issuer.example/userinfo", "groups")
+ if err != nil {
+ t.Fatal(err)
+ }
+ response, err := client.Get("https://issuer.example/userinfo")
+ if err != nil {
+ t.Fatal(err)
+ }
+ b, _ := io.ReadAll(response.Body)
+ response.Body.Close()
+ if string(b) != body {
+ t.Fatal("response changed")
+ }
+ }
+}
+
+func TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse(t *testing.T) {
+ original := mockUserInfo(`{"unrelated":true}`, 200)
+ guarded, err := RequireCurrentUserInfoClaim(original, "https://issuer.example/userinfo", "groups")
+ if err != nil {
+ t.Fatal(err)
+ }
+ response, err := original.Get("https://issuer.example/userinfo")
+ if err != nil {
+ t.Fatal("original client mutated")
+ }
+ response.Body.Close()
+ response, err = guarded.Get("https://issuer.example/unrelated")
+ if err != nil {
+ t.Fatal("unrelated endpoint changed")
+ }
+ response.Body.Close()
+ if guarded.CheckRedirect == nil || guarded.CheckRedirect(nil, nil) == nil {
+ t.Fatal("redirect can bypass guard")
+ }
+ for _, endpoint := range []string{"", "http://issuer.example/userinfo", "https://user:pass@issuer.example/userinfo", "https://issuer.example/userinfo#fragment"} {
+ if _, err := RequireCurrentUserInfoClaim(original, endpoint, "groups"); err == nil {
+ t.Fatal("invalid endpoint accepted")
+ }
+ }
+}
--- a/services/auth/source/oauth2/source.go
+++ b/services/auth/source/oauth2/source.go
@@ -22,6 +22,7 @@
Scopes []string
AttributeSSHPublicKey string
RequiredClaimName string
+ RequiredClaimValues []string `json:",omitempty"`
RequiredClaimValue string
GroupClaimName string
AdminGroup string

File diff suppressed because one or more lines are too long

View file

@ -38,7 +38,7 @@ env:
USER_ENGINE_APPLICATION_ID: coulomb-social
USER_ENGINE_EXPECTED_AUDIENCE: user-engine-portal
# USER_ENGINE_BASE_URL set via secret or here when stable
USER_ENGINE_BASE_URL: https://users.92-205-62-239.nip.io
USER_ENGINE_BASE_URL: https://users.coulomb.social
NETKINGDOM_REGISTRATION_URL: https://users.92-205-62-239.nip.io/register
probes:

View file

@ -1,16 +0,0 @@
apiVersion: v2
name: inter-hub
description: |
Interaction Hub Framework reference deployment for Railiance01.
type: application
version: 0.1.0
appVersion: "0.2.0-alpha.1"
keywords:
- ihf
- inter-hub
- railiance
home: https://forgejo.coulomb.social/coulomb/inter-hub-haskell
sources:
- https://forgejo.coulomb.social/coulomb/inter-hub-haskell
maintainers:
- name: railiance-apps

View file

@ -1,28 +0,0 @@
{{/*
Chart name + release name produce a unique resource name.
*/}}
{{- define "interhub.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s" $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "interhub.labels" -}}
app: {{ include "interhub.fullname" . }}
app.kubernetes.io/name: {{ include "interhub.fullname" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/part-of: railiance-apps
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" }}
{{- end -}}
{{- define "interhub.selectorLabels" -}}
app: {{ include "interhub.fullname" . }}
{{- end -}}
{{- define "interhub.image" -}}
{{- if not .Values.image.tag -}}
{{- fail "image.tag is required - pin it in helm/inter-hub-values.yaml or pass --set image.tag=<sha>" -}}
{{- end -}}
{{- printf "%s:%s" .Values.image.repository .Values.image.tag -}}
{{- end -}}

View file

@ -1,59 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "interhub.fullname" . }}
labels: {{- include "interhub.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels: {{- include "interhub.selectorLabels" . | nindent 6 }}
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels: {{- include "interhub.labels" . | nindent 8 }}
spec:
securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: inter-hub
image: {{ include "interhub.image" . | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
securityContext: {{- toYaml .Values.securityContext | nindent 12 }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
protocol: TCP
envFrom:
- secretRef:
name: {{ .Values.envSecretName | quote }}
{{- if .Values.probes.enabled }}
readinessProbe:
httpGet:
path: {{ .Values.probes.path }}
port: {{ .Values.probes.port }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
failureThreshold: {{ .Values.probes.readiness.failureThreshold }}
livenessProbe:
httpGet:
path: {{ .Values.probes.path }}
port: {{ .Values.probes.port }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }}
failureThreshold: {{ .Values.probes.liveness.failureThreshold }}
{{- end }}
resources: {{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector: {{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity: {{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations: {{- toYaml . | nindent 8 }}
{{- end }}

View file

@ -1,28 +0,0 @@
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "interhub.fullname" . }}
labels: {{- include "interhub.labels" . | nindent 4 }}
annotations:
{{- toYaml .Values.ingress.annotations | nindent 4 }}
spec:
ingressClassName: {{ .Values.ingress.className }}
{{- if .Values.ingress.tls }}
tls:
- hosts:
- {{ .Values.ingress.host }}
secretName: {{ include "interhub.fullname" . }}-tls
{{- end }}
rules:
- host: {{ .Values.ingress.host }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ include "interhub.fullname" . }}
port:
number: {{ .Values.service.port }}
{{- end }}

View file

@ -1,13 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "interhub.fullname" . }}
labels: {{- include "interhub.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
protocol: TCP
name: http
selector: {{- include "interhub.selectorLabels" . | nindent 4 }}

View file

@ -1,55 +0,0 @@
image:
repository: forgejo.coulomb.social/coulomb/inter-hub
tag: "" # required; pinned via helm/inter-hub-values.yaml or --set image.tag=<sha>
pullPolicy: IfNotPresent
replicaCount: 1
service:
type: ClusterIP
port: 8000
targetPort: 8000
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 1000m
memory: 2Gi
# Env from the K8s Secret created from inter-hub's SOPS handoff file:
# deploy/railiance/secrets/inter-hub.env.sops.yaml
envSecretName: inter-hub-env
ingress:
enabled: true
className: traefik
host: hub.coulomb.social
tls: true
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
cert-manager.io/cluster-issuer: letsencrypt-prod
probes:
enabled: true
path: /
port: 8000
liveness:
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readiness:
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
podSecurityContext: {}
securityContext: {}
nodeSelector: {}
tolerations: []
affinity: {}

View file

@ -32,11 +32,21 @@ app.kubernetes.io/component: landing
{{- end -}}
{{- define "reuse.image" -}}
{{- if and .Values.authSourceAllowlist (not .Values.image.digest) -}}
{{- fail "authenticated source allowlist requires reviewed hardened image.digest" -}}
{{- end -}}
{{- if .Values.image.digest -}}
{{- if not (regexMatch "^sha256:[a-f0-9]{64}$" .Values.image.digest) -}}
{{- fail "image.digest must be sha256 plus 64 lowercase hex characters" -}}
{{- end -}}
{{- printf "%s@%s" .Values.image.repository .Values.image.digest -}}
{{- else -}}
{{- if not .Values.image.tag -}}
{{- fail "image.tag is required - pin it in helm/reuse-surface-values.yaml" -}}
{{- end -}}
{{- printf "%s:%s" .Values.image.repository .Values.image.tag -}}
{{- end -}}
{{- end -}}
{{- define "reuse.landingImage" -}}
{{- if not .Values.landing.image.tag -}}

View file

@ -36,6 +36,8 @@ spec:
- secretRef:
name: {{ .Values.envSecretName | quote }}
env:
- name: REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST
value: {{ .Values.authSourceAllowlist | toJson | quote }}
- name: REUSE_SURFACE_LISTEN_HOST
value: "0.0.0.0"
- name: REUSE_SURFACE_LISTEN_PORT

View file

@ -0,0 +1,30 @@
{
"$schema": "https://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"image": {
"type": "object",
"properties": {
"digest": {
"type": "string",
"pattern": "^(|sha256:[a-f0-9]{64})$"
}
}
},
"authSourceAllowlist": {
"type": "object",
"propertyNames": {
"pattern": "^[A-Z][A-Z0-9_]*$"
},
"additionalProperties": {
"type": "array",
"uniqueItems": true,
"items": {
"type": "string",
"pattern": "^https://[^\\s]+$"
},
"minItems": 1
}
}
}
}

View file

@ -1,6 +1,7 @@
image:
repository: forgejo.coulomb.social/coulomb/reuse-surface
tag: ""
digest: ""
pullPolicy: IfNotPresent
replicaCount: 1
@ -26,6 +27,10 @@ resources:
envSecretName: reuse-surface-env
# Non-secret exact URL bindings for authenticated remote metadata fetches.
# Nonempty bindings require an immutable digest; use only a reviewed hardened image.
authSourceAllowlist: {}
landing:
enabled: false
image:

View file

@ -13,6 +13,7 @@ app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/part-of: railiance-apps
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" }}
railiance.io/release-unit: vergabe
{{- end -}}
{{- define "vergabe.selectorLabels" -}}

View file

@ -0,0 +1,119 @@
# Railiance apps preparation review — CUST81 private metadata reader R2
Reviewer: delegated railiance-apps preparation worker, supervised local preparation
assignment. No promotion evidence or existing own-scope supervision record was
found; the descriptive preparation record grants no runtime authority. This is
an S5 release-contract review grounded in INTENT principles 1–5 (workloads,
release contracts, repeatable operations, explicit S3 handoff, verified health).
It is not a founder decision, independent platform credential review or reuse
application source review. No live changes, commits, pushes, hub writes, image
publishes, login or credential retrieval occurred.
Disposition: **approved for integration as local preparation only; live promotion blocked**.
Canonical review disposition: `approved`. This accepts the bounded S5 proposal
and its explicit runtime gates, not live execution or another owner’s review.
## Proposed changes and owner boundaries
The chart accepts an optional immutable image digest, adds a non-secret exact
`REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST` environment binding, and fails rendering
when a nonempty allowlist lacks an immutable digest. Schema checks enforce
HTTPS allowlist entries and valid digest syntax. Production values continue
using their existing `main-6cbc862` tag; production ExternalSecret is unchanged.
The preparation override contains exactly 14 reviewed pinned metadata URLs and
an empty digest deliberately preventing deployment. A synthetic all-zero digest
in local tests is not an image and must never be promoted.
The proposed phase-B ExternalSecret preserves existing two keys, target metadata
and existing `openbao-reuse` ClusterSecretStore, adding only
`REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN` from
`workloads/reuse/reuse-surface/forgejo-source-read:FORGEJO_SOURCE_TOKEN`.
Platform owns the dedicated KV path/policy/store read scope, CCR-2026-0046 and
credential issuance; apps neither provisions nor consumes an admin credential.
Digest presence cannot prove the hardened application is included. The reuse
owner must provide published build digest, source revision, tests and review
binding *both* authenticated redirect rejection and exact environment-name/URL
credential routing. A reader must not reach the existing vulnerable runtime.
## Ordered promotion gates — not executed
1. Confirm own-scope supervision and founder change approval, reviewed CCR,
exact seven-source owner consent packet and scoped Forgejo read-only issuer
receipt. Integrate provisional RAPPS-WP-0017 through root owner coordination.
2. Run metadata inventory helper with an explicitly verified railiance01
kubeconfig; compare existing authenticated registrations with newly
fail-closed image behavior. The helper reads only SQLite registration
metadata, never env/Secrets, captures command errors and refuses unsafe
URLs. If it refuses, resolve via the source owner without emitting raw
objects. Any existing authenticated source requires separately reviewed
exact URL bindings including capability entry URLs before promotion; do not
silently orphan sources or broaden the 14-URL map. Missing inventory blocks
phase A. Store only safe names, counts and validated metadata URLs.
3. Read live release image/revision, replica state, PVC storage/access modes and
readiness. Confirm node external IP `92.205.62.239`, not retired coulombcore.
Record requested CPU/memory headroom and pending pods before rollout. Existing
API requests 100m CPU/256Mi, surge1 requires that much additional allocatable
capacity; landing unchanged. Confirm the existing PVC supports simultaneous
pod scheduling/mount on this single node; stop if RWO/RWOP or mount conflicts
prevent it. No rollout strategy/resource/PVC changes are proposed. Do not
change other workloads merely to obtain capacity.
4. Once a real reviewed digest exists, fill the preparation values; render
exact chart with production values plus proposal overrides. Server dry-run
and client diff must cover every rendered chart object and the phase-B ESO
manifest. Preserve stderr in memory and emit sanitized status only; never
print live Secrets, Helm secret values, annotations or raw API failures.
Existing release owner and any Argo adoption must be verified before choosing
owner Helm upgrade versus GitOps sync; neither is executed here.
5. Phase A: approved release of hardened digest plus approved allowlist, with
existing ESO fields only. Owner Helm path must use atomic upgrade, timeout
and wait; verify actual running imageID matches digest, API health/probes,
public federation baseline63 and credential-routing refusal/redirect tests.
Deployment failure blocks phase B. Database/PVC remains unchanged. Collect a
separately approved safe backup/recovery receipt before any risky live release.
6. Phase B: after verified phase A, execute reviewed platform credential action
and ESO reader mapping. Verify only ESO condition/status and key-presence
booleans via approved suppressing helpers, then restart/sync the hardened
workload so envFrom obtains the reader. Never dump Secret or process env.
7. Reuse owner runs native exact-byte authenticated proof for all14 URLs and
seven capability entries using pinned consent hashes. Only then register
seven sources through the scoped write route and verify hosted70/70,
no duplicates/conflicting IDs and public federation preserves all63.
## Rollback contract
Before execution capture previous approved release revision, digest/tag and
safe configuration, public discovery baseline and database recovery receipt.
If phase A fails before credential delivery, atomic restore of previous release
is safe because no reader has been supplied. After phase B, **never downgrade to
vulnerable runtime with reader still available**: revoke the dedicated reader
at Forgejo/OpenBao via credential owner, remove phase-B ESO mapping and materialized
reader key through reviewed silent maintenance, restart current hardened pods,
verify absence/revocation, then restore prior approved chart/image only if needed.
Metadata registrations may remain inaccessible until re-established; record the
regression explicitly. Do not roll back the PVC or use stale tokens as recovery.
## Local evidence and limitations
`helm lint charts/reuse-surface -f helm/reuse-surface-values.yaml` passed.
`python3 docs/assessments/cust81-private-reader/verify_preparation.py` passed ten
checks, including exact14 roundtrip, digest/schema refusal, retention of current
strategy/resources/PVC, correct isolated ESO mapping, metadata URL sanitization
and subprocess error suppression. `local-verification-r2.json` contains command
statuses without raw output. No cluster inventory, resource headroom,
server dry-run/diff, image publish, live approval or native fetch has been proven.
## Revision history — same proposal, no second trial
R1 root review found the inventory SQL incorrectly selected `$.index_url` rather
than the native HubStore registration field `$.url`. R1's local checks did not
exercise the pod SQL and therefore missed this defect. R1 must not be executed;
it remains preserved in the root assembly archive. R2 selects `$.url` and tests
the exact read-only pod SQL against a real SQLite registration payload containing
`repo`, `url`, `auth_env`, timestamps and an unrelated sentinel description, plus
an anonymous registration. Only expected names/URL emerge; rows remain unchanged.
R2 also accepts canonical Forgejo raw branch/commit/tag index routes while still
refusing userinfo, non-HTTPS, credentials in queries and fragments. Untrusted
successful subprocess output is refused without raw echo, as are failures.
No live inventory command has run. This corrective refinement is the same
CUST81-APPS-PRIVATE-READER-RELEASE proposal revision2, not an unchanged success.

View file

@ -0,0 +1,96 @@
{
"status": "passed",
"local_only": true,
"real_image_digest": null,
"synthetic_digest_used_only_to_check_rendering": true,
"checks": [
"production rendering unchanged except empty fail-closed allowlist",
"unknown/malformed digest fails",
"HTTP allowlist fails schema",
"exact14 URLs roundtrip",
"strategy resources PVC retained",
"dedicated ESO mapping preserves current fields/store",
"metadata sanitizer excludes userinfo/query secrets/fragments",
"kubectl failure output discarded",
"exact pod SQL real payload URL keys readonly fixture and anonymous filtering",
"legacy Forgejo branch URL accepted without weakening credential query refusal"
],
"helm_calls": [
{
"command": [
"helm",
"template",
"reuse",
"/tmp/cust81-apps-reader-owner/charts/reuse-surface",
"-f",
"/tmp/cust81-apps-reader-owner/helm/reuse-surface-values.yaml"
],
"exit_code": 0,
"expected_success": true
},
{
"command": [
"helm",
"template",
"reuse",
"/tmp/cust81-apps-reader-owner/charts/reuse-surface",
"-f",
"/tmp/cust81-apps-reader-owner/helm/reuse-surface-values.yaml",
"-f",
"/tmp/cust81-apps-reader-owner/docs/assessments/cust81-private-reader/private-reader-values.r1.yaml"
],
"exit_code": 1,
"expected_success": false
},
{
"command": [
"helm",
"template",
"reuse",
"/tmp/cust81-apps-reader-owner/charts/reuse-surface",
"-f",
"/tmp/cust81-apps-reader-owner/helm/reuse-surface-values.yaml",
"-f",
"/tmp/cust81-apps-reader-owner/docs/assessments/cust81-private-reader/private-reader-values.r1.yaml",
"--set",
"image.digest=sha256:bad"
],
"exit_code": 1,
"expected_success": false
},
{
"command": [
"helm",
"template",
"reuse",
"/tmp/cust81-apps-reader-owner/charts/reuse-surface",
"-f",
"/tmp/cust81-apps-reader-owner/helm/reuse-surface-values.yaml",
"-f",
"/tmp/cust81-apps-reader-owner/docs/assessments/cust81-private-reader/private-reader-values.r1.yaml",
"--set-json",
"authSourceAllowlist={\"BAD\":[\"http://attacker.invalid\"]}",
"--set",
"image.digest=sha256:0000000000000000000000000000000000000000000000000000000000000000"
],
"exit_code": 1,
"expected_success": false
},
{
"command": [
"helm",
"template",
"reuse",
"/tmp/cust81-apps-reader-owner/charts/reuse-surface",
"-f",
"/tmp/cust81-apps-reader-owner/helm/reuse-surface-values.yaml",
"-f",
"/tmp/cust81-apps-reader-owner/docs/assessments/cust81-private-reader/private-reader-values.r1.yaml",
"--set",
"image.digest=sha256:0000000000000000000000000000000000000000000000000000000000000000"
],
"exit_code": 0,
"expected_success": true
}
]
}

View file

@ -0,0 +1,30 @@
{
"reviewer_owner": "railiance-apps",
"reviewer_assignment": "delegated-railiance-apps-cust81-preparation",
"disposition": "approved",
"approved_scope": "isolated S5 chart/ESO preparation integration only",
"live_execution_approval": false,
"workitem": "RAPPS-WP-0017",
"intent_basis": [
"Release Contracts over Source Internals",
"Repeatable Operations",
"Clear Lower-Layer Handoffs",
"Evidence Before Confidence"
],
"review_artifact_sha256": "a3a2f2db6f0a8461a0d8299825c842c8ce427f59c86238884609d463837b3b75",
"reviewed_manifest_sha256": "45d467c2bb48462a388f5df4ba5188492d406bdddbddfbf3487bd2077486dec8",
"reviewed_patch_sha256": "39ebaa7f4050f9204fe8ee38364fbbce874903447ac45ccd408cf434aa6e4a73",
"gates": [
"unknown reviewed hardened image digest",
"metadata-only existing authenticated source inventory/migration",
"live node headroom/PVC/readiness survey",
"server dry-run and client diff",
"release-specific EUR estimates and founder live-change approval",
"phase A verified before phase B credential delivery",
"approved executed platform credential CCR",
"native14 URL/7 metadata hash proof then70/70 hosted"
],
"proposal_id": "CUST81-APPS-PRIVATE-READER-RELEASE-R1",
"revision": 2,
"refinement_or_rescue": true
}

View file

@ -0,0 +1,57 @@
# Apps concrete phase A proposal review
Disposition: `approved` for proposal preparation and local render only.
This supplement preserves the reviewed core R2 patch unchanged. It supplies
actual `reuse` Helm release / `reuse` namespace chart rendering with the locally
built hardened image digest. It does not authorize publication, execute a live
change or approve platform's CCR. INTENT basis: source-controlled release
contracts, explicit S3 handoff and evidence before confidence.
The proposed image is
`forgejo.coulomb.social/coulomb/reuse-surface@sha256:2364b7e7859daccf9340a063654e2b09eed1bc27e0adf7b23cbb4b30135a7db0`.
The local build receipt confirms no-network/read-only health and native source
hash smoke. Registry publication is false. Before apply the exact reviewed
artifact must be published and independently verified by registry manifest
digest. If publication changes manifest representation/digest, revise and review
the bound proposal rather than silently replacing the digest.
Current observations are node `92.205.62.239`, CPU requests3560m of4000m (440m
free), API100m/256Mi, replicas1 with surge1/unavailable0, PVC
`reuse-surface-data` Bound RWO1Gi, and Helm ownership without Argo tracking.
The proposed chart retains resources, strategy and PVC. One100m/256Mi surge fits
observed CPU headroom; recheck memory, pending pods and node headroom immediately
before rollout. RWO single-node does not prohibit two pods on that same node,
but driver mount behavior and SQLite concurrent-access readiness remain
verification gates. Do not change deployment strategy or PVC to bypass a failure.
Exact readonly authenticated-source inventory is0 at its receipt timestamp.
This clears the observed migration gate, but recheck before apply; no credential
or Secret/env was read. Existing Kubernetes-auth `openbao-reuse` store is Ready
with namespace/ServiceAccount/role matching the consumer; its current
ExternalSecret contains only the two old fields and remains unchanged in phase A.
Local full-chart render and assertions passed: exact proposed image and14 URL
allowlist; same100m/256Mi requests; same1surge/0unavailable/replicas1; no Secret,
ExternalSecret or store objects added. Full chart includes existing landing,
ingress, services and PVC and must receive normal full-chart server dry-run and
client diff, not a Deployment-only narrow review.
Incremental infrastructure cost is EUR0 because node, replica steady state,
requests, services and PVC capacity remain unchanged. Build/registry execution
cost is not independently accounted. Credible operational loss is qualitative
and not priced in EUR: rollout failure or concurrent SQLite/PVC access may cause
service interruption or recovery work. No autopilot grant is inferred. Exact
human review must accept these estimates and remaining readiness gates.
Before founder-authorized deploy, capture current Helm revision and safe rollback
reference (old running digest
`sha256:2c1514cb3f5e1297ffeeac912ba36a70f8a2591588a5a4643bc58293e97132de`),
known recovery receipt and63 public capability baseline. Use actual Helm release
`reuse`, namespace`reuse`, owner atomic/wait/timeout path after safe dry-run/diff.
No exact deploy command is executed by this proposal. Phase A retains the current
runtime Secret fields and supplies no reader. Verify running imageID, readiness,
public federation and credential refusal/redirect behavior before phase B.
If phase A fails with no reader delivered, restore the approved previous release.
After phase B, revoke/remove reader and verify its absence before any vulnerable
image restoration. Phase B is independently gated by approved/executed CCR and
native14 URL/7 entry-hash proof before70/70 hosted discovery.

View file

@ -0,0 +1,12 @@
{
"proposal_id": "CUST81-APPS-PHASE-A-LOCAL-IMAGE-R1",
"reviewer_owner": "railiance-apps",
"disposition": "approved",
"approved_scope": "preparation only, local full-chart render, supervised founder review required",
"files": {
"docs/assessments/cust81-private-reader/phase-a-local-image-r1/apps-owner-release-review.md": "a74e96986798a727fc87c0d3c578601579778dfd8521796d0bb81df8451c751b",
"docs/assessments/cust81-private-reader/phase-a-local-image-r1/phase-a-full-chart.proposed.yaml": "36c20c3e16757d3e888a3db02853032b33e791aa04a6055fe001121254345b81",
"docs/assessments/cust81-private-reader/phase-a-local-image-r1/phase-a-preparation-receipt.json": "3282a17ae3724619e40dd1f34846d83b2297d9ae00030618dbd269e8e2fd7d39",
"docs/assessments/cust81-private-reader/phase-a-local-image-r1/phase-a-values.proposed.yaml": "35284857ec987fc7a929f5c2eecdb44c7d3367f7835904692947de09918ab8f1"
}
}

View file

@ -0,0 +1,478 @@
# PREPARATION RENDER ONLY; no registry publication or server validation proved.
---
# Source: reuse-surface/templates/landing-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: reuse-surface-landing
labels:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/instance: reuse
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/part-of: railiance-apps
helm.sh/chart: reuse-surface-0.1.0
app.kubernetes.io/component: landing
data:
index.html: |
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex,nofollow">
<title>REUSE federation endpoint</title>
<style>
:root {
color-scheme: light;
--ink: #1d2733;
--muted: #536271;
--line: #d9e0e7;
--paper: #f8fafc;
--accent: #2066a8;
--accent-dark: #164b7e;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
color: var(--ink);
background: var(--paper);
display: grid;
place-items: center;
padding: 32px 18px;
}
main {
width: min(100%, 720px);
border: 1px solid var(--line);
border-radius: 8px;
background: #ffffff;
padding: clamp(28px, 5vw, 48px);
box-shadow: 0 18px 48px rgba(29, 39, 51, 0.08);
}
.eyebrow {
margin: 0 0 12px;
color: var(--accent-dark);
font-size: 0.86rem;
font-weight: 700;
text-transform: uppercase;
}
h1 {
margin: 0;
font-size: clamp(2rem, 7vw, 3rem);
line-height: 1.05;
}
p {
max-width: 62ch;
margin: 18px 0 0;
color: var(--muted);
font-size: 1rem;
line-height: 1.65;
}
.actions {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-top: 28px;
}
a {
color: var(--accent);
}
.button {
display: inline-flex;
min-height: 44px;
align-items: center;
justify-content: center;
border-radius: 6px;
background: var(--accent);
color: #ffffff;
padding: 0 18px;
font-weight: 700;
text-decoration: none;
}
.button:focus,
.button:hover {
background: var(--accent-dark);
}
.links {
margin-top: 26px;
padding-top: 22px;
border-top: 1px solid var(--line);
display: grid;
gap: 10px;
}
</style>
</head>
<body>
<main>
<p class="eyebrow">reuse.coulomb.social</p>
<h1>REUSE federation endpoint</h1>
<p>This is the Railiance REUSE capability federation service. It is an API endpoint for automated clients and operators; there is no browser login for this service.</p>
<div class="actions">
<a class="button" href="/v1/federated">Open federated index</a>
</div>
<nav class="links" aria-label="Service links">
<a href="/health">Health check</a>
<a href="/v1/federated">Federated capability index</a>
<a href="https://forgejo.coulomb.social/coulomb/railiance-apps/src/branch/main/docs/reuse-surface-on-railiance01.md">Operator runbook</a>
</nav>
</main>
</body>
</html>
---
# Source: reuse-surface/templates/pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: reuse-surface-data
labels:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/instance: reuse
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/part-of: railiance-apps
helm.sh/chart: reuse-surface-0.1.0
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
# Source: reuse-surface/templates/landing-service.yaml
apiVersion: v1
kind: Service
metadata:
name: reuse-surface-landing
labels:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/instance: reuse
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/part-of: railiance-apps
helm.sh/chart: reuse-surface-0.1.0
app.kubernetes.io/component: landing
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/instance: reuse
app.kubernetes.io/component: landing
ports:
- name: http
port: 8080
targetPort: http
protocol: TCP
---
# Source: reuse-surface/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: reuse-surface
labels:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/instance: reuse
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/part-of: railiance-apps
helm.sh/chart: reuse-surface-0.1.0
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/instance: reuse
app.kubernetes.io/component: api
ports:
- name: http
port: 8000
targetPort: http
protocol: TCP
---
# Source: reuse-surface/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: reuse-surface
labels:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/instance: reuse
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/part-of: railiance-apps
helm.sh/chart: reuse-surface-0.1.0
app.kubernetes.io/component: api
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/instance: reuse
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/instance: reuse
app.kubernetes.io/component: api
spec:
enableServiceLinks: false
securityContext:
{}
containers:
- name: reuse-surface
image: "forgejo.coulomb.social/coulomb/reuse-surface@sha256:2364b7e7859daccf9340a063654e2b09eed1bc27e0adf7b23cbb4b30135a7db0"
imagePullPolicy: IfNotPresent
command: ["reuse-surface", "serve"]
securityContext:
{}
ports:
- name: http
containerPort: 8000
protocol: TCP
envFrom:
- secretRef:
name: "reuse-surface-env"
env:
- name: REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST
value: "{\"REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN\":[\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/capabilities/capability.railiance.openbao-package.md?ref=35672be138036c8433a5ac5de91e068c03c2b124\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/indexes/capabilities.yaml?ref=35672be138036c8433a5ac5de91e068c03c2b124\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/capabilities/capability.railiance.policy-publication-package.md?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/indexes/capabilities.yaml?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/capabilities/capability.railiance.postgres-isolation-recovery.md?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/indexes/capabilities.yaml?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/capabilities/capability.railiance.sbom-runtime-package.md?ref=adbedd349e8df97be43a1f57db7095af45ff29d1\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/indexes/capabilities.yaml?ref=adbedd349e8df97be43a1f57db7095af45ff29d1\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/capabilities/capability.railiance.telemetry-package.md?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/indexes/capabilities.yaml?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/capabilities/capability.railiance.tenant-engine-package.md?ref=9bdc86572728783a5ed94266f04f7b12d414c557\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/indexes/capabilities.yaml?ref=9bdc86572728783a5ed94266f04f7b12d414c557\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/capabilities/capability.railiance.user-engine-package.md?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/indexes/capabilities.yaml?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738\"]}"
- name: REUSE_SURFACE_LISTEN_HOST
value: "0.0.0.0"
- name: REUSE_SURFACE_LISTEN_PORT
value: "8000"
- name: REUSE_SURFACE_DB
value: "/data/reuse.db"
- name: REUSE_SURFACE_CACHE_DIR
value: "/data/cache"
volumeMounts:
- name: data
mountPath: /data
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 15
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 256Mi
volumes:
- name: data
persistentVolumeClaim:
claimName: reuse-surface-data
---
# Source: reuse-surface/templates/landing-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: reuse-surface-landing
labels:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/instance: reuse
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/part-of: railiance-apps
helm.sh/chart: reuse-surface-0.1.0
app.kubernetes.io/component: landing
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/instance: reuse
app.kubernetes.io/component: landing
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/instance: reuse
app.kubernetes.io/component: landing
spec:
enableServiceLinks: false
containers:
- name: landing
image: "nginxinc/nginx-unprivileged:1.27-alpine"
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
protocol: TCP
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 3
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 10
periodSeconds: 30
timeoutSeconds: 3
failureThreshold: 3
resources:
limits:
cpu: 50m
memory: 64Mi
requests:
cpu: 10m
memory: 32Mi
volumeMounts:
- name: landing-page
mountPath: /usr/share/nginx/html/index.html
subPath: index.html
readOnly: true
volumes:
- name: landing-page
configMap:
name: reuse-surface-landing
---
# Source: reuse-surface/templates/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: reuse-surface
labels:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/instance: reuse
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/part-of: railiance-apps
helm.sh/chart: reuse-surface-0.1.0
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
ingressClassName: traefik
tls:
- hosts:
- reuse.coulomb.social
secretName: reuse-surface-tls
rules:
- host: reuse.coulomb.social
http:
paths:
- path: /health
pathType: Exact
backend:
service:
name: reuse-surface
port:
number: 8000
- path: /v1
pathType: Prefix
backend:
service:
name: reuse-surface
port:
number: 8000
---
# Source: reuse-surface/templates/landing-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: reuse-surface-landing
labels:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/instance: reuse
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/part-of: railiance-apps
helm.sh/chart: reuse-surface-0.1.0
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
traefik.ingress.kubernetes.io/router.priority: "1"
spec:
ingressClassName: traefik
tls:
- hosts:
- reuse.coulomb.social
secretName: reuse-surface-tls
rules:
- host: reuse.coulomb.social
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: reuse-surface-landing
port:
number: 8080
---
# Source: reuse-surface/templates/redirect-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: reuse-surface-http-redirect
labels:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/instance: reuse
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/part-of: railiance-apps
helm.sh/chart: reuse-surface-0.1.0
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
traefik.ingress.kubernetes.io/router.middlewares: "reuse-reuse-surface-redirect-https@kubernetescrd"
spec:
ingressClassName: traefik
rules:
- host: reuse.coulomb.social
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: reuse-surface
port:
number: 8000
---
# Source: reuse-surface/templates/redirect-middleware.yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: reuse-surface-redirect-https
labels:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/instance: reuse
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/part-of: railiance-apps
helm.sh/chart: reuse-surface-0.1.0
spec:
redirectScheme:
scheme: https
permanent: true

View file

@ -0,0 +1,67 @@
{
"proposal_id": "CUST81-APPS-PHASE-A-LOCAL-IMAGE-R1",
"disposition": "approved",
"approved_scope": "concrete proposal preparation and local render only; not publish or production deploy",
"workitem": "RAPPS-WP-0017-T02",
"release": "reuse",
"namespace": "reuse",
"deployment": "reuse-surface",
"image_reference": "forgejo.coulomb.social/coulomb/reuse-surface@sha256:2364b7e7859daccf9340a063654e2b09eed1bc27e0adf7b23cbb4b30135a7db0",
"registry_publication_verified": false,
"phase_b_credential_delivery_included": false,
"incremental_infrastructure_cost_eur": 0,
"cost_basis": "existing node, same requests and replicas, same PVC, no new service or purchased capacity; build/registry execution cost not independently accounted",
"credible_loss_bound_eur": null,
"risk_basis": "qualitative supervised change: transient rollout or SQLite concurrent-access outage; RWO PVC single-node mounts need preflight; no customer production admitted; data recovery and exact atomic rollback required before execution",
"autopilot_authority": false,
"local_render_checks": "passed",
"evidence": {
"hardened-reader-local-image-r1.json": {
"path": "/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/hardened-reader-local-image-r1.json",
"sha256": "ed7c2516925d7f16b573a70b5e48d5656a4875e9eb94bcc8760a8bdcbe07f59c"
},
"live-authenticated-source-inventory-r1.json": {
"path": "/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/live-authenticated-source-inventory-r1.json",
"sha256": "ba669d6231c8c611191c7ad28f5914d6c47332032b23b0ea40a13f753e883073"
},
"reuse-release-runtime-metadata-r1.json": {
"path": "/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/reuse-release-runtime-metadata-r1.json",
"sha256": "5ec901cf41b9f52628ff636c23b5bbc4789df6a9118f5cff0ff8518b287357e9"
},
"reuse-release-owner-eso-metadata-r1.json": {
"path": "/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/reuse-release-owner-eso-metadata-r1.json",
"sha256": "ff9aa207ae081d27d86820747a993d4bbc290ba2c354eef72388233d6733adb3"
}
},
"observed_runtime": {
"node_ip": "92.205.62.239",
"cpu_free_m": 440,
"api_surge_cpu_m": 100,
"api_surge_memory_mi": 256,
"pvc": "reuse-surface-data",
"pvc_access": "ReadWriteOnce",
"pvc_capacity": "1Gi",
"previous_image_digest": "forgejo.coulomb.social/coulomb/reuse-surface@sha256:2c1514cb3f5e1297ffeeac912ba36a70f8a2591588a5a4643bc58293e97132de",
"authenticated_sources": 0
},
"gates": [
"publish exact reviewed image and verify registry digest; local digest may require reviewed update if registry transforms manifest",
"founder approval for exact phase A and later credential CCR/phase B",
"recheck target headroom memory node/PVC mount scheduling and authenticated inventory immediately before apply",
"bounded safe server dry-run/client diff and current release revision/rollback capture",
"no ESO reader field during phase A; verify actual imageID health and63public baseline before phase B",
"revoke/remove delivered reader before any downgrade to old vulnerable image"
],
"render_command": [
"helm",
"template",
"reuse",
"charts/reuse-surface",
"--namespace",
"reuse",
"-f",
"helm/reuse-surface-values.yaml",
"-f",
"docs/assessments/cust81-private-reader/phase-a-local-image-r1/phase-a-values.proposed.yaml"
]
}

View file

@ -0,0 +1,665 @@
diff --git a/docs/assessments/cust81-private-reader/phase-a-local-image-r1/apps-owner-release-review.md b/docs/assessments/cust81-private-reader/phase-a-local-image-r1/apps-owner-release-review.md
new file mode 100644
index 0000000..0079fe5
--- /dev/null
+++ b/docs/assessments/cust81-private-reader/phase-a-local-image-r1/apps-owner-release-review.md
@@ -0,0 +1,57 @@
+# Apps concrete phase A proposal review
+
+Disposition: `approved` for proposal preparation and local render only.
+This supplement preserves the reviewed core R2 patch unchanged. It supplies
+actual `reuse` Helm release / `reuse` namespace chart rendering with the locally
+built hardened image digest. It does not authorize publication, execute a live
+change or approve platform's CCR. INTENT basis: source-controlled release
+contracts, explicit S3 handoff and evidence before confidence.
+
+The proposed image is
+`forgejo.coulomb.social/coulomb/reuse-surface@sha256:2364b7e7859daccf9340a063654e2b09eed1bc27e0adf7b23cbb4b30135a7db0`.
+The local build receipt confirms no-network/read-only health and native source
+hash smoke. Registry publication is false. Before apply the exact reviewed
+artifact must be published and independently verified by registry manifest
+digest. If publication changes manifest representation/digest, revise and review
+the bound proposal rather than silently replacing the digest.
+
+Current observations are node `92.205.62.239`, CPU requests3560m of4000m (440m
+free), API100m/256Mi, replicas1 with surge1/unavailable0, PVC
+`reuse-surface-data` Bound RWO1Gi, and Helm ownership without Argo tracking.
+The proposed chart retains resources, strategy and PVC. One100m/256Mi surge fits
+observed CPU headroom; recheck memory, pending pods and node headroom immediately
+before rollout. RWO single-node does not prohibit two pods on that same node,
+but driver mount behavior and SQLite concurrent-access readiness remain
+verification gates. Do not change deployment strategy or PVC to bypass a failure.
+
+Exact readonly authenticated-source inventory is0 at its receipt timestamp.
+This clears the observed migration gate, but recheck before apply; no credential
+or Secret/env was read. Existing Kubernetes-auth `openbao-reuse` store is Ready
+with namespace/ServiceAccount/role matching the consumer; its current
+ExternalSecret contains only the two old fields and remains unchanged in phase A.
+
+Local full-chart render and assertions passed: exact proposed image and14 URL
+allowlist; same100m/256Mi requests; same1surge/0unavailable/replicas1; no Secret,
+ExternalSecret or store objects added. Full chart includes existing landing,
+ingress, services and PVC and must receive normal full-chart server dry-run and
+client diff, not a Deployment-only narrow review.
+
+Incremental infrastructure cost is EUR0 because node, replica steady state,
+requests, services and PVC capacity remain unchanged. Build/registry execution
+cost is not independently accounted. Credible operational loss is qualitative
+and not priced in EUR: rollout failure or concurrent SQLite/PVC access may cause
+service interruption or recovery work. No autopilot grant is inferred. Exact
+human review must accept these estimates and remaining readiness gates.
+
+Before founder-authorized deploy, capture current Helm revision and safe rollback
+reference (old running digest
+`sha256:2c1514cb3f5e1297ffeeac912ba36a70f8a2591588a5a4643bc58293e97132de`),
+known recovery receipt and63 public capability baseline. Use actual Helm release
+`reuse`, namespace`reuse`, owner atomic/wait/timeout path after safe dry-run/diff.
+No exact deploy command is executed by this proposal. Phase A retains the current
+runtime Secret fields and supplies no reader. Verify running imageID, readiness,
+public federation and credential refusal/redirect behavior before phase B.
+If phase A fails with no reader delivered, restore the approved previous release.
+After phase B, revoke/remove reader and verify its absence before any vulnerable
+image restoration. Phase B is independently gated by approved/executed CCR and
+native14 URL/7 entry-hash proof before70/70 hosted discovery.
diff --git a/docs/assessments/cust81-private-reader/phase-a-local-image-r1/phase-a-full-chart.proposed.yaml b/docs/assessments/cust81-private-reader/phase-a-local-image-r1/phase-a-full-chart.proposed.yaml
new file mode 100644
index 0000000..4059c3a
--- /dev/null
+++ b/docs/assessments/cust81-private-reader/phase-a-local-image-r1/phase-a-full-chart.proposed.yaml
@@ -0,0 +1,478 @@
+# PREPARATION RENDER ONLY; no registry publication or server validation proved.
+---
+# Source: reuse-surface/templates/landing-configmap.yaml
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: reuse-surface-landing
+ labels:
+ app.kubernetes.io/name: reuse-surface
+ app.kubernetes.io/instance: reuse
+ app.kubernetes.io/managed-by: Helm
+ app.kubernetes.io/version: "0.1.0"
+ app.kubernetes.io/part-of: railiance-apps
+ helm.sh/chart: reuse-surface-0.1.0
+ app.kubernetes.io/component: landing
+data:
+ index.html: |
+
+ <!doctype html>
+ <html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="robots" content="noindex,nofollow">
+ <title>REUSE federation endpoint</title>
+ <style>
+ :root {
+ color-scheme: light;
+ --ink: #1d2733;
+ --muted: #536271;
+ --line: #d9e0e7;
+ --paper: #f8fafc;
+ --accent: #2066a8;
+ --accent-dark: #164b7e;
+ }
+ * {
+ box-sizing: border-box;
+ }
+ body {
+ margin: 0;
+ min-height: 100vh;
+ font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ color: var(--ink);
+ background: var(--paper);
+ display: grid;
+ place-items: center;
+ padding: 32px 18px;
+ }
+ main {
+ width: min(100%, 720px);
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ background: #ffffff;
+ padding: clamp(28px, 5vw, 48px);
+ box-shadow: 0 18px 48px rgba(29, 39, 51, 0.08);
+ }
+ .eyebrow {
+ margin: 0 0 12px;
+ color: var(--accent-dark);
+ font-size: 0.86rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ }
+ h1 {
+ margin: 0;
+ font-size: clamp(2rem, 7vw, 3rem);
+ line-height: 1.05;
+ }
+ p {
+ max-width: 62ch;
+ margin: 18px 0 0;
+ color: var(--muted);
+ font-size: 1rem;
+ line-height: 1.65;
+ }
+ .actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ margin-top: 28px;
+ }
+ a {
+ color: var(--accent);
+ }
+ .button {
+ display: inline-flex;
+ min-height: 44px;
+ align-items: center;
+ justify-content: center;
+ border-radius: 6px;
+ background: var(--accent);
+ color: #ffffff;
+ padding: 0 18px;
+ font-weight: 700;
+ text-decoration: none;
+ }
+ .button:focus,
+ .button:hover {
+ background: var(--accent-dark);
+ }
+ .links {
+ margin-top: 26px;
+ padding-top: 22px;
+ border-top: 1px solid var(--line);
+ display: grid;
+ gap: 10px;
+ }
+ </style>
+ </head>
+ <body>
+ <main>
+ <p class="eyebrow">reuse.coulomb.social</p>
+ <h1>REUSE federation endpoint</h1>
+ <p>This is the Railiance REUSE capability federation service. It is an API endpoint for automated clients and operators; there is no browser login for this service.</p>
+ <div class="actions">
+ <a class="button" href="/v1/federated">Open federated index</a>
+ </div>
+ <nav class="links" aria-label="Service links">
+ <a href="/health">Health check</a>
+ <a href="/v1/federated">Federated capability index</a>
+ <a href="https://forgejo.coulomb.social/coulomb/railiance-apps/src/branch/main/docs/reuse-surface-on-railiance01.md">Operator runbook</a>
+ </nav>
+ </main>
+ </body>
+ </html>
+---
+# Source: reuse-surface/templates/pvc.yaml
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: reuse-surface-data
+ labels:
+ app.kubernetes.io/name: reuse-surface
+ app.kubernetes.io/instance: reuse
+ app.kubernetes.io/managed-by: Helm
+ app.kubernetes.io/version: "0.1.0"
+ app.kubernetes.io/part-of: railiance-apps
+ helm.sh/chart: reuse-surface-0.1.0
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+---
+# Source: reuse-surface/templates/landing-service.yaml
+apiVersion: v1
+kind: Service
+metadata:
+ name: reuse-surface-landing
+ labels:
+ app.kubernetes.io/name: reuse-surface
+ app.kubernetes.io/instance: reuse
+ app.kubernetes.io/managed-by: Helm
+ app.kubernetes.io/version: "0.1.0"
+ app.kubernetes.io/part-of: railiance-apps
+ helm.sh/chart: reuse-surface-0.1.0
+ app.kubernetes.io/component: landing
+spec:
+ type: ClusterIP
+ selector:
+ app.kubernetes.io/name: reuse-surface
+ app.kubernetes.io/instance: reuse
+ app.kubernetes.io/component: landing
+ ports:
+ - name: http
+ port: 8080
+ targetPort: http
+ protocol: TCP
+---
+# Source: reuse-surface/templates/service.yaml
+apiVersion: v1
+kind: Service
+metadata:
+ name: reuse-surface
+ labels:
+ app.kubernetes.io/name: reuse-surface
+ app.kubernetes.io/instance: reuse
+ app.kubernetes.io/managed-by: Helm
+ app.kubernetes.io/version: "0.1.0"
+ app.kubernetes.io/part-of: railiance-apps
+ helm.sh/chart: reuse-surface-0.1.0
+spec:
+ type: ClusterIP
+ selector:
+ app.kubernetes.io/name: reuse-surface
+ app.kubernetes.io/instance: reuse
+ app.kubernetes.io/component: api
+ ports:
+ - name: http
+ port: 8000
+ targetPort: http
+ protocol: TCP
+---
+# Source: reuse-surface/templates/deployment.yaml
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: reuse-surface
+ labels:
+ app.kubernetes.io/name: reuse-surface
+ app.kubernetes.io/instance: reuse
+ app.kubernetes.io/managed-by: Helm
+ app.kubernetes.io/version: "0.1.0"
+ app.kubernetes.io/part-of: railiance-apps
+ helm.sh/chart: reuse-surface-0.1.0
+ app.kubernetes.io/component: api
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: reuse-surface
+ app.kubernetes.io/instance: reuse
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 0
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: reuse-surface
+ app.kubernetes.io/instance: reuse
+ app.kubernetes.io/component: api
+ spec:
+ enableServiceLinks: false
+ securityContext:
+ {}
+ containers:
+ - name: reuse-surface
+ image: "forgejo.coulomb.social/coulomb/reuse-surface@sha256:2364b7e7859daccf9340a063654e2b09eed1bc27e0adf7b23cbb4b30135a7db0"
+ imagePullPolicy: IfNotPresent
+ command: ["reuse-surface", "serve"]
+ securityContext:
+ {}
+ ports:
+ - name: http
+ containerPort: 8000
+ protocol: TCP
+ envFrom:
+ - secretRef:
+ name: "reuse-surface-env"
+ env:
+ - name: REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST
+ value: "{\"REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN\":[\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/capabilities/capability.railiance.openbao-package.md?ref=35672be138036c8433a5ac5de91e068c03c2b124\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/indexes/capabilities.yaml?ref=35672be138036c8433a5ac5de91e068c03c2b124\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/capabilities/capability.railiance.policy-publication-package.md?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/indexes/capabilities.yaml?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/capabilities/capability.railiance.postgres-isolation-recovery.md?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/indexes/capabilities.yaml?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/capabilities/capability.railiance.sbom-runtime-package.md?ref=adbedd349e8df97be43a1f57db7095af45ff29d1\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/indexes/capabilities.yaml?ref=adbedd349e8df97be43a1f57db7095af45ff29d1\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/capabilities/capability.railiance.telemetry-package.md?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/indexes/capabilities.yaml?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/capabilities/capability.railiance.tenant-engine-package.md?ref=9bdc86572728783a5ed94266f04f7b12d414c557\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/indexes/capabilities.yaml?ref=9bdc86572728783a5ed94266f04f7b12d414c557\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/capabilities/capability.railiance.user-engine-package.md?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/indexes/capabilities.yaml?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738\"]}"
+ - name: REUSE_SURFACE_LISTEN_HOST
+ value: "0.0.0.0"
+ - name: REUSE_SURFACE_LISTEN_PORT
+ value: "8000"
+ - name: REUSE_SURFACE_DB
+ value: "/data/reuse.db"
+ - name: REUSE_SURFACE_CACHE_DIR
+ value: "/data/cache"
+ volumeMounts:
+ - name: data
+ mountPath: /data
+ readinessProbe:
+ httpGet:
+ path: /health
+ port: 8000
+ initialDelaySeconds: 10
+ periodSeconds: 10
+ timeoutSeconds: 5
+ failureThreshold: 3
+ livenessProbe:
+ httpGet:
+ path: /health
+ port: 8000
+ initialDelaySeconds: 15
+ periodSeconds: 30
+ timeoutSeconds: 5
+ failureThreshold: 3
+ resources:
+ limits:
+ cpu: 500m
+ memory: 512Mi
+ requests:
+ cpu: 100m
+ memory: 256Mi
+ volumes:
+ - name: data
+ persistentVolumeClaim:
+ claimName: reuse-surface-data
+---
+# Source: reuse-surface/templates/landing-deployment.yaml
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: reuse-surface-landing
+ labels:
+ app.kubernetes.io/name: reuse-surface
+ app.kubernetes.io/instance: reuse
+ app.kubernetes.io/managed-by: Helm
+ app.kubernetes.io/version: "0.1.0"
+ app.kubernetes.io/part-of: railiance-apps
+ helm.sh/chart: reuse-surface-0.1.0
+ app.kubernetes.io/component: landing
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: reuse-surface
+ app.kubernetes.io/instance: reuse
+ app.kubernetes.io/component: landing
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 0
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: reuse-surface
+ app.kubernetes.io/instance: reuse
+ app.kubernetes.io/component: landing
+ spec:
+ enableServiceLinks: false
+ containers:
+ - name: landing
+ image: "nginxinc/nginx-unprivileged:1.27-alpine"
+ imagePullPolicy: IfNotPresent
+ ports:
+ - name: http
+ containerPort: 8080
+ protocol: TCP
+ readinessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 3
+ periodSeconds: 10
+ timeoutSeconds: 3
+ failureThreshold: 3
+ livenessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 10
+ periodSeconds: 30
+ timeoutSeconds: 3
+ failureThreshold: 3
+ resources:
+ limits:
+ cpu: 50m
+ memory: 64Mi
+ requests:
+ cpu: 10m
+ memory: 32Mi
+ volumeMounts:
+ - name: landing-page
+ mountPath: /usr/share/nginx/html/index.html
+ subPath: index.html
+ readOnly: true
+ volumes:
+ - name: landing-page
+ configMap:
+ name: reuse-surface-landing
+---
+# Source: reuse-surface/templates/ingress.yaml
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: reuse-surface
+ labels:
+ app.kubernetes.io/name: reuse-surface
+ app.kubernetes.io/instance: reuse
+ app.kubernetes.io/managed-by: Helm
+ app.kubernetes.io/version: "0.1.0"
+ app.kubernetes.io/part-of: railiance-apps
+ helm.sh/chart: reuse-surface-0.1.0
+ annotations:
+ cert-manager.io/cluster-issuer: letsencrypt-prod
+ traefik.ingress.kubernetes.io/router.entrypoints: websecure
+ traefik.ingress.kubernetes.io/router.tls: "true"
+spec:
+ ingressClassName: traefik
+ tls:
+ - hosts:
+ - reuse.coulomb.social
+ secretName: reuse-surface-tls
+ rules:
+ - host: reuse.coulomb.social
+ http:
+ paths:
+ - path: /health
+ pathType: Exact
+ backend:
+ service:
+ name: reuse-surface
+ port:
+ number: 8000
+ - path: /v1
+ pathType: Prefix
+ backend:
+ service:
+ name: reuse-surface
+ port:
+ number: 8000
+---
+# Source: reuse-surface/templates/landing-ingress.yaml
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: reuse-surface-landing
+ labels:
+ app.kubernetes.io/name: reuse-surface
+ app.kubernetes.io/instance: reuse
+ app.kubernetes.io/managed-by: Helm
+ app.kubernetes.io/version: "0.1.0"
+ app.kubernetes.io/part-of: railiance-apps
+ helm.sh/chart: reuse-surface-0.1.0
+ annotations:
+ traefik.ingress.kubernetes.io/router.entrypoints: websecure
+ traefik.ingress.kubernetes.io/router.tls: "true"
+ traefik.ingress.kubernetes.io/router.priority: "1"
+spec:
+ ingressClassName: traefik
+ tls:
+ - hosts:
+ - reuse.coulomb.social
+ secretName: reuse-surface-tls
+ rules:
+ - host: reuse.coulomb.social
+ http:
+ paths:
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: reuse-surface-landing
+ port:
+ number: 8080
+---
+# Source: reuse-surface/templates/redirect-ingress.yaml
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: reuse-surface-http-redirect
+ labels:
+ app.kubernetes.io/name: reuse-surface
+ app.kubernetes.io/instance: reuse
+ app.kubernetes.io/managed-by: Helm
+ app.kubernetes.io/version: "0.1.0"
+ app.kubernetes.io/part-of: railiance-apps
+ helm.sh/chart: reuse-surface-0.1.0
+ annotations:
+ traefik.ingress.kubernetes.io/router.entrypoints: web
+ traefik.ingress.kubernetes.io/router.middlewares: "reuse-reuse-surface-redirect-https@kubernetescrd"
+spec:
+ ingressClassName: traefik
+ rules:
+ - host: reuse.coulomb.social
+ http:
+ paths:
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: reuse-surface
+ port:
+ number: 8000
+---
+# Source: reuse-surface/templates/redirect-middleware.yaml
+apiVersion: traefik.io/v1alpha1
+kind: Middleware
+metadata:
+ name: reuse-surface-redirect-https
+ labels:
+ app.kubernetes.io/name: reuse-surface
+ app.kubernetes.io/instance: reuse
+ app.kubernetes.io/managed-by: Helm
+ app.kubernetes.io/version: "0.1.0"
+ app.kubernetes.io/part-of: railiance-apps
+ helm.sh/chart: reuse-surface-0.1.0
+spec:
+ redirectScheme:
+ scheme: https
+ permanent: true
diff --git a/docs/assessments/cust81-private-reader/phase-a-local-image-r1/phase-a-preparation-receipt.json b/docs/assessments/cust81-private-reader/phase-a-local-image-r1/phase-a-preparation-receipt.json
new file mode 100644
index 0000000..c58f4b4
--- /dev/null
+++ b/docs/assessments/cust81-private-reader/phase-a-local-image-r1/phase-a-preparation-receipt.json
@@ -0,0 +1,67 @@
+{
+ "proposal_id": "CUST81-APPS-PHASE-A-LOCAL-IMAGE-R1",
+ "disposition": "approved",
+ "approved_scope": "concrete proposal preparation and local render only; not publish or production deploy",
+ "workitem": "RAPPS-WP-0017-T02",
+ "release": "reuse",
+ "namespace": "reuse",
+ "deployment": "reuse-surface",
+ "image_reference": "forgejo.coulomb.social/coulomb/reuse-surface@sha256:2364b7e7859daccf9340a063654e2b09eed1bc27e0adf7b23cbb4b30135a7db0",
+ "registry_publication_verified": false,
+ "phase_b_credential_delivery_included": false,
+ "incremental_infrastructure_cost_eur": 0,
+ "cost_basis": "existing node, same requests and replicas, same PVC, no new service or purchased capacity; build/registry execution cost not independently accounted",
+ "credible_loss_bound_eur": null,
+ "risk_basis": "qualitative supervised change: transient rollout or SQLite concurrent-access outage; RWO PVC single-node mounts need preflight; no customer production admitted; data recovery and exact atomic rollback required before execution",
+ "autopilot_authority": false,
+ "local_render_checks": "passed",
+ "evidence": {
+ "hardened-reader-local-image-r1.json": {
+ "path": "/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/hardened-reader-local-image-r1.json",
+ "sha256": "ed7c2516925d7f16b573a70b5e48d5656a4875e9eb94bcc8760a8bdcbe07f59c"
+ },
+ "live-authenticated-source-inventory-r1.json": {
+ "path": "/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/live-authenticated-source-inventory-r1.json",
+ "sha256": "ba669d6231c8c611191c7ad28f5914d6c47332032b23b0ea40a13f753e883073"
+ },
+ "reuse-release-runtime-metadata-r1.json": {
+ "path": "/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/reuse-release-runtime-metadata-r1.json",
+ "sha256": "5ec901cf41b9f52628ff636c23b5bbc4789df6a9118f5cff0ff8518b287357e9"
+ },
+ "reuse-release-owner-eso-metadata-r1.json": {
+ "path": "/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/reuse-release-owner-eso-metadata-r1.json",
+ "sha256": "ff9aa207ae081d27d86820747a993d4bbc290ba2c354eef72388233d6733adb3"
+ }
+ },
+ "observed_runtime": {
+ "node_ip": "92.205.62.239",
+ "cpu_free_m": 440,
+ "api_surge_cpu_m": 100,
+ "api_surge_memory_mi": 256,
+ "pvc": "reuse-surface-data",
+ "pvc_access": "ReadWriteOnce",
+ "pvc_capacity": "1Gi",
+ "previous_image_digest": "forgejo.coulomb.social/coulomb/reuse-surface@sha256:2c1514cb3f5e1297ffeeac912ba36a70f8a2591588a5a4643bc58293e97132de",
+ "authenticated_sources": 0
+ },
+ "gates": [
+ "publish exact reviewed image and verify registry digest; local digest may require reviewed update if registry transforms manifest",
+ "founder approval for exact phase A and later credential CCR/phase B",
+ "recheck target headroom memory node/PVC mount scheduling and authenticated inventory immediately before apply",
+ "bounded safe server dry-run/client diff and current release revision/rollback capture",
+ "no ESO reader field during phase A; verify actual imageID health and63public baseline before phase B",
+ "revoke/remove delivered reader before any downgrade to old vulnerable image"
+ ],
+ "render_command": [
+ "helm",
+ "template",
+ "reuse",
+ "charts/reuse-surface",
+ "--namespace",
+ "reuse",
+ "-f",
+ "helm/reuse-surface-values.yaml",
+ "-f",
+ "docs/assessments/cust81-private-reader/phase-a-local-image-r1/phase-a-values.proposed.yaml"
+ ]
+}
diff --git a/docs/assessments/cust81-private-reader/phase-a-local-image-r1/phase-a-values.proposed.yaml b/docs/assessments/cust81-private-reader/phase-a-local-image-r1/phase-a-values.proposed.yaml
new file mode 100644
index 0000000..6e6f81e
--- /dev/null
+++ b/docs/assessments/cust81-private-reader/phase-a-local-image-r1/phase-a-values.proposed.yaml
@@ -0,0 +1,21 @@
+# PROPOSAL ONLY; digest is locally built, NOT published.
+# Publish exact reviewed artifact and verify registry digest before any apply.
+image:
+ repository: forgejo.coulomb.social/coulomb/reuse-surface
+ digest: sha256:2364b7e7859daccf9340a063654e2b09eed1bc27e0adf7b23cbb4b30135a7db0
+authSourceAllowlist:
+ REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN:
+ - https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/capabilities/capability.railiance.openbao-package.md?ref=35672be138036c8433a5ac5de91e068c03c2b124
+ - https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/indexes/capabilities.yaml?ref=35672be138036c8433a5ac5de91e068c03c2b124
+ - https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/capabilities/capability.railiance.policy-publication-package.md?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3
+ - https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/indexes/capabilities.yaml?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3
+ - https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/capabilities/capability.railiance.postgres-isolation-recovery.md?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5
+ - https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/indexes/capabilities.yaml?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5
+ - https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/capabilities/capability.railiance.sbom-runtime-package.md?ref=adbedd349e8df97be43a1f57db7095af45ff29d1
+ - https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/indexes/capabilities.yaml?ref=adbedd349e8df97be43a1f57db7095af45ff29d1
+ - https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/capabilities/capability.railiance.telemetry-package.md?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5
+ - https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/indexes/capabilities.yaml?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5
+ - https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/capabilities/capability.railiance.tenant-engine-package.md?ref=9bdc86572728783a5ed94266f04f7b12d414c557
+ - https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/indexes/capabilities.yaml?ref=9bdc86572728783a5ed94266f04f7b12d414c557
+ - https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/capabilities/capability.railiance.user-engine-package.md?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738
+ - https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/indexes/capabilities.yaml?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738
diff --git a/docs/assessments/cust81-private-reader/phase-a-local-image-r1/manifest.json b/docs/assessments/cust81-private-reader/phase-a-local-image-r1/manifest.json
new file mode 100644
index 0000000..15cc477
--- /dev/null
+++ b/docs/assessments/cust81-private-reader/phase-a-local-image-r1/manifest.json
@@ -0,0 +1,12 @@
+{
+ "proposal_id": "CUST81-APPS-PHASE-A-LOCAL-IMAGE-R1",
+ "reviewer_owner": "railiance-apps",
+ "disposition": "approved",
+ "approved_scope": "preparation only, local full-chart render, supervised founder review required",
+ "files": {
+ "docs/assessments/cust81-private-reader/phase-a-local-image-r1/apps-owner-release-review.md": "a74e96986798a727fc87c0d3c578601579778dfd8521796d0bb81df8451c751b",
+ "docs/assessments/cust81-private-reader/phase-a-local-image-r1/phase-a-full-chart.proposed.yaml": "36c20c3e16757d3e888a3db02853032b33e791aa04a6055fe001121254345b81",
+ "docs/assessments/cust81-private-reader/phase-a-local-image-r1/phase-a-preparation-receipt.json": "3282a17ae3724619e40dd1f34846d83b2297d9ae00030618dbd269e8e2fd7d39",
+ "docs/assessments/cust81-private-reader/phase-a-local-image-r1/phase-a-values.proposed.yaml": "35284857ec987fc7a929f5c2eecdb44c7d3367f7835904692947de09918ab8f1"
+ }
+}

View file

@ -0,0 +1,21 @@
# PROPOSAL ONLY; digest is locally built, NOT published.
# Publish exact reviewed artifact and verify registry digest before any apply.
image:
repository: forgejo.coulomb.social/coulomb/reuse-surface
digest: sha256:2364b7e7859daccf9340a063654e2b09eed1bc27e0adf7b23cbb4b30135a7db0
authSourceAllowlist:
REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN:
- https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/capabilities/capability.railiance.openbao-package.md?ref=35672be138036c8433a5ac5de91e068c03c2b124
- https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/indexes/capabilities.yaml?ref=35672be138036c8433a5ac5de91e068c03c2b124
- https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/capabilities/capability.railiance.policy-publication-package.md?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3
- https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/indexes/capabilities.yaml?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3
- https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/capabilities/capability.railiance.postgres-isolation-recovery.md?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5
- https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/indexes/capabilities.yaml?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5
- https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/capabilities/capability.railiance.sbom-runtime-package.md?ref=adbedd349e8df97be43a1f57db7095af45ff29d1
- https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/indexes/capabilities.yaml?ref=adbedd349e8df97be43a1f57db7095af45ff29d1
- https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/capabilities/capability.railiance.telemetry-package.md?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5
- https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/indexes/capabilities.yaml?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5
- https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/capabilities/capability.railiance.tenant-engine-package.md?ref=9bdc86572728783a5ed94266f04f7b12d414c557
- https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/indexes/capabilities.yaml?ref=9bdc86572728783a5ed94266f04f7b12d414c557
- https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/capabilities/capability.railiance.user-engine-package.md?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738
- https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/indexes/capabilities.yaml?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738

View file

@ -0,0 +1,53 @@
# Apps phase B delivery preparation review
Disposition: `approved` for exact bounded preparation only. No founder credential,
source-activation or production rollout authorization is inferred. Own workitem
RAPPS-WP-0017-T03; INTENT basis is release contracts, explicit S3 handoffs,
repeatable recovery and evidence before confidence.
Full ExternalSecret server dry-run accepted and client diff verified only the
single new reader item plus native remoteRef defaults. Both old fields, existing
store, target and all other source spec/metadata are unchanged. The first strict
live equality check correctly refused on native ESO defaults, with no apply;
subsequent projection explicitly verified those defaults. The approved source
replacement is distinct from the current active file and must replace it only
after consent, then pass normal owner source publication before live apply.
Server acceptance does not prove the dedicated KV field or ESO read policy exists.
Phase B must follow verified phase A *and* terminal verified platform provisioning
under the exact approved CCR. Verify current API pod ready, spec image index and
imageID against published OCI index or its linux/amd64 child (exclude attestation
manifest), source federation/private verification module hashes and exact14 URL
allowlist. Confirm ESO Ready and selected reader-key presence only, then controlled
restart with fresh headroom/PVC readiness and capture-only output. No token or env
value enters argv, source, receipts or logs.
The native proof wrapper consumes only three exact-byte non-secret packets via
stdin, verifies their compiled hashes and module/source hashes, verifies the
complete14 URL allowlist, and invokes the application's native fetch path without
cache. Temporary packet files are0600 and removed. Four local refusal fixtures
passed; no real credentials or network requests were used. Its output must be
combined with separately verified API pod UID/readiness/image metadata; a
process receipt alone does not prove the target pod identity. Any missing,
mis-hashed, non-exact scope or native fetch outcome refuses success. Native
proof requires all7 sources and14 metadata fetches before registrations/final70.
Rollback ordering is explicit: revoke dedicated Forgejo PAT and verify; restore
source-controlled two-field ESO mapping to stop rematerialization; remove only
the materialized reader key through a captured exact JSON patch; restart the
current hardened deployment and verify reader absence in new pod/materialized
Secret plus issuer revocation. Only then may an independently approved rollback
to vulnerable Helm revision10 occur. No old-token fallback, default DB restore,
credential re-mint or Secret/env dump is proposed.
All exact predecessor/replacement digests, SSH delivery/restart/removal commands,
proof hashes/packet paths and gate schema are in phase-b-execution-proposal-r1.json.
No credential creation, ESO apply, Secret mutation, restart or deploy was executed.
Revision2 of the same proposal corrects malformed shell quoting in the original
reader-removal command. Original proposal/review/manifest remain archived.
The replacement is structured SSH argv with `shlex.join` over exact remote
kubectl argv and compact JSON patch. Offline shell parsing roundtrips exactly
one removal operation at `/data/REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN`; no
other key is touched. This refinement is not another successful trial.
Disposition remains approved preparation only, never founder live authority.

View file

@ -0,0 +1,13 @@
{
"id": "CUST81-PHASE-B-COMMAND-PARSER-CHECK-R2",
"status": "passed",
"proposal_revision": 2,
"original_proposal_sha256": "sha256:572af68fcf56072c1fd846101a76611885acd6efedfe5dba10d96d9f77f701e0",
"ssh_argv_count": 3,
"remote_argv_roundtrip": true,
"patch_operation_count": 1,
"patch_op": "remove",
"patch_path": "/data/REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN",
"other_field_changes": false,
"live_command_executed": false
}

View file

@ -0,0 +1,31 @@
{
"id": "CUST81-APPS-PHASE-B-ESO-PREFLIGHT-R1",
"checked_at": "2026-10-14T23:55:29.993576+00:00",
"target_node_ip": "92.205.62.239",
"status": "verified_preparation",
"active_predecessor_path": "/home/worsch/railiance-apps/manifests/reuse-surface-runtime-externalsecret.yaml",
"active_predecessor_sha256": "e036084aec9fad3bea63cc0d591c3b40a392ab9f54d9226a2d2b3fd7194d8a47",
"proposed_replacement_path": "/home/worsch/railiance-apps/docs/assessments/cust81-private-reader/reuse-surface-runtime-externalsecret.r1.yaml",
"proposed_replacement_sha256": "adfb34c0ee4d01bc602f57cd2320f1658465fb8f8660978828fffa05a5b604b5",
"old_two_fields_retained": true,
"existing_store_unchanged": true,
"live_spec_projected_matches_predecessor": true,
"native_remoteRef_defaults_verified": true,
"previous_attempt": "full live data equality assertion refused on native ESO defaults; no command apply executed, original refusal retained",
"results": [
{
"operation": "server_dry_run",
"exit_code": 0,
"raw_stdout_stderr_discarded": true
},
{
"operation": "client_diff",
"exit_code": 1,
"exact_new_reader_item_plus_native_defaults_only": true,
"raw_values_annotations_stderr_discarded": true
}
],
"credentials_provisioned": false,
"live_apply_performed": false,
"limitation": "server acceptance does not prove dedicated KV field exists or ESO consumer policy reader path granted"
}

View file

@ -0,0 +1,18 @@
{
"proposal_id": "CUST81-APPS-PHASE-B-EXECUTION-PROPOSAL-R1",
"revision": 2,
"owner": "railiance-apps",
"disposition": "approved",
"approved_scope": "bounded source preparation only; no active ESO or live action",
"founder_live_approval": false,
"refinement_or_rescue": true,
"files": {
"apps-owner-phase-b-review.md": "sha256:496c842f4a284d6b2904c027dfd1e8aab85567f19fa74a228f27c4cc578ce026",
"command-parser-check-r2.json": "sha256:5080bdd2ece186200aec1a37b4450c99a50d435444f0f850277b82252516d6e1",
"eso-preflight-r1.json": "sha256:e39f07427af80fd036e6eec21202efe194fff2db67b8523bb07052e1388d8fab",
"native-proof-wrapper-local-checks-r1.json": "sha256:c5fcee128f7b4653ecfc0c455aab57a7efa8f6a9de382af69d447d8065091bab",
"native_runtime_proof_stdin.py": "sha256:1edc317e8563a120b62a6830eebb2875c144b45c57a491eb7da849095c5d1933",
"phase-b-execution-proposal-r1.json": "sha256:13f19158e09b0693a64ff400ecbc3e5ec09d28673731e87733d1e2fad9bd3222",
"run_eso_preflight.py": "sha256:fd42789518e9a6c1305fc3828bf1afb8c9a4e8238b4eeed30f7e7ec05656f09b"
}
}

View file

@ -0,0 +1,15 @@
{
"id": "CUST81-NATIVE-RUNTIME-PROOF-PREPARATION-CHECKS-R1",
"status": "passed",
"wrapper_sha256": "1edc317e8563a120b62a6830eebb2875c144b45c57a491eb7da849095c5d1933",
"fixture_cases": [
"missing_reader",
"malformed_allowlist",
"tampered_packet",
"native_refusal"
],
"native_network_requests": 0,
"real_credentials_used": false,
"failure_output_sentinel_excluded": true,
"scope_refusal_before_any_fetch": true
}

View file

@ -0,0 +1,38 @@
"""Approved preparation only: execute in verified hardened API pod after phase B.
stdin envelope contains only three base64-encoded hash-bound non-secret packets.
No token accepted; native module consumes its reviewed runtime source credential.
"""
import base64,hashlib,json,os,tempfile
from pathlib import Path
import sys
from reuse_surface import federation, private_source_verify as native
EXPECTED={'proposal':'7819eb51d249f5c7aff4df9fb520afcfb76603b05a4104439499102b6c20af99','disclosure':'46ac4fe49a08054cc215aade2fbaf3b36ee334520997857ed84f530b52da9d35','consent':'4a4602ad926e3af855d6ddba4731430d33e4a7432c507ba0a5193b9f43056b52'}
FEDI='dbaa40f25cc0f090143578cd1c7068cb2cb00960fafd17d5239bf8a5bac514dd'
NATIVE='c67a284091cc4dd62dca56658a14b8ea1357e2b3df1518120da4c51e0ee8d85c'
receipt={'id':'CUST81-NATIVE-RUNTIME-PROOF-R1','status':'unverified','raw_errors_or_values_emitted':False}
try:
source_sha=hashlib.sha256(Path(federation.__file__).read_bytes()).hexdigest()
helper_sha=hashlib.sha256(Path(native.__file__).read_bytes()).hexdigest()
assert source_sha==FEDI and helper_sha==NATIVE
envelope=json.loads(sys.stdin.buffer.read(1048576))
assert set(envelope)==set(EXPECTED)
with tempfile.TemporaryDirectory(prefix='cust81-native-packets-') as temporary:
paths={}
for name,digest in EXPECTED.items():
content=base64.b64decode(envelope[name],validate=True)
assert hashlib.sha256(content).hexdigest()==digest
p=Path(temporary)/(name+'.json');p.write_bytes(content);p.chmod(0o600);paths[name]=p
sources=list(native._prepared_sources(paths['proposal'],paths['disclosure'],paths['consent']))
expected_urls={native._raw_url(source['repo'],source['consented_commit'],path) for source,files in sources for path in files}
allow=json.loads(os.environ.get('REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST','{}'))
assert set(allow)=={native.AUTH_ENV} and len(allow[native.AUTH_ENV])==14 and set(allow[native.AUTH_ENV])==expected_urls
# Presence only: never print/read the secret value here. Native acquisition
# is subsequently performed only at its reviewed exact URL routing boundary.
key_present=native.AUTH_ENV in os.environ
assert key_present
result=native.verify(paths['proposal'],paths['disclosure'],paths['consent'])
receipt.update(source_sha256=source_sha,helper_sha256=helper_sha,packet_sha256=EXPECTED,runtime_allowlist_exact14=True,reader_key_present=True,native_fetch=result,status='verified' if result['all_verified'] else 'native_fetch_failed')
except Exception:
receipt.update(status='runtime_or_native_proof_refused',error='hash_scope_or_native_validation_failed')
print(json.dumps(receipt,sort_keys=True))
raise SystemExit(0 if receipt['status']=='verified' else 1)

View file

@ -0,0 +1,88 @@
{
"id": "CUST81-APPS-PHASE-B-EXECUTION-PROPOSAL-R1",
"disposition": "approved",
"approved_scope": "bounded preparation only; founder live go still required",
"status": "prepared_not_executed",
"workitem": "RAPPS-WP-0017-T03",
"active_predecessor_sha256": "e036084aec9fad3bea63cc0d591c3b40a392ab9f54d9226a2d2b3fd7194d8a47",
"replacement_sha256": "adfb34c0ee4d01bc602f57cd2320f1658465fb8f8660978828fffa05a5b604b5",
"preconditions": [
"verified phase A current API pod ready pinned hardened image/source hashes",
"platform provisioner terminal verified and approved CCR revision executed",
"Forgejo exact7 readonly grants and PAT receipt verified",
"native ESO path-read policy/store issuer receipt verified",
"founder consent to exact phase B source update and delivery"
],
"source_update_after_consent": {
"command": "cp docs/assessments/cust81-private-reader/reuse-surface-runtime-externalsecret.r1.yaml manifests/reuse-surface-runtime-externalsecret.yaml",
"publication": "owner commits only exact replacement and normal rmgr sync --push; verify primary exact source commit before liveapply",
"not_executed": true
},
"apply_command": "ssh railiance01 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl apply -f /tmp/cust81-phase-b/reuse-surface-runtime-externalsecret.yaml'",
"sync_verify_before_restart": "wait for ESO Ready and selected reader-key-presence boolean via reviewed captured-output wrapper; never raw Secret/annotation/env output",
"restart_command": "ssh railiance01 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl rollout restart deployment/reuse-surface --namespace reuse'",
"restart_wait_command": "ssh railiance01 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl rollout status deployment/reuse-surface --namespace reuse --timeout=300s'",
"headroom_gate": "recheck100m/256Mi surge headroom/PVC and actual hardened deployment before restart",
"cleanup_order": [
"credential owner revokes exact dedicated Forgejo PAT and verifies revocation; no oldtoken fallback",
"restore reviewed two-field ESO manifest through source-controlled owner lane and apply, stops rematerialization",
"remove only REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN from reuse/reuse-surface-env with captured-output exact JSON patch",
"restart/wait current hardened deployment, verify reader-key absence boolean in new pod and materialized Secret plus exact token revocation",
"only then may separately approved Helm rollback reuse10 restore vulnerable priorimage; do not restore DB by default"
],
"raw_output_contract": "all mutating/proof child stdout/stderr captured/discarded; only whitelisted outcome/proof JSON may reach receipts",
"runtime_proof": {
"pod_metadata_schema": [
"verified API pod name and UID",
"Ready true",
"expected spec image OCI indexsha2364b7e...",
"actual imageID equals index or linux/amd64childsha1a69354af2bc87d4baaecc4b02e13fda468efd3746024f440cc6d138beeee2ab",
"node92.205.62.239",
"source/header-routing/native helper hashes verified",
"exact14 allowlist boolean",
"reader-key presence boolean",
"native14fetch/7consentedhashall_verified"
],
"wrapper_path": "/tmp/cust81-apps-reader-owner/docs/assessments/cust81-private-reader/phase-b-r1/native_runtime_proof_stdin.py",
"wrapper_sha256": "1edc317e8563a120b62a6830eebb2875c144b45c57a491eb7da849095c5d1933",
"packets": {
"proposal": {
"path": "/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/private-reader-provisioning-preparation-r1.json",
"sha256": "7819eb51d249f5c7aff4df9fb520afcfb76603b05a4104439499102b6c20af99"
},
"disclosure": {
"path": "/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/private-source-disclosure-proposal-r1.json",
"sha256": "46ac4fe49a08054cc215aade2fbaf3b36ee334520997857ed84f530b52da9d35"
},
"consent": {
"path": "/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/private-source-owner-consent-r1.json",
"sha256": "4a4602ad926e3af855d6ddba4731430d33e4a7432c507ba0a5193b9f43056b52"
}
},
"invocation": "capture kubectl --kubeconfig <verifiedconfig> -n reuse exec -i <metadata-verified current API pod> -c reuse-surface -- python3 -c <exact hash-bound wrapper source>; stdin JSON envelope {proposal:base64(exactbytes),disclosure:base64(exactbytes),consent:base64(exactbytes)}; no token/credential in argv or stdin; wrapper writes/removes only temporary nonsecret packet files",
"exit_success_required": true
},
"no_live_credential_or_apply_action": true,
"revision": 2,
"refinement_or_rescue": true,
"original_revision_sha256": "sha256:572af68fcf56072c1fd846101a76611885acd6efedfe5dba10d96d9f77f701e0",
"reader_key_removal_ssh_argv": [
"ssh",
"railiance01",
"env KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl patch secret reuse-surface-env --namespace reuse --type=json -p '[{\"op\":\"remove\",\"path\":\"/data/REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN\"}]'"
],
"reader_key_removal_remote_argv": [
"env",
"KUBECONFIG=/etc/rancher/k3s/k3s.yaml",
"kubectl",
"patch",
"secret",
"reuse-surface-env",
"--namespace",
"reuse",
"--type=json",
"-p",
"[{\"op\":\"remove\",\"path\":\"/data/REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN\"}]"
],
"reader_key_removal_offline_check": "passed shell roundtrip exact single remove op on only selected reader key; no other data field/annotation/spec touched"
}

View file

@ -0,0 +1,30 @@
from pathlib import Path
import subprocess,json,yaml,hashlib,datetime
art=Path(__file__).resolve().parent
canonical=Path('/home/worsch/railiance-apps')
old=canonical/'manifests/reuse-surface-runtime-externalsecret.yaml';new=canonical/'docs/assessments/cust81-private-reader/reuse-surface-runtime-externalsecret.r1.yaml'
base=yaml.safe_load(old.read_text());proposal=yaml.safe_load(new.read_text())
assert proposal['spec']['data'][:-1]==base['spec']['data']
item=proposal['spec']['data'][-1]
assert item=={'secretKey':'REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN','remoteRef':{'key':'workloads/reuse/reuse-surface/forgejo-source-read','property':'FORGEJO_SOURCE_TOKEN'}}
assert {k:v for k,v in proposal.items() if k!='spec'}=={k:v for k,v in base.items() if k!='spec'}
assert {k:v for k,v in proposal['spec'].items() if k!='data'}=={k:v for k,v in base['spec'].items() if k!='data'}
kube=['kubectl','--kubeconfig','/home/worsch/.kube/config-railiance01']
p=subprocess.run(kube+['get','nodes','-o','json'],capture_output=True,timeout=20);nodes=json.loads(p.stdout);assert p.returncode==0 and len(nodes['items'])==1 and any(a['address']=='92.205.62.239' for a in nodes['items'][0]['status']['addresses'])
p=subprocess.run(kube+['-n','reuse','get','externalsecret','reuse-surface-runtime','-o','json'],capture_output=True,timeout=20);assert p.returncode==0;live=json.loads(p.stdout)
rows=[{'secretKey':r['secretKey'],'remoteRef':{k:r['remoteRef'][k] for k in ('key','property')}} for r in live['spec']['data']]
assert rows==base['spec']['data'] and live['spec']['secretStoreRef']==base['spec']['secretStoreRef']
assert all({k:v for k,v in r['remoteRef'].items() if k not in ('key','property')}=={'conversionStrategy':'Default','decodingStrategy':'None','metadataPolicy':'None'} for r in live['spec']['data'])
results=[]
for args in [['apply','--dry-run=server','-f',str(new)],['diff','-f',str(new)]]:
p=subprocess.run(kube+['-n','reuse']+args,capture_output=True,timeout=60)
if args[0]=='diff':
changed=[line[1:].strip() for line in p.stdout.decode(errors='replace').splitlines() if line.startswith(('+','-')) and not line.startswith(('+++','---'))]
intended={'- remoteRef:', 'key: workloads/reuse/reuse-surface/forgejo-source-read','property: FORGEJO_SOURCE_TOKEN','secretKey: REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN','conversionStrategy: Default','decodingStrategy: None','metadataPolicy: None'}
other=[line for line in changed if line not in intended and not line.startswith('generation:')]
assert not other
results.append({'operation':'client_diff','exit_code':p.returncode,'exact_new_reader_item_plus_native_defaults_only':True,'raw_values_annotations_stderr_discarded':True})
else:results.append({'operation':'server_dry_run','exit_code':p.returncode,'raw_stdout_stderr_discarded':True})
assert results[0]['exit_code']==0 and results[1]['exit_code'] in (0,1)
r={'id':'CUST81-APPS-PHASE-B-ESO-PREFLIGHT-R1','checked_at':datetime.datetime.now(datetime.timezone.utc).isoformat(),'target_node_ip':'92.205.62.239','status':'verified_preparation','active_predecessor_path':str(old),'active_predecessor_sha256':hashlib.sha256(old.read_bytes()).hexdigest(),'proposed_replacement_path':str(new),'proposed_replacement_sha256':hashlib.sha256(new.read_bytes()).hexdigest(),'old_two_fields_retained':True,'existing_store_unchanged':True,'live_spec_projected_matches_predecessor':True,'native_remoteRef_defaults_verified':True,'previous_attempt':'full live data equality assertion refused on native ESO defaults; no command apply executed, original refusal retained','results':results,'credentials_provisioned':False,'live_apply_performed':False,'limitation':'server acceptance does not prove dedicated KV field exists or ESO consumer policy reader path granted'}
(art/'eso-preflight-r1.json').write_text(json.dumps(r,indent=2)+'\n');print(json.dumps(r,indent=2))

View file

@ -0,0 +1,29 @@
{
"proposal_id": "CUST81-APPS-PRIVATE-READER-RELEASE-R1",
"status": "local_verified_live_blocked",
"real_image_digest": null,
"live_execution": false,
"cost_estimate_eur": null,
"risk_estimate_eur": null,
"estimate_status": "live cost/loss bounds require release-specific runtime headroom, recovery and image evidence; not executable before supervisor-approved estimates",
"paths": {
"charts/reuse-surface/templates/_helpers.tpl": "sha256:789606a31ac8da6dfb7a5f6ace6a77269e3be3867d2e9a11e52b19aadf6977df",
"charts/reuse-surface/templates/deployment.yaml": "sha256:2ec0be6d9aa24ed026b0c6629ab8ba003669eb0460d722fe413cd4934111540c",
"charts/reuse-surface/values.schema.json": "sha256:ea73e027e4dcd832cc0bd48fc7ccfa29158153033ca02a8dc7b25b7df0d6c2b5",
"charts/reuse-surface/values.yaml": "sha256:d1aeff71df0c53e4ecb3f4ebaac50ced7691b52c673fe9b49fbaeee6ca02c226",
"docs/assessments/cust81-private-reader/apps-owner-review-r2.md": "sha256:a3a2f2db6f0a8461a0d8299825c842c8ce427f59c86238884609d463837b3b75",
"docs/assessments/cust81-private-reader/local-verification-r2.json": "sha256:d723561845a05a76be9554596011c267fc67dd24953160f3bd7bd26a85c4812c",
"docs/assessments/cust81-private-reader/private-reader-values.r1.yaml": "sha256:e02a319aca9c4860cdb8338009e60af6723bdb1fc17a591db0824273206eff83",
"docs/assessments/cust81-private-reader/reuse-surface-runtime-externalsecret.r1.yaml": "sha256:adfb34c0ee4d01bc602f57cd2320f1658465fb8f8660978828fffa05a5b604b5",
"docs/assessments/cust81-private-reader/runtime-auth-source-allowlist-r1.json": "sha256:e1948c537e206b30fc72b9e4732a3f600d92e131813e6f7ecfd4bfbb18c27286",
"docs/assessments/cust81-private-reader/supervision-preparation-r1.json": "sha256:232f195026d704164b5973b5098002205445ff3ed6b0d2dd0c4a3c92c6ad9ca5",
"docs/assessments/cust81-private-reader/verify_preparation.py": "sha256:c07921ea3fcd7c646aa6f28f34f7cdcb552e893341fe53dc7102ee376541d04d",
"scripts/reuse_authenticated_source_inventory.py": "sha256:42eed5513db090d338daec39258b89a4ce2e9369d175c3c8312904ed68867ee1",
"workplans/RAPPS-WP-0017-reuse-private-reader-release.md": "sha256:3a114a461647b46f3cc8e0ff9aa16b69aed0429cbc1578edf9b0fed5e220cc55"
},
"chart_patch_sha256": "39ebaa7f4050f9204fe8ee38364fbbce874903447ac45ccd408cf434aa6e4a73",
"revision": 2,
"refinement_or_rescue": true,
"original_revision_preserved": "root owner-assembly/apps-r1 archive",
"correction": "pod SQL native registration url projection plus exact SQLite readonly fixture, safe legacy routes and untrusted successful output refusal"
}

View file

@ -0,0 +1,5 @@
# PREPARATION ONLY: empty digest deliberately prevents rendering/deployment.
# Fill only after reuse-surface publishes and reviews the fully hardened image.
image:
digest: ""
authSourceAllowlist: {"REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN": ["https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/capabilities/capability.railiance.openbao-package.md?ref=35672be138036c8433a5ac5de91e068c03c2b124", "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/indexes/capabilities.yaml?ref=35672be138036c8433a5ac5de91e068c03c2b124", "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/capabilities/capability.railiance.policy-publication-package.md?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3", "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/indexes/capabilities.yaml?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3", "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/capabilities/capability.railiance.postgres-isolation-recovery.md?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5", "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/indexes/capabilities.yaml?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5", "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/capabilities/capability.railiance.sbom-runtime-package.md?ref=adbedd349e8df97be43a1f57db7095af45ff29d1", "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/indexes/capabilities.yaml?ref=adbedd349e8df97be43a1f57db7095af45ff29d1", "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/capabilities/capability.railiance.telemetry-package.md?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5", "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/indexes/capabilities.yaml?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5", "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/capabilities/capability.railiance.tenant-engine-package.md?ref=9bdc86572728783a5ed94266f04f7b12d414c557", "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/indexes/capabilities.yaml?ref=9bdc86572728783a5ed94266f04f7b12d414c557", "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/capabilities/capability.railiance.user-engine-package.md?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738", "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/indexes/capabilities.yaml?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738"]}

View file

@ -0,0 +1,46 @@
# PROPOSAL ONLY: phase B, after hardened runtime verified and CCR approved.
# reuse-surface hub runtime secrets from OpenBao (RAILIANCE-WP-0011 / CCR-2026-0005).
#
# OpenBao path (platform KV v2): workloads/reuse/reuse-surface/runtime-secrets
# Fields: REUSE_SURFACE_TOKEN, REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET
#
# Prereqs on railiance01:
# - External Secrets Operator (make eso-deploy)
# - ClusterSecretStore openbao-reuse (railiance-platform openbao-secretstore)
# - OpenBao policy workload-kv-read-reuse-surface-runtime + ESO token
# (make reuse-openbao-eso-token-apply)
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: reuse-surface-runtime
namespace: reuse
labels:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/part-of: railiance-apps
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: openbao-reuse
target:
# Explicit target metadata prevents ESO copying source last-applied annotations.
template:
metadata:
labels:
app.kubernetes.io/name: reuse-surface
app.kubernetes.io/part-of: railiance-apps
name: reuse-surface-env
creationPolicy: Owner
data:
- secretKey: REUSE_SURFACE_TOKEN
remoteRef:
key: workloads/reuse/reuse-surface/runtime-secrets
property: REUSE_SURFACE_TOKEN
- secretKey: REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET
remoteRef:
key: workloads/reuse/reuse-surface/runtime-secrets
property: REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET
- secretKey: REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN
remoteRef:
key: workloads/reuse/reuse-surface/forgejo-source-read
property: FORGEJO_SOURCE_TOKEN

View file

@ -0,0 +1,18 @@
{
"REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN": [
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/capabilities/capability.railiance.openbao-package.md?ref=35672be138036c8433a5ac5de91e068c03c2b124",
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/indexes/capabilities.yaml?ref=35672be138036c8433a5ac5de91e068c03c2b124",
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/capabilities/capability.railiance.policy-publication-package.md?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/indexes/capabilities.yaml?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/capabilities/capability.railiance.postgres-isolation-recovery.md?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/indexes/capabilities.yaml?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/capabilities/capability.railiance.sbom-runtime-package.md?ref=adbedd349e8df97be43a1f57db7095af45ff29d1",
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/indexes/capabilities.yaml?ref=adbedd349e8df97be43a1f57db7095af45ff29d1",
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/capabilities/capability.railiance.telemetry-package.md?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5",
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/indexes/capabilities.yaml?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5",
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/capabilities/capability.railiance.tenant-engine-package.md?ref=9bdc86572728783a5ed94266f04f7b12d414c557",
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/indexes/capabilities.yaml?ref=9bdc86572728783a5ed94266f04f7b12d414c557",
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/capabilities/capability.railiance.user-engine-package.md?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738",
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/indexes/capabilities.yaml?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738"
]
}

View file

@ -0,0 +1,24 @@
{
"proposal_id": "CUST81-APPS-PRIVATE-READER-RELEASE-R1",
"owner": "railiance-apps",
"review_revision": 3,
"disposition": "approved",
"approved_scope": "ordinary source-only standard digest representation correction; no live runtime change",
"refinement_or_rescue": true,
"original_manifest_sha256": "45d467c2bb48462a388f5df4ba5188492d406bdddbddfbf3487bd2077486dec8",
"qualified_manifest_sha256": "324757f27a36090e34101adf9a68feb8f19530463303e53fec46abe2092943b8",
"source_file_digests_unchanged": true,
"scanner_rule": "generic-api-key",
"affected_field": "paths[docs/assessments/cust81-private-reader/reuse-surface-runtime-externalsecret.r1.yaml]",
"scanner_version": "8.30.1",
"pinned_scanner_sha256": "88f91962aa2f93ac6ab281d553b9e125f5197bbbce38f9f2437f7299c32e5509",
"original_scanner_exit": 20,
"candidate_scanner_exit": 0,
"mechanism": "standard sha256-qualified metadata identifier; unchanged scanner/config, no ignore/exception/bypass",
"backup_ref": "custody/cust81-apps-unpublished-r2",
"original_commits": [
"4e8e0f5",
"6a8a54d"
],
"live_execution_approved": false
}

View file

@ -0,0 +1,10 @@
{
"agent": "delegated-railiance-apps-cust81-preparation",
"mode": "supervised",
"scope": "isolated local chart/config/ESO proposal preparation, local verification only",
"supervisor": "root custodian coordinating founder-authorized work",
"promotion_evidence": null,
"runtime_enforcement_verified": false,
"authority_granted": false,
"live_execution_authorized": false
}

View file

@ -0,0 +1,94 @@
"""Local chart and non-disclosure checks only; never contacts a cluster."""
import hashlib
import importlib.util
import json
from pathlib import Path
import subprocess
import yaml
ROOT = Path(__file__).resolve().parents[3]
ARTIFACT = Path(__file__).resolve().parent
calls = []
def render(extra, success=True):
command = ['helm', 'template', 'reuse', str(ROOT / 'charts/reuse-surface'), '-f', str(ROOT / 'helm/reuse-surface-values.yaml'), *extra]
proc = subprocess.run(command, capture_output=True, text=True)
calls.append({'command': command, 'exit_code': proc.returncode, 'expected_success': success})
assert (proc.returncode == 0) == success
return list(yaml.safe_load_all(proc.stdout)) if success else []
def api(docs):
return next(d for d in docs if d and d.get('kind') == 'Deployment' and d['metadata']['name'] == 'reuse-surface')
current = api(render([]))
assert current['spec']['template']['spec']['containers'][0]['image'].endswith(':main-6cbc862')
assert next(e['value'] for e in current['spec']['template']['spec']['containers'][0]['env'] if e['name']=='REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST') == '{}'
proposal = ['-f', str(ARTIFACT / 'private-reader-values.r1.yaml')]
render(proposal, False) # unknown real digest is a hard gate
# Synthetic digest is render-only, and expressly not promotion/build evidence.
render(proposal+['--set', 'image.digest=sha256:bad'], False)
render(proposal+['--set-json', 'authSourceAllowlist={"BAD":["http://attacker.invalid"]}', '--set', 'image.digest=sha256:'+'0'*64], False)
docs = render(proposal+['--set', 'image.digest=sha256:'+'0'*64])
deployment = api(docs)
container = deployment['spec']['template']['spec']['containers'][0]
assert container['image'].endswith('@sha256:'+'0'*64)
allowlist=json.loads(next(e['value'] for e in container['env'] if e['name']=='REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST'))
expected = json.loads((ARTIFACT/'runtime-auth-source-allowlist-r1.json').read_text())
assert allowlist==expected and len(next(iter(allowlist.values())))==14
assert deployment['spec']['strategy'] == current['spec']['strategy']
assert container['resources']==current['spec']['template']['spec']['containers'][0]['resources']
assert deployment['spec']['template']['spec']['volumes']==current['spec']['template']['spec']['volumes']
assert container['volumeMounts']==current['spec']['template']['spec']['containers'][0]['volumeMounts']
secret=yaml.safe_load((ARTIFACT/'reuse-surface-runtime-externalsecret.r1.yaml').read_text())
baseline=yaml.safe_load((ROOT/'manifests/reuse-surface-runtime-externalsecret.yaml').read_text())
assert secret['spec']['data'][:-1]==baseline['spec']['data']
assert secret['spec']['secretStoreRef']==baseline['spec']['secretStoreRef']
assert secret['spec']['data'][-1]=={'secretKey':'REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN','remoteRef':{'key':'workloads/reuse/reuse-surface/forgejo-source-read','property':'FORGEJO_SOURCE_TOKEN'}}
spec=importlib.util.spec_from_file_location('inventory',ROOT/'scripts/reuse_authenticated_source_inventory.py')
mod=importlib.util.module_from_spec(spec);spec.loader.exec_module(mod)
url=expected['REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN'][1]
assert len(mod.sanitize([['rapp-openbao','FIXTURE_READ_TOKEN',url]]))==1
legacy='https://forgejo.coulomb.social/coulomb/rapp-openbao/raw/branch/main/registry/indexes/capabilities.yaml'
assert len(mod.sanitize([['rapp-openbao','FIXTURE_READ_TOKEN',legacy]]))==1
# Exercise the EXACT pod code against actual native registration keys in SQLite.
# Replacing only the fixture DB path leaves the metadata SELECT/read-only mode intact.
import sqlite3,tempfile
with tempfile.TemporaryDirectory(prefix='apps-reader-fixture-') as temp:
fixture=Path(temp)/'reuse.db'
with sqlite3.connect(fixture) as db:
db.execute('CREATE TABLE registrations(repo TEXT PRIMARY KEY, payload TEXT NOT NULL, registered_at TEXT, updated_at TEXT)')
db.execute('INSERT INTO registrations VALUES(?,?,?,?)', ('rapp-openbao',json.dumps({'repo':'rapp-openbao','url':url,'auth_env':'FIXTURE_READ_TOKEN','description':'fixture-secret-never-emit'}),'fixture','fixture'))
db.execute('INSERT INTO registrations VALUES(?,?,?,?)', ('anonymous',json.dumps({'repo':'anonymous','url':legacy}),'fixture','fixture'))
pod_code=mod.POD_CODE.replace('/data/reuse.db',str(fixture))
proc=subprocess.run(['python3','-c',pod_code],capture_output=True,text=True,check=True)
assert json.loads(proc.stdout)==[['rapp-openbao','FIXTURE_READ_TOKEN',url]]
assert 'fixture-secret' not in proc.stdout
assert len(mod.sanitize(json.loads(proc.stdout)))==1
with sqlite3.connect(fixture) as db: assert db.execute('SELECT count(*) FROM registrations').fetchone()==(2,)
assert 'mode=ro' in pod_code and '$.index_url' not in pod_code
assert 'os.environ' not in pod_code and 'getenv' not in pod_code and 'kubectl' not in pod_code
for bad in ['https://token@forgejo.coulomb.social/x', url+'&token=fixture-secret','http://attacker.invalid',url+'#fixture-secret',legacy+'?token=fixture-secret',legacy+'/../credentials',legacy+'%3Ftoken=fixture-secret']:
try: mod.sanitize([['rapp-openbao','FIXTURE_READ_TOKEN',bad]])
except ValueError: pass
else: raise AssertionError('unsafe URL accepted')
# Errors from kubectl are discarded before output; no subprocess invoked in test.
original=mod.subprocess.run
class Failure:
returncode=1;stdout=b'fixture-secret';stderr=b'fixture-secret'
mod.subprocess.run=lambda *args,**kwargs: Failure()
import sys,io,contextlib
argv=sys.argv;sys.argv=['inventory','--kubeconfig','fixture-path']
buf=io.StringIO()
with contextlib.redirect_stdout(buf): assert mod.main()==1
assert 'fixture-secret' not in buf.getvalue()
class UnsafeSuccess:
returncode=0;stdout=json.dumps([['rapp-openbao','FIXTURE_READ_TOKEN',url+'&token=fixture-secret']]).encode();stderr=b''
mod.subprocess.run=lambda *args,**kwargs: UnsafeSuccess()
buf=io.StringIO()
with contextlib.redirect_stdout(buf): assert mod.main()==1
assert 'fixture-secret' not in buf.getvalue()
sys.argv=argv;mod.subprocess.run=original
result={'status':'passed','local_only':True,'real_image_digest':None,'synthetic_digest_used_only_to_check_rendering':True,'checks':['production rendering unchanged except empty fail-closed allowlist','unknown/malformed digest fails','HTTP allowlist fails schema','exact14 URLs roundtrip','strategy resources PVC retained','dedicated ESO mapping preserves current fields/store','metadata sanitizer excludes userinfo/query secrets/fragments','kubectl failure output discarded','exact pod SQL real payload URL keys readonly fixture and anonymous filtering','legacy Forgejo branch URL accepted without weakening credential query refusal'],'helm_calls':calls}
(ARTIFACT/'local-verification-r2.json').write_text(json.dumps(result,indent=2)+'\n')
print(json.dumps({'status':'passed','checks':len(result['checks'])}))

View file

@ -9,7 +9,7 @@
| Host | Kubeconfig (operator workstation) | API |
| --- | --- | --- |
| CoulombCore | `~/.kube/config` | tunnel `k3s-api-coulombcore` |
| railiance01 | `~/.kube/config-hosteurope` | local / hosteurope |
| railiance01 | `~/.kube/config-railiance01` | local / hosteurope |
## Production-of-record (POR) decisions

View file

@ -0,0 +1,39 @@
# Completed Coulomb identity/export image release
The exact supervised proposal is
`sha256:9f1daf6c5fb56b2b4fc9817f28f5ad090c884b0295f7d6f4469e42d48fa8d59f`.
Resolved execution decision: `75b94bb6-4709-495f-91fe-43e53e2867ed`.
The full founder packet is
[coulomb-social release review](/home/worsch/coulomb-social/docs/release/identity-export-release-review-2026-10-31.md).
`tools/coulomb-image-release.py` prepares the capsule by default. `--preflight`
checks the live target without capture or deployment. `--execute --expect-digest`
requires the founder's own interactive terminal and the exact typed proposal.
The node controller freezes verified source bytes into its private durable
journal before execution. An existing attempt refuses blind replay.
This distinct controller adapts the previously reviewed storage controller
from owner source 45d6178. It requires migration 0003 and the global constraint
already present, adds exact migration-history equality, verifies installed
helpers during preflight, verifies the desired complete spec through an image-only
server-dry-run patch and verifies rollback readiness/specification. Unknown
helper changes refuse overwrite. Historical storage commands/capsules remain
unchanged and must not be replayed.
Candidate values live in `release/identity-export-values.yaml`; the current
`helm/coulomb-social-values.yaml` now matches verified revision 19/image522bcd7f.
The installed snapshot helper matches the reviewed bytes (dcc8dd2a).
No unit/timer, sender, grant, apex or data-restore operation is included.
Validation: 19 release safety cases pass; Ruff, Python compile and Helm lint pass.
The sealed native preflight is retained in
`docs/evidence/2026-10-31-coulomb-image-release-preflight.json`.
Four hash-bound candidate evidence files travel inside the capsule. The product
review names source 441 tests, actual packaged 25 tests, all 80 packaged source matches
and the retained isolated recovery/export/view checks with their limits.
Founder execution and independent postflight are verified at revision19. The
product independently retrieved/restored the fresh point and verified packaged
client export on the corrected data. T05 is complete. Native receipt, independent
postflight and live packaged-source proof are under docs/evidence/2026-10-31-coulomb-image-release-*.json.
The historical launcher/capsule is retained for audit and must not be replayed.

View file

@ -9,7 +9,7 @@ platform tooling.
| Host | IP | Kubeconfig | Tunnel |
| --- | --- | --- | --- |
| CoulombCore (production apps) | `92.205.130.254` | `~/.kube/config` via `k3s-api-coulombcore` | `bridge up k3s-api-coulombcore` |
| Railiance01 (Forgejo S5) | `92.205.62.239` | `~/.kube/config-hosteurope` | `bridge up k3s-api-railiance01` if configured |
| Railiance01 (Forgejo S5) | `92.205.62.239` | `~/.kube/config-railiance01` | `bridge up k3s-api-railiance01` if configured |
Core Hub, `vergabe-teilnahme`, and `apps-pg` currently run on **CoulombCore**.
Railiance01 hosts Forgejo and a overlapping subset of platform databases.

View file

@ -0,0 +1,44 @@
{
"stable_proposal": "NK-FORGEJO-TYPE12-SOURCE-20261011-R1",
"memo_id": "infd-20261011-forgejo-type12-source-r1",
"memo_version": 1,
"review_id": "review-126c6c90-11ba-4609-b8d1-75e054c246cd",
"approval_id": "8e9332c8-754a-420c-b8b7-445cb4fe6765",
"disposition_id": "disp-315d54ad-20f1-40e6-862c-6c7d2e8f0ea8",
"presentation_id": "pres-5a52f932-8c65-46cc-8054-0264f47f934a",
"actual_human_actor": {
"kind": "person",
"sub": "uid=platform-root,ou=people,dc=netkingdom,dc=local"
},
"actual_disposition_at": "2026-10-11T06:47:39Z",
"confirmed_nonstale": true,
"exact_human_packet_manifest_sha256": "0b377d647ca748cd03c92f9792396412048055d4bde9f7bb06ff6d413f7acd68",
"submission_digest": "sha256:585cee731de30d2b37883a57abfd9eb32f023ee694e8d911989109d5caf80569",
"canonical_case12_source_sha256": "1c868c1da7d9077fae97bc6bc029706ec3dc418cfeaae30dc0f92b5b4ba6f2ab",
"full_render_digest": "sha256:c0afd5576cdae0cbc807eccb2b9fa87ac668f15e32ec7cf79d295bdb23c75b35",
"kind": "human-reference-migration-design-source-receipt",
"case": "forgejo-netkingdom-oidc",
"type": "oidc-browser-relying-party@1.2",
"declared_case_owner": {
"repo": "railiance-apps",
"person": "founder"
},
"actual_human_design_approval_roles_requested_in_exact_memo": [
"declared-consumer-owner",
"founder"
],
"scope": "Separate owner design receipt of exact human source approval; not an agent Apps-owner review and not canonical case acceptance. The requesting coordinator transcribes only the actual founder act matching exact memo terms.",
"canonical_case12_status": "proposed",
"canonical_case_acceptance_recorded": false,
"type_implemented": false,
"bound_claims_confirmed": false,
"execution_permitted": false,
"native_or_credential_or_session_authority": false,
"existing_driver": "ORGREF-WP-0002",
"consumer_task": "RAPPS-WP-0016-T02",
"type_source_receipt_repo": "net-kingdom",
"type_source_receipt_path": "security-case-type-revisions/1.2/oidc-browser-relying-party/decision/acknowledgment.json",
"recording_act": "Transcribe confirmed exact human reference-migration design approval only; requesting coordinator records actual fulfilled roles. No agent Apps-owner review.",
"recorded_status": "source-design-approved",
"type_source_receipt_sha256": "2dcc220febf125a17b3769f7014f18b63979d7121e0f7056a2b0d8b1ae6031b0"
}

View file

@ -0,0 +1,18 @@
{
"schema": "railiance-apps.forgejo-breakglass-signin.v1",
"task": "RAPPS-WP-0016-T01",
"forgejo": "https://forgejo.coulomb.social",
"account": "forgejo_admin",
"auth": "local password (API basic auth)",
"http_status": 200,
"login_matches": true,
"is_admin": true,
"custody": {
"openbao_path": "platform/workloads/forgejo/break-glass-admin",
"field": "PASSWORD",
"version": "1"
},
"verified_at": "2026-10-17T09:41:18Z",
"result": "pass",
"values_emitted": false
}

View file

@ -0,0 +1,83 @@
{
"proposal_id": "POLICY-PUB-20261019-BUILD-MODE-GUARDRAIL-R1",
"approved_revision": 1,
"approved_packet_hashlist_sha256": "309ef01650b65bc6b28e86daad9b2835a242631cf60e0896e849461bb917504f",
"approval_ref": "conversation:call_5157a09329d34746a3fdf857d5a47b8d:item0",
"result": "verified",
"helm_revision": 10,
"rollback_required": false,
"rollback_revision": 9,
"candidate_source_revision": "9fabcce1cb4c83165bcaba8be58d76788b4d87a1",
"candidate_origin": "local owner-format build; native CI run89 retained failed, no new CI success claimed",
"change_gate": {
"decision": "the-custodian/docs/kubernetes-change-gate-decision.md",
"target_tier": "production",
"readiness_state": "production-approved",
"path": "direct-administer-transition",
"auth_mode": "ADMINISTER @ realm:kubernetes/railiance01",
"activation": "APPROVED",
"evidence_boundary": "target-audited",
"transition_ends": "2026-12-21"
},
"native": {
"capture_utc": "2026-10-19T13:35:06.098696+00:00",
"cluster_mutated": false,
"expected_node_ip": "92.205.62.239",
"expected_node_ip_present": true,
"helm_revision": 10,
"helm_status": "deployed",
"image_matches_candidate": true,
"machine_id_sha256": "fe7367a60951922122af7ba18ed2cde65c7e0a232bbedfb045cb575de2094c2f",
"ready_replicas": 1,
"release_pin_matches": {
"publication-manifest-digest": true,
"source-inventory-digest": true,
"source-set-digest": true
},
"replicas": 1,
"secret_values_observed": false,
"ssh_alias": "railiance01"
},
"public": {
"verified": true,
"capture_utc": "2026-10-19T13:36:20.944233+00:00",
"hashes": {
"publication_manifest_digest": "8c74b4e414f7760429600b7ba0b3627fcfe438417bfe6695f55acfa6905d352e",
"source_inventory_digest": "e84453b6b411a7d66d2b5efbf083cd0033441d6b12983326442933da87bf61fd",
"source_lock_digest": "c150c6c0745441b4b69884ecc6cf55e97db26080fcf4c2224775b1f755580529"
},
"source_set_digest": "b00535063c56ede08f0a292b301a47add18bf70f81904bf3f434c8faa5ebe682",
"document_count": 74,
"previous73_source_digests_unchanged": true,
"added_document": "canon-build-mode-change-authorization",
"all_canonical_and_revision_pages_byte_equal_candidate": true,
"page_and_artifact_count": 150,
"guardrail_expiry_banner": "Expires on: production-close-down",
"guardrail_json_byte_equal_verified_candidate": true,
"guardrail_json_sha256": "a86462a57bb1df5804d3088881af22569082d43ff68bcab2b67cbe56b6fc46cd",
"tls_verified": true,
"refinements": [
"Initial verifier incorrectly expected hosting transition date2026-12-21 in canon expiry.",
"Second verifier expected source expires_on label rather than rendered Expires on.",
"Correct final assertion matches approved rendered banner; both failed checks retained, artifact and deployment unchanged."
]
},
"deployment": {
"started_utc": "2026-10-19T13:34:20.566225+00:00",
"command": [
"make",
"policy-nexus-deploy",
"POLICY_NEXUS_TARGET=railiance01"
],
"atomic": true,
"attempt": 1,
"approval_ref": "conversation:call_5157a09329d34746a3fdf857d5a47b8d:item0",
"completed_utc": "2026-10-19T13:34:26.648713+00:00",
"returncode": 0,
"raw_output_suppressed": true
},
"owner_source_commits": {
"rapp-policy-nexus": "6ead90ffa4ae5742c37c292485762e83113382c3",
"railiance-apps": "3be92daf74a7572319bfe94dd882a70430528343"
}
}

View file

@ -0,0 +1,155 @@
# Vergabe target-hardware synthetic replay — 2026-10-21
Task: `RAPPS-WP-0014-T04` (feeds `CUST-WP-0071-T02`, `RESOURCE-WP-0008-T02`).
Authority: founder decision of 2026-10-21
(`the-custodian/docs/decisions/2026-10-21-cust-wp-0071-delivery-interview.md`):
isolated replay in a throwaway namespace on railiance01, reads elsewhere only.
No customer data was read or written; the live `vergabe-demo-company` deployment,
its database (`apps-pg`), quotas, policies and nodes were not modified (live
Deployment generation 4, same digest, Ready 1/1 before and after). No Secret was
read. This is synthetic sizing evidence, not human/recovery acceptance (T03).
## Deployed release (read-only capture, 2026-10-21)
| Item | Value |
| --- | --- |
| Cluster / node | context `default` → 92.205.62.239, single k3s v1.35.1 control-plane node |
| Node CPU / memory | 4 vCPU `AMD EPYC Processor (with IBPB)`, 16 GiB; requests 3605m (90%) / limits 37500m; reservation residual 395m |
| Image | `forgejo.coulomb.social/coulomb/vergabe-teilnahme@sha256:a26444f59c259698159c69ccb96f73dc648a261ece4c86bb2037a9d977870d91` (source `c067993`, per `docs/vergabe-demo-company-binding.md`) |
| Chart / rollout | `vergabe-teilnahme-0.2.1`, one replica, Recreate, init `migrate`; Deployment revision 4 (rollout restart 2026-09-25). Helm release revision not re-read (release storage is a Secret) |
| Gunicorn | no command override → image CMD `--workers 3` (sync), default 30s timeout |
| Resources (app and migrate) | requests 60m CPU / 256Mi, limits 1000m CPU / 1Gi |
| Settings | `vergabe_teilnahme.settings.prod`, SSO via NetKingdom, `envFrom` Secret reference `vergabe-demo-env` (not read) |
| Database binding | namespace label `railiance.io/postgres-client=apps-pg`: shared CNPG `databases/apps-pg`, PostgreSQL 16, 100m/256Mi request, 1000m/1Gi limit, WAL archiving + `wal_level=logical` |
| Storage | media 5Gi and app-state 1Gi PVCs, `local-path` |
Drift vs. the 2026-10-19 local test image (`vergabe-synthetic:local-11f7c8a`,
`sha256:57da46…`): source `11f7c8a` is 16 commits ahead of deployed `c067993`;
9 application files differ (+181/−10: ausschreibungen forms/views/templates,
core views, search partial); Dockerfile, `uv.lock`, `pyproject.toml` identical.
The local runs used 2 workers; the deployed image runs 3. This replay used the
deployed digest itself, so the drift does not affect these results.
## Replay configuration (pinned)
Namespace `vergabe-sizing-20261021` (manifests in `harness/bench.yaml`,
`harness/driver.yaml`), all pods on the same node:
- **app**: deployed digest, image CMD (3 workers), init `migrate`, 60m/256Mi request,
1000m/1Gi limit, live securityContext/probes, Recreate; media on a fresh
`local-path` PVC, issue state emptyDir. Settings = harness synthetic settings
(`harness/synthetic_settings.py`: base settings, SSO off, password login).
- **db**: fresh `docker.io/library/postgres@sha256:4b7183ac…` (PostgreSQL 16),
emptyDir, 100m/256Mi request, 1000m/1Gi limit (matching apps-pg resources).
- **driver**: deployed digest running `vergabe-teilnahme/scripts/synthetic_pilot.py`
`runtime: remote` (commit `dca6683`; executed copy and config retained, digests
`c8da867f…` / `9045f2c7…` match `raw/result.json.gz`), 100m request/1000m limit.
In-cluster HTTP via ClusterIP Service — chosen over port-forward so latency
excludes the WSL/SSH tunnel. Restart deletes the app pod through a namespaced
Role (pods get/list/delete) and waits for a different ready pod.
- NetworkPolicy: ingress/egress within the namespace, DNS, API server only;
preflight confirmed the live Service was unreachable.
- Profile `config/synthetic-pilot-phases.json` unchanged except runtime fields
(`synthetic-pilot-target-remote.json`): 2 users, 100 seed tenders, 1 MiB
documents, 30s idle, 90s ordinary (0.2s think), 30s burst (0 think), restart,
60s recovery; 11 named operations including edits.
- Sampling: 1s cgroup v2 `cpu.stat`/`memory.current`/`cpu.pressure` inside app,
db and driver containers plus node-wide `/proc/stat` (`harness/sampler.sh`);
`kubectl top` every 15s; Prometheus (cadvisor) per-namespace CPU/throttling.
The sampler shell itself costs ~11–12m per container and is included.
Timeline (UTC): namespace created 06:26; pre-run idle baseline 06:27:14–06:29:26;
harness idle 06:29:31–06:30:01; ordinary 06:30:01–06:31:32; burst –06:32:03;
restart –06:32:22 (18.98s to ready incl. migrate); recovery –06:33:23; post
idle –06:34:28; namespace deletion requested 06:35:25, completed 06:36:18; namespace and its PV verified absent (`raw/teardown.txt`).
## Results
Driver: complete, `provisional_pass: true`, `measurement_complete: true`, no gaps.
| Phase | Ordinary req / err / p95 | Transfer req / err / p95 |
| --- | --- | --- |
| ordinary | 1903 / 0 / 0.142s | 422 / 0 / 0.085s |
| burst | 697 / 0 / 0.192s | 154 / 0 / 0.069s |
| recovery | 1075 / 0 / 0.214s | 238 / 0 / 0.073s |
| **all** | **3675 / 0 / 0.192s** | **814 / 0 / 0.076s** |
Per operation p95 (all phases): list 0.262s, search 0.255s, detail 0.146s,
creates/edits 0.059–0.066s, upload 0.088s, download 0.050s; login 1.27s and
login form 0.62s (n=6, first requests after idle). ~24.7 requests/s closed loop —
far above two human users' pace.
| Window | App CPU mean / p95 / peak | Throttled periods | App memory peak | DB CPU mean / p95 | Node busy mean (free mean / free p5) |
| --- | --- | --- | --- | --- | --- |
| pre-run baseline | 12m / 15m / 16m | 0% | 166 MiB | 22m / 66m | 1348m (2652m / 1499m) |
| ordinary | 711m / 845m / 1003m | 20.4% | 403 MiB | 388m / 501m | 2979m (1021m / 78m) |
| burst | 953m / 1004m / 1011m | 65.8% | 482 MiB | 430m / 539m | 2806m (1194m / 278m) |
| recovery | 861m / 957m / 982m | 41.5% | 322 MiB | 335m / 399m | 2624m (1376m / 392m) |
| all load phases | 775m / 998m / 1020m | 35.7% (22.8s throttled) | 483 MiB | 341m / 500m | 2790m (1210m / 147m) |
| post | 12m | 0% | 323 MiB | 22m | 1413m (2587m) |
App memory is cgroup `memory.current` (includes page cache); no OOM, no restarts.
CPU pressure (PSI some) on the app was 15–21% of wall time under load.
Incremental PostgreSQL CPU ≈ 320m mean / ~480m p95 over its idle baseline, on
the isolated DB only (live `apps-pg` stayed at ~8m during the window). Driver
added ~84m mean.
Competing workloads (Prometheus, workload window): all containers 1622m mean, of
which the replay namespace 915m; others led by databases 121m, state-hub 120m,
telemetry 102m, forgejo 78m (max 385m), activity-core 59m, core-hub 43m; live
vergabe ~0m. Node-wide busy includes ~0.7 core of k3s/system outside containers.
Pre-run competing load left ~2.65 cores free on average; during the replay the
node reached near-saturation at peaks (free p5 ~150m) largely through the replay
itself (app + db + driver ≈ 1.2 cores). Requests during the run were 3865m of
4000m (replay 260m of the 395m residual, released at teardown).
## Coverage and gaps
- Representative: exact deployed image/digest, worker count, CPU/memory
requests and limits (CFS quota 100000/100000 verified), Recreate + init migrate
restart path, target hardware and real shared node contention.
- Not representative: synthetic settings instead of prod (no SSO/OIDC, insecure
cookies, no whitenoise manifest storage); no Traefik ingress/TLS/browser; DB is a
standalone PostgreSQL 16 pod, not shared CNPG `apps-pg` (WAL archiving, logical
WAL, other tenants) — the ~320m incremental DB demand would land on apps-pg's
1000m limit/100m request in production and is not proven there.
- Load is a synthetic closed loop (~25 req/s), not observed human demand; one run
with ~3.5 min of load (pinned profile), ~9 min namespace lifetime.
- Node contention was partly self-induced; contention from other tenants was
light at this hour. A busier moment (forgejo CI, backups) is not covered.
- The 60m request is a scheduling weight/reservation, not a quota; the 1000m
limit is the quota that throttled. Sampler exec overhead is in the app cgroup.
## Bounded recommendation (for RESOURCE-WP-0008-T02 / CUST-WP-0071-T02)
- **CPU limit 1000m: keep.** Under an aggressive synthetic mix it throttles
(36% of periods, 66% in burst) yet ordinary p95 stays ≤0.22s per phase — ~9×
margin to the 2s target, zero failed operations, including restart recovery.
- **CPU request 60m: keep for the two-user invited pilot**, provisionally. Even
with the node near saturation and only a 60m weight, latency stayed far inside
target; two humans generate a small fraction of this load. Raising it (e.g.
250m) is not required by these results and would consume most of the 395m
residual — an allocation-owner decision, not made here.
- **Memory limit 1Gi: keep** (peak 483 MiB incl. page cache, ~2× margin).
**Memory request 256Mi: keep, consider 512Mi** at the next allocation review,
since loaded working set (320–480 MiB) exceeds the request; node memory requests
are 61%, so this is low-risk but not urgent.
- **Database**: budget ~0.3–0.5 core incremental on apps-pg at this synthetic
rate; acceptable against its 1000m limit for a two-user pilot, unverified on the
shared cluster.
- **Revisit when**: user count/companies or corpus grow, image or worker count
changes, live p95 >1s or any 5xx, node requests >95% or sustained node free CPU
<500m outside benchmarks, apps-pg CPU >600m sustained, or the pilot moves to
railiance02/other hardware.
## Files
- `analysis.json` — per-window cgroup statistics, per-operation p95, phase results
(`harness/analyze.py raw/ analysis.json` after gunzip)
- `raw/result.json.gz` — driver output (all operations, phase UTC windows, host facts)
- `raw/{app,db,driver}-cgroup.txt.gz` — 1s cgroup/node samples (`POD` lines mark
the restart re-attach; the `exit code 137` exec termination is that restart)
- `raw/kubectl-top.txt.gz`, `raw/prometheus-{baseline,workload}.json.gz`
- `raw/windows.txt`, `raw/pod-provenance.txt`, `raw/namespace-events.txt`,
`raw/teardown.txt`, `raw/driver-stdout.txt`
- `harness/` — manifests, settings, samplers, orchestrator, executed driver/config

View file

@ -0,0 +1,923 @@
{
"windows_utc": {
"baseline_pre": [
"2026-10-21T06:27:14.447540Z",
"2026-10-21T06:29:26.175778Z"
],
"harness_idle": [
"2026-10-21T06:29:31.522662Z",
"2026-10-21T06:30:01.522800Z"
],
"ordinary": [
"2026-10-21T06:30:01.523056Z",
"2026-10-21T06:31:32.589152Z"
],
"burst": [
"2026-10-21T06:31:32.589194Z",
"2026-10-21T06:32:03.275085Z"
],
"restart": [
"2026-10-21T06:32:03.275117Z",
"2026-10-21T06:32:22.256585Z"
],
"recovery": [
"2026-10-21T06:32:22.256609Z",
"2026-10-21T06:33:23.057045Z"
],
"workload_all_load_phases": [
"2026-10-21T06:30:01.523056Z",
"2026-10-21T06:33:23.057045Z"
],
"post": [
"2026-10-21T06:33:22.885983Z",
"2026-10-21T06:34:28.753426Z"
]
},
"cgroup": {
"app": {
"pods": [
"vergabe-app-579448bf49-2fsn2",
"vergabe-app-579448bf49-plp4j"
],
"baseline_pre": {
"samples": 126,
"seconds": 128.0,
"cpu_mean_m": 12.2,
"cpu_p95_m": 14.7,
"cpu_peak_m": 16.2,
"cfs_periods": 317,
"cfs_throttled_periods": 0,
"throttle_ratio": 0.0,
"throttled_seconds": 0.0,
"psi_cpu_some_share": 0.002,
"mem_peak_mib": 165.6,
"mem_mean_mib": 165.2,
"node_busy_mean_m": 1348,
"node_busy_p95_m": 2501,
"node_free_mean_m": 2652,
"node_free_p5_m": 1499,
"load1_max": 4.91
},
"harness_idle": {
"samples": 28,
"seconds": 28.4,
"cpu_mean_m": 11.5,
"cpu_p95_m": 13.4,
"cpu_peak_m": 13.6,
"cfs_periods": 76,
"cfs_throttled_periods": 0,
"throttle_ratio": 0.0,
"throttled_seconds": 0.0,
"psi_cpu_some_share": 0.002,
"mem_peak_mib": 165.3,
"mem_mean_mib": 165.1,
"node_busy_mean_m": 1538,
"node_busy_p95_m": 2800,
"node_free_mean_m": 2462,
"node_free_p5_m": 1200,
"load1_max": 2.91
},
"ordinary": {
"samples": 88,
"seconds": 90.2,
"cpu_mean_m": 711.4,
"cpu_p95_m": 845.4,
"cpu_peak_m": 1003.2,
"cfs_periods": 902,
"cfs_throttled_periods": 184,
"throttle_ratio": 0.204,
"throttled_seconds": 4.85,
"psi_cpu_some_share": 0.15,
"mem_peak_mib": 402.5,
"mem_mean_mib": 295.2,
"node_busy_mean_m": 2979,
"node_busy_p95_m": 3922,
"node_free_mean_m": 1021,
"node_free_p5_m": 78,
"load1_max": 7.83
},
"burst": {
"samples": 29,
"seconds": 29.9,
"cpu_mean_m": 952.9,
"cpu_p95_m": 1003.7,
"cpu_peak_m": 1011.1,
"cfs_periods": 298,
"cfs_throttled_periods": 196,
"throttle_ratio": 0.658,
"throttled_seconds": 8.94,
"psi_cpu_some_share": 0.211,
"mem_peak_mib": 481.8,
"mem_mean_mib": 443.4,
"node_busy_mean_m": 2806,
"node_busy_p95_m": 3722,
"node_free_mean_m": 1194,
"node_free_p5_m": 278,
"load1_max": 8.19
},
"restart": {
"samples": 10,
"seconds": 10.3,
"cpu_mean_m": 403.3,
"cpu_p95_m": 1019.7,
"cpu_peak_m": 1019.7,
"cfs_periods": 54,
"cfs_throttled_periods": 39,
"throttle_ratio": 0.722,
"throttled_seconds": 2.37,
"psi_cpu_some_share": 0.235,
"mem_peak_mib": 483.3,
"mem_mean_mib": 154.0,
"node_busy_mean_m": 1908,
"node_busy_p95_m": 3359,
"node_free_mean_m": 2092,
"node_free_p5_m": 641,
"load1_max": 7.84
},
"recovery": {
"samples": 59,
"seconds": 60.6,
"cpu_mean_m": 860.6,
"cpu_p95_m": 956.8,
"cpu_peak_m": 982.3,
"cfs_periods": 605,
"cfs_throttled_periods": 251,
"throttle_ratio": 0.415,
"throttled_seconds": 6.17,
"psi_cpu_some_share": 0.152,
"mem_peak_mib": 322.2,
"mem_mean_mib": 251.0,
"node_busy_mean_m": 2624,
"node_busy_p95_m": 3608,
"node_free_mean_m": 1376,
"node_free_p5_m": 392,
"load1_max": 6.66
},
"workload_all_load_phases": {
"samples": 189,
"seconds": 194.0,
"cpu_mean_m": 775.3,
"cpu_p95_m": 998.3,
"cpu_peak_m": 1019.7,
"cfs_periods": 1889,
"cfs_throttled_periods": 675,
"throttle_ratio": 0.357,
"throttled_seconds": 22.77,
"psi_cpu_some_share": 0.166,
"mem_peak_mib": 483.3,
"mem_mean_mib": 295.7,
"node_busy_mean_m": 2790,
"node_busy_p95_m": 3853,
"node_free_mean_m": 1210,
"node_free_p5_m": 147,
"load1_max": 8.19
},
"post": {
"samples": 63,
"seconds": 63.9,
"cpu_mean_m": 12.3,
"cpu_p95_m": 14.3,
"cpu_peak_m": 15.1,
"cfs_periods": 161,
"cfs_throttled_periods": 0,
"throttle_ratio": 0.0,
"throttled_seconds": 0.0,
"psi_cpu_some_share": 0.001,
"mem_peak_mib": 322.5,
"mem_mean_mib": 322.2,
"node_busy_mean_m": 1413,
"node_busy_p95_m": 2414,
"node_free_mean_m": 2587,
"node_free_p5_m": 1586,
"load1_max": 5.75
}
},
"db": {
"pods": [
"vergabe-db-7c4bffdd48-w4jz4"
],
"baseline_pre": {
"samples": 127,
"seconds": 128.5,
"cpu_mean_m": 21.5,
"cpu_p95_m": 65.7,
"cpu_peak_m": 71.7,
"cfs_periods": 333,
"cfs_throttled_periods": 0,
"throttle_ratio": 0.0,
"throttled_seconds": 0.0,
"psi_cpu_some_share": 0.002,
"mem_peak_mib": 91.3,
"mem_mean_mib": 90.4,
"node_busy_mean_m": 1346,
"node_busy_p95_m": 2448,
"node_free_mean_m": 2654,
"node_free_p5_m": 1552,
"load1_max": 4.91
},
"harness_idle": {
"samples": 29,
"seconds": 29.3,
"cpu_mean_m": 20.9,
"cpu_p95_m": 64.5,
"cpu_peak_m": 64.9,
"cfs_periods": 75,
"cfs_throttled_periods": 0,
"throttle_ratio": 0.0,
"throttled_seconds": 0.0,
"psi_cpu_some_share": 0.003,
"mem_peak_mib": 93.0,
"mem_mean_mib": 92.7,
"node_busy_mean_m": 1522,
"node_busy_p95_m": 2680,
"node_free_mean_m": 2478,
"node_free_p5_m": 1320,
"load1_max": 3.15
},
"ordinary": {
"samples": 88,
"seconds": 89.9,
"cpu_mean_m": 387.8,
"cpu_p95_m": 501.1,
"cpu_peak_m": 532.2,
"cfs_periods": 892,
"cfs_throttled_periods": 2,
"throttle_ratio": 0.002,
"throttled_seconds": 0.02,
"psi_cpu_some_share": 0.067,
"mem_peak_mib": 101.4,
"mem_mean_mib": 95.7,
"node_busy_mean_m": 2974,
"node_busy_p95_m": 3895,
"node_free_mean_m": 1026,
"node_free_p5_m": 105,
"load1_max": 8.19
},
"burst": {
"samples": 29,
"seconds": 29.6,
"cpu_mean_m": 430.1,
"cpu_p95_m": 539.1,
"cpu_peak_m": 544.4,
"cfs_periods": 293,
"cfs_throttled_periods": 2,
"throttle_ratio": 0.007,
"throttled_seconds": 0.06,
"psi_cpu_some_share": 0.062,
"mem_peak_mib": 113.1,
"mem_mean_mib": 97.1,
"node_busy_mean_m": 2798,
"node_busy_p95_m": 3664,
"node_free_mean_m": 1202,
"node_free_p5_m": 336,
"load1_max": 8.19
},
"restart": {
"samples": 18,
"seconds": 18.3,
"cpu_mean_m": 24.3,
"cpu_p95_m": 67.7,
"cpu_peak_m": 67.7,
"cfs_periods": 55,
"cfs_throttled_periods": 0,
"throttle_ratio": 0.0,
"throttled_seconds": 0.0,
"psi_cpu_some_share": 0.009,
"mem_peak_mib": 103.3,
"mem_mean_mib": 101.4,
"node_busy_mean_m": 2650,
"node_busy_p95_m": 3961,
"node_free_mean_m": 1350,
"node_free_p5_m": 39,
"load1_max": 7.84
},
"recovery": {
"samples": 58,
"seconds": 58.9,
"cpu_mean_m": 335.0,
"cpu_p95_m": 398.7,
"cpu_peak_m": 408.1,
"cfs_periods": 581,
"cfs_throttled_periods": 1,
"throttle_ratio": 0.002,
"throttled_seconds": 0.0,
"psi_cpu_some_share": 0.041,
"mem_peak_mib": 111.3,
"mem_mean_mib": 106.9,
"node_busy_mean_m": 2621,
"node_busy_p95_m": 3524,
"node_free_mean_m": 1379,
"node_free_p5_m": 476,
"load1_max": 6.66
},
"workload_all_load_phases": {
"samples": 196,
"seconds": 199.8,
"cpu_mean_m": 340.8,
"cpu_p95_m": 500.0,
"cpu_peak_m": 544.4,
"cfs_periods": 1837,
"cfs_throttled_periods": 5,
"throttle_ratio": 0.003,
"throttled_seconds": 0.08,
"psi_cpu_some_share": 0.052,
"mem_peak_mib": 113.1,
"mem_mean_mib": 99.8,
"node_busy_mean_m": 2813,
"node_busy_p95_m": 3894,
"node_free_mean_m": 1187,
"node_free_p5_m": 106,
"load1_max": 8.19
},
"post": {
"samples": 64,
"seconds": 64.8,
"cpu_mean_m": 21.7,
"cpu_p95_m": 65.1,
"cpu_peak_m": 79.6,
"cfs_periods": 161,
"cfs_throttled_periods": 0,
"throttle_ratio": 0.0,
"throttled_seconds": 0.0,
"psi_cpu_some_share": 0.002,
"mem_peak_mib": 101.3,
"mem_mean_mib": 98.1,
"node_busy_mean_m": 1420,
"node_busy_p95_m": 2509,
"node_free_mean_m": 2580,
"node_free_p5_m": 1491,
"load1_max": 5.75
}
},
"driver": {
"pods": [
"synthetic-driver"
],
"baseline_pre": {
"samples": 126,
"seconds": 127.9,
"cpu_mean_m": 11.6,
"cpu_p95_m": 13.2,
"cpu_peak_m": 13.7,
"cfs_periods": 267,
"cfs_throttled_periods": 0,
"throttle_ratio": 0.0,
"throttled_seconds": 0.0,
"psi_cpu_some_share": 0.002,
"mem_peak_mib": 6.6,
"mem_mean_mib": 6.3,
"node_busy_mean_m": 1348,
"node_busy_p95_m": 2510,
"node_free_mean_m": 2652,
"node_free_p5_m": 1490,
"load1_max": 4.91
},
"harness_idle": {
"samples": 28,
"seconds": 28.4,
"cpu_mean_m": 11.6,
"cpu_p95_m": 13.1,
"cpu_peak_m": 13.1,
"cfs_periods": 59,
"cfs_throttled_periods": 0,
"throttle_ratio": 0.0,
"throttled_seconds": 0.0,
"psi_cpu_some_share": 0.002,
"mem_peak_mib": 63.3,
"mem_mean_mib": 63.0,
"node_busy_mean_m": 1537,
"node_busy_p95_m": 2829,
"node_free_mean_m": 2463,
"node_free_p5_m": 1171,
"load1_max": 2.91
},
"ordinary": {
"samples": 88,
"seconds": 90.0,
"cpu_mean_m": 91.7,
"cpu_p95_m": 117.2,
"cpu_peak_m": 124.8,
"cfs_periods": 891,
"cfs_throttled_periods": 0,
"throttle_ratio": 0.0,
"throttled_seconds": 0.0,
"psi_cpu_some_share": 0.029,
"mem_peak_mib": 75.9,
"mem_mean_mib": 72.4,
"node_busy_mean_m": 2977,
"node_busy_p95_m": 3912,
"node_free_mean_m": 1023,
"node_free_p5_m": 88,
"load1_max": 7.83
},
"burst": {
"samples": 29,
"seconds": 29.6,
"cpu_mean_m": 98.5,
"cpu_p95_m": 124.8,
"cpu_peak_m": 129.3,
"cfs_periods": 289,
"cfs_throttled_periods": 0,
"throttle_ratio": 0.0,
"throttled_seconds": 0.0,
"psi_cpu_some_share": 0.026,
"mem_peak_mib": 78.4,
"mem_mean_mib": 76.4,
"node_busy_mean_m": 2790,
"node_busy_p95_m": 3387,
"node_free_mean_m": 1210,
"node_free_p5_m": 613,
"load1_max": 8.19
},
"restart": {
"samples": 18,
"seconds": 18.3,
"cpu_mean_m": 22.4,
"cpu_p95_m": 25.5,
"cpu_peak_m": 25.5,
"cfs_periods": 112,
"cfs_throttled_periods": 0,
"throttle_ratio": 0.0,
"throttled_seconds": 0.0,
"psi_cpu_some_share": 0.009,
"mem_peak_mib": 73.7,
"mem_mean_mib": 71.3,
"node_busy_mean_m": 2650,
"node_busy_p95_m": 3990,
"node_free_mean_m": 1350,
"node_free_p5_m": 10,
"load1_max": 7.84
},
"recovery": {
"samples": 59,
"seconds": 60.1,
"cpu_mean_m": 82.2,
"cpu_p95_m": 95.4,
"cpu_peak_m": 139.7,
"cfs_periods": 594,
"cfs_throttled_periods": 0,
"throttle_ratio": 0.0,
"throttled_seconds": 0.0,
"psi_cpu_some_share": 0.02,
"mem_peak_mib": 80.9,
"mem_mean_mib": 77.2,
"node_busy_mean_m": 2635,
"node_busy_p95_m": 3540,
"node_free_mean_m": 1365,
"node_free_p5_m": 460,
"load1_max": 6.66
},
"workload_all_load_phases": {
"samples": 197,
"seconds": 201.1,
"cpu_mean_m": 84.4,
"cpu_p95_m": 117.7,
"cpu_peak_m": 200.8,
"cfs_periods": 1911,
"cfs_throttled_periods": 0,
"throttle_ratio": 0.0,
"throttled_seconds": 0.0,
"psi_cpu_some_share": 0.024,
"mem_peak_mib": 80.9,
"mem_mean_mib": 74.4,
"node_busy_mean_m": 2820,
"node_busy_p95_m": 3912,
"node_free_mean_m": 1180,
"node_free_p5_m": 88,
"load1_max": 8.19
},
"post": {
"samples": 64,
"seconds": 64.9,
"cpu_mean_m": 16.3,
"cpu_p95_m": 13.2,
"cpu_peak_m": 317.3,
"cfs_periods": 137,
"cfs_throttled_periods": 0,
"throttle_ratio": 0.0,
"throttled_seconds": 0.0,
"psi_cpu_some_share": 0.002,
"mem_peak_mib": 79.1,
"mem_mean_mib": 19.2,
"node_busy_mean_m": 1423,
"node_busy_p95_m": 2535,
"node_free_mean_m": 2577,
"node_free_p5_m": 1465,
"load1_max": 5.75
}
}
},
"per_operation": {
"burst/detail": {
"n": 77,
"errors": 0,
"p95_s": 0.1132,
"max_s": 0.1706
},
"burst/document-download": {
"n": 77,
"errors": 0,
"p95_s": 0.0447,
"max_s": 0.0592
},
"burst/document-upload": {
"n": 77,
"errors": 0,
"p95_s": 0.075,
"max_s": 0.1055
},
"burst/list": {
"n": 77,
"errors": 0,
"p95_s": 0.2974,
"max_s": 0.3233
},
"burst/login": {
"n": 2,
"errors": 0,
"p95_s": 1.1775,
"max_s": 1.1775
},
"burst/login-form": {
"n": 2,
"errors": 0,
"p95_s": 0.0281,
"max_s": 0.0281
},
"burst/lot-create": {
"n": 77,
"errors": 0,
"p95_s": 0.0581,
"max_s": 0.0971
},
"burst/lot-edit": {
"n": 77,
"errors": 0,
"p95_s": 0.0536,
"max_s": 0.0743
},
"burst/search": {
"n": 77,
"errors": 0,
"p95_s": 0.2652,
"max_s": 0.3359
},
"burst/task-create": {
"n": 77,
"errors": 0,
"p95_s": 0.0599,
"max_s": 0.1778
},
"burst/task-edit": {
"n": 77,
"errors": 0,
"p95_s": 0.0597,
"max_s": 0.0759
},
"burst/tender-create": {
"n": 77,
"errors": 0,
"p95_s": 0.0772,
"max_s": 0.0845
},
"burst/tender-edit": {
"n": 77,
"errors": 0,
"p95_s": 0.0623,
"max_s": 0.0732
},
"ordinary/detail": {
"n": 211,
"errors": 0,
"p95_s": 0.1758,
"max_s": 0.3877
},
"ordinary/document-download": {
"n": 211,
"errors": 0,
"p95_s": 0.0537,
"max_s": 0.11
},
"ordinary/document-upload": {
"n": 211,
"errors": 0,
"p95_s": 0.1051,
"max_s": 0.4418
},
"ordinary/list": {
"n": 211,
"errors": 0,
"p95_s": 0.194,
"max_s": 0.3779
},
"ordinary/login": {
"n": 2,
"errors": 0,
"p95_s": 1.2574,
"max_s": 1.2574
},
"ordinary/login-form": {
"n": 2,
"errors": 0,
"p95_s": 0.3288,
"max_s": 0.3288
},
"ordinary/lot-create": {
"n": 211,
"errors": 0,
"p95_s": 0.0696,
"max_s": 0.1603
},
"ordinary/lot-edit": {
"n": 211,
"errors": 0,
"p95_s": 0.0763,
"max_s": 0.1017
},
"ordinary/search": {
"n": 211,
"errors": 0,
"p95_s": 0.2294,
"max_s": 0.6217
},
"ordinary/task-create": {
"n": 211,
"errors": 0,
"p95_s": 0.0697,
"max_s": 0.1338
},
"ordinary/task-edit": {
"n": 211,
"errors": 0,
"p95_s": 0.0686,
"max_s": 0.1146
},
"ordinary/tender-create": {
"n": 211,
"errors": 0,
"p95_s": 0.0626,
"max_s": 0.1187
},
"ordinary/tender-edit": {
"n": 211,
"errors": 0,
"p95_s": 0.0633,
"max_s": 0.1171
},
"recovery/detail": {
"n": 119,
"errors": 0,
"p95_s": 0.131,
"max_s": 0.1379
},
"recovery/document-download": {
"n": 119,
"errors": 0,
"p95_s": 0.0475,
"max_s": 0.0818
},
"recovery/document-upload": {
"n": 119,
"errors": 0,
"p95_s": 0.08,
"max_s": 0.1587
},
"recovery/list": {
"n": 119,
"errors": 0,
"p95_s": 0.3302,
"max_s": 0.3757
},
"recovery/login": {
"n": 2,
"errors": 0,
"p95_s": 1.2653,
"max_s": 1.2653
},
"recovery/login-form": {
"n": 2,
"errors": 0,
"p95_s": 0.6157,
"max_s": 0.6157
},
"recovery/lot-create": {
"n": 119,
"errors": 0,
"p95_s": 0.0526,
"max_s": 0.0945
},
"recovery/lot-edit": {
"n": 119,
"errors": 0,
"p95_s": 0.0602,
"max_s": 0.1186
},
"recovery/search": {
"n": 119,
"errors": 0,
"p95_s": 0.3193,
"max_s": 0.4237
},
"recovery/task-create": {
"n": 119,
"errors": 0,
"p95_s": 0.0558,
"max_s": 0.0888
},
"recovery/task-edit": {
"n": 119,
"errors": 0,
"p95_s": 0.0577,
"max_s": 0.0959
},
"recovery/tender-create": {
"n": 119,
"errors": 0,
"p95_s": 0.0489,
"max_s": 0.0661
},
"recovery/tender-edit": {
"n": 119,
"errors": 0,
"p95_s": 0.0556,
"max_s": 0.0753
}
},
"per_operation_all_phases": {
"detail": {
"n": 407,
"errors": 0,
"p95_s": 0.1463
},
"document-download": {
"n": 407,
"errors": 0,
"p95_s": 0.0498
},
"document-upload": {
"n": 407,
"errors": 0,
"p95_s": 0.0878
},
"list": {
"n": 407,
"errors": 0,
"p95_s": 0.2624
},
"login": {
"n": 6,
"errors": 0,
"p95_s": 1.2653
},
"login-form": {
"n": 6,
"errors": 0,
"p95_s": 0.6157
},
"lot-create": {
"n": 407,
"errors": 0,
"p95_s": 0.0613
},
"lot-edit": {
"n": 407,
"errors": 0,
"p95_s": 0.0621
},
"search": {
"n": 407,
"errors": 0,
"p95_s": 0.2553
},
"task-create": {
"n": 407,
"errors": 0,
"p95_s": 0.0621
},
"task-edit": {
"n": 407,
"errors": 0,
"p95_s": 0.0657
},
"tender-create": {
"n": 407,
"errors": 0,
"p95_s": 0.0661
},
"tender-edit": {
"n": 407,
"errors": 0,
"p95_s": 0.0587
}
},
"summary": {
"ordinary": {
"requests": 3675,
"errors": 0,
"p95_seconds": 0.19182809395715594
},
"document_transfer": {
"requests": 814,
"errors": 0,
"p95_seconds": 0.07621732295956463
},
"complete": true,
"provisional_pass": true,
"measurement_complete": true,
"measurement_gaps": [],
"startup_seconds": 0.04661138809751719,
"duration_seconds": 201.53947580303065,
"completed_iterations": {
"ordinary": [
106,
105
],
"burst": [
39,
38
],
"recovery": [
59,
60
]
},
"host": {
"logical_cpus": 4,
"affinity_cpus": 4,
"cpu_model": "AMD EPYC Processor (with IBPB)",
"memory_total": "MemTotal: 16377032 kB",
"cgroup_cpu_max": "100000 100000",
"app_cpu_quota": 1.0
}
},
"phase_results": [
{
"name": "ordinary",
"kind": "load",
"seconds": 91.06608834001236,
"started_at": "2026-10-21T06:30:01.523056+00:00",
"finished_at": "2026-10-21T06:31:32.589152+00:00",
"expected_requests": 2325,
"actual_requests": 2325,
"complete": true,
"ordinary": {
"requests": 1903,
"errors": 0,
"p95_seconds": 0.14162223995663226
},
"document_transfer": {
"requests": 422,
"errors": 0,
"p95_seconds": 0.08520571992266923
}
},
{
"name": "burst",
"kind": "load",
"seconds": 30.685886312043294,
"started_at": "2026-10-21T06:31:32.589194+00:00",
"finished_at": "2026-10-21T06:32:03.275085+00:00",
"expected_requests": 851,
"actual_requests": 851,
"complete": true,
"ordinary": {
"requests": 697,
"errors": 0,
"p95_seconds": 0.19206618797034025
},
"document_transfer": {
"requests": 154,
"errors": 0,
"p95_seconds": 0.06853019201662391
}
},
{
"name": "restart",
"kind": "restart",
"seconds": 18.981462653027847,
"started_at": "2026-10-21T06:32:03.275117+00:00",
"finished_at": "2026-10-21T06:32:22.256585+00:00",
"ready": true
},
{
"name": "recovery",
"kind": "load",
"seconds": 60.80042844603304,
"started_at": "2026-10-21T06:32:22.256609+00:00",
"finished_at": "2026-10-21T06:33:23.057045+00:00",
"expected_requests": 1313,
"actual_requests": 1313,
"complete": true,
"ordinary": {
"requests": 1075,
"errors": 0,
"p95_seconds": 0.21410848898813128
},
"document_transfer": {
"requests": 238,
"errors": 0,
"p95_seconds": 0.07255190098658204
}
}
]
}

View file

@ -0,0 +1,73 @@
import json, math, sys, gzip, collections
from datetime import datetime
R = sys.argv[1]
def ts(x): return datetime.fromisoformat(x.replace("Z", "+00:00").replace(".", ".", 1)[:26] + ("+00:00" if "+" not in x[19:] and not x.endswith("Z") else "")).timestamp() if False else _ts(x)
def _ts(x):
x = x.rstrip("Z")
if "+" in x[19:]: x = x[:x.index("+", 19)]
if "." in x: a, b = x.split("."); x = a + "." + b[:6]
return datetime.fromisoformat(x + "+00:00").timestamp()
def p95(v):
v = sorted(v); return v[max(0, math.ceil(0.95 * len(v)) - 1)] if v else None
def parse(path):
segs, cur, s = [], None, None
for line in open(path):
line = line.rstrip("\n")
if line.startswith("POD "): cur = {"pod": line.split()[1], "samples": []}; segs.append(cur)
elif line.startswith("S "):
s = {"t": float(line[2:])}; cur["samples"].append(s)
elif s is None: continue
elif line.startswith("M "): s["mem"] = int(line[2:])
elif line.startswith("P "): s["psi_some_total"] = int(line.split("total=")[1])
elif line.startswith("N "):
f = [int(x) for x in line.split()[2:]]; s["node_total"] = sum(f[:8]); s["node_idle"] = f[3] + f[4]
elif line.startswith("L "): s["load1"] = float(line.split()[1])
elif line.split() and line.split()[0] in ("usage_usec","nr_periods","nr_throttled","throttled_usec"):
k, v = line.split(); s[k] = int(v)
for seg in segs: seg["samples"] = [x for x in seg["samples"] if "mem" in x and "node_total" in x and "usage_usec" in x]
return segs
def window(segs, a, b, cores=4):
rates, mems, thr_p, per_p, thr_us, nodebusy, psi, load = [], [], 0, 0, 0, [], 0, []
span = 0; used = 0
for seg in segs:
xs = [x for x in seg["samples"] if a <= x["t"] <= b]
for x in xs: mems.append(x["mem"]); load.append(x["load1"])
for p, q in zip(xs, xs[1:]):
dt = q["t"] - p["t"]
if dt <= 0 or dt > 5: continue
du = q["usage_usec"] - p["usage_usec"]; used += du; span += dt
rates.append(du / 1e3 / dt)
per_p += q.get("nr_periods",0) - p.get("nr_periods",0); thr_p += q.get("nr_throttled",0) - p.get("nr_throttled",0)
thr_us += q.get("throttled_usec",0) - p.get("throttled_usec",0)
psi += q["psi_some_total"] - p["psi_some_total"]
tot = q["node_total"] - p["node_total"]; idle = q["node_idle"] - p["node_idle"]
if tot > 0: nodebusy.append(cores * (tot - idle) / tot * 1000)
if not rates: return None
return {"samples": len(rates), "seconds": round(span,1), "cpu_mean_m": round(used/1e3/span,1), "cpu_p95_m": round(p95(rates),1), "cpu_peak_m": round(max(rates),1),
"cfs_periods": per_p, "cfs_throttled_periods": thr_p, "throttle_ratio": round(thr_p/per_p,3) if per_p else None, "throttled_seconds": round(thr_us/1e6,2),
"psi_cpu_some_share": round(psi/1e6/span,3), "mem_peak_mib": round(max(mems)/2**20,1), "mem_mean_mib": round(sum(mems)/len(mems)/2**20,1),
"node_busy_mean_m": round(sum(nodebusy)/len(nodebusy)), "node_busy_p95_m": round(p95(nodebusy)), "node_free_mean_m": round(cores*1000-sum(nodebusy)/len(nodebusy)),
"node_free_p5_m": round(cores*1000-p95(nodebusy)), "load1_max": max(load)}
win = {}
for line in open(f"{R}/windows.txt"):
k, *v = line.split(); win[k] = v
res = json.load(open(f"{R}/result.json"))
W = {"baseline_pre": (_ts(win["baseline_start"][0]), _ts(win["driver_start"][0])),
"harness_idle": (_ts(res["idle_window"]["started_at"]), _ts(res["idle_window"]["finished_at"]))}
for p in res["phase_results"]: W[p["name"]] = (_ts(p["started_at"]), _ts(p["finished_at"]))
loads = [p for p in res["phase_results"] if p["kind"] == "load"]
W["workload_all_load_phases"] = (_ts(loads[0]["started_at"]), _ts(loads[-1]["finished_at"]))
W["post"] = (_ts(win["driver_exit"][1]), _ts(win["post_end"][0]))
out = {"windows_utc": {k: [datetime.utcfromtimestamp(a).isoformat()+"Z", datetime.utcfromtimestamp(b).isoformat()+"Z"] for k,(a,b) in W.items()}, "cgroup": {}}
for name in ("app", "db", "driver"):
segs = parse(f"{R}/{name}-cgroup.txt")
out["cgroup"][name] = {"pods": [s["pod"] for s in segs], **{k: window(segs, a, b) for k, (a, b) in W.items() if k != "workload_all_load_phases" or True}}
ops = collections.defaultdict(list)
for r in res["operations"]: ops[(r["phase"], r["operation"])].append(r)
out["per_operation"] = {f"{ph}/{op}": {"n": len(v), "errors": sum(not x["ok"] for x in v), "p95_s": round(p95([x["seconds"] for x in v]),4), "max_s": round(max(x["seconds"] for x in v),4)} for (ph, op), v in sorted(ops.items())}
allops = collections.defaultdict(list)
for r in res["operations"]: allops[r["operation"]].append(r)
out["per_operation_all_phases"] = {op: {"n": len(v), "errors": sum(not x["ok"] for x in v), "p95_s": round(p95([x["seconds"] for x in v]),4)} for op, v in sorted(allops.items())}
out["summary"] = {k: res[k] for k in ("ordinary","document_transfer","complete","provisional_pass","measurement_complete","measurement_gaps","startup_seconds","duration_seconds","completed_iterations","host")}
out["phase_results"] = [{k: v for k, v in p.items() if k != "operations"} for p in res["phase_results"]]
json.dump(out, open(sys.argv[2], "w"), indent=1)

View file

@ -0,0 +1,125 @@
apiVersion: v1
kind: Namespace
metadata:
name: vergabe-sizing-20261021
labels:
railiance.io/purpose: synthetic-sizing
railiance.io/task: rapps-wp-0014-t04
railiance.io/ephemeral: "true"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata: {name: bench-media, namespace: vergabe-sizing-20261021}
spec:
accessModes: [ReadWriteOnce]
storageClassName: local-path
resources: {requests: {storage: 5Gi}}
---
apiVersion: apps/v1
kind: Deployment
metadata: {name: vergabe-db, namespace: vergabe-sizing-20261021}
spec:
replicas: 1
strategy: {type: Recreate}
selector: {matchLabels: {app: vergabe-sizing-db}}
template:
metadata: {labels: {app: vergabe-sizing-db}}
spec:
automountServiceAccountToken: false
containers:
- name: postgres
image: docker.io/library/postgres@sha256:4b7183ac05f8ef417db21fd72d71047a4238340c261d3cc3ddb6d579ab5071ae
imagePullPolicy: IfNotPresent
env:
- {name: POSTGRES_DB, value: synthetic}
- {name: POSTGRES_USER, value: synthetic}
- {name: POSTGRES_PASSWORD, value: synthetic-local-only}
- {name: PGDATA, value: /var/lib/postgresql/data/pgdata}
ports: [{containerPort: 5432}]
readinessProbe: {exec: {command: [pg_isready, -U, synthetic]}, periodSeconds: 5}
resources: {requests: {cpu: 100m, memory: 256Mi}, limits: {cpu: "1", memory: 1Gi}}
volumeMounts: [{name: data, mountPath: /var/lib/postgresql/data}]
volumes: [{name: data, emptyDir: {}}]
---
apiVersion: v1
kind: Service
metadata: {name: vergabe-db, namespace: vergabe-sizing-20261021}
spec: {selector: {app: vergabe-sizing-db}, ports: [{port: 5432, targetPort: 5432}]}
---
apiVersion: apps/v1
kind: Deployment
metadata: {name: vergabe-app, namespace: vergabe-sizing-20261021}
spec:
replicas: 1
strategy: {type: Recreate}
selector: {matchLabels: {app: vergabe-sizing-app}}
template:
metadata: {labels: {app: vergabe-sizing-app}}
spec:
automountServiceAccountToken: false
securityContext: {fsGroup: 999, runAsGroup: 999, runAsNonRoot: true, runAsUser: 999}
initContainers:
- name: migrate
image: forgejo.coulomb.social/coulomb/vergabe-teilnahme@sha256:a26444f59c259698159c69ccb96f73dc648a261ece4c86bb2037a9d977870d91
imagePullPolicy: IfNotPresent
command: [python, manage.py, migrate, --noinput]
env: &env
- {name: DJANGO_SETTINGS_MODULE, value: synthetic_settings}
- {name: PYTHONPATH, value: /synthetic}
- {name: SECRET_KEY, value: synthetic-disposable-not-a-deployment-key}
- {name: DATABASE_URL, value: "postgres://synthetic:synthetic-local-only@vergabe-db:5432/synthetic"}
resources: &res {requests: {cpu: 60m, memory: 256Mi}, limits: {cpu: "1", memory: 1Gi}}
securityContext: &sc {allowPrivilegeEscalation: false, capabilities: {drop: [ALL]}, readOnlyRootFilesystem: false}
volumeMounts: [{name: settings, mountPath: /synthetic}]
containers:
- name: app
image: forgejo.coulomb.social/coulomb/vergabe-teilnahme@sha256:a26444f59c259698159c69ccb96f73dc648a261ece4c86bb2037a9d977870d91
imagePullPolicy: IfNotPresent
env: *env
ports: [{containerPort: 8000, name: http}]
readinessProbe: {httpGet: {path: /health/, port: 8000, httpHeaders: [{name: Host, value: vergabe-app}]}, initialDelaySeconds: 5, periodSeconds: 10, timeoutSeconds: 5, failureThreshold: 3}
livenessProbe: {httpGet: {path: /health/, port: 8000, httpHeaders: [{name: Host, value: vergabe-app}]}, initialDelaySeconds: 30, periodSeconds: 30, timeoutSeconds: 5, failureThreshold: 3}
resources: *res
securityContext: *sc
volumeMounts:
- {name: settings, mountPath: /synthetic}
- {name: media, mountPath: /app/media}
- {name: app-state, mountPath: /app/.issue-facade}
volumes:
- {name: settings, configMap: {name: synthetic-settings}}
- {name: media, persistentVolumeClaim: {claimName: bench-media}}
- {name: app-state, emptyDir: {}}
---
apiVersion: v1
kind: Service
metadata: {name: vergabe-app, namespace: vergabe-sizing-20261021}
spec: {selector: {app: vergabe-sizing-app}, ports: [{port: 80, targetPort: 8000}]}
---
apiVersion: v1
kind: ServiceAccount
metadata: {name: synthetic-driver, namespace: vergabe-sizing-20261021}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata: {name: synthetic-driver-restart, namespace: vergabe-sizing-20261021}
rules: [{apiGroups: [""], resources: [pods], verbs: [get, list, delete]}]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata: {name: synthetic-driver-restart, namespace: vergabe-sizing-20261021}
roleRef: {apiGroup: rbac.authorization.k8s.io, kind: Role, name: synthetic-driver-restart}
subjects: [{kind: ServiceAccount, name: synthetic-driver, namespace: vergabe-sizing-20261021}]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: {name: synthetic-isolation, namespace: vergabe-sizing-20261021}
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
ingress: [{from: [{podSelector: {}}]}]
egress:
- to: [{podSelector: {}}]
- to: [{namespaceSelector: {matchLabels: {kubernetes.io/metadata.name: kube-system}}, podSelector: {matchLabels: {k8s-app: kube-dns}}}]
ports: [{port: 53, protocol: UDP}, {port: 53, protocol: TCP}]
- to: [{ipBlock: {cidr: 92.205.62.239/32}}, {ipBlock: {cidr: 10.43.0.1/32}}]
ports: [{port: 6443, protocol: TCP}, {port: 443, protocol: TCP}]

View file

@ -0,0 +1,53 @@
{
"users": 2,
"workers": 3,
"iterations": 100000,
"tenders": 100,
"document_bytes": 1048576,
"think_seconds": 0.2,
"timeout_seconds": 30,
"ordinary_p95_seconds": 2.0,
"duration_seconds": 180,
"idle_seconds": 30,
"runtime": "remote",
"app_image": "forgejo.coulomb.social/coulomb/vergabe-teilnahme@sha256:a26444f59c259698159c69ccb96f73dc648a261ece4c86bb2037a9d977870d91",
"app_cpu_limit": 1.0,
"app_memory_limit": "1Gi",
"operations": [
"list",
"search",
"tender-create",
"detail",
"tender-edit",
"lot-create",
"lot-edit",
"task-create",
"task-edit",
"document-upload",
"document-download"
],
"phases": [
{
"name": "ordinary",
"kind": "load",
"duration_seconds": 90,
"think_seconds": 0.2
},
{
"name": "burst",
"kind": "load",
"duration_seconds": 30,
"think_seconds": 0
},
{
"name": "restart",
"kind": "restart"
},
{
"name": "recovery",
"kind": "load",
"duration_seconds": 60,
"think_seconds": 0.2
}
]
}

View file

@ -0,0 +1,29 @@
apiVersion: v1
kind: Pod
metadata: {name: synthetic-driver, namespace: vergabe-sizing-20261021, labels: {app: vergabe-sizing-driver}}
spec:
serviceAccountName: synthetic-driver
restartPolicy: Never
securityContext: {runAsGroup: 999, runAsNonRoot: true, runAsUser: 999}
containers:
- name: driver
image: forgejo.coulomb.social/coulomb/vergabe-teilnahme@sha256:a26444f59c259698159c69ccb96f73dc648a261ece4c86bb2037a9d977870d91
imagePullPolicy: IfNotPresent
workingDir: /app
command: [sleep, "7200"]
env:
- {name: PYTHONPATH, value: /app}
- {name: SYNTHETIC_BASE_URL, value: "http://vergabe-app"}
- {name: SYNTHETIC_DATABASE_URL, value: "postgres://synthetic:synthetic-local-only@vergabe-db:5432/synthetic"}
- {name: SYNTHETIC_RESTART_SELECTOR, value: "app=vergabe-sizing-app"}
- {name: SYNTHETIC_SOURCE_REVISION, value: "deployed image c067993037f1cdeeb7b22d5600b51c649bc239db; driver from vergabe-teilnahme working tree"}
- {name: SYNTHETIC_DATABASE_DESCRIPTION, value: "fresh PostgreSQL 16 pod in throwaway namespace (docker.io/library/postgres:16 pinned digest)"}
- {name: SYNTHETIC_TRANSPORT_DESCRIPTION, value: "in-cluster HTTP via ClusterIP Service to Gunicorn (image CMD, 3 workers)"}
resources: {requests: {cpu: 100m, memory: 256Mi}, limits: {cpu: "1", memory: 1Gi}}
securityContext: {allowPrivilegeEscalation: false, capabilities: {drop: [ALL]}}
volumeMounts:
- {name: driver, mountPath: /driver}
- {name: out, mountPath: /out}
volumes:
- {name: driver, configMap: {name: synthetic-driver}}
- {name: out, emptyDir: {}}

View file

@ -0,0 +1,15 @@
#!/bin/bash
NS=vergabe-sizing-20261021; W=$SCRATCH; R=$W/raw; STOP=$R/stop; rm -f $STOP
$SCRATCH/sampler.sh app=vergabe-sizing-app app $R/app-cgroup.txt $STOP &
$SCRATCH/sampler.sh app=vergabe-sizing-db postgres $R/db-cgroup.txt $STOP &
$SCRATCH/sampler.sh app=vergabe-sizing-driver driver $R/driver-cgroup.txt $STOP &
$SCRATCH/top.sh $R/kubectl-top.txt $STOP &
echo "baseline_start $(date -u +%FT%T.%NZ)" > $R/windows.txt
sleep 120
echo "driver_start $(date -u +%FT%T.%NZ)" >> $R/windows.txt
kubectl -n $NS exec synthetic-driver -- python /driver/synthetic_pilot.py --config /driver/config.json --output /out/result.json > $R/driver-stdout.txt 2>&1
echo "driver_exit $? $(date -u +%FT%T.%NZ)" >> $R/windows.txt
sleep 60
echo "post_end $(date -u +%FT%T.%NZ)" >> $R/windows.txt
touch $STOP; sleep 3; wait
echo done

View file

@ -0,0 +1,25 @@
import json, subprocess, sys, urllib.parse
start, end, out = sys.argv[1], sys.argv[2], sys.argv[3]
P = "/api/v1/namespaces/telemetry/services/telemetry-prometheus:9090/proxy/api/v1/query_range?"
def q(expr, step="15s"):
url = P + urllib.parse.urlencode({"query": expr, "start": start, "end": end, "step": step})
return json.loads(subprocess.check_output(["kubectl", "get", "--raw", url]))
res = {"window": [start, end], "queries": {}}
for name, expr in {
"cpu_by_namespace_cores": 'sum by (namespace) (rate(container_cpu_usage_seconds_total{container!="",image!=""}[1m]))',
"throttled_ratio_by_namespace": 'sum by (namespace) (rate(container_cpu_cfs_throttled_periods_total{container!=""}[1m])) / sum by (namespace) (rate(container_cpu_cfs_periods_total{container!=""}[1m]))',
"memory_working_set_by_namespace_bytes": 'sum by (namespace) (container_memory_working_set_bytes{container!="",image!=""})',
"apps_pg_cpu_cores": 'sum by (pod) (rate(container_cpu_usage_seconds_total{namespace="databases",container!="",image!=""}[1m]))',
"live_vergabe_cpu_cores": 'sum (rate(container_cpu_usage_seconds_total{namespace="vergabe-demo-company",container!="",image!=""}[1m]))',
}.items():
r = q(expr)
res["queries"][name] = {"expr": expr, "result": r["data"]["result"]}
json.dump(res, open(out, "w"))
# summary: mean per namespace cpu
s = {}
for series in res["queries"]["cpu_by_namespace_cores"]["result"]:
v = [float(x[1]) for x in series["values"]]
s[series["metric"].get("namespace", "?")] = (round(1000*sum(v)/len(v)), round(1000*max(v)))
tot = sum(a for a, b in s.values())
print("container_total_mean_m", tot)
for k, v in sorted(s.items(), key=lambda kv: -kv[1][0])[:15]: print(k, "mean_m", v[0], "max_m", v[1])

View file

@ -0,0 +1,16 @@
#!/bin/bash
# usage: sampler.sh <label-selector> <container> <outfile> <stopfile>
NS=vergabe-sizing-20261021; SEL=$1; C=$2; OUT=$3; STOP=$4
LOOP='while :; do echo "S $(date +%s.%N)"; cat /sys/fs/cgroup/cpu.stat; echo "M $(cat /sys/fs/cgroup/memory.current)"; echo "P $(head -1 /sys/fs/cgroup/cpu.pressure)"; echo "N $(head -1 /proc/stat)"; echo "L $(cat /proc/loadavg)"; sleep 1; done'
while [ ! -e "$STOP" ]; do
POD=$(kubectl -n $NS get pod -l "$SEL" --field-selector=status.phase=Running -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
if [ -n "$POD" ]; then
echo "POD $POD $(date +%s.%N)" >> "$OUT"
timeout 1500 kubectl -n $NS exec "$POD" -c "$C" -- sh -c "$LOOP" >> "$OUT" 2>>"$OUT.err" &
PID=$!
while kill -0 $PID 2>/dev/null && [ ! -e "$STOP" ]; do sleep 1; done
kill $PID 2>/dev/null
echo "DETACH $(date +%s.%N)" >> "$OUT"
fi
sleep 0.5
done

View file

@ -0,0 +1,12 @@
from vergabe_teilnahme.settings.base import *
DEBUG = False
ALLOWED_HOSTS = ['vergabe-app']
NETKINGDOM_ENABLED = False
MEDIA_ROOT = '/app/media'
ISSUE_FACADE_LOCAL_DB = '/app/.issue-facade/issues.db'
ISSUE_FACADE_GITEA = None
FORCE_SCRIPT_NAME = None
MEDIA_URL = "/media/"
EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
SESSION_COOKIE_SECURE = False
CSRF_COOKIE_SECURE = False

View file

@ -0,0 +1,3 @@
#!/bin/bash
OUT=$1; STOP=$2
while [ ! -e "$STOP" ]; do echo "T $(date -u +%s)"; kubectl top pods -A --no-headers 2>&1; kubectl top node --no-headers 2>&1; sleep 15; done >> $OUT

View file

@ -0,0 +1 @@
{"ordinary": {"requests": 3675, "errors": 0, "p95_seconds": 0.19182809395715594}, "document_transfer": {"requests": 814, "errors": 0, "p95_seconds": 0.07621732295956463}, "complete": true, "provisional_pass": true}

View file

@ -0,0 +1,36 @@
T R O M
<nil> Scheduled vergabe-app-579448bf49-2fsn2 Successfully assigned vergabe-sizing-20261021/vergabe-app-579448bf49-2fsn2 to 239.62.205.92.host.secureserver.net
<nil> Scheduled vergabe-db-7c4bffdd48-w4jz4 Successfully assigned vergabe-sizing-20261021/vergabe-db-7c4bffdd48-w4jz4 to 239.62.205.92.host.secureserver.net
<nil> Scheduled synthetic-driver Successfully assigned vergabe-sizing-20261021/synthetic-driver to 239.62.205.92.host.secureserver.net
<nil> Scheduled vergabe-app-579448bf49-plp4j Successfully assigned vergabe-sizing-20261021/vergabe-app-579448bf49-plp4j to 239.62.205.92.host.secureserver.net
2026-10-21T06:26:14Z ScalingReplicaSet vergabe-db Scaled up replica set vergabe-db-7c4bffdd48 from 0 to 1
2026-10-21T06:26:14Z SuccessfulCreate vergabe-db-7c4bffdd48 Created pod: vergabe-db-7c4bffdd48-w4jz4
2026-10-21T06:26:14Z WaitForFirstConsumer bench-media waiting for first consumer to be created before binding
2026-10-21T06:26:16Z SuccessfulCreate vergabe-app-579448bf49 Created pod: vergabe-app-579448bf49-2fsn2
2026-10-21T06:26:16Z ScalingReplicaSet vergabe-app Scaled up replica set vergabe-app-579448bf49 from 0 to 1
2026-10-21T06:26:16Z Pulled vergabe-db-7c4bffdd48-w4jz4 Container image "docker.io/library/postgres@sha256:4b7183ac05f8ef417db21fd72d71047a4238340c261d3cc3ddb6d579ab5071ae" already present on machine and can be accessed by the pod
2026-10-21T06:26:16Z Created vergabe-db-7c4bffdd48-w4jz4 Container created
2026-10-21T06:26:16Z Provisioning bench-media External provisioner is provisioning volume for claim "vergabe-sizing-20261021/bench-media"
2026-10-21T06:26:16Z Started vergabe-db-7c4bffdd48-w4jz4 Container started
2026-10-21T06:26:19Z Unhealthy vergabe-db-7c4bffdd48-w4jz4 Readiness probe failed: /var/run/postgresql:5432 - no response
2026-10-21T06:26:21Z Created synthetic-driver Container created
2026-10-21T06:26:21Z ExternalProvisioning bench-media Waiting for a volume to be created either by the external provisioner 'rancher.io/local-path' or manually by the system administrator. If volume creation is delayed, please verify that the provisioner is running and correctly registered.
2026-10-21T06:26:21Z Pulled synthetic-driver Container image "forgejo.coulomb.social/coulomb/vergabe-teilnahme@sha256:a26444f59c259698159c69ccb96f73dc648a261ece4c86bb2037a9d977870d91" already present on machine and can be accessed by the pod
2026-10-21T06:26:22Z Started synthetic-driver Container started
2026-10-21T06:26:25Z ProvisioningSucceeded bench-media Successfully provisioned volume pvc-a23ccb84-ecd8-47af-9832-6d8b09c48e38
2026-10-21T06:26:26Z Started vergabe-app-579448bf49-2fsn2 Container started
2026-10-21T06:26:26Z Created vergabe-app-579448bf49-2fsn2 Container created
2026-10-21T06:26:26Z Pulled vergabe-app-579448bf49-2fsn2 Container image "forgejo.coulomb.social/coulomb/vergabe-teilnahme@sha256:a26444f59c259698159c69ccb96f73dc648a261ece4c86bb2037a9d977870d91" already present on machine and can be accessed by the pod
2026-10-21T06:26:32Z Started vergabe-app-579448bf49-2fsn2 Container started
2026-10-21T06:26:32Z Created vergabe-app-579448bf49-2fsn2 Container created
2026-10-21T06:26:32Z Pulled vergabe-app-579448bf49-2fsn2 Container image "forgejo.coulomb.social/coulomb/vergabe-teilnahme@sha256:a26444f59c259698159c69ccb96f73dc648a261ece4c86bb2037a9d977870d91" already present on machine and can be accessed by the pod
2026-10-21T06:32:03Z SuccessfulCreate vergabe-app-579448bf49 Created pod: vergabe-app-579448bf49-plp4j
2026-10-21T06:32:03Z Killing vergabe-app-579448bf49-2fsn2 Stopping container app
2026-10-21T06:32:04Z Started vergabe-app-579448bf49-plp4j Container started
2026-10-21T06:32:04Z Created vergabe-app-579448bf49-plp4j Container created
2026-10-21T06:32:04Z Pulled vergabe-app-579448bf49-plp4j Container image "forgejo.coulomb.social/coulomb/vergabe-teilnahme@sha256:a26444f59c259698159c69ccb96f73dc648a261ece4c86bb2037a9d977870d91" already present on machine and can be accessed by the pod
2026-10-21T06:32:04Z Unhealthy vergabe-app-579448bf49-2fsn2 Readiness probe failed: Get "http://10.42.0.96:8000/health/": dial tcp 10.42.0.96:8000: connect: connection refused
2026-10-21T06:32:09Z Started vergabe-app-579448bf49-plp4j Container started
2026-10-21T06:32:09Z Created vergabe-app-579448bf49-plp4j Container created
2026-10-21T06:32:09Z Pulled vergabe-app-579448bf49-plp4j Container image "forgejo.coulomb.social/coulomb/vergabe-teilnahme@sha256:a26444f59c259698159c69ccb96f73dc648a261ece4c86bb2037a9d977870d91" already present on machine and can be accessed by the pod

View file

@ -0,0 +1,4 @@
synthetic-driver driver forgejo.coulomb.social/coulomb/vergabe-teilnahme@sha256:a26444f59c259698159c69ccb96f73dc648a261ece4c86bb2037a9d977870d91 0 {} ['running']
vergabe-app-579448bf49-plp4j migrate forgejo.coulomb.social/coulomb/vergabe-teilnahme@sha256:a26444f59c259698159c69ccb96f73dc648a261ece4c86bb2037a9d977870d91 0 {} ['terminated']
vergabe-app-579448bf49-plp4j app forgejo.coulomb.social/coulomb/vergabe-teilnahme@sha256:a26444f59c259698159c69ccb96f73dc648a261ece4c86bb2037a9d977870d91 0 {} ['running']
vergabe-db-7c4bffdd48-w4jz4 postgres docker.io/library/postgres@sha256:4b7183ac05f8ef417db21fd72d71047a4238340c261d3cc3ddb6d579ab5071ae 0 {} ['running']

View file

@ -0,0 +1,7 @@
delete_requested 2026-10-21T06:35:25Z
namespace "vergabe-sizing-20261021" deleted
delete_returned 2026-10-21T06:36:18Z
Error from server (NotFound): namespaces "vergabe-sizing-20261021" not found
Error from server (NotFound): persistentvolumes "pvc-a23ccb84-ecd8-47af-9832-6d8b09c48e38" not found
0
live generation=4 image=forgejo.coulomb.social/coulomb/vergabe-teilnahme@sha256:a26444f59c259698159c69ccb96f73dc648a261ece4c86bb2037a9d977870d91 ready=1

View file

@ -0,0 +1,4 @@
baseline_start 2026-10-21T06:27:14.447540583Z
driver_start 2026-10-21T06:29:26.175778371Z
driver_exit 0 2026-10-21T06:33:22.885983171Z
post_end 2026-10-21T06:34:28.753426744Z

View file

@ -0,0 +1,49 @@
{
"schema": "coulomb.independent-image-release-postflight.v1",
"status": "live_image_release_verified",
"observed_at": "2026-10-31T07:31:19.932895+00:00",
"proposal_digest": "sha256:9f1daf6c5fb56b2b4fc9817f28f5ad090c884b0295f7d6f4469e42d48fa8d59f",
"helm_revision": 19,
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:522bcd7fce92436765a60c1e4afa2598f3cc40913352153ff1c4319a99ca92ed",
"source": "23ffe2cf4b214666754a2420558943438e54251a",
"cluster_deployment_pvc_identity_verified": true,
"complete_deployment_spec_verified": true,
"sole_ready_pvc_writer": true,
"data": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 92,
"business_rows_sha256": "6243955479f7861fcdcd79b62f7f1e9f9a27c05a17bd2c17b792ebc98b2fa7b6",
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0,
"actual_owner_pages_rendered": 40,
"unsaved_outsider_page_denials": 40,
"production_identity_or_grant_created": false
},
"installed_helper_sha256": {
"/opt/coulomb-social-backup/paired-backup.py": "43e119acb6a4933b4e144c349a091e074e1a91ab61dbe15a65c8264c1d47661a",
"/opt/coulomb-social-backup/snapshot-content-and-db.py": "dcc8dd2a3989f279597595f9260b31b24fcc9b6dc787f04c17ef0491c53d830d",
"/opt/railiance-platform/scripts/primary_application_archive.py": "a3f0a61e869a6012edb4b5288be14e3b0c4a9a1273b090320ab146d12e5aa94d"
},
"snapshot_server_dry_run": "passed",
"public_status": {
"/healthz": 200,
"/": 200,
"/static/spaces/copy-content.js": 200
},
"timers": {
"coulomb-social-paired-backup.timer": {
"LoadState": "not-found",
"ActiveState": "inactive"
},
"coulomb-social-backup-freshness.timer": {
"LoadState": "not-found",
"ActiveState": "inactive"
}
},
"production_data_modified": false
}

View file

@ -0,0 +1,25 @@
{
"schema": "coulomb.guarded-image-release.v1",
"proposal_digest": "sha256:9f1daf6c5fb56b2b4fc9817f28f5ad090c884b0295f7d6f4469e42d48fa8d59f",
"started_at": "2026-10-31T03:41:35.362323+00:00",
"status": "live_release_preflight_verified",
"production_data_restored": false,
"timers_activated": false,
"node_request_percent": {
"cpu": 68,
"memory": 76
},
"preflight": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 92,
"business_rows_sha256": "6243955479f7861fcdcd79b62f7f1e9f9a27c05a17bd2c17b792ebc98b2fa7b6",
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0
},
"live_changes": false
}

View file

@ -0,0 +1,70 @@
{
"completed_at": "2026-10-31T07:15:24.659887+00:00",
"elapsed_seconds": 99.14,
"existing_global_constraint_preserved": true,
"fresh_paired_point": {
"age_recipient": "age1qm30jk8q0cezey652dk2dhcr3d9vs528v6lg8mavl6d4q3e3vgnqmrmyy3",
"application": "coulomb-social",
"application_restore_verified": false,
"captured_at": "2026-10-31T07:13:52Z",
"ciphertext_bytes": 2181848,
"ciphertext_sha256": "840e633047fddd88c6e6a1a3d6909d9af1ad80eaba8c90f83d8e81188dfc17a4",
"completed_at": "2026-10-31T07:14:50.236959+00:00",
"content_sha256": "644db8f09684bd4a79c126a01a329904351c30bb340295b29c8bb0ca71092dfe",
"database_sha256": "498a11e221dcdef38075d23ef89a26f213316c6164addb70d47bfcb98a1999a7",
"destination": "s3://railiance-platform-pg-backup/platform-pg/application-archives/coulomb-social/2026/10/31/20261031T071352Z-abf5997f01a14d9cb05e4bba.tar.age",
"encrypted_bytes": 2181848,
"encrypted_sha256": "840e633047fddd88c6e6a1a3d6909d9af1ad80eaba8c90f83d8e81188dfc17a4",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:512819463a1902b43a615beee14be7bf97e6a05e016385082b9380692ad1a8cd",
"immutable_create": true,
"local_archive": "20261031T071352Z-abf5997f01a14d9cb05e4bba.tar.age",
"object_key": "platform-pg/application-archives/coulomb-social/2026/10/31/20261031T071352Z-abf5997f01a14d9cb05e4bba.tar.age",
"pause_and_capture_seconds": 56.34,
"remote_retrieval_verified": true,
"schema": "platform.application-primary-archive.v1",
"status": "offsite_fetched_verified",
"version_id": "1793430889823443"
},
"helm_revision": 19,
"node_request_percent": {
"cpu": 68,
"memory": 76
},
"pid": 3639216,
"postflight": {
"actual_owner_pages_rendered": 40,
"business_rows": 92,
"business_rows_sha256": "6243955479f7861fcdcd79b62f7f1e9f9a27c05a17bd2c17b792ebc98b2fa7b6",
"business_tables": 12,
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"duplicate_slug_groups": 0,
"global_slug_constraint": true,
"migration_0003_applied": true,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"production_identity_or_grant_created": false,
"tenant_slug_constraint": true,
"unsaved_outsider_page_denials": 40
},
"preflight": {
"business_rows": 92,
"business_rows_sha256": "6243955479f7861fcdcd79b62f7f1e9f9a27c05a17bd2c17b792ebc98b2fa7b6",
"business_tables": 12,
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"duplicate_slug_groups": 0,
"global_slug_constraint": true,
"migration_0003_applied": true,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"tenant_slug_constraint": true
},
"private_values_printed": false,
"production_data_restored": false,
"proposal_digest": "sha256:9f1daf6c5fb56b2b4fc9817f28f5ad090c884b0295f7d6f4469e42d48fa8d59f",
"public_health_landing_static_verified": true,
"schema": "coulomb.guarded-image-release.v1",
"snapshot_pin_refreshed": true,
"started_at": "2026-10-31T07:13:45.511369+00:00",
"status": "image_candidate_deployed_verified",
"timers_activated": false
}

View file

@ -0,0 +1,10 @@
{
"schema": "coulomb.live-source-provenance.v1",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:522bcd7fce92436765a60c1e4afa2598f3cc40913352153ff1c4319a99ca92ed",
"source": "23ffe2cf4b214666754a2420558943438e54251a",
"packaged_files": 80,
"all_packaged_source_bytes_match": true,
"packaged_manifest_sha256": "6b5a7b339d88f3c5eafb70a33aa28a7d3972988645b246ae1a92a3f159d72528",
"live_read_only_probe": true,
"production_data_changed": false
}

View file

@ -0,0 +1,68 @@
{
"completed_at": "2026-10-31T00:29:28.113460+00:00",
"elapsed_seconds": 99.527,
"fresh_paired_point": {
"age_recipient": "age1qm30jk8q0cezey652dk2dhcr3d9vs528v6lg8mavl6d4q3e3vgnqmrmyy3",
"application": "coulomb-social",
"application_restore_verified": false,
"captured_at": "2026-10-31T00:27:56Z",
"ciphertext_bytes": 2181848,
"ciphertext_sha256": "f7be502c7e122283e70b10d2fd8a2cbf9550d2261192ba11f94d945a9f39af51",
"completed_at": "2026-10-31T00:28:53.504056+00:00",
"content_sha256": "22b694a56178a70274df55bdfc60f2cc0fe24ec66563c2e952058e5d8cf2154a",
"database_sha256": "99e704023d0832aa29dcfd7695dd436dea58038ae48e3ab142d0ec2b916eb574",
"destination": "s3://railiance-platform-pg-backup/platform-pg/application-archives/coulomb-social/2026/10/31/20261031T002756Z-c31b84b48b3874bdaa87cbf5.tar.age",
"encrypted_bytes": 2181848,
"encrypted_sha256": "f7be502c7e122283e70b10d2fd8a2cbf9550d2261192ba11f94d945a9f39af51",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:9a646d8e01a61ba5316d3c2eb0273064e17f2d0300ad07af766ccedda65ecb18",
"immutable_create": true,
"local_archive": "20261031T002756Z-c31b84b48b3874bdaa87cbf5.tar.age",
"object_key": "platform-pg/application-archives/coulomb-social/2026/10/31/20261031T002756Z-c31b84b48b3874bdaa87cbf5.tar.age",
"pause_and_capture_seconds": 56.63,
"remote_retrieval_verified": true,
"schema": "platform.application-primary-archive.v1",
"status": "offsite_fetched_verified",
"version_id": "1793406532815253"
},
"global_constraint_applied": true,
"helm_revision": 18,
"node_request_percent": {
"cpu": 68,
"memory": 76
},
"pid": 2724118,
"postflight": {
"actual_owner_pages_rendered": 40,
"business_rows": 92,
"business_rows_sha256": "b65c4a32d554d741569df5622c09f31423f6c18aa1c4edc5154c4a95e3511508",
"business_tables": 12,
"content_files": 86,
"content_files_sha256": "fd9ea2fa6890ad960e45dff308713ccd1aa303c502dd0ceb0cc26908326216a4",
"duplicate_slug_groups": 0,
"global_slug_constraint": true,
"migration_0003_applied": true,
"production_identity_or_grant_created": false,
"tenant_slug_constraint": true,
"unsaved_outsider_page_denials": 40
},
"preflight": {
"business_rows": 92,
"business_rows_sha256": "b65c4a32d554d741569df5622c09f31423f6c18aa1c4edc5154c4a95e3511508",
"business_tables": 12,
"content_files": 86,
"content_files_sha256": "fd9ea2fa6890ad960e45dff308713ccd1aa303c502dd0ceb0cc26908326216a4",
"duplicate_slug_groups": 0,
"global_slug_constraint": false,
"migration_0003_applied": false,
"tenant_slug_constraint": true
},
"private_values_printed": false,
"production_data_restored": false,
"proposal_digest": "sha256:5fd7ccd8f68057346367d81efb82b48280a16dd31d87683a5bd83e60e3d1837e",
"public_health_landing_static_verified": true,
"schema": "coulomb.guarded-storage-release.v1",
"snapshot_pin_refreshed": true,
"started_at": "2026-10-31T00:27:48.578472+00:00",
"status": "storage_candidate_deployed_verified",
"timers_activated": false
}

View file

@ -0,0 +1,63 @@
{
"schema": "custodian.owner-review.v1",
"review": "NK-WP-0048-T16",
"owner": "railiance-apps",
"reviewed_at": "2026-10-17",
"reviewer": "railiance-apps (Claude Code session, source-only)",
"verdict": "needs-change",
"grants": "nothing; no admission, pin, dispatch or native action",
"request": "the-custodian docs/changes/netkingdom-target-2026-10-17/nk48-t16-owner-review/README.md @ bffeee9",
"frozen_input": {
"repo": "net-kingdom",
"commit": "ff8f597d5f2f32ce8e82e370c0ad43aa98dc31b7",
"files": {
"sso-mfa/k8s/keycape/deliver-consumer-secret-adapter.py": "sha256:d046eed63090d4e0849a931e2650ccfd89d3b686843aed9e5cb35a0e18e513ee",
"sso-mfa/k8s/keycape/handover-adapter.py": "sha256:0efc300a7b2765bce8da140d5c4666d9cb30daf02b4cbf0e0df96cba7c33d026",
"sso-mfa/k8s/keycape/deliver-consumer-secret-adapter.md": "sha256:4662cff37f2a445f450d5d9d9255cd4315f9d4e4a9adde2cb8d52bd8f1f1212b",
"sso-mfa/k8s/keycape/handover-adapter.md": "sha256:9756921a9a51eea3d9b2dd04b9f135bf0712dfe0c0cff638173da85ce7f5ddaa",
"sso-mfa/k8s/keycape/descriptor-composition-1.2.candidate.json": "sha256:745bb053e6776e8b96224563188405920227e4a17d7783ffa6b36c122be41b09"
},
"hash_check": "both adapter .py hashes equal coordinator-review-r3.json hashes_verified"
},
"option_preference": {
"choice": "C",
"reason": "Both Apps steps change the production forgejo namespace and stay owner-run with the founder's go per change. Apps does not want Warden to dispatch them; Warden verifying Apps receipts is acceptable. Apps does not object to B for mint-secret, which is Platform's step."
},
"will_implement": {
"deliver-consumer-secret": {
"answer": "yes, as an owner-run operator path, not a Warden-dispatched one",
"owner_apply": "Commit the consumer ServiceAccount (automount false), SecretStore (serviceAccountRef, audiences [openbao], role workload-kv-read-keycape-forgejo-forgejo) and ExternalSecret (Owner creation, Retain deletion, path workloads/keycape/forgejo, key CLIENT_SECRET only) as manifests in this repo, apply them under RAPPS-WP-0016-T02, and emit the value-free netkingdom.consumer-secret-delivery.v1 receipt from kubectl metadata (uid, created, Ready).",
"owner_remove": "Revert the Forgejo oauth source secret reference first (helm rollback to the noted revision), then remove only the created objects in the order ExternalSecret, SecretStore, ServiceAccount, and report consumer_reference_reverted=true.",
"accepted_as_is": ["receipt schema and per-object metadata", "value-free rule", "created-only rollback", "custody path platform/workloads/keycape/forgejo field CLIENT_SECRET (net-kingdom 074b3a0)"]
},
"hand-over": {
"answer": "yes, attended (actor human), lane name apps-consumer-handover accepted",
"record_handover": "Receipt store is this repo: a committed evidence file docs/evidence/<date>-forgejo-netkingdom-handover.json; identifier is path@commit; created=false when a file with the same observation digest is already committed.",
"remove_handover": "A revert commit of that evidence file; confirmed only when the revert is on main."
}
},
"consumer_verification": {
"principle": "Login and group enforcement are observed at the consumer, never inferred from registry CAS, the client registration or the ExternalSecret being Ready.",
"login_verified": "An attended browser sign-in through 'Sign in with netkingdom' on Forgejo by a member of the case access group succeeds, and Forgejo shows the account linked to auth source 'netkingdom' (admin API: user's login_source / external login entry).",
"enforces_groups": "Positive and negative probe: (1) a NetKingdom test user that is not in any case access group is refused or gets no team access; (2) adding that user to the mapped group grants the mapped Forgejo team membership on next sign-in; (3) removing it revokes the team membership. This is the RAPPS-WP-0016-T03 acceptance test.",
"observation_body": "The digest passed to record_handover covers the client_id, the groups probed, the Forgejo user ids and team ids observed, and the timestamps of the three probes. No tokens, cookies or secret values."
},
"needs_change": [
{
"file": "sso-mfa/k8s/keycape/handover-adapter.py",
"finding": "verify_consumer returns bare booleans (login_verified, enforces_groups). The adapter cannot tell an observed consumer result from a self-asserted one.",
"requested": "Require the verifier result to carry the observation evidence (or its authenticated identifier) for a positive login, a negative non-member probe and an add/remove membership probe, and include that in the digest."
},
{
"file": "sso-mfa/k8s/keycape/deliver-consumer-secret-adapter.py",
"finding": "Descriptor actor is 'agent' for a step that changes a production consumer namespace. Under railiance-apps rules, production-touching applies need the founder's go per change.",
"requested": "Actor 'human' (attended), or an explicit per-change founder approval reference in the binding. Lane name consumer-owner-deployment is otherwise accepted."
}
],
"carried_limits": {
"5_injected_stand_ins": "Accepted as a known limit; turned into task RAPPS-WP-0016-T05 (real Apps owner_apply/owner_remove, verifier and receipt store).",
"6_mint_then_revoke_failure": "Platform-owned; no Apps position.",
"others": "1-4 are net-kingdom / ops-warden concerns; Apps accepts them as known limits for its two steps."
},
"not_reviewed": ["mint-secret", "deliver-secret", "ensure-groups", "bind-env", "register-client", "composer", "checker artifact", "kernel admission"]
}

View file

@ -23,11 +23,11 @@ Hostname decision: `the-custodian/docs/forgejo-production-decisions.md`.
| Server | railiance01 `92.205.62.239` |
| Namespace | `forgejo` |
| Helm release | `forgejo` |
| HTTP service | `forgejo-gitea-http` (chart naming; ingress must target this) |
| HTTP service | `forge-http` (chart naming; ingress must target this) |
| Chart | `gitea-charts/gitea` **12.5.0** (Forgejo-compatible; 12.6+ needs Gitea 1.26 `config edit-ini`) |
| Image | `code.forgejo.org/forgejo/forgejo:11.0.3` |
| Database | `forgejo-db-rw.databases.svc.cluster.local:5432` |
| Kubeconfig | `~/.kube/config-hosteurope` |
| Kubeconfig | `~/.kube/config-railiance01` |
## Bootstrap (first deploy)
@ -36,8 +36,8 @@ Hostname decision: `the-custodian/docs/forgejo-production-decisions.md`.
```bash
cd ~/railiance-platform
# One-time: create and SOPS-encrypt helm/forgejo-db-secret.sops.yaml from template
KUBECONFIG=~/.kube/config-hosteurope make forgejo-db-deploy
KUBECONFIG=~/.kube/config-hosteurope make forgejo-db-status
KUBECONFIG=~/.kube/config-railiance01 make forgejo-db-deploy
KUBECONFIG=~/.kube/config-railiance01 make forgejo-db-status
```
### 2. Application secrets (apps)
@ -71,7 +71,7 @@ make forgejo-runner-status
- `strategy.type: Recreate` in `helm/forgejo-values.yaml` — avoids leveldb queue lock
on the shared RWO PVC during rolling updates.
- Actions enabled via `gitea.config.actions.ENABLED: true`.
- Ingress backend service name is `forgejo-gitea-http` (Helm release naming).
- Ingress backend service name is `forge-http` (Helm release naming).
## Day-2 operator targets
@ -104,6 +104,46 @@ make forgejo-runner-registration-sops-bootstrap
make check-sops SOPS_SENTINEL=helm/forgejo-runner-registration.sops.yaml
```
## Break-glass administrator (RAPPS-WP-0016-T01)
The floor under every sign-in change: one local site admin that works even when
NetKingdom, KeyCape or the OAuth source is broken.
| Item | Value |
| --- | --- |
| Account | `forgejo_admin` (chart bootstrap admin, local password, site admin) |
| Custody | OpenBao coulombcore `platform/workloads/forgejo/break-glass-admin`, field `PASSWORD` |
| Delivered to cluster | No. Not synced by ESO; only a human retrieves it |
| Who | The founder. Agents never read or hold the value |
The SOPS admin password in `helm/forgejo-secrets.sops.yaml` is only the chart's
initial value (`passwordMode: initialOnlyRequireReset`); Helm upgrades do not
reset the account. It is not the break-glass credential.
Seal or rotate, then verify (founder, interactive):
```bash
make forgejo-breakglass-seal # new password -> OpenBao first, then Forgejo
make forgejo-breakglass-verify # one local sign-in; writes docs/evidence/<date>-forgejo-breakglass-signin.json
```
Recovery when normal sign-in is broken:
1. `bao kv get -field=PASSWORD platform/workloads/forgejo/break-glass-admin`
(operator token on `https://bao.coulomb.social`).
2. Sign in at `https://forgejo.coulomb.social/user/login` as `forgejo_admin`
with local credentials. Local password sign-in stays enabled for this account
even if RAPPS-WP-0016-T04 restricts it for everyone else.
3. Repair the authentication source or users from Site Administration.
4. If the password itself is lost: with cluster access, run
`make forgejo-breakglass-seal` again (it sets the password through the
Forgejo CLI in the pod, so it needs no working sign-in).
5. Afterwards rotate (`make forgejo-breakglass-seal`), verify, and record the use.
Where it is logged: OpenBao audit log (retrieval), Forgejo log and the account's
last-login time (sign-in), the evidence file in `docs/evidence/`, and a State Hub
progress event naming why break-glass was used.
## SMTP (IONOS) — password reset / notifications
Decision: `the-custodian/docs/forgejo-production-decisions.md` (IONOS, 2026-07-07).
@ -145,10 +185,10 @@ Bootstrap (railiance01):
```bash
cd ~/railiance-apps
KUBECONFIG=~/.kube/config-hosteurope make forgejo-openbao-eso-token-apply
KUBECONFIG=~/.kube/config-hosteurope make forgejo-mailer-es-deploy
KUBECONFIG=~/.kube/config-hosteurope make forgejo-mailer-es-status # expect SecretSynced
KUBECONFIG=~/.kube/config-hosteurope make forgejo-deploy
KUBECONFIG=~/.kube/config-railiance01 make forgejo-openbao-eso-token-apply
KUBECONFIG=~/.kube/config-railiance01 make forgejo-mailer-es-deploy
KUBECONFIG=~/.kube/config-railiance01 make forgejo-mailer-es-status # expect SecretSynced
KUBECONFIG=~/.kube/config-railiance01 make forgejo-deploy
```
Do not put the SMTP password in `helm/forgejo-secrets.sops.yaml`.

View file

@ -19,7 +19,7 @@ docker push forgejo.coulomb.social/coulomb/REPO:TAG
Cluster pull (railiance01):
```bash
KUBECONFIG=~/.kube/config-hosteurope crictl pull forgejo.coulomb.social/coulomb/REPO:TAG
KUBECONFIG=~/.kube/config-railiance01 crictl pull forgejo.coulomb.social/coulomb/REPO:TAG
```
Smoke: `make forgejo-smoke`

View file

@ -120,7 +120,7 @@ see the correction above.
## Redeploy / recovery
```bash
export KUBECONFIG=$HOME/.kube/config-hosteurope
export KUBECONFIG=$HOME/.kube/config-railiance01
kubectl apply -f manifests/informed-decision-origin.yaml
kubectl apply -f manifests/informed-decision-ingress.yaml
kubectl apply -f manifests/informed-decision-http-redirect.yaml
@ -132,11 +132,10 @@ curl -sSI https://decisions.coulomb.social/ | head -1
That `curl` succeeded on 2026-09-10, which is the gate `informed-decision` was
waiting on.
**Note on the kubeconfig:** `~/.kube/config-hosteurope` names port `16443`, but
the `k3s-api-railiance01` ops-bridge tunnel currently listens on `16444`.
Override with `--server https://127.0.0.1:16444` or fix the kubeconfig; `bridge
check` reports the tunnel healthy either way, so it does not surface the
mismatch.
**Note on the kubeconfig:** use `~/.kube/config-railiance01`, which targets the
`k3s-api-railiance01` ops-bridge tunnel on `16444`. The retired
`~/.kube/config-hosteurope` names port `16443`; `bridge check` reports the tunnel
healthy either way, so it does not surface that mismatch.
## HTTP → HTTPS

View file

@ -7,6 +7,14 @@ deployments on CoulombCore and railiance01 are scaled to `0`.
This document preserves the Railiance01 Helm surface for audit only. Do not
deploy Inter-Hub to production.
**Removed 2026-10-21 (`RAPPS-WP-0018`, founder-approved wave 2).** The
`charts/inter-hub` chart, `helm/inter-hub-values.yaml`, the `inter-hub-*` Make
targets, `tools/inter-hub-smoke.sh` and the
`.gitea/workflows/inter-hub-production-deploy.yaml` workflow are deleted. The
release (0 replicas, latent 250m/512Mi request) is uninstalled with
`helm uninstall inter-hub -n inter-hub`. The commands below no longer exist;
recover them from git history if ever needed.
## Hosts and release surface
| Server | IP | Role |
@ -22,7 +30,7 @@ deploy Inter-Hub to production.
| Values | `helm/inter-hub-values.yaml` |
| Image | `forgejo.coulomb.social/coulomb/inter-hub:<tag>` |
| Secret | `inter-hub-env` |
| Production kubeconfig | `~/.kube/config-hosteurope` |
| Production kubeconfig | `~/.kube/config-railiance01` |
## Current evidence
@ -50,7 +58,7 @@ Read-only checks on 2026-06-15 showed:
## Deploy
Use the Railiance01 kubeconfig. The Makefile defaults to
`~/.kube/config-hosteurope` and fails fast when it is missing.
`~/.kube/config-railiance01` and fails fast when it is missing.
```bash
make inter-hub-render-baseline
@ -86,7 +94,7 @@ production trigger. It requires:
- `image_tag`, the immutable inter-hub image tag to deploy;
- `confirm`, with the exact value `deploy-inter-hub-production`;
- an approved self-hosted runner with Railiance01 kubeconfig access at
`~/.kube/config-hosteurope` or an equivalent `RAILIANCE01_KUBECONFIG`
`~/.kube/config-railiance01` or an equivalent `RAILIANCE01_KUBECONFIG`
override;
- registry access through `skopeo`, `crane`, `docker manifest inspect`, or the
registry HTTP API.

View file

@ -51,23 +51,26 @@ ambient `kubectl` context for production app deploys.
The production Makefile targets default to:
```text
~/.kube/config-hosteurope
~/.kube/config-railiance01
```
It reaches the API through the `k3s-api-railiance01` ops-bridge tunnel on
`127.0.0.1:16444` (`bridge up k3s-api-railiance01` if it is down). The older
`~/.kube/config-hosteurope` names port `16443` and is retired.
Restore it from Railiance01 when missing:
```bash
ssh tegwick@92.205.62.239 'sudo cat /etc/rancher/k3s/k3s.yaml' \
| sed 's|127.0.0.1|92.205.62.239|' > ~/.kube/config-hosteurope
chmod 600 ~/.kube/config-hosteurope
export KUBECONFIG=~/.kube/config-hosteurope
| sed 's|127.0.0.1:6443|127.0.0.1:16444|' > ~/.kube/config-railiance01
chmod 600 ~/.kube/config-railiance01
export KUBECONFIG=~/.kube/config-railiance01
```
The app-specific targets also accept explicit overrides:
```bash
REUSE_KUBECONFIG=~/.kube/config-hosteurope make reuse-status
INTER_HUB_KUBECONFIG=~/.kube/config-hosteurope make inter-hub-status
REUSE_KUBECONFIG=~/.kube/config-railiance01 make reuse-status
```
## SOPS Age Key Bootstrap

View file

@ -0,0 +1,57 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: forgejo-keycape-forgejo-eso
namespace: forgejo
labels:
app.kubernetes.io/name: forgejo
app.kubernetes.io/part-of: railiance-apps
automountServiceAccountToken: false
---
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: openbao-keycape-forgejo-consumer
namespace: forgejo
spec:
provider:
vault:
server: http://openbao.openbao.svc:8200
path: platform
version: v2
auth:
kubernetes:
mountPath: kubernetes
role: external-secrets-keycape-forgejo-forgejo
serviceAccountRef:
name: forgejo-keycape-forgejo-eso
audiences:
- openbao
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: forgejo-netkingdom-oidc-client
namespace: forgejo
labels:
app.kubernetes.io/name: forgejo
app.kubernetes.io/part-of: railiance-apps
spec:
refreshInterval: 15m
secretStoreRef:
name: openbao-keycape-forgejo-consumer
kind: SecretStore
target:
name: forgejo-netkingdom-oidc-client
creationPolicy: Owner
deletionPolicy: Retain
template:
metadata:
labels:
app.kubernetes.io/name: forgejo
app.kubernetes.io/part-of: railiance-apps
data:
- secretKey: CLIENT_SECRET
remoteRef:
key: workloads/keycape/forgejo
property: CLIENT_SECRET

View file

@ -0,0 +1,666 @@
{
"eso_rbac_bindings": [
{
"kind": "ClusterRoleBinding",
"name": "external-secrets-cert-controller",
"namespace": null,
"roleRef": {
"apiGroup": "rbac.authorization.k8s.io",
"kind": "ClusterRole",
"name": "external-secrets-cert-controller"
},
"rules": [
{
"apiGroups": [
"apiextensions.k8s.io"
],
"resources": [
"customresourcedefinitions"
],
"verbs": [
"get",
"list",
"watch",
"update",
"patch"
]
},
{
"apiGroups": [
"admissionregistration.k8s.io"
],
"resources": [
"validatingwebhookconfigurations"
],
"verbs": [
"list",
"watch",
"get"
]
},
{
"apiGroups": [
"admissionregistration.k8s.io"
],
"resourceNames": [
"secretstore-validate",
"externalsecret-validate"
],
"resources": [
"validatingwebhookconfigurations"
],
"verbs": [
"update",
"patch"
]
},
{
"apiGroups": [
""
],
"resources": [
"endpoints"
],
"verbs": [
"list",
"get",
"watch"
]
},
{
"apiGroups": [
""
],
"resources": [
"events"
],
"verbs": [
"create",
"patch"
]
},
{
"apiGroups": [
""
],
"resources": [
"secrets"
],
"verbs": [
"get",
"list",
"watch",
"update",
"patch"
]
},
{
"apiGroups": [
"coordination.k8s.io"
],
"resources": [
"leases"
],
"verbs": [
"get",
"create",
"update",
"patch"
]
}
],
"subjects": [
{
"kind": "ServiceAccount",
"name": "external-secrets-cert-controller",
"namespace": "external-secrets"
}
]
},
{
"kind": "ClusterRoleBinding",
"name": "external-secrets-controller",
"namespace": null,
"roleRef": {
"apiGroup": "rbac.authorization.k8s.io",
"kind": "ClusterRole",
"name": "external-secrets-controller"
},
"rules": [
{
"apiGroups": [
"external-secrets.io"
],
"resources": [
"secretstores",
"clustersecretstores",
"externalsecrets",
"clusterexternalsecrets",
"pushsecrets",
"clusterpushsecrets"
],
"verbs": [
"get",
"list",
"watch"
]
},
{
"apiGroups": [
"external-secrets.io"
],
"resources": [
"externalsecrets",
"externalsecrets/status",
"externalsecrets/finalizers",
"secretstores",
"secretstores/status",
"secretstores/finalizers",
"clustersecretstores",
"clustersecretstores/status",
"clustersecretstores/finalizers",
"clusterexternalsecrets",
"clusterexternalsecrets/status",
"clusterexternalsecrets/finalizers",
"pushsecrets",
"pushsecrets/status",
"pushsecrets/finalizers",
"clusterpushsecrets",
"clusterpushsecrets/status",
"clusterpushsecrets/finalizers"
],
"verbs": [
"get",
"update",
"patch"
]
},
{
"apiGroups": [
"generators.external-secrets.io"
],
"resources": [
"generatorstates"
],
"verbs": [
"get",
"list",
"watch",
"create",
"update",
"patch",
"delete",
"deletecollection"
]
},
{
"apiGroups": [
"generators.external-secrets.io"
],
"resources": [
"acraccesstokens",
"clustergenerators",
"ecrauthorizationtokens",
"fakes",
"gcraccesstokens",
"githubaccesstokens",
"quayaccesstokens",
"passwords",
"stssessiontokens",
"uuids",
"vaultdynamicsecrets",
"webhooks",
"grafanas"
],
"verbs": [
"get",
"list",
"watch"
]
},
{
"apiGroups": [
""
],
"resources": [
"serviceaccounts",
"namespaces"
],
"verbs": [
"get",
"list",
"watch"
]
},
{
"apiGroups": [
""
],
"resources": [
"configmaps"
],
"verbs": [
"get",
"list",
"watch"
]
},
{
"apiGroups": [
""
],
"resources": [
"secrets"
],
"verbs": [
"get",
"list",
"watch",
"create",
"update",
"delete",
"patch"
]
},
{
"apiGroups": [
""
],
"resources": [
"serviceaccounts/token"
],
"verbs": [
"create"
]
},
{
"apiGroups": [
""
],
"resources": [
"events"
],
"verbs": [
"create",
"patch"
]
},
{
"apiGroups": [
"external-secrets.io"
],
"resources": [
"externalsecrets"
],
"verbs": [
"create",
"update",
"delete"
]
},
{
"apiGroups": [
"external-secrets.io"
],
"resources": [
"pushsecrets"
],
"verbs": [
"create",
"update",
"delete"
]
}
],
"subjects": [
{
"kind": "ServiceAccount",
"name": "external-secrets",
"namespace": "external-secrets"
}
]
},
{
"kind": "RoleBinding",
"name": "external-secrets-leaderelection",
"namespace": "external-secrets",
"roleRef": {
"apiGroup": "rbac.authorization.k8s.io",
"kind": "Role",
"name": "external-secrets-leaderelection"
},
"rules": [
{
"apiGroups": [
""
],
"resourceNames": [
"external-secrets-controller"
],
"resources": [
"configmaps"
],
"verbs": [
"get",
"update",
"patch"
]
},
{
"apiGroups": [
""
],
"resources": [
"configmaps"
],
"verbs": [
"create"
]
},
{
"apiGroups": [
"coordination.k8s.io"
],
"resources": [
"leases"
],
"verbs": [
"get",
"create",
"update",
"patch"
]
}
],
"subjects": [
{
"kind": "ServiceAccount",
"name": "external-secrets",
"namespace": "external-secrets"
}
]
}
],
"external-secrets_deployments": [
{
"images": [
"oci.external-secrets.io/external-secrets/external-secrets:v0.16.1"
],
"name": "external-secrets",
"serviceAccount": "external-secrets",
"uid": "e6017556-523a-4b26-b9d1-4c0c90cef483"
},
{
"images": [
"oci.external-secrets.io/external-secrets/external-secrets:v0.16.1"
],
"name": "external-secrets-cert-controller",
"serviceAccount": "external-secrets-cert-controller",
"uid": "5134a56f-aa05-4764-9b27-c56e92203c7d"
},
{
"images": [
"oci.external-secrets.io/external-secrets/external-secrets:v0.16.1"
],
"name": "external-secrets-webhook",
"serviceAccount": "external-secrets-webhook",
"uid": "6e6e4a6e-4c2c-4659-af56-d4281f2fe229"
}
],
"external-secrets_externalsecrets": [],
"external-secrets_secretstores": [],
"external-secrets_serviceaccounts": [
{
"automountServiceAccountToken": null,
"name": "default",
"uid": "cc29abe8-5ace-4cc9-b479-cf8db84df42d"
},
{
"automountServiceAccountToken": false,
"name": "eso-token-renewer",
"uid": "4d44afd7-59e9-44de-9e99-8cd620be41de"
},
{
"automountServiceAccountToken": null,
"name": "external-secrets",
"uid": "c918dc22-b1ee-4e1f-a8f8-1d15ac0d5a10"
},
{
"automountServiceAccountToken": null,
"name": "external-secrets-cert-controller",
"uid": "3c78bb78-a3c5-4953-93d4-cf6621e1200a"
},
{
"automountServiceAccountToken": null,
"name": "external-secrets-webhook",
"uid": "c75dc3ba-42db-4a59-8952-6c94d85d054c"
}
],
"forgejo_deployments": [
{
"images": [
"code.forgejo.org/forgejo/forgejo:11.0.3-rootless"
],
"name": "forgejo-gitea",
"serviceAccount": "default",
"uid": "e8eff0aa-64bb-4b44-9a80-a66612986355"
},
{
"images": [
"docker:27-dind",
"code.forgejo.org/forgejo/runner:6.3.1"
],
"name": "forgejo-runner",
"serviceAccount": "default",
"uid": "89c8a056-bcae-4940-acdd-2eb2768e108a"
}
],
"forgejo_externalsecrets": [
{
"conditions": [
{
"type": "Ready",
"status": "True",
"reason": "SecretSynced"
}
],
"data": [
{
"key": "workloads/forgejo/forgejo-mailer",
"property": "PASSWD",
"secretKey": "MAILER_PASSWD"
}
],
"name": "forgejo-mailer",
"store": {
"kind": "ClusterSecretStore",
"name": "openbao-forgejo"
},
"target": "forgejo-mailer",
"uid": "cbbea71e-70a8-4010-aca9-9136920f2f58"
}
],
"forgejo_secretstores": [],
"forgejo_serviceaccounts": [
{
"automountServiceAccountToken": null,
"name": "default",
"uid": "2ae15248-4700-46a6-84c6-e3bb3cd33323"
},
{
"automountServiceAccountToken": false,
"name": "forgejo-mailer-eso",
"uid": "e2214835-32dd-4333-a9c2-6d248ab8bafe"
}
],
"native_mutation": false,
"observed_at": "2026-10-11T01:20:37.519031+00:00",
"secret_value_reads": false,
"secretstore_crd": {
"uid": "1eefa5c9-6c2a-4a3b-84c6-3941488201c2",
"versions": [
{
"kubernetes_auth_schema": {
"description": "Kubernetes authenticates with Vault by passing the ServiceAccount\ntoken stored in the named Secret resource to the Vault server.",
"properties": {
"mountPath": {
"default": "kubernetes",
"description": "Path where the Kubernetes authentication backend is mounted in Vault, e.g:\n\"kubernetes\"",
"type": "string"
},
"role": {
"description": "A required field containing the Vault Role to assume. A Role binds a\nKubernetes ServiceAccount with a set of Vault policies.",
"type": "string"
},
"secretRef": {
"description": "Optional secret field containing a Kubernetes ServiceAccount JWT used\nfor authenticating with Vault. If a name is specified without a key,\n`token` is the default. If one is not specified, the one bound to\nthe controller will be used.",
"properties": {
"key": {
"description": "A key in the referenced Secret.\nSome instances of this field may be defaulted, in others it may be required.",
"maxLength": 253,
"minLength": 1,
"pattern": "^[-._a-zA-Z0-9]+$",
"type": "string"
},
"name": {
"description": "The name of the Secret resource being referred to.",
"maxLength": 253,
"minLength": 1,
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$",
"type": "string"
},
"namespace": {
"description": "The namespace of the Secret resource being referred to.\nIgnored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.",
"maxLength": 63,
"minLength": 1,
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$",
"type": "string"
}
},
"type": "object"
},
"serviceAccountRef": {
"description": "Optional service account field containing the name of a kubernetes ServiceAccount.\nIf the service account is specified, the service account secret token JWT will be used\nfor authenticating with Vault. If the service account selector is not supplied,\nthe secretRef will be used instead.",
"properties": {
"audiences": {
"description": "Audience specifies the `aud` claim for the service account token\nIf the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity\nthen this audiences will be appended to the list",
"items": {
"type": "string"
},
"type": "array"
},
"name": {
"description": "The name of the ServiceAccount resource being referred to.",
"maxLength": 253,
"minLength": 1,
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$",
"type": "string"
},
"namespace": {
"description": "Namespace of the resource being referred to.\nIgnored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.",
"maxLength": 63,
"minLength": 1,
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$",
"type": "string"
}
},
"required": [
"name"
],
"type": "object"
}
},
"required": [
"mountPath",
"role"
],
"type": "object"
},
"name": "v1",
"served": true,
"storage": true
},
{
"kubernetes_auth_schema": {
"description": "Kubernetes authenticates with Vault by passing the ServiceAccount\ntoken stored in the named Secret resource to the Vault server.",
"properties": {
"mountPath": {
"default": "kubernetes",
"description": "Path where the Kubernetes authentication backend is mounted in Vault, e.g:\n\"kubernetes\"",
"type": "string"
},
"role": {
"description": "A required field containing the Vault Role to assume. A Role binds a\nKubernetes ServiceAccount with a set of Vault policies.",
"type": "string"
},
"secretRef": {
"description": "Optional secret field containing a Kubernetes ServiceAccount JWT used\nfor authenticating with Vault. If a name is specified without a key,\n`token` is the default. If one is not specified, the one bound to\nthe controller will be used.",
"properties": {
"key": {
"description": "A key in the referenced Secret.\nSome instances of this field may be defaulted, in others it may be required.",
"maxLength": 253,
"minLength": 1,
"pattern": "^[-._a-zA-Z0-9]+$",
"type": "string"
},
"name": {
"description": "The name of the Secret resource being referred to.",
"maxLength": 253,
"minLength": 1,
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$",
"type": "string"
},
"namespace": {
"description": "The namespace of the Secret resource being referred to.\nIgnored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.",
"maxLength": 63,
"minLength": 1,
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$",
"type": "string"
}
},
"type": "object"
},
"serviceAccountRef": {
"description": "Optional service account field containing the name of a kubernetes ServiceAccount.\nIf the service account is specified, the service account secret token JWT will be used\nfor authenticating with Vault. If the service account selector is not supplied,\nthe secretRef will be used instead.",
"properties": {
"audiences": {
"description": "Audience specifies the `aud` claim for the service account token\nIf the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity\nthen this audiences will be appended to the list",
"items": {
"type": "string"
},
"type": "array"
},
"name": {
"description": "The name of the ServiceAccount resource being referred to.",
"maxLength": 253,
"minLength": 1,
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$",
"type": "string"
},
"namespace": {
"description": "Namespace of the resource being referred to.\nIgnored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.",
"maxLength": 63,
"minLength": 1,
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$",
"type": "string"
}
},
"required": [
"name"
],
"type": "object"
}
},
"required": [
"mountPath",
"role"
],
"type": "object"
},
"name": "v1beta1",
"served": true,
"storage": false
}
]
}
}

View file

@ -0,0 +1,31 @@
import json,subprocess,sys,hashlib,datetime
out={'observed_at':datetime.datetime.now(datetime.timezone.utc).isoformat(),'native_mutation':False,'secret_value_reads':False}
def get(args):
r=subprocess.run(['kubectl',*args,'-o','json'],capture_output=True,text=True)
if r.returncode:return None
try:return json.loads(r.stdout)
except Exception:return None
for ns in ['external-secrets','forgejo']:
deps=get(['get','deployments','-n',ns]);out[ns+'_deployments']=None if deps is None else [{'name':d['metadata']['name'],'uid':d['metadata']['uid'],'serviceAccount':d['spec']['template']['spec'].get('serviceAccountName','default'),'images':[c['image'] for c in d['spec']['template']['spec']['containers']]} for d in deps['items']]
sas=get(['get','serviceaccounts','-n',ns]);out[ns+'_serviceaccounts']=None if sas is None else [{'name':s['metadata']['name'],'uid':s['metadata']['uid'],'automountServiceAccountToken':s.get('automountServiceAccountToken')} for s in sas['items']]
stores=get(['get','secretstores','-n',ns]);out[ns+'_secretstores']=None if stores is None else [{'name':s['metadata']['name'],'uid':s['metadata']['uid'],'vault':{k:v for k,v in s['spec'].get('provider',{}).get('vault',{}).items() if k in ['server','path','version','auth']},'conditions':[{k:c[k] for k in ['type','status','reason'] if k in c} for c in s.get('status',{}).get('conditions',[])]} for s in stores['items']]
es=get(['get','externalsecrets','-n',ns]);out[ns+'_externalsecrets']=None if es is None else [{'name':s['metadata']['name'],'uid':s['metadata']['uid'],'store':s['spec']['secretStoreRef'],'target':s['spec'].get('target',{}).get('name'),'data':[{'secretKey':x.get('secretKey'),'key':x.get('remoteRef',{}).get('key'),'property':x.get('remoteRef',{}).get('property')} for x in s['spec'].get('data',[])],'conditions':[{k:c[k] for k in ['type','status','reason'] if k in c} for c in s.get('status',{}).get('conditions',[])]} for s in es['items']]
crd=get(['get','crd','secretstores.external-secrets.io'])
if crd:
versions=[]
for v in crd['spec']['versions']:
a=v['schema']['openAPIV3Schema']['properties']['spec']['properties']['provider']['properties']['vault']['properties']['auth']['properties']['kubernetes']
versions.append({'name':v['name'],'served':v['served'],'storage':v['storage'],'kubernetes_auth_schema':a})
out['secretstore_crd']={'uid':crd['metadata']['uid'],'versions':versions}
bindings=[]
for resource in ['clusterrolebindings','rolebindings']:
obj=get(['get',resource,'-A'])
if obj is None:continue
for b in obj['items']:
if any(s.get('kind')=='ServiceAccount' and s.get('namespace')=='external-secrets' for s in b.get('subjects',[])):
ref=b['roleRef'];args=['get',ref['kind'].lower(),ref['name']]
if ref['kind']=='Role':args+=['-n',b['metadata']['namespace']]
role=get(args)
bindings.append({'kind':b['kind'],'name':b['metadata']['name'],'namespace':b['metadata'].get('namespace'),'subjects':b.get('subjects',[]),'roleRef':ref,'rules':None if role is None else role.get('rules',[])})
out['eso_rbac_bindings']=bindings
print(json.dumps(out,sort_keys=True))

View file

@ -0,0 +1,39 @@
# Forgejo consumer ESO reader — concrete source proposal
RAPPS-WP-0016 already chooses ESO delivery for the OIDC client secret. This proposal fills its missing reader identity without asking the founder to make that same source choice again. It is not a live apply, a new credential grant, or first-case success. Existing ORGREF-WP-0002 remains the project driver; CCR42, MASON-WP-0010-T03 and NK-WP-0048-T06 remain the owner work, rather than another workplan.
Actual read-only railiance01 metadata shows ESO v0.16.1 running as external-secrets/external-secrets, served v1/v1beta1 SecretStore CRDs supporting serviceAccountRef.audiences, and controller RBAC permitting serviceaccounts/token create. Forgejo and its runner currently share forgejo/default; they must not become the OpenBao reader identity. No consumer namespace SecretStore or dedicated OIDC-delivery SA exists in the observed metadata. Existing forgejo-mailer-eso is a separate SMTP lane and remains unchanged. No OpenBao login, capability check, auth operation, Secret value, Secret object retrieval, or native materialization was performed.
## Exact supported reader
`consumer-delivery.yaml` prepares exactly three namespaced objects: ServiceAccount forgejo/forgejo-keycape-forgejo-eso with automount=false and no legacy token refs; SecretStore forgejo/openbao-keycape-forgejo-consumer; ExternalSecret forgejo/forgejo-netkingdom-oidc-client. The store requests a token for that dedicated serviceAccountRef, audiences=[openbao], and authenticates to OpenBao Kubernetes role external-secrets-keycape-forgejo-forgejo. The role binds exactly that SA and namespace, only workload-kv-read-keycape-forgejo-forgejo, no default policy, and a 900-second maximum OpenBao token lifetime. ESO's installed source requests a 600-second Kubernetes token and can fall back to legacy SA tokens; owner admission must ensure this new SA has no legacy tokens, never create one to bypass a failed TokenRequest.
The namespace SecretStore cannot select another namespace's SA; serviceAccountRef.namespace is omitted. No extra RBAC, projected workload token, Deployment SA replacement or controller identity change is proposed. Namespace/default is not an OpenBao-bound reader. ESO remains the shared trusted controller: it already has clusterwide TokenRequest and Secret permissions and can request this SA's token. This separates reader binding/revocation and avoids default identity reuse; it does not isolate this secret from controller compromise or grant new cryptographic isolation.
The exact KV data ACL is platform/data/workloads/keycape/forgejo, read only, plus existing self-token lookup/revoke stanzas. No metadata/list/parent/sibling access is added. Delivery maps only remote workloads/keycape/forgejo field CLIENT_SECRET to target key CLIENT_SECRET. Target creationPolicy=Owner and deletionPolicy=Retain preserve fail-safe remote-deletion behavior. Refresh is 15m; the value is never present in this source. Existing internal OpenBao HTTP endpoint and controller custody behavior are retained, not upgraded into a TLS/isolation claim.
## Type and structural boundary
NetKingdom's proposed successor type 1.2 adds explicit case parameter consumer_namespace, one sixth structural object consumer-eso-role, and separate owner deliver-consumer-secret follow-through/readiness/rollback. All original five rendered structural bodies compare equal to current1.1. Existing acknowledged type 1.1 and accepted case 1.1 remain byte-preserved; the proposed 1.2 case has status proposed and no copied acceptance. Full proposed render is c0afd5576cdae0cbc807eccb2b9fa87ac668f15e32ec7cf79d295bdb23c75b35. Existing acceptance does not silently extend to the new role/runtime delivery. Owner review and exact type/case acknowledgment precede any build, metadata apply or materialized-status receipt.
The current Mason builder gives every role-backed reader the ESO draft suffix. With two reader roles it silently overwrites one CCR. The new regression proves that defect against current source; the minimal staged fix retains ESO for the KeyCape reader and Forgejo for consumer-reader-policy, plus refuses duplicate output identities.42 actual focused builder/plan/structure tests pass with corrected source, including both independent CCRs, platform validation and a complete six-object structural request. Existing1.1 tests remain passing. No fake proposed 1.2 acceptance is used: Mason correctly refuses to build a merely proposed successor. Test fixtures alone temporarily acknowledge synthetic roles to exercise builder semantics.
The proposed CCR42 body uses proposed status, bound_claims_confirmed=false, explicit pending1.2 acknowledgment and old CCR's exact historical hash/provenance. Owner validation passes with the expected apply-blocking warning. It is stored separately from the actual in-flight CCR42; no historical review or applied CCR41 is rewritten. Folding this reviewed body into CCR42 remains an owner reconciliation with retained original revision, not a fresh parallel request.
## Admission, native proof and recovery
Before an exact scoped native proposal, refresh ESO image/CRD/RBAC, Forgejo/default and runner identity, the three proposed object identities and target metadata through Kubernetes PartialObjectMetadata only. Classify absent, exact preexisting-matched or conflicting; refuse a legacy-token SA, unknown ownership, changed role/policy, audience mismatch or shared target. Never read Secret data. Preserve current UIDs/resourceVersions, authenticated owner change receipts and the exact type/case/render/source pins. No placeholder SA claim substitutes for actual owner admission.
Platform materialization applies only the reviewed exact-path policy and dedicated Kubernetes-auth role after its own approved CCR path. Fresh owner metadata/capability receipts must prove exact binding and distinguish case-created from preexisting; no minted value or successful delivery is required to prove structure. Mason's existing typed structural contract then covers all six objects, including source layout and catalog pointer; source-only bodies are not authenticated native evidence. Warden native dispatch/descriptors/session receipt and per-owner provenance still need their existing accepted integration review.
Consumer follow-through applies the three admitted objects, then checks SecretStore/ExternalSecret Ready status and exact target UID/ownerReference through metadata only. Shared/matched objects never acquire delete authority. If ESO reports failure, stop; no manual Secret seeding or controller/static-token fallback is admitted. Minting, KeyCape client/env/groups, and Forgejo Helm OIDC/group enforcement stay distinct existing first-case steps. Forgejo gitea.oauth secret-consumption settings were not changed or Helm-render-verified here; they must be prepared/reviewed before T02 handover, with current local login intact and T01 break-glass acceptance still a prerequisite. Delivery Ready alone does not complete browser/MFA/group checks or forge closure.
Created-only rollback first reverts the consumer secret reference/auth source through its owner Helm revision. Remove only the admitted trial-created ExternalSecret and independently proven case-created target UID/ownerReference; preserve matched/shared targets. Then remove only created namespace SecretStore and SA. Remove only the created OpenBao role after revoking its issued tokens or proving expiry, then created policy. Role/SA/store deletion does not revoke already issued OpenBao tokens; deletion alone is not deactivation proof. Retain native/audit/source receipts and stop if references, UIDs or ownership changed. Do not infer delete authority from Mason's legacy planned rollback inventory.
RAPPS-WP-0016-T02, current CCR42, MASON-WP-0010-T03 and first-case acceptance remain unfinished. ORGREF-WP-0002 holds this owner return. Human-only break-glass/account-link/MFA/first-case acknowledgment gates remain separate from source preparation; no new human login is requested by this packet.
## Supplementary generated-CCR refinement after Platform conditional review
Exact prior 271 packet is preserved at /tmp/forgejo-consumer-eso-refinement-original-271, alongside original c48. Platform found two additional generator gaps: hardcoded keycape target for the consumer, and delete-only consumer-role lifecycle. Both meaningful counterexamples fail against 271 source before the fix. Current staged generator derives target.workload from consuming_service only for consumer-reader-policy, preserving all KeyCape ESO output fields. A role-backed consumer CCR requires reverting consumer reference, authenticated case-created ownership, preservation of preexisting/shared objects, issued-token owner revocation or independently verified expiry before deactivation, and no new login/remint during recovery. Actual owner native authority remains required; generated wording is not an implemented revocation adapter.
Forty-four focused builder/plan/structure tests now pass. Earlier 42 and original counterexample/failure history remain retained. The old committed 1.1 bundle still mislabels the consumer workload keycape and therefore correctly fails fresh exact target matching after this correction. That historical bundle is preserved; the checker is not weakened or grandfathered. Local fixtures alone use corrected/rehashed consumer metadata, with a separate regression proving old bundle refusal. Current 1.1 unbound consumer generation changes only its previously wrong target.workload; KeyCape reader output remains unchanged. The proposed 1.2 render/type/case/source remains pending exact acknowledgment, with identical six-object structural body and no native completion claim. Fresh source build/bundle owner reconciliation must occur after exact acceptance before any native apply/materialization; old CCR or bundle approval is not silently reassigned. New source/test hashes are in validation.json and generated projection receipt.

View file

@ -0,0 +1,37 @@
[
{
"repo": "net-kingdom",
"path": "security-case-types/oidc-browser-relying-party/type.yaml",
"sha256": "f705c64e4875e9b8e18a912fb51c135e77898926d6a451f5cb74ab3c0cbe4d1c"
},
{
"repo": "net-kingdom",
"path": "security-cases/forgejo-netkingdom-oidc.yaml",
"sha256": "bcc2c6d5bae0d28242b3cdb71e74d8ca297c2afe04766b55d53597f86893351d"
},
{
"repo": "ops-mason",
"path": "src/ops_mason/case_build.py",
"sha256": "249cc8574939d2c3193f968309781c472c96c199e04206c336463b4d65ea6659"
},
{
"repo": "ops-mason",
"path": "src/ops_mason/case_structure.py",
"sha256": "71032a5ea613f5226f552f796656599c1a7447a38428f890595d8f252831ce80"
},
{
"repo": "railiance-platform",
"path": "credential-change-requests/CCR-2026-0042-forgejo-netkingdom-oidc-consumer-reader.yaml",
"sha256": "a36ab567839c12660c8ca468c8e4c6a67e367c92ae2ae51851b8e34c35a277f3"
},
{
"repo": "railiance-platform",
"path": "credential-change-requests/CCR-2026-0041-forgejo-netkingdom-oidc-keycape-eso-reader.yaml",
"sha256": "e69077ad29166770e4a57b4c4105615e272822d8fde11df591d0c127b158ac67"
},
{
"repo": "railiance-apps",
"path": "workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md",
"sha256": "a74f9fbc3300739bc0d8e31f1de88c0b7226ff2f6990e7cbb3ae433ac84fdb5e"
}
]

View file

@ -0,0 +1,11 @@
[
{
"url": "https://github.com/external-secrets/external-secrets/blob/v0.16.1/pkg/provider/vault/auth_kubernetes.go",
"sha256": "e53f43c0748e43f408b5a7c5323c12c0fb5fb4299a33718181f6d70945112071",
"finding": "serviceAccountRef requests TokenRequest with600second lifetime, then legacy-token fallback if unavailable; namespace local for SecretStore; no serviceAccountRef means mounted controller token path"
},
{
"url": "https://external-secrets.io/latest/provider/hashicorp-vault/",
"finding": "Official current documentation cross-check; actual runtime compatibility established by v0.16.1 source and installed CRD/RBAC instead of assuming latest behavior."
}
]

Some files were not shown because too many files have changed in this diff Show more