Compare commits

..

No commits in common. "main" and "csoc-authelia-mail-custody" have entirely different histories.

681 changed files with 933 additions and 77754 deletions

View file

@ -12,7 +12,7 @@ make apps-pg-status # shared apps-pg cluster health (read-only)
make pg-status # confirms retired Bitnami postgresql-ha is absent
make valkey-status # confirms Valkey is absent
make consumption-preflight CONSUMING_ENTITY=entity:<slug> ESTIMATE_EUR=<eur>
make forgejo-db-deploy | apps-pg-deploy # production-touching
make db-deploy | apps-pg-deploy # production-touching
```
`pg-deploy` and `valkey-deploy` are fail-closed. `*-status` / `*-logs`

136
.custodian-brief.md Normal file
View file

@ -0,0 +1,136 @@
<!-- custodian-brief: generated by fix-consistency — do not edit manually -->
# Custodian Brief — railiance-platform
**Domain:** financials
**Last synced:** 2026-10-06 12:27 UTC
**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)*
## Active Workstreams
### Coordinate audit-core temporary custody and recovery exercises
Progress: 2/4 done | workplan_id: `f4640325-e89c-591d-b58e-ec6b087900ac`
**Open tasks:**
- ! T02 — Define the runtime database lease recovery exercise `fda4262a`
*(wait: Value-safe lease revoke/expire exercise must be coordinated with rapp-postgres and audit-core.)*
- ! T03 — Define the coordinated railiance01 reboot exercise `ba729d18`
*(wait: Single-node outage exercise needs operator access and a declared restart order coordinated with railiance-infra, railiance-cluster, audit-core and the OpenBao package owner.)*
### ArgoCD phase B: adopt the four existing Applications on railiance01
Progress: 2/8 done | workplan_id: `98140775-3b9a-5cf9-9af6-722502d487dc`
**Open tasks:**
- ! Adopt openbao-secretstore (lowest risk) `6d5fc801`
*(wait: Adopted 2026-09-21; the follow-on step (issue-core adoption, then automation) waits on the founder's go-ahead.)*
- ! Adopt issue-core (from rapp-issue-core) `0e619658`
*(wait: Founder go-ahead plus rapp-issue-core's agreement (hub message) that ArgoCD owns production and make deploy stops being a production path.)*
- ! Adopt target-revenue `d418068a`
*(wait: Adopted 2026-09-21; remaining preconditions and the next step wait on the founder's go-ahead.)*
- ! Adopt external-secrets (highest risk) `4ca54868`
*(wait: Founder go-ahead for the ESO adoption after adding CRD Prune=false,Delete=false and re-running the diff.)*
- ! Turn the root back to the production lane `844ff115`
*(wait: Founder go-ahead to restore automated prune/selfHeal on railiance-apps-root once T03-T06 are proven.)*
- ! Plan phase C: retire coulombcore's ArgoCD `55d1382f`
*(wait: Planning only; needs an operator read-only check of coulombcore's ArgoCD, outside agent session scope.)*
### Close Forgejo primary backup coverage on Scaleway
Progress: 3/4 done | workplan_id: `7beec1a7-aa82-5a36-9a66-6b60008a2455`
**Open tasks:**
- ! Establish primary full-archive delivery and application recovery `a4807df0`
*(wait: Operator chooses the independent archive prefix and confirms retention/caller contract and bounded worker credential delivery before the primary archive retrieval.)*
### Coordinate KeyCape live Secret exposure recovery
Progress: 4/6 done | workplan_id: `b2c25a01-4a80-55c1-90cf-8538000f7e0e`
**Open tasks:**
- ! T03 — Collect exact owner acknowledgements `714ae011`
*(wait: Attended resolver run: postchecks outstanding; NetKingdom asked to package the complete sequence as one receipt-producing command for the next attended session.)*
- ! T06 — Publish the Railiance/OpenBao custody handoff `3b9748c4`
*(wait: Platform/OpenBao owner must publish the non-secret routing receipt for both lanes through the attended handoff; values are never inferred.)*
### Close S3 service assurance and ownership gaps
Progress: 4/7 done | workplan_id: `ca639c3d-3a87-5fa4-ad13-6f2e014b0c84`
**Open tasks:**
- ! Make backup freshness and recurring recovery evidence checkable `d64446fb`
*(wait: activity-core replied (not accepted yet): role acceptance is the founder decision ACTIVITY-WP-0042-T01 (D2); then ACTIVITY-WP-0042 bounded operation, S3 OpenBao credential route, repaired openbao-activity-core store and per-run approval-engine approval.)*
- ! Produce S3 signals and prove their delivery to the evidence owner `5351e0e4`
*(wait: Needs a concrete receiving contract from railiance-telemetry (RTEL-WP-0002) before health transport is accepted; unmonitored state retained meanwhile.)*
- ! Obtain acceptance for compatibility assets and derived-record cleanup `3b74f79c`
*(wait: Owner-ready handoffs to railiance-forge, rapp-openbao and the ArgoCD bootstrap owner await acceptance; S3 custody contracts stay here until then.)*
### Adopt activity-core application runtime into railiance01 GitOps
Progress: 2/3 done | workplan_id: `b8cf2fc2-60c2-5d4e-a60a-55f1304d9f54`
**Open tasks:**
- ! Observe adoption and enforce bounded promotion readiness `63b44949`
*(wait: 24h soak after adoption, then founder confirmation of the scoped identity and checks before automated sync; ACTIVITY-WP-0041 owns publication and the promotion guard.)*
### Prepare generated attended windows and explicit value-custody choice
Progress: 3/4 done | workplan_id: `0c7e3b0c-1c36-568c-af51-b06f912a0f1d`
**Open tasks:**
- ! Bind a real dated window and owner receipt manifests `1ae5a6b8`
*(wait: Platform preparation and founder design choice are complete. Independent informed-decision runtime source/publication, KeyCape custody/registration/issuance, secrets-engine consumer custody coordinates and provider setup-link handoff remain; dependency-requirements.md names the exact owner deliveries.)*
### Implement reviewed credential lanes with separate owner gates
Progress: 3/9 done | workplan_id: `975db491-5412-5e27-8e34-14a2417bb039`
**Open tasks:**
- ! Accept and provision secrets-engine service JWT login `e0c82ea9`
*(wait: KEY-WP-0037 now owns protected custody, real registration and native issuance. Platform issuer-reader source CCR-2026-0033 is prepared. secrets-engine must accept exact consumer host/file/delivery/rotation coordinates before the separate custody child is finalized; KeyCape exact-primary identity repair and separate live go remain.)*
- ! Implement the platform operator-write CCR contract and Fluid lane `f47e5bc5`
*(wait: MASON-WP-0005-T02 source contract is done (ops-mason message 79570eb8). The attended policy/schema apply remains with the open provisioning decision.)*
- ! Admit the separate approval client-side readers `68bff751`
*(wait: INFD-WP-0001-T07/T08 terminal; remaining gate is human approval of the scoped delivery proof; front door stays non-resolvable.)*
- ! Dispose of the ungoverned whynot-design npm duplicate `0dd7c9a5`
*(wait: Founder HOLD (2026-09-21) on the legacy whynot-design npm pointer; custody disposition is an attended decision.)*
- ! Admit the two factory audit-sender custody lanes `0ef52c26`
*(wait: Provisioned and delivered 2026-09-11 (CCR-2026-0021/0022 applied); do not repeat. Receiver rollout with evidence_kind and native producer acceptance passed 2026-09-11. Remaining human acceptance: independent archive readback and revocation of the interim audit bearer tokens.)*
- ! Founder approval of the CCR-2026-0033 custody child `7c753be6`
### Onboard rapp-policy-nexus to the ArgoCD production lane
Progress: 3/5 done | workplan_id: `ec41a4bd-df18-5b07-9b63-ccb80d9f001c`
**Open tasks:**
- ! Adopt policy-nexus (live, founder go-ahead) `8638fa73`
*(wait: Founder go-ahead: the merge is a live production change (CONSTRUCT @ railiance-platform, activation=APPROVED); also needs the root automation restored in RPF-WP-0044-T07.)*
- ! Prepare the Application and the dry-run evidence `2afb1aac`
*(wait: Draft is prepared. The attended read-only helm history / kubectl diff remains. This session did not touch the cluster.)*
### Coordinate KeyCape live Secret exposure recovery
Progress: 4/6 done | workplan_id: `038bc3c0-4492-5b91-95eb-ae515ca205df`
**Open tasks:**
- ! T03 — Collect exact owner acknowledgements `e019c166`
*(wait: Canonical RPF-WP-0027-T03: complete platform custody acknowledgements remain missing.)*
- ! T06 — Publish the Railiance/OpenBao custody handoff `69326850`
*(wait: Canonical RPF-WP-0027-T06: complete field/auth/expiry/handoff contract absent; September 15 operator block remains in force.)*
### Coordinate audit-core temporary custody and recovery exercises
Progress: 2/4 done | workplan_id: `88c4ef7f-0af8-580e-90dc-a2bae2675a4d`
**Open tasks:**
- ! T02 — Define the runtime database lease recovery exercise `caba7fcd`
*(wait: Canonical RPF-WP-0015-T02: approved sender identity, fresh bounded attended window, named abort operator and live recovery receipt required.)*
- ! T03 — Define the coordinated railiance01 reboot exercise `09cf4065`
*(wait: Canonical RPF-WP-0015-T03: fresh outage approval, off-host snapshot, quorum/access evidence and owner execution required.)*
### Retract public OpenBao listener behind operator-only access
Progress: 2/3 done | workplan_id: `6f8a6cbc-c076-5f0a-ade2-281a7ec71360`
**Open tasks:**
- ! T03 — Complete the attended operator cutover `99f8b41f`
## Inbox Hygiene
**Missing thread_id:** 1 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

@ -1,43 +0,0 @@
# RPF-WP-0061-T03: self-service CCR intake gate.
# ccr/* branches: exactly one new in-profile CCR (+ its new policy file) from a
# listed submitter, nothing else changed. main: post-merge id uniqueness.
# Source comes from the exact-revision archive, as in keycape-factor-acceptance.
name: CCR intake gate
on:
push:
branches: ["ccr/**", main]
workflow_dispatch:
jobs:
gate:
runs-on: ubuntu-latest
container:
image: python:3.12-slim@sha256:d764629ce0ddd8c71fd371e9901efb324a95789d2315a47db7e4d27e78f1b0e9
steps:
- name: Fetch exact revision and main
run: |
set -eu
python3 - <<'PYCODE'
import os,tarfile,tempfile,urllib.request
from pathlib import Path
base='https://forgejo.coulomb.social/'+os.environ['GITHUB_REPOSITORY']+'/archive/'
for name,ref in (('branch',os.environ['GITHUB_SHA']),('main','main')):
root=Path('/tmp/ccr-intake')/name;root.mkdir(parents=True)
with urllib.request.urlopen(base+ref+'.tar.gz',timeout=30) as response,tempfile.TemporaryFile() as archive:
archive.write(response.read());archive.seek(0)
with tarfile.open(fileobj=archive,mode='r:gz') as tar:tar.extractall(root,filter='data')
PYCODE
pip install --quiet --disable-pip-version-check --root-user-action=ignore --timeout 60 pyyaml==6.0.2 jsonschema==4.23.0
- name: Gate ccr branch
if: startsWith(github.ref, 'refs/heads/ccr/')
run: |
set -eu
cd /tmp/ccr-intake/branch/*
main_root=$(echo /tmp/ccr-intake/main/*)
PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s tests -p 'test_ccr_intake.py'
python3 scripts/credential-change.py check-branch --branch "$GITHUB_REF" --main-root "$main_root"
- name: Check CCR ids on main
if: github.ref == 'refs/heads/main'
run: |
set -eu
cd /tmp/ccr-intake/branch/*
python3 scripts/credential-change.py check-ids

View file

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

View file

@ -1,8 +0,0 @@
# Explicit opt-in recipe; no environment files or external make includes.
CUSTODIAN_SOURCE ?= /home/worsch/the-custodian
GENERATED_REPO_SLUG ?= railiance-platform
.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_SLUG)"
generated-work-records-check:
python3 "$(CUSTODIAN_SOURCE)/scripts/generate_staged_work_records.py" --repo . --repo-slug "$(GENERATED_REPO_SLUG)" --check

4
.gitignore vendored
View file

@ -13,7 +13,6 @@ helm/*.yaml
!helm/*-probe.yaml
!helm/vergabe-demo-company-role.yaml
!helm/vergabe-demo-company-database.yaml
!helm/playwright-consumer-access.yaml
# ArgoCD repository credentials — encrypt locally, never commit
argocd/repositories/*.repository.sops.yaml
@ -33,6 +32,3 @@ tools/vendor/bin/kubectl
# Derived local Repo Manager cache
.repo-manager/
# Generated workstation orientation; keep local, regenerate from State Hub.
/.custodian-brief.md

133
Makefile
View file

@ -58,10 +58,18 @@ CREDENTIAL_HELPER_PURPOSE ?= flex-auth-openbao-smoke
##@ CloudNative PG (cnpg) — primary database operator
db-deploy db-status db-shell db-logs: ## Retired Gitea-era gitea-db targets; use forgejo-db-*
@echo "gitea-db was the CoulombCore-era Gitea database and does not exist on railiance01." >&2
@echo "Use forgejo-db-deploy / forgejo-db-status / forgejo-db-shell (RAPPS-WP-0019-T05)." >&2
@exit 2
db-deploy: ## Apply Gitea cnpg Cluster (creates gitea-db in databases namespace)
$(KUBECTL) apply -f helm/gitea-db-cluster.yaml
db-status: ## Show cnpg cluster health
$(KUBECTL) cnpg status gitea-db -n databases 2>/dev/null || \
$(KUBECTL) get cluster gitea-db -n databases -o wide
db-shell: ## Open psql shell on gitea-db primary
$(KUBECTL) cnpg psql gitea-db -n databases -- -U gitea gitea
db-logs: ## Tail gitea-db primary logs
$(KUBECTL) logs -n databases -l cnpg.io/cluster=gitea-db -f --tail=50
##@ Forgejo database (railiance01 production forge)
@ -305,23 +313,6 @@ openbao-validate-emergency-evidence: ## Validate non-secret OpenBao emergency se
OPENBAO_EMERGENCY_EVIDENCE='$(OPENBAO_EMERGENCY_EVIDENCE)' \
scripts/openbao-validate-emergency-drill-evidence.sh
OPENBAO_REHEARSAL_IMAGE ?= quay.io/openbao/openbao:2.5.4
openbao-isolated-restore-test: ## RPF-WP-0064 offline tests: admission, v2 receipt verifier, launcher (no Docker)
PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_openbao_isolated_restore
openbao-isolated-restore-image: ## Pull the public OpenBao image used by the synthetic rehearsal (no custody)
docker pull -q $(OPENBAO_REHEARSAL_IMAGE)
openbao-isolated-restore-rehearsal: ## RPF-WP-0064-T04 native rehearsal: --network none containers, generated material only
PYTHONDONTWRITEBYTECODE=1 python3 -m unittest -v tests.test_openbao_isolated_restore_native
openbao-verify-restore-receipt: ## Verify a v2 isolated-restore receipt (RECEIPT=/abs/path SOURCE_ROOT=clean checkout [NOT_AFTER=])
python3 scripts/openbao-isolated-restore/verify-restore-receipt.py --receipt '$(RECEIPT)' \
--source-root '$(or $(SOURCE_ROOT),$(CURDIR))' $(if $(NOT_AFTER),--not-after '$(NOT_AFTER)')
.PHONY: openbao-isolated-restore-test openbao-isolated-restore-image openbao-isolated-restore-rehearsal openbao-verify-restore-receipt
##@ Credential broker
credential-grants-validate: ## Validate non-secret credential grant catalog
@ -460,9 +451,6 @@ argocd-repo-apply: ## Apply a SOPS-encrypted ArgoCD repository Secret (set ARGOC
(echo "ERROR: set ARGOCD_REPOSITORY_SECRET=argocd/repositories/<repo>.repository.sops.yaml"; exit 1)
sops -d $(ARGOCD_REPOSITORY_SECRET) | $(KUBECTL) apply -f -
argocd-pin-drift: ## Read-only: compare railiance01 child Application pins in Git with live (run before any root sync)
python3 scripts/argocd_child_pin_drift.py
argocd-status: ## Show Railiance ArgoCD projects, root app, and registered repos
$(KUBECTL) get appprojects.argoproj.io -n $(ARGOCD_NAMESPACE) \
railiance-bootstrap railiance-tenants railiance-platform-addons
@ -538,82 +526,17 @@ audit-database-lease-approval-check: ## Validate an approved T02 receipt without
python3 scripts/audit-core-database-lease-recovery.py validate-approval \
--approval "$(AUDIT_DATABASE_LEASE_APPROVAL)"
ATTENDED_EXEC = python3 scripts/openbao-attended-exec.py --
AUDIT_T02_NEED_APPROVAL = @test -n "$(AUDIT_DATABASE_LEASE_APPROVAL)" || { echo 'set AUDIT_DATABASE_LEASE_APPROVAL=/path/to/approved-receipt.json' >&2; exit 2; }
AUDIT_T02_NEED_DRIVER = @test -n "$(AUDIT_DATABASE_LEASE_DRIVER)" || { echo 'set AUDIT_DATABASE_LEASE_DRIVER=/path/to/approved-load-driver' >&2; exit 2; }
AUDIT_SYNTHETIC_SENDER_NEED_CONFIRM = @test -n "$(AUDIT_SYNTHETIC_SENDER_CONFIRM)" || { echo 'set AUDIT_SYNTHETIC_SENDER_CONFIRM to the confirm string printed by preflight' >&2; exit 2; }
AUDIT_T02_NEED_BEARER = @test -n "$(AUDIT_T02_TOKEN_FILE)" || { echo 'set AUDIT_T02_TOKEN_FILE=/abs/path/outside/git (0700 dir)' >&2; exit 2; }
audit-t02-attended-window: ## One contained login: project, port-forward, T02 exercise, evidence, cleanup
$(AUDIT_T02_NEED_APPROVAL)
$(AUDIT_T02_NEED_DRIVER)
$(AUDIT_SYNTHETIC_SENDER_NEED_CONFIRM)
$(AUDIT_T02_NEED_BEARER)
@test -n "$(AUDIT_T02_EVIDENCE_DIR)" || { echo 'set AUDIT_T02_EVIDENCE_DIR=/abs/dir/outside/git (0700)' >&2; exit 2; }
$(ATTENDED_EXEC) scripts/t02-attended-window.py \
--approval "$(abspath $(AUDIT_DATABASE_LEASE_APPROVAL))" \
--load-driver "$(abspath $(AUDIT_DATABASE_LEASE_DRIVER))" \
--bearer-file "$(AUDIT_T02_TOKEN_FILE)" --evidence-dir "$(AUDIT_T02_EVIDENCE_DIR)" \
--confirm "$(AUDIT_SYNTHETIC_SENDER_CONFIRM)" $(if $(AUDIT_T02_PORT),--port $(AUDIT_T02_PORT)) \
$(if $(AUDIT_T02_MODE),--mode "$(AUDIT_T02_MODE)")
audit-database-lease-recovery-exercise: ## Exercise step alone, inside its own contained login (prefer audit-t02-attended-window)
$(AUDIT_T02_NEED_APPROVAL)
$(AUDIT_T02_NEED_DRIVER)
@test -n "$(AUDIT_T02_RESULT_FILE)" || { echo 'set AUDIT_T02_RESULT_FILE=/abs/new/file outside Git (the contained child must stay silent)' >&2; exit 2; }
$(ATTENDED_EXEC) scripts/audit-core-database-lease-recovery.py exercise --contained --result-file "$(AUDIT_T02_RESULT_FILE)" \
--approval "$(abspath $(AUDIT_DATABASE_LEASE_APPROVAL))" \
--load-driver "$(abspath $(AUDIT_DATABASE_LEASE_DRIVER))" \
audit-database-lease-recovery-exercise: ## Run the separately approved attended T02 exercise
@test -n "$(AUDIT_DATABASE_LEASE_APPROVAL)" || { echo 'set AUDIT_DATABASE_LEASE_APPROVAL=/path/to/approved-receipt.json' >&2; exit 2; }
@test -n "$(AUDIT_DATABASE_LEASE_DRIVER)" || { echo 'set AUDIT_DATABASE_LEASE_DRIVER=/path/to/approved-load-driver' >&2; exit 2; }
python3 scripts/audit-core-database-lease-recovery.py exercise \
--approval "$(AUDIT_DATABASE_LEASE_APPROVAL)" \
--load-driver "$(AUDIT_DATABASE_LEASE_DRIVER)" \
--confirm RAILIANCE-WP-0024-T02:attended
audit-synthetic-sender-test: ## Unit tests for the synthetic sender lane and T02 window (no network)
PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_audit_core_synthetic_sender_lane tests.test_t02_attended_window
audit-synthetic-sender-preflight: ## Read-only T02 sender gate; cluster checks only (registry metadata needs --contained)
$(AUDIT_T02_NEED_APPROVAL)
python3 scripts/audit_core_synthetic_sender_lane.py preflight --approval "$(AUDIT_DATABASE_LEASE_APPROVAL)" \
$(if $(AUDIT_T02_TOKEN_FILE),--bearer-file "$(AUDIT_T02_TOKEN_FILE)") $(AUDIT_SYNTHETIC_SENDER_PREFLIGHT_ARGS)
audit-synthetic-sender-project: ## Projection step alone, inside its own contained login (prefer audit-t02-attended-window)
$(AUDIT_T02_NEED_APPROVAL)
$(AUDIT_SYNTHETIC_SENDER_NEED_CONFIRM)
$(AUDIT_T02_NEED_BEARER)
@test -n "$(AUDIT_T02_RESULT_FILE)" || { echo 'set AUDIT_T02_RESULT_FILE=/abs/new/file outside Git (the contained child must stay silent)' >&2; exit 2; }
$(ATTENDED_EXEC) scripts/audit_core_synthetic_sender_lane.py project --contained --result-file "$(AUDIT_T02_RESULT_FILE)" \
--approval "$(abspath $(AUDIT_DATABASE_LEASE_APPROVAL))" \
--bearer-file "$(AUDIT_T02_TOKEN_FILE)" --confirm "$(AUDIT_SYNTHETIC_SENDER_CONFIRM)"
audit-synthetic-sender-cleanup: ## Idempotent cleanup inside a contained login (evidence or abort reason required)
$(AUDIT_T02_NEED_APPROVAL)
$(AUDIT_SYNTHETIC_SENDER_NEED_CONFIRM)
@test -n "$(AUDIT_T02_EVIDENCE)$(AUDIT_SYNTHETIC_SENDER_ABORT_REASON)" || { echo 'set AUDIT_T02_EVIDENCE=/path/to/exercise-result.json or AUDIT_SYNTHETIC_SENDER_ABORT_REASON=...' >&2; exit 2; }
@test -n "$(AUDIT_T02_RESULT_FILE)" || { echo 'set AUDIT_T02_RESULT_FILE=/abs/new/file outside Git (the contained child must stay silent)' >&2; exit 2; }
$(ATTENDED_EXEC) scripts/audit_core_synthetic_sender_lane.py cleanup --contained --result-file "$(AUDIT_T02_RESULT_FILE)" \
--approval "$(abspath $(AUDIT_DATABASE_LEASE_APPROVAL))" \
--confirm "$(AUDIT_SYNTHETIC_SENDER_CONFIRM)" \
$(if $(AUDIT_T02_TOKEN_FILE),--bearer-file "$(AUDIT_T02_TOKEN_FILE)") \
$(if $(AUDIT_T02_EVIDENCE),--exercise-evidence "$(abspath $(AUDIT_T02_EVIDENCE))") \
$(if $(AUDIT_SYNTHETIC_SENDER_ABORT_REASON),--abort-reason "$(AUDIT_SYNTHETIC_SENDER_ABORT_REASON)")
railiance01-reboot-preflight: ## Read-only gate for the coordinated single-node reboot
python3 scripts/audit-core-recovery-preflight.py node-reboot $(RAILIANCE01_REBOOT_PREFLIGHT_ARGS)
.PHONY: openbao-reboot-snapshot openbao-reboot-snapshot-test
openbao-reboot-snapshot: ## Attended Raft snapshot -> age -> verify -> offsite; silent child writes SNAPSHOT_RECEIPT
@test -n "$(SNAPSHOT_SCENARIO_ID)" || { echo 'set SNAPSHOT_SCENARIO_ID=<scenario id, e.g. RPF-WP-0015-T03-...>' >&2; exit 2; }
@test -n "$(SNAPSHOT_RECEIPT)" || { echo 'set SNAPSHOT_RECEIPT=/abs/new/file outside Git (the contained child must stay silent)' >&2; exit 2; }
@test ! -e "$(SNAPSHOT_RECEIPT)" || { echo 'SNAPSHOT_RECEIPT already exists' >&2; exit 2; }
@test -n "$(SNAPSHOT_CUSTODIAN_ESCROW_REF)" || { echo 'set SNAPSHOT_CUSTODIAN_ESCROW_REF=<where a decryption key is held outside OpenBao>' >&2; exit 2; }
env -u OPENBAO_TOKEN -u VAULT_TOKEN -u BAO_TOKEN \
$(ATTENDED_EXEC) scripts/openbao-reboot-snapshot.py \
--scenario-id "$(SNAPSHOT_SCENARIO_ID)" --receipt-file "$(abspath $(SNAPSHOT_RECEIPT))" \
$(if $(SNAPSHOT_PRIVATE_DIR),--private-dir "$(abspath $(SNAPSHOT_PRIVATE_DIR))") \
--custodian-escrow-ref "$(SNAPSHOT_CUSTODIAN_ESCROW_REF)" \
$(if $(SNAPSHOT_CUSTODIAN_RECIPIENT),--custodian-recipient "$(SNAPSHOT_CUSTODIAN_RECIPIENT)")
openbao-reboot-snapshot-test: ## Offline unit tests for the attended snapshot lane (no OpenBao, no network)
PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_openbao_reboot_snapshot
wp0024-owner-show: ## Show one owner's hash-bound review contract (set OWNER)
@test -n "$(OWNER)" || { echo 'set OWNER=audit-core|rapp-postgres|railiance-cluster|railiance-infra' >&2; exit 2; }
@python3 scripts/wp0024-owner-review.py show --owner "$(OWNER)"
@ -637,7 +560,7 @@ wp0024-t02-driver-register: ## Register committed audit-core driver source witho
@python3 scripts/wp0024-t02-driver-candidate.py register \
--repo "$(abspath ../audit-core)" --driver "$(DRIVER)" --reviewer "$(REVIEWER)"
.PHONY: audit-t02-attended-window audit-synthetic-sender-test audit-synthetic-sender-preflight audit-synthetic-sender-project audit-synthetic-sender-cleanup audit-whitehat-e2-preflight audit-whitehat-e2-project audit-whitehat-e2-cleanup audit-database-lease-recovery-preflight audit-database-lease-approval-check audit-database-lease-recovery-exercise railiance01-reboot-preflight wp0024-owner-show wp0024-owner-verify wp0024-owner-status wp0024-t02-driver-show wp0024-t02-driver-status wp0024-t02-driver-register apps-pg-isolation-probe
.PHONY: audit-whitehat-e2-preflight audit-whitehat-e2-project audit-whitehat-e2-cleanup audit-database-lease-recovery-preflight audit-database-lease-approval-check audit-database-lease-recovery-exercise railiance01-reboot-preflight wp0024-owner-show wp0024-owner-verify wp0024-owner-status wp0024-t02-driver-show wp0024-t02-driver-status wp0024-t02-driver-register apps-pg-isolation-probe
.PHONY: db-deploy db-status db-shell db-logs forgejo-db-deploy forgejo-db-status forgejo-db-shell apps-pg-deploy apps-pg-status apps-pg-shell apps-pg-logs net-kingdom-pg-inter-hub-networkpolicy-deploy pg-deploy pg-status pg-pgpool-check valkey-deploy valkey-status openbao-repo openbao-dry-run openbao-overlay-apply openbao-verify-login-overlay openbao-deploy openbao-status openbao-verify openbao-verify-post-unseal openbao-configure-initial openbao-configure-ssh openbao-verify-ssh openbao-verify-authenticated openbao-configure-external-secrets-issue-core openbao-configure-external-secrets-activity-core openbao-configure-external-secrets-forgejo openbao-validate-restore-evidence openbao-validate-emergency-evidence credential-grants-validate credential-change-applier-dry-run credential-change-applier-apply-plan credential-change-applier-apply credential-change-runbook credential-change-record-evidence credential-change-lifecycle-plan credential-change-lifecycle-event credential-change-import-inventory agent-high-risk-boundary-check openbao-credential-change-appliers-dry-run openbao-configure-credential-change-appliers openbao-token-grants-dry-run openbao-configure-token-grants openbao-verify-token-grants-dry-run openbao-verify-token-grants openbao-verify-token-grants-smoke credential-helper-dry-run credential-tests credential-exec-ops-warden-smoke argocd-bootstrap-dry-run argocd-bootstrap-deploy argocd-repo-apply argocd-status backup forgejo-backup forgejo-backup-dry-run forgejo-backup-status forgejo-package-prune forgejo-package-prune-dry-run reef-deployables apps-pg-evidence consumption-preflight help
##@ S3 service assurance (read-only)
@ -659,21 +582,3 @@ keycape-factor-tests: ## Validate factor custody, scope, renewal and config pres
.PHONY: keycape-factor-live-acceptance
keycape-factor-live-acceptance: ## Verify native scope and rotation (creates bounded proof/renewal Jobs)
PYTHONDONTWRITEBYTECODE=1 python3 scripts/keycape_factor_acceptance.py
# 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
##@ Service-identity metadata (NK-WP-0054-T04, RPF-WP-0067)
.PHONY: service-identity-metadata-tests service-identity-metadata-openbao service-identity-metadata-postgresql-sql
service-identity-metadata-tests: ## Offline fixture tests for the value-safe identity metadata exporter
PYTHONDONTWRITEBYTECODE=1 python3 -m unittest -v tests.test_service_identity_metadata_export
service-identity-metadata-openbao: ## Read-only OpenBao auth-role/policy export via the operator's bao login (OUT=file)
python3 scripts/service_identity_metadata_export.py --output "$${OUT:-openbao-identity-metadata.json}" openbao
service-identity-metadata-postgresql-sql: ## Print the read-only pg_roles/pg_auth_members SELECT
@python3 scripts/service_identity_metadata_export.py postgresql-sql

View file

@ -106,13 +106,6 @@ description: S3 custody, policy and approved runtime credential delivery; packag
keywords: [openbao, secrets, vault-compatible, secret-management, dynamic-credentials, audit, kubernetes-auth]
```
```capability
type: security
title: Supervised agent observer access
description: custodian-agents namespace and ServiceAccount with ClusterRole custodian-supervised-observer (curated read; no secrets, configmaps, logs, exec or writes) for founder-issued short-lived agent tokens, plus the OpenBao agt SSH role limited to permit-pty. Writes are routed through gated-executor approvals.
keywords: [rbac, serviceaccount, supervised-agents, read-only, openbao, ssh-role]
```
## Getting oriented
Read [AGENTS.md](AGENTS.md), [INTENT.md](INTENT.md), then
@ -161,41 +154,3 @@ remain RPF-WP-0038-T04.
The account cutover and archive-integrity fix do not establish that coverage.
WP-0029 retains secondary credential invalidation/recovery; its completion
must not be presented as full primary-backup assurance.
## Supervised agent access — 2026-10-20
RPF-WP-0062 adds the `custodian-agents` namespace, the `custodian-agent`
ServiceAccount and ClusterRole `custodian-supervised-observer`: curated read
only, with no secrets, configmaps, logs, exec or writes. It is synced and
pinned at `567d2c6`; the founder issues 24-hour tokens for it under
CUST-WP-0073. The OpenBao `agt` SSH role now allows only the `permit-pty`
extension (`e415da9`). `scripts/openbao-apply-ssh-engine.sh` previously never
wrote roles, because it passed `bao` twice and exited 127; it is fixed at `4b2b72a`.
Privileged actions for these agents go through gated-executor and not through
this role.
## Repository maintenance reuse
The tracked `.generated-work-records.json` adopts the Custodian staged-source
adapter for WORK-RECORDS.md. `make generated-work-records` uses the existing
State Hub renderer through `.generated-work-records.mk`; the shared pre-commit
hook runs this isolated recipe only for opted-in repositories. The corresponding
`make generated-work-records-check` is read-only. Canonical repository identity
is explicit and does not depend on the checkout directory.
Unstaged source changes are preserved; conflicts and unstaged generated-file
changes stop the commit. For `git commit --only`, generate first and include
WORK-RECORDS.md with the selected source paths. Existing generated-only guards
and exact-scope secret scans remain in place. This is repository maintenance,
not a runtime service, authorization decision, deployment or publication.
Other index generators and hub-derived briefs retain their owner boundaries.
## Local orientation brief
`.custodian-brief.md` is a generated local orientation snapshot. It remains on
disk for offline use and is root-ignored rather than versioned. Regenerate it
with the State Hub brief renderer; workplans and other authored repository
records remain authoritative. Brief refreshes must not create Git commits.

View file

@ -8,10 +8,7 @@
| Kind | ID | Status | Lane | Source |
| --- | --- | --- | --- | --- |
| workplan | RPF-WP-ADHOC-2026-10-10 | finished | — | workplans/ADHOC-2026-10-10.md |
| workplan | RPF-WP-ADHOC-2026-10-11 | finished | — | workplans/ADHOC-2026-10-11.md |
| workplan | RPF-WP-ADHOC-2026-10-19 | finished | — | workplans/ADHOC-2026-10-19.md |
| workplan | RPF-WP-0015 | active | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md |
| workplan | RPF-WP-0015 | blocked | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md |
| workplan | RPF-WP-0025 | finished | — | workplans/RPF-WP-0025-openbao-operator-only-access.md |
| workplan | RPF-WP-0027 | blocked | — | workplans/RPF-WP-0027-keycape-live-secret-exposure-recovery.md |
| workplan | RPF-WP-0029 | finished | — | workplans/RPF-WP-0029-backup-credential-default-removal.md |
@ -29,35 +26,10 @@
| workplan | RPF-WP-0047 | finished | — | workplans/RPF-WP-0047-bao-coulomb-social-notice-page.md |
| workplan | RPF-WP-0048 | blocked | — | workplans/RPF-WP-0048-activity-core-gitops-adoption.md |
| workplan | RPF-WP-0049 | finished | — | workplans/RPF-WP-0049-platform-root-custody-record-delivery.md |
| workplan | RPF-WP-0050 | finished | — | workplans/RPF-WP-0050-attended-provisioning-preparation.md |
| workplan | RPF-WP-0050 | blocked | — | workplans/RPF-WP-0050-attended-provisioning-preparation.md |
| workplan | RPF-WP-0051 | finished | — | workplans/RPF-WP-0051-informed-decision-requester-workload-delivery.md |
| workplan | RPF-WP-0052 | finished | — | workplans/RPF-WP-0052-authelia-platform-smtp-admission.md |
| workplan | RPF-WP-0053 | active | — | workplans/RPF-WP-0053-coulomb-paired-backup-admission.md |
| workplan | RPF-WP-0054 | finished | — | workplans/RPF-WP-0054-keycape-client-secret-generate-lane.md |
| workplan | RPF-WP-0055 | backlog | — | workplans/RPF-WP-0055-keycape-client-secret-rotation-residual.md |
| workplan | RPF-WP-0056 | blocked | — | workplans/RPF-WP-0056-playwright-consumer-admission.md |
| workplan | RPF-WP-0057 | active | — | workplans/RPF-WP-0057-sign-in-diagnosis-caller-proof.md |
| workplan | RPF-WP-0058 | active | — | workplans/RPF-WP-0058-reuse-private-source-reader.md |
| workplan | RPF-WP-0059 | active | — | workplans/RPF-WP-0059-hourly-cadence-receiver-custody.md |
| workplan | RPF-WP-0060 | active | — | workplans/RPF-WP-0060-browser-case-platform-bindings.md |
| workplan | RPF-WP-0061 | active | — | workplans/RPF-WP-0061-self-service-ccr-intake.md |
| workplan | RPF-WP-0062 | finished | — | workplans/RPF-WP-0062-custodian-agent-observer-identity.md |
| workplan | RPF-WP-0063 | finished | — | workplans/RPF-WP-0063-wave2-resource-right-sizing.md |
| workplan | RPF-WP-0064 | active | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| workplan | RPF-WP-0065 | proposed | — | workplans/RPF-WP-0065-netkingdom-role-claim-bindings.md |
| workplan | RPF-WP-0066 | proposed | — | workplans/RPF-WP-0066-openbao-durable-audit-snapshots-conformance.md |
| workplan | RPF-WP-0067 | active | — | workplans/RPF-WP-0067-netkingdom-identity-receipt-and-metadata-answers.md |
| workplan | RPF-WP-0068 | active | — | workplans/RPF-WP-0068-email-connect-platform-smtp.md |
| workplan | RPF-WP-0069 | active | — | workplans/RPF-WP-0069-bounded-executor-lanes.md |
| task | RPF-WP-ADHOC-2026-10-10-T01 | done | — | workplans/ADHOC-2026-10-10.md |
| task | RPF-WP-ADHOC-2026-10-11-T01 | done | — | workplans/ADHOC-2026-10-11.md |
| task | RPF-WP-ADHOC-2026-10-11-T02 | done | — | workplans/ADHOC-2026-10-11.md |
| task | RPF-WP-ADHOC-2026-10-11-T03 | done | — | workplans/ADHOC-2026-10-11.md |
| task | RPF-WP-ADHOC-2026-10-19-T01 | done | — | workplans/ADHOC-2026-10-19.md |
| task | RPF-WP-ADHOC-2026-10-19-T02 | done | — | workplans/ADHOC-2026-10-19.md |
| task | RPF-WP-ADHOC-2026-10-19-T03 | done | — | workplans/ADHOC-2026-10-19.md |
| task | RPF-WP-0015-T01 | done | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md |
| task | RPF-WP-0015-T02 | done | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md |
| task | RPF-WP-0015-T02 | wait | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md |
| task | RPF-WP-0015-T03 | wait | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md |
| task | RPF-WP-0015-T04 | done | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md |
| task | RPF-WP-0025-T01 | done | — | workplans/RPF-WP-0025-openbao-operator-only-access.md |
@ -77,15 +49,10 @@
| task | RPF-WP-0035-T03 | wait | — | workplans/RPF-WP-0035-credential-lane-implementation.md |
| task | RPF-WP-0035-T04 | done | — | workplans/RPF-WP-0035-credential-lane-implementation.md |
| task | RPF-WP-0035-T05 | done | — | workplans/RPF-WP-0035-credential-lane-implementation.md |
| task | RPF-WP-0035-T06 | done | — | workplans/RPF-WP-0035-credential-lane-implementation.md |
| task | RPF-WP-0035-T06 | wait | — | workplans/RPF-WP-0035-credential-lane-implementation.md |
| task | RPF-WP-0035-T07 | wait | — | workplans/RPF-WP-0035-credential-lane-implementation.md |
| task | RPF-WP-0035-T08 | wait | — | workplans/RPF-WP-0035-credential-lane-implementation.md |
| task | RPF-WP-0035-T09 | done | — | workplans/RPF-WP-0035-credential-lane-implementation.md |
| task | RPF-WP-0035-T10 | wait | — | workplans/RPF-WP-0035-credential-lane-implementation.md |
| task | RPF-WP-0035-T11 | done | — | workplans/RPF-WP-0035-credential-lane-implementation.md |
| task | RPF-WP-0035-T12 | progress | — | workplans/RPF-WP-0035-credential-lane-implementation.md |
| task | RPF-WP-0035-T13 | done | — | workplans/RPF-WP-0035-credential-lane-implementation.md |
| task | RPF-WP-0035-T14 | wait | — | workplans/RPF-WP-0035-credential-lane-implementation.md |
| task | RPF-WP-0035-T09 | wait | — | workplans/RPF-WP-0035-credential-lane-implementation.md |
| task | RPF-WP-0036-T01 | done | — | workplans/RPF-WP-0036-platform-service-assurance.md |
| task | RPF-WP-0036-T02 | done | — | workplans/RPF-WP-0036-platform-service-assurance.md |
| task | RPF-WP-0036-T03 | wait | — | workplans/RPF-WP-0036-platform-service-assurance.md |
@ -113,12 +80,12 @@
| task | RPF-WP-0043-T05 | done | — | workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md |
| task | RPF-WP-0044-T01 | done | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T02 | done | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T03 | done | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T04 | done | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T05 | done | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T03 | wait | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T04 | wait | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T05 | wait | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T06 | wait | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T07 | wait | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T08 | done | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T08 | wait | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0045-T01 | done | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md |
| task | RPF-WP-0045-T02 | done | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md |
| task | RPF-WP-0045-T03 | done | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md |
@ -144,95 +111,11 @@
| task | RPF-WP-0049-T05 | done | — | workplans/RPF-WP-0049-platform-root-custody-record-delivery.md |
| task | RPF-WP-0050-T01 | done | — | workplans/RPF-WP-0050-attended-provisioning-preparation.md |
| task | RPF-WP-0050-T02 | done | — | workplans/RPF-WP-0050-attended-provisioning-preparation.md |
| task | RPF-WP-0050-T03 | cancel | — | workplans/RPF-WP-0050-attended-provisioning-preparation.md |
| task | RPF-WP-0050-T03 | wait | — | workplans/RPF-WP-0050-attended-provisioning-preparation.md |
| task | RPF-WP-0050-T04 | done | — | workplans/RPF-WP-0050-attended-provisioning-preparation.md |
| task | RPF-WP-0051-T01 | done | — | workplans/RPF-WP-0051-informed-decision-requester-workload-delivery.md |
| task | RPF-WP-0051-T02 | done | — | workplans/RPF-WP-0051-informed-decision-requester-workload-delivery.md |
| task | RPF-WP-0051-T03 | done | — | workplans/RPF-WP-0051-informed-decision-requester-workload-delivery.md |
| task | RPF-WP-0052-T01 | done | — | workplans/RPF-WP-0052-authelia-platform-smtp-admission.md |
| task | RPF-WP-0052-T02 | done | — | workplans/RPF-WP-0052-authelia-platform-smtp-admission.md |
| task | RPF-WP-0052-T03 | done | — | workplans/RPF-WP-0052-authelia-platform-smtp-admission.md |
| task | RPF-WP-0053-T01 | done | — | workplans/RPF-WP-0053-coulomb-paired-backup-admission.md |
| task | RPF-WP-0053-T02 | progress | — | workplans/RPF-WP-0053-coulomb-paired-backup-admission.md |
| task | RPF-WP-0053-T03 | progress | — | workplans/RPF-WP-0053-coulomb-paired-backup-admission.md |
| task | RPF-WP-0053-T04 | todo | — | workplans/RPF-WP-0053-coulomb-paired-backup-admission.md |
| task | RPF-WP-0053-T05 | progress | — | workplans/RPF-WP-0053-coulomb-paired-backup-admission.md |
| task | RPF-WP-0053-T06 | done | — | workplans/RPF-WP-0053-coulomb-paired-backup-admission.md |
| task | RPF-WP-0054-T01 | done | — | workplans/RPF-WP-0054-keycape-client-secret-generate-lane.md |
| task | RPF-WP-0054-T02 | done | — | workplans/RPF-WP-0054-keycape-client-secret-generate-lane.md |
| task | RPF-WP-0054-T03 | done | — | workplans/RPF-WP-0054-keycape-client-secret-generate-lane.md |
| task | RPF-WP-0054-T04 | done | — | workplans/RPF-WP-0054-keycape-client-secret-generate-lane.md |
| task | RPF-WP-0054-T05 | done | — | workplans/RPF-WP-0054-keycape-client-secret-generate-lane.md |
| task | RPF-WP-0054-T06 | done | — | workplans/RPF-WP-0054-keycape-client-secret-generate-lane.md |
| task | RPF-WP-0054-T07 | cancel | — | workplans/RPF-WP-0054-keycape-client-secret-generate-lane.md |
| task | RPF-WP-0055-T01 | todo | — | workplans/RPF-WP-0055-keycape-client-secret-rotation-residual.md |
| task | RPF-WP-0056-T01 | done | — | workplans/RPF-WP-0056-playwright-consumer-admission.md |
| task | RPF-WP-0056-T02 | done | — | workplans/RPF-WP-0056-playwright-consumer-admission.md |
| task | RPF-WP-0056-T03 | wait | — | workplans/RPF-WP-0056-playwright-consumer-admission.md |
| task | RPF-WP-0056-T04 | done | — | workplans/RPF-WP-0056-playwright-consumer-admission.md |
| task | RPF-WP-0056-T05 | done | — | workplans/RPF-WP-0056-playwright-consumer-admission.md |
| task | RPF-WP-0056-T06 | done | — | workplans/RPF-WP-0056-playwright-consumer-admission.md |
| task | RPF-WP-0057-T01 | done | — | workplans/RPF-WP-0057-sign-in-diagnosis-caller-proof.md |
| task | RPF-WP-0057-T02 | done | — | workplans/RPF-WP-0057-sign-in-diagnosis-caller-proof.md |
| task | RPF-WP-0057-T03 | done | — | workplans/RPF-WP-0057-sign-in-diagnosis-caller-proof.md |
| task | RPF-WP-0057-T04 | progress | — | workplans/RPF-WP-0057-sign-in-diagnosis-caller-proof.md |
| task | RPF-WP-0058-T01 | done | — | workplans/RPF-WP-0058-reuse-private-source-reader.md |
| task | RPF-WP-0058-T02 | done | — | workplans/RPF-WP-0058-reuse-private-source-reader.md |
| task | RPF-WP-0058-T03 | done | — | workplans/RPF-WP-0058-reuse-private-source-reader.md |
| task | RPF-WP-0058-T04 | done | — | workplans/RPF-WP-0058-reuse-private-source-reader.md |
| task | RPF-WP-0058-T05 | wait | — | workplans/RPF-WP-0058-reuse-private-source-reader.md |
| task | RPF-WP-0059-T01 | done | — | workplans/RPF-WP-0059-hourly-cadence-receiver-custody.md |
| task | RPF-WP-0059-T02 | done | — | workplans/RPF-WP-0059-hourly-cadence-receiver-custody.md |
| task | RPF-WP-0059-T03 | done | — | workplans/RPF-WP-0059-hourly-cadence-receiver-custody.md |
| task | RPF-WP-0059-T04 | progress | — | workplans/RPF-WP-0059-hourly-cadence-receiver-custody.md |
| task | RPF-WP-0060-T01 | progress | — | workplans/RPF-WP-0060-browser-case-platform-bindings.md |
| task | RPF-WP-0060-T02 | done | — | workplans/RPF-WP-0060-browser-case-platform-bindings.md |
| task | RPF-WP-0060-T03 | todo | — | workplans/RPF-WP-0060-browser-case-platform-bindings.md |
| task | RPF-WP-0060-T04 | todo | — | workplans/RPF-WP-0060-browser-case-platform-bindings.md |
| task | RPF-WP-0061-T01 | done | — | workplans/RPF-WP-0061-self-service-ccr-intake.md |
| task | RPF-WP-0061-T02 | done | — | workplans/RPF-WP-0061-self-service-ccr-intake.md |
| task | RPF-WP-0061-T03 | done | — | workplans/RPF-WP-0061-self-service-ccr-intake.md |
| task | RPF-WP-0061-T04 | done | — | workplans/RPF-WP-0061-self-service-ccr-intake.md |
| task | RPF-WP-0061-T05 | wait | — | workplans/RPF-WP-0061-self-service-ccr-intake.md |
| task | RPF-WP-0062-T01 | done | — | workplans/RPF-WP-0062-custodian-agent-observer-identity.md |
| task | RPF-WP-0062-T02 | done | — | workplans/RPF-WP-0062-custodian-agent-observer-identity.md |
| task | RPF-WP-0062-T03 | done | — | workplans/RPF-WP-0062-custodian-agent-observer-identity.md |
| task | RPF-WP-0063-T01 | done | — | workplans/RPF-WP-0063-wave2-resource-right-sizing.md |
| task | RPF-WP-0064-T01 | done | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-0064-T02 | done | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-0064-T03 | done | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-0064-T04 | done | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-0064-T05 | wait | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-0064-T06 | done | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-0064-T07 | done | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-0064-T08 | done | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-0064-T09 | todo | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-0064-T10 | done | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-0065-T01 | done | — | workplans/RPF-WP-0065-netkingdom-role-claim-bindings.md |
| task | RPF-WP-0065-T02 | done | — | workplans/RPF-WP-0065-netkingdom-role-claim-bindings.md |
| task | RPF-WP-0065-T03 | done | — | workplans/RPF-WP-0065-netkingdom-role-claim-bindings.md |
| task | RPF-WP-0065-T04 | done | — | workplans/RPF-WP-0065-netkingdom-role-claim-bindings.md |
| task | RPF-WP-0065-T05 | wait | — | workplans/RPF-WP-0065-netkingdom-role-claim-bindings.md |
| task | RPF-WP-0066-T01 | todo | — | workplans/RPF-WP-0066-openbao-durable-audit-snapshots-conformance.md |
| task | RPF-WP-0066-T02 | wait | — | workplans/RPF-WP-0066-openbao-durable-audit-snapshots-conformance.md |
| task | RPF-WP-0066-T03 | todo | — | workplans/RPF-WP-0066-openbao-durable-audit-snapshots-conformance.md |
| task | RPF-WP-0066-T04 | wait | — | workplans/RPF-WP-0066-openbao-durable-audit-snapshots-conformance.md |
| task | RPF-WP-0066-T05 | wait | — | workplans/RPF-WP-0066-openbao-durable-audit-snapshots-conformance.md |
| task | RPF-WP-0066-T06 | todo | — | workplans/RPF-WP-0066-openbao-durable-audit-snapshots-conformance.md |
| task | RPF-WP-0067-T01 | done | — | workplans/RPF-WP-0067-netkingdom-identity-receipt-and-metadata-answers.md |
| task | RPF-WP-0067-T02 | done | — | workplans/RPF-WP-0067-netkingdom-identity-receipt-and-metadata-answers.md |
| task | RPF-WP-0067-T03 | wait | — | workplans/RPF-WP-0067-netkingdom-identity-receipt-and-metadata-answers.md |
| task | RPF-WP-0068-T01 | done | — | workplans/RPF-WP-0068-email-connect-platform-smtp.md |
| task | RPF-WP-0068-T02 | done | — | workplans/RPF-WP-0068-email-connect-platform-smtp.md |
| task | RPF-WP-0068-T03 | wait | — | workplans/RPF-WP-0068-email-connect-platform-smtp.md |
| task | RPF-WP-0068-T04 | todo | — | workplans/RPF-WP-0068-email-connect-platform-smtp.md |
| task | RPF-WP-0069-T01 | done | — | workplans/RPF-WP-0069-bounded-executor-lanes.md |
| task | RPF-WP-0069-T02 | done | — | workplans/RPF-WP-0069-bounded-executor-lanes.md |
| task | RPF-WP-0069-T03 | wait | — | workplans/RPF-WP-0069-bounded-executor-lanes.md |
| task | RPF-WP-0069-T04 | done | — | workplans/RPF-WP-0069-bounded-executor-lanes.md |
| task | RPF-WP-0069-T05 | wait | — | workplans/RPF-WP-0069-bounded-executor-lanes.md |
| task | RPF-WP-0069-T06 | cancel | — | workplans/RPF-WP-0069-bounded-executor-lanes.md |
| task | RPF-WP-0069-T07 | todo | — | workplans/RPF-WP-0069-bounded-executor-lanes.md |
| intake | RPF-IN-0001 | closed | — | intakes/intakes.md |
| intake | RPF-IN-0002 | open | — | intakes/intakes.md |
| intake | RPF-IN-0003 | open | — | intakes/intakes.md |

View file

@ -0,0 +1,35 @@
# Static notice for bao.coulomb.social (RPF-WP-0047). Serves one page; proxies nothing.
server {
listen 8080;
server_name _;
server_tokens off;
root /usr/share/nginx/html;
add_header Content-Security-Policy "default-src 'none'; style-src 'unsafe-inline'; frame-ancestors 'none'" always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer" always;
add_header Cache-Control "no-store" always;
add_header X-Robots-Tag "noindex, nofollow" always;
location = / {
try_files /index.html =404;
}
location = /robots.txt {
default_type text/plain;
return 200 "User-agent: *\nDisallow: /\n";
}
location = /healthz {
access_log off;
default_type text/plain;
return 200 "ok\n";
}
# Any other path (e.g. /v1/sys/health, /ui/) is 404 with the same notice, never 200.
location / {
error_page 404 /index.html;
return 404;
}
location = /index.html {
internal;
}
}

View file

@ -0,0 +1,19 @@
<!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>Not publicly available</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 36rem; margin: 15vh auto; padding: 0 1rem; color: #222; background: #fff; line-height: 1.5; }
@media (prefers-color-scheme: dark) { body { color: #ddd; background: #111; } }
h1 { font-size: 1.4rem; }
</style>
</head>
<body>
<h1>Not publicly available</h1>
<p>This address belongs to an internal service that is not offered publicly.</p>
<p>There is nothing to sign in to here. If you were sent to this address, please contact the person who gave it to you.</p>
</body>
</html>

View file

@ -0,0 +1,18 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# RPF-WP-0047. Read only by argocd/railiance01/applications/bao-notice.application.yaml.
resources:
- manifests.yaml
generatorOptions:
disableNameSuffixHash: false
configMapGenerator:
- name: bao-notice-conf
namespace: bao-notice
files: [default.conf]
- name: bao-notice-html
namespace: bao-notice
files: [index.html]

View file

@ -0,0 +1,177 @@
# RPF-WP-0047: static "not publicly available" notice for bao.coulomb.social.
# OpenBao stays non-public (RMASTER-WP-0020-T09). This namespace has no route
# to OpenBao: ingress only from Traefik, no egress at all.
apiVersion: v1
kind: Namespace
metadata:
name: bao-notice
labels:
app.kubernetes.io/part-of: railiance-platform
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: bao-notice-isolation
namespace: bao-notice
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
app.kubernetes.io/name: traefik
ports:
- {protocol: TCP, port: 8080}
egress: []
---
# cert-manager HTTP-01 solver pods run in this namespace on 8089 during
# issuance and renewal; Traefik must reach them or the challenge gets 502.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: bao-notice-acme-solver
namespace: bao-notice
spec:
podSelector:
matchLabels:
acme.cert-manager.io/http01-solver: "true"
policyTypes: [Ingress]
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
app.kubernetes.io/name: traefik
ports:
- {protocol: TCP, port: 8089}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: bao-notice
namespace: bao-notice
labels:
app.kubernetes.io/name: bao-notice
spec:
replicas: 1
revisionHistoryLimit: 2
selector:
matchLabels:
app.kubernetes.io/name: bao-notice
template:
metadata:
labels:
app.kubernetes.io/name: bao-notice
spec:
automountServiceAccountToken: false
enableServiceLinks: false
securityContext:
runAsNonRoot: true
runAsUser: 101
runAsGroup: 101
seccompProfile:
type: RuntimeDefault
containers:
- name: nginx
image: nginxinc/nginx-unprivileged@sha256:65e3e85dbaed8ba248841d9d58a899b6197106c23cb0ff1a132b7bfe0547e4c0
ports:
- {name: http, containerPort: 8080}
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
resources:
requests: {cpu: 5m, memory: 16Mi}
limits: {cpu: 100m, memory: 64Mi}
readinessProbe:
httpGet: {path: /healthz, port: http}
periodSeconds: 20
livenessProbe:
httpGet: {path: /healthz, port: http}
periodSeconds: 60
volumeMounts:
- {name: conf, mountPath: /etc/nginx/conf.d, readOnly: true}
- {name: html, mountPath: /usr/share/nginx/html, readOnly: true}
- {name: tmp, mountPath: /tmp}
volumes:
- name: conf
configMap:
name: bao-notice-conf
items: [{key: default.conf, path: default.conf}]
- name: html
configMap:
name: bao-notice-html
items: [{key: index.html, path: index.html}]
- name: tmp
emptyDir: {sizeLimit: 16Mi}
---
apiVersion: v1
kind: Service
metadata:
name: bao-notice
namespace: bao-notice
spec:
selector:
app.kubernetes.io/name: bao-notice
ports:
- {name: http, port: 80, targetPort: http}
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: redirect-https
namespace: bao-notice
spec:
redirectScheme:
scheme: https
permanent: true
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: bao-notice
namespace: bao-notice
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: [bao.coulomb.social]
secretName: bao-notice-tls
rules:
- host: bao.coulomb.social
http:
paths:
- path: /
pathType: Prefix
backend:
service: {name: bao-notice, port: {number: 80}}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: bao-notice-http-redirect
namespace: bao-notice
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
traefik.ingress.kubernetes.io/router.middlewares: bao-notice-redirect-https@kubernetescrd
traefik.ingress.kubernetes.io/router.priority: "1"
spec:
ingressClassName: traefik
rules:
- host: bao.coulomb.social
http:
paths:
- path: /
pathType: Prefix
backend:
service: {name: bao-notice, port: {number: 80}}

View file

@ -1,53 +0,0 @@
# Curated read-only observer (founder decision 2026-10-20, CUST-WP-0073-T02).
# Verbs: get/list/watch only. Explicitly excluded: secrets, configmaps,
# pods/log, pods/exec, pods/attach, pods/portforward, serviceaccounts/token,
# nodes/proxy, any write verb, any wildcard. Privileged actions go through the
# separate gated executor; nothing here grants writes.
# Enforced offline by tests/test_custodian_agent_rbac.py.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: custodian-supervised-observer
labels:
app.kubernetes.io/part-of: railiance-gitops
railiance-platform/component: custodian-agent-identity
rules:
- apiGroups: [""]
resources:
- namespaces
- nodes
- pods
- services
- endpoints
- events
- persistentvolumeclaims
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources:
- deployments
- statefulsets
- daemonsets
- replicasets
verbs: ["get", "list", "watch"]
- apiGroups: ["discovery.k8s.io"]
resources: ["endpointslices"]
verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list", "watch"]
- apiGroups: ["events.k8s.io"]
resources: ["events"]
verbs: ["get", "list", "watch"]
- apiGroups: ["argoproj.io"]
resources:
- applications
- appprojects
verbs: ["get", "list", "watch"]
- apiGroups: ["postgresql.cnpg.io"]
resources:
- clusters
- backups
- scheduledbackups
verbs: ["get", "list", "watch"]
- nonResourceURLs: ["/version", "/healthz", "/livez", "/readyz"]
verbs: ["get"]

View file

@ -1,15 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: custodian-agent-supervised-observer
labels:
app.kubernetes.io/part-of: railiance-gitops
railiance-platform/component: custodian-agent-identity
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: custodian-supervised-observer
subjects:
- kind: ServiceAccount
name: custodian-agent
namespace: custodian-agents

View file

@ -1,14 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# RPF-WP-0062 / CUST-WP-0073-T02. Read only by
# argocd/railiance01/applications/custodian-agents.application.yaml.
# Read-only observer identity for supervised custodian agents. No Secret, no
# static token, no write verb. Credentials are short-lived TokenRequest tokens
# issued by the supervisor (see workplans/RPF-WP-0062-custodian-agent-observer-identity.md).
resources:
- namespace.yaml
- serviceaccount.yaml
- clusterrole.yaml
- clusterrolebinding.yaml

View file

@ -1,7 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: custodian-agents
labels:
app.kubernetes.io/part-of: railiance-gitops
railiance-platform/component: custodian-agent-identity

View file

@ -1,11 +0,0 @@
# No token Secret is declared for this ServiceAccount, and none may be added.
# Tokens are issued per session by the supervisor via TokenRequest.
apiVersion: v1
kind: ServiceAccount
metadata:
name: custodian-agent
namespace: custodian-agents
labels:
app.kubernetes.io/part-of: railiance-gitops
railiance-platform/component: custodian-agent-identity
automountServiceAccountToken: false

View file

@ -1,99 +0,0 @@
# custodian-allocation-review (CUST-WP-0071-T05) — prepared, not deployed
Weekly workload-demand and allocation assessment for railiance01, selected by
the founder on 2026-10-21 (the-custodian
`docs/decisions/2026-10-21-cust-wp-0071-delivery-interview.md`). Not
referenced by any Application. The draft Application is in
`argocd/railiance01/drafts/`. The CronJob is `suspend: true`.
| Item | Value |
|---|---|
| Executor owner | railiance-platform (platform-addons, ArgoCD `railiance-platform-addons` project) |
| Assessment/report owner | the-custodian (CUST-WP-0071-T05); code in `the-custodian/scripts/weekly_allocation_incluster.py` |
| Schedule | `0 8 * * 1`, `timeZone: Europe/Berlin` (Monday 08:00), `concurrencyPolicy: Forbid`, `startingDeadlineSeconds: 21600` |
| Telemetry window | 7d ending Monday 06:00 UTC of the run's ISO week (DST-independent, so consecutive weeks are comparable) |
| Image | `python:3.12-slim` pinned by digest (same as eso-token-renewer); code and data come from a ConfigMap |
| Identity | SA `custodian-assessment/custodian-allocation-review`, ClusterRole `custodian-allocation-review-reader` |
| RBAC | get/list on core `nodes`, `pods`, `persistentvolumeclaims`, `persistentvolumes`; `storage.k8s.io/storageclasses`; `metrics.k8s.io/nodes,pods`. No watch, no writes, no secrets, configmaps, logs, exec or proxy |
| Prometheus | `http://telemetry-prometheus.telemetry.svc:9090`, plain HTTP, no credential |
| Delivery | One State Hub message per newly retained week, `custodian-allocation-review` → `the-custodian`, sent to `STATE_HUB_URL=http://state-hub.state-hub.svc:8000`. The message id is recorded in `receipts.jsonl` |
| Network | `networkpolicy.yaml`: the namespace denies all ingress and egress by default. Egress is allowed only to DNS (kube-system/kube-dns 53 UDP/TCP), kube-apiserver (10.43.0.1:443 and k3s endpoint 92.205.62.239:6443), Prometheus (telemetry, 9090) and the State Hub API (state-hub `app=state-hub`, 8000) |
| Storage | PVC `custodian-allocation-review-data` (local-path, 1Gi, ArgoCD `Prune=false,Delete=false`): `reports/<YYYY>-W<ww>.json`, `runs/<week>/<attempt>/` inputs, append-only `receipts.jsonl` |
| Resources | requests 20m/96Mi, limits 500m/256Mi, `activeDeadlineSeconds: 900`, `backoffLimit: 2` |
## Behaviour
- Writes only to the PVC and the one hub message per retained week. It never
resizes anything or creates tasks. The next Custodian session reviews the
message and records owner-repo tasks (ADR-001).
- Idempotency: an existing report for the week means the run does nothing and
succeeds. A failed attempt writes a `failed` receipt and no report, so the Job
retry or a manual Job collects again.
- Missed weeks: each retained receipt lists `missed_weeks` since the previous
report. Comparison across a gap stays unknown.
- Delivery is idempotent. A week that already has a message id is never
reposted. A failed post exits 1, and the Job retry posts for the
already-retained week.
- Missed and failed runs are caught by the-custodian's daily security flow
(`scripts/weekly_allocation_check.py`). It flags a failed latest receipt, or
a missing report for the due ISO week after Monday 12:00 Europe/Berlin. It
stays inactive until `CUSTODIAN_ALLOCATION_RECEIPTS=hub` is set.
- Workload rows remain `investigate`. The queries are namespace-level because
kube-state-metrics does not export `kube_pod_labels`.
## Vendored sources
`src/` is generated by `scripts/vendor_custodian_allocation_review.py` from
clean owner commits, and `vendor.lock.json` records each commit and sha256.
`--check` verifies them, and `tests/test_custodian_allocation_review.py`
enforces the check offline. To change the job code, commit in the owner repo
and re-vendor. Never edit `src/` by hand.
## Activation (founder go-ahead required; after CUST-WP-0071-T04 completes)
1. Review this directory and the vendored sources. Re-vendor if the owner
repositories have moved on, and keep `--check` passing.
2. rapp-telemetry:
- Merge `helm/values-railiance01-retention.pending.yaml` (10d / 10GB) into
`helm/values-railiance01.yaml` and run the attended helm upgrade.
- Apply `manifests/custodian-assessment-prometheus-ingress.pending.yaml`.
Without it, `telemetry-isolation` blocks port 9090 and every query
records a transport error.
3. Widen the ArgoCD project. This commit already adds `PersistentVolumeClaim`
and `networking.k8s.io/NetworkPolicy` to the `namespaceResourceWhitelist` in
`argocd/railiance01/bootstrap/02-railiance-platform-addons-project.yaml`.
The bootstrap is applied by hand, so apply that AppProject during the
change window.
4. Set `targetRevision` in the draft Application to the reviewed commit. Move
the draft to `argocd/railiance01/applications/`, run
`make argocd-pin-drift`, then hand-sync `railiance-apps-root` and then
`custodian-allocation-review`.
5. Acceptance run, still suspended:
`kubectl -n custodian-assessment create job --from=cronjob/custodian-allocation-review accept-1`.
Check that `receipts.jsonl` shows `retained`, `failed_queries: []`,
`delivery: posted` with a `message_id`, and that the hub message reached
`the-custodian`. Run it on a Monday, because it claims that ISO week's
report slot.
6. Activate the daily missed/failed-run check: add
`Environment=CUSTODIAN_ALLOCATION_RECEIPTS=hub` to the-custodian
`infra/security-flow/custodian-security-flow.service`. Exercise it once by
leaving a week without a report, or by pointing `--allocation-receipts` at a
failed receipt.
7. Unsuspend through a reviewed commit (`suspend: false`) and a new
`targetRevision`, not a live patch.
## Rollback
- Stop: commit `suspend: true` and sync. In an emergency, run
`kubectl -n custodian-assessment patch cronjob custodian-allocation-review -p '{"spec":{"suspend":true}}'`,
then commit the same change.
- Remove: delete the Application (it has no finalizer, so the resources stay),
then delete the CronJob, RBAC, NetworkPolicies and ConfigMap. Keep the PVC unless the history
is explicitly abandoned, because local-path deletes the data with the claim.
## Still open before T05 can be accepted
These items are not provided here: an explicit owner acknowledgment beyond
the hub message being read, how owner reviews reach `/data/reviews.json`, and
workload-level metrics. Reports stay at namespace level; the founder declined a
pod-label allowlist for now.

View file

@ -1,82 +0,0 @@
# CUST-WP-0071-T05: weekly workload-demand and allocation assessment.
# Founder decision 2026-10-21 (the-custodian
# docs/decisions/2026-10-21-cust-wp-0071-delivery-interview.md): in-cluster
# CronJob, Monday 08:00 Europe/Berlin, scoped read-only ServiceAccount,
# persisted weekly aggregates. suspend: true until review and T04 completion;
# unsuspend only by a reviewed commit (see README.md).
apiVersion: batch/v1
kind: CronJob
metadata:
name: custodian-allocation-review
namespace: custodian-assessment
labels:
app.kubernetes.io/name: custodian-allocation-review
app.kubernetes.io/part-of: railiance-platform
spec:
schedule: '0 8 * * 1'
timeZone: Europe/Berlin
suspend: true
concurrencyPolicy: Forbid
# Late starts lose coverage at the 7-day retention edge; after 6h the week
# is left to the next run, whose receipt lists it under missed_weeks.
startingDeadlineSeconds: 21600
successfulJobsHistoryLimit: 4
failedJobsHistoryLimit: 4
jobTemplate:
spec:
activeDeadlineSeconds: 900
backoffLimit: 2
ttlSecondsAfterFinished: 1209600
template:
metadata:
labels:
app.kubernetes.io/name: custodian-allocation-review
spec:
serviceAccountName: custodian-allocation-review
# Token needed for the read-only API calls; the SA itself does not
# automount, so only this pod template receives it.
automountServiceAccountToken: true
restartPolicy: Never
securityContext:
runAsNonRoot: true
runAsUser: 65534
runAsGroup: 65534
fsGroup: 65534
seccompProfile:
type: RuntimeDefault
containers:
- name: assess
image: python:3.12-slim@sha256:d764629ce0ddd8c71fd371e9901efb324a95789d2315a47db7e4d27e78f1b0e9
command: [python3, /app/weekly_allocation_incluster.py]
env:
- {name: PYTHONDONTWRITEBYTECODE, value: '1'}
- {name: ASSESSMENT_DATA_DIR, value: /data}
- {name: ASSESSMENT_SOURCE_DIR, value: /app}
- {name: ASSESSMENT_OBSERVER, value: /app/observe_cluster_resources.py}
- {name: ASSESSMENT_QUERIES, value: /app/weekly-allocation-queries.json}
- {name: ASSESSMENT_OWNERSHIP, value: /app/namespace-ownership.json}
- {name: ASSESSMENT_INVENTORY, value: /app/reef-railiance-k3s.json}
- {name: ASSESSMENT_REVIEWS, value: /data/reviews.json}
- {name: ASSESSMENT_EXECUTOR, value: railiance-platform/custodian-assessment/cronjob/custodian-allocation-review}
- {name: PROMETHEUS_URL, value: 'http://telemetry-prometheus.telemetry.svc:9090'}
# One message per newly retained week (custodian-allocation-review -> the-custodian).
- {name: STATE_HUB_URL, value: 'http://state-hub.state-hub.svc:8000'}
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
resources:
requests: {cpu: 20m, memory: 96Mi}
limits: {cpu: 500m, memory: 256Mi}
volumeMounts:
- {name: source, mountPath: /app, readOnly: true}
- {name: data, mountPath: /data}
- {name: tmp, mountPath: /tmp}
volumes:
- name: source
configMap: {name: custodian-allocation-review-source, defaultMode: 0444}
- name: data
persistentVolumeClaim: {claimName: custodian-allocation-review-data}
- name: tmp
emptyDir: {sizeLimit: 16Mi}

View file

@ -1,28 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# CUST-WP-0071-T05. NOT referenced by any Application yet: the draft lives in
# argocd/railiance01/drafts/custodian-allocation-review.application.yaml.
# src/ is vendored from owner repos (the-custodian, railiance-cluster,
# resource-control); vendor.lock.json pins the source commits and sha256.
# Refresh with tools/vendor_custodian_allocation_review.py, never by hand.
namespace: custodian-assessment
resources:
- namespace.yaml
- rbac.yaml
- pvc.yaml
- networkpolicy.yaml
- cronjob.yaml
configMapGenerator:
- name: custodian-allocation-review-source
files:
- src/weekly_allocation_incluster.py
- src/weekly_allocation_report.py
- src/reconcile_railiance_allocation.py
- src/observe_cluster_resources.py
- src/weekly-allocation-queries.json
- src/namespace-ownership.json
- src/reef-railiance-k3s.json

View file

@ -1,9 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: custodian-assessment
labels:
app.kubernetes.io/part-of: railiance-gitops
railiance-platform/component: custodian-allocation-review
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest

View file

@ -1,70 +0,0 @@
# CUST-WP-0071-T05 (founder decision 2026-10-21): default-deny egress and no
# ingress for the custodian-assessment namespace. The job may reach only:
# DNS kube-system/kube-dns (CoreDNS) 53 UDP/TCP
# kube-apiserver 10.43.0.1:443 (the `kubernetes` Service) and its k3s
# endpoint 92.205.62.239:6443 (same pair as rapp-telemetry's
# telemetry-isolation, since k3s may match either side of DNAT)
# Prometheus telemetry/prometheus pods 9090
# State Hub API state-hub/state-hub pods 8000 (the Service listens on 8000)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: custodian-assessment-default-deny
namespace: custodian-assessment
labels:
app.kubernetes.io/name: custodian-allocation-review
app.kubernetes.io/part-of: railiance-platform
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: custodian-allocation-review-egress
namespace: custodian-assessment
labels:
app.kubernetes.io/name: custodian-allocation-review
app.kubernetes.io/part-of: railiance-platform
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: custodian-allocation-review
policyTypes: [Egress]
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- {protocol: UDP, port: 53}
- {protocol: TCP, port: 53}
- to:
- ipBlock: {cidr: 10.43.0.1/32}
ports:
- {protocol: TCP, port: 443}
- to:
- ipBlock: {cidr: 92.205.62.239/32}
ports:
- {protocol: TCP, port: 6443}
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: telemetry
podSelector:
matchLabels:
app.kubernetes.io/name: prometheus
ports:
- {protocol: TCP, port: 9090}
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: state-hub
podSelector:
matchLabels:
app: state-hub
ports:
- {protocol: TCP, port: 8000}

View file

@ -1,21 +0,0 @@
# Weekly reports, per-attempt inputs and receipts.jsonl outlive the 7-day
# Prometheus retention here. local-path reclaimPolicy is Delete, so ArgoCD must
# never prune or delete this claim: removing it destroys the history.
# Requires PersistentVolumeClaim in the railiance-platform-addons AppProject
# namespaceResourceWhitelist before the Application can sync (activation step).
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: custodian-allocation-review-data
namespace: custodian-assessment
labels:
app.kubernetes.io/name: custodian-allocation-review
app.kubernetes.io/part-of: railiance-platform
annotations:
argocd.argoproj.io/sync-options: Prune=false,Delete=false
spec:
accessModes: [ReadWriteOnce]
storageClassName: local-path
resources:
requests:
storage: 1Gi

View file

@ -1,49 +0,0 @@
# CUST-WP-0071-T05 (founder decision 2026-10-21): read-only, scoped to exactly
# the reads made by weekly_allocation_incluster.py (see its API_PATHS and
# METRICS_PATHS). Verbs get/list only; no watch, no write verb, no wildcard,
# no secrets, configmaps, logs, exec, proxy or token subresources. Prometheus
# access is network-level (rapp-telemetry NetworkPolicy), not RBAC.
# Enforced offline by tests/test_custodian_allocation_review.py.
apiVersion: v1
kind: ServiceAccount
metadata:
name: custodian-allocation-review
namespace: custodian-assessment
labels:
app.kubernetes.io/name: custodian-allocation-review
app.kubernetes.io/part-of: railiance-platform
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: custodian-allocation-review-reader
labels:
app.kubernetes.io/name: custodian-allocation-review
app.kubernetes.io/part-of: railiance-platform
rules:
- apiGroups: [""]
resources: [nodes, pods, persistentvolumeclaims, persistentvolumes]
verbs: [get, list]
- apiGroups: [storage.k8s.io]
resources: [storageclasses]
verbs: [get, list]
- apiGroups: [metrics.k8s.io]
resources: [nodes, pods]
verbs: [get, list]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: custodian-allocation-review-reader
labels:
app.kubernetes.io/name: custodian-allocation-review
app.kubernetes.io/part-of: railiance-platform
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: custodian-allocation-review-reader
subjects:
- kind: ServiceAccount
name: custodian-allocation-review
namespace: custodian-assessment

View file

@ -1,205 +0,0 @@
{
"host": {
"cluster_resource_id": "resource:railiance:reef-railiance:k3s",
"owner": "railiance-cluster",
"resource_id": "resource:hosteurope:railiance01",
"same_cpus_as_cluster": true
},
"namespaces": {
"activity-core": {
"owner": "activity-core",
"service": "activity-core",
"tenant": "unknown"
},
"approval-engine": {
"owner": "approval-engine",
"service": "approval-engine",
"tenant": "unknown"
},
"argocd": {
"owner": "railiance-enablement",
"service": "argocd",
"tenant": "unknown"
},
"audit-core": {
"owner": "audit-core",
"service": "audit-core",
"tenant": "unknown"
},
"bao-notice": {
"owner": "railiance-platform",
"service": "bao-notice",
"tenant": "unknown"
},
"canned-prompts": {
"owner": "canned-prompts",
"service": "canned-prompts",
"tenant": "unknown"
},
"cert-manager": {
"owner": "railiance-cluster",
"service": "cert-manager",
"tenant": "unknown"
},
"cnpg-system": {
"owner": "rapp-postgres",
"service": "cloudnative-pg",
"tenant": "unknown"
},
"core-hub": {
"owner": "core-hub",
"service": "core-hub",
"tenant": "unknown"
},
"coulomb": {
"owner": "unknown",
"service": "ihp-railiance-probe",
"tenant": "unknown"
},
"coulomb-social": {
"owner": "coulomb-social",
"service": "coulomb-social",
"tenant": "unknown"
},
"databases": {
"owner": "rapp-postgres",
"service": "apps-pg",
"tenant": "unknown"
},
"email-connect": {
"owner": "email-connect",
"service": "email-connect",
"tenant": "unknown"
},
"external-secrets": {
"owner": "railiance-platform",
"service": "external-secrets",
"tenant": "unknown"
},
"flex-auth": {
"owner": "flex-auth",
"service": "flex-auth",
"tenant": "unknown"
},
"forgejo": {
"owner": "railiance-forge",
"service": "forgejo",
"tenant": "unknown"
},
"informed-decision": {
"owner": "informed-decision",
"service": "informed-decision",
"tenant": "unknown"
},
"issue-core": {
"owner": "issue-core",
"service": "issue-core",
"tenant": "unknown"
},
"knative-serving": {
"owner": "rail-knative",
"service": "knative-serving",
"tenant": "unknown"
},
"kourier-system": {
"owner": "rail-knative",
"service": "kourier",
"tenant": "unknown"
},
"kube-system": {
"owner": "railiance-cluster",
"service": "k3s-control-plane",
"tenant": "unknown"
},
"mas": {
"owner": "mail-address-semantics",
"service": "mas-live-test",
"tenant": "unknown"
},
"mfa": {
"owner": "net-kingdom",
"service": "lldap-mfa",
"tenant": "unknown"
},
"openbao": {
"owner": "railiance-platform",
"service": "openbao",
"tenant": "unknown"
},
"platform-pg-drill": {
"owner": "rapp-postgres",
"service": "platform-pg-drill",
"tenant": "unknown"
},
"playwright": {
"owner": "railiance-platform",
"service": "rapp-playwright",
"tenant": "unknown"
},
"policy-nexus": {
"owner": "policy-nexus",
"service": "policy-nexus",
"tenant": "unknown"
},
"rapp-qonto": {
"owner": "rapp-qonto",
"service": "qonto",
"tenant": "unknown"
},
"rapp-qonto-egress": {
"owner": "rapp-qonto",
"service": "qonto-egress",
"tenant": "tenant:friendly:binky"
},
"rein-aharness": {
"owner": "rein-aharness",
"service": "rein-aharness",
"tenant": "unknown"
},
"reuse": {
"owner": "reuse-surface",
"service": "reuse-surface",
"tenant": "unknown"
},
"sbom-nexus": {
"owner": "sbom-nexus",
"service": "sbom-nexus",
"tenant": "unknown"
},
"sso": {
"owner": "net-kingdom",
"service": "keycape-authelia",
"tenant": "unknown"
},
"state-hub": {
"owner": "state-hub",
"service": "state-hub",
"tenant": "unknown"
},
"target-revenue": {
"owner": "target-revenue",
"service": "target-revenue",
"tenant": "unknown"
},
"telemetry": {
"owner": "rapp-telemetry",
"service": "prometheus-grafana",
"tenant": "unknown"
},
"tenant-engine": {
"owner": "tenant-engine",
"service": "tenant-engine",
"tenant": "unknown"
},
"user-engine": {
"owner": "user-engine",
"service": "user-engine",
"tenant": "unknown"
},
"vergabe-demo-company": {
"owner": "railiance-apps",
"service": "vergabe-teilnahme",
"tenant": "unknown"
}
}
}

View file

@ -1,334 +0,0 @@
#!/usr/bin/env python3
"""Capture non-secret cluster capacity, utilization, and allocation evidence."""
from __future__ import annotations
import argparse
import json
import re
import subprocess
import sys
from datetime import UTC, datetime
from pathlib import Path
from typing import Any
CPU_FACTORS = {"n": 0.000001, "u": 0.001, "m": 1.0, "": 1000.0}
BYTE_FACTORS = {
"": 1,
"Ki": 1024,
"Mi": 1024**2,
"Gi": 1024**3,
"Ti": 1024**4,
"K": 1000,
"M": 1000**2,
"G": 1000**3,
"T": 1000**4,
}
def cpu_millicores(value: str | None) -> int:
match = re.fullmatch(r"([0-9]+(?:\.[0-9]+)?)(n|u|m)?", str(value or "0"))
if not match:
raise ValueError(f"unsupported CPU quantity: {value}")
return round(float(match.group(1)) * CPU_FACTORS[match.group(2) or ""])
def bytes_value(value: str | None) -> int:
match = re.fullmatch(r"([0-9]+(?:\.[0-9]+)?)(Ki|Mi|Gi|Ti|K|M|G|T)?", str(value or "0"))
if not match:
raise ValueError(f"unsupported byte quantity: {value}")
return round(float(match.group(1)) * BYTE_FACTORS[match.group(2) or ""])
def workload_name(pod: dict[str, Any]) -> str:
metadata = pod.get("metadata") or {}
labels = metadata.get("labels") or {}
for key in ("app.kubernetes.io/part-of", "app.kubernetes.io/name", "app"):
if labels.get(key):
return str(labels[key])
owners = metadata.get("ownerReferences") or []
if owners and owners[0].get("name"):
return str(owners[0]["name"])
return re.sub(r"-[a-f0-9]{6,}(?:-[a-z0-9]{5})?$", "", str(metadata.get("name", "unknown")))
def pod_effective_requests(pod: dict[str, Any]) -> tuple[int, int]:
"""Pod request is max(sum(containers), max(init containers))."""
spec = pod.get("spec") or {}
containers = spec.get("containers") or []
inits = spec.get("initContainers") or []
def req(container: dict[str, Any], key: str) -> str | None:
return ((container.get("resources") or {}).get("requests") or {}).get(key)
cpu = sum(cpu_millicores(req(c, "cpu")) for c in containers)
mem = sum(bytes_value(req(c, "memory")) for c in containers)
init_cpu = max((cpu_millicores(req(c, "cpu")) for c in inits), default=0)
init_mem = max((bytes_value(req(c, "memory")) for c in inits), default=0)
return max(cpu, init_cpu), max(mem, init_mem)
def parse_top(lines: str | None, *, pods: bool) -> dict[tuple[str, str] | str, dict[str, int]]:
result: dict[tuple[str, str] | str, dict[str, int]] = {}
for line in (lines or "").splitlines():
fields = line.split()
if pods and len(fields) >= 4:
result[(fields[0], fields[1])] = {
"cpu_millicores": cpu_millicores(fields[2]),
"memory_bytes": bytes_value(fields[3]),
}
elif not pods and len(fields) >= 4:
result[fields[0]] = {
"cpu_millicores": cpu_millicores(fields[1]),
# kubectl top nodes: NAME CPU CPU% MEMORY MEMORY%
"memory_bytes": bytes_value(fields[3]),
}
return result
def build_observation(snapshots: dict[str, Any], captured_at: str) -> dict[str, Any]:
gaps: list[str] = []
node_top = parse_top(snapshots.get("node_top", ""), pods=False)
pod_top = parse_top(snapshots.get("pod_top", ""), pods=True)
if snapshots.get("node_top") is None:
gaps.append("node metrics unavailable")
if snapshots.get("pod_top") is None:
gaps.append("pod metrics unavailable")
nodes = []
alloc_cpu = alloc_memory = alloc_pods = 0
for node in snapshots["nodes"].get("items") or []:
meta = node.get("metadata") or {}
status = node.get("status") or {}
alloc = status.get("allocatable") or {}
labels = meta.get("labels") or {}
name = str(meta.get("name"))
topology = {
"region": labels.get("topology.kubernetes.io/region"),
"zone": labels.get("topology.kubernetes.io/zone"),
"hostname": labels.get("kubernetes.io/hostname"),
"provider_id": (node.get("spec") or {}).get("providerID"),
}
if not topology["region"] or not topology["zone"]:
gaps.append(f"node {name} lacks independent region/zone labels")
row = {
"name": name,
"ready": any(c.get("type") == "Ready" and c.get("status") == "True" for c in status.get("conditions") or []),
"allocatable": {
"cpu_millicores": cpu_millicores(alloc.get("cpu")),
"memory_bytes": bytes_value(alloc.get("memory")),
"ephemeral_storage_bytes": bytes_value(alloc.get("ephemeral-storage")),
"pods": int(alloc.get("pods") or 0),
},
"topology": topology,
"observed": node_top.get(name),
}
nodes.append(row)
alloc_cpu += row["allocatable"]["cpu_millicores"]
alloc_memory += row["allocatable"]["memory_bytes"]
alloc_pods += row["allocatable"]["pods"]
workloads: dict[tuple[str, str], dict[str, Any]] = {}
pending_unscheduled: list[dict[str, Any]] = []
node_allocated: dict[str, dict[str, int]] = {
node["name"]: {"cpu_millicores": 0, "memory_bytes": 0} for node in nodes
}
scheduled_cpu = scheduled_memory = 0
for pod in snapshots["pods"].get("items") or []:
meta = pod.get("metadata") or {}
spec = pod.get("spec") or {}
status = pod.get("status") or {}
phase = str(status.get("phase") or "")
if phase in {"Succeeded", "Failed"}:
continue
namespace = str(meta.get("namespace", "default"))
pod_name = str(meta.get("name"))
cpu, mem = pod_effective_requests(pod)
node_name = spec.get("nodeName")
if phase == "Pending" and not node_name:
pending_unscheduled.append({
"namespace": namespace,
"name": pod_name,
"cpu_millicores": cpu,
"memory_bytes": mem,
})
continue
if node_name in node_allocated:
node_allocated[node_name]["cpu_millicores"] += cpu
node_allocated[node_name]["memory_bytes"] += mem
scheduled_cpu += cpu
scheduled_memory += mem
name = workload_name(pod)
key = (namespace, name)
row = workloads.setdefault(key, {
"namespace": namespace,
"workload": name,
"pods": 0,
"requests": {"cpu_millicores": 0, "memory_bytes": 0},
"limits": {"cpu_millicores": 0, "memory_bytes": 0},
"observed": {"cpu_millicores": 0, "memory_bytes": 0} if snapshots.get("pod_top") is not None else None,
})
row["pods"] += 1
row["requests"]["cpu_millicores"] += cpu
row["requests"]["memory_bytes"] += mem
for container in spec.get("containers") or []:
limits = (container.get("resources") or {}).get("limits") or {}
row["limits"]["cpu_millicores"] += cpu_millicores(limits.get("cpu"))
row["limits"]["memory_bytes"] += bytes_value(limits.get("memory"))
metric = pod_top.get((namespace, pod_name))
if metric and row["observed"] is not None:
row["observed"]["cpu_millicores"] += metric["cpu_millicores"]
row["observed"]["memory_bytes"] += metric["memory_bytes"]
storage_classes = []
for item in snapshots["storageclasses"].get("items") or []:
storage_classes.append({
"name": (item.get("metadata") or {}).get("name"),
"provisioner": item.get("provisioner"),
"reclaim_policy": item.get("reclaimPolicy"),
"volume_binding_mode": item.get("volumeBindingMode"),
"default": (item.get("metadata") or {}).get("annotations", {}).get("storageclass.kubernetes.io/is-default-class") == "true",
})
pv_capacity = sum(bytes_value(((item.get("spec") or {}).get("capacity") or {}).get("storage")) for item in snapshots["pvs"].get("items") or [])
pvc_rows = []
pvc_requested = 0
for item in snapshots["pvcs"].get("items") or []:
meta = item.get("metadata") or {}
spec = item.get("spec") or {}
requested = bytes_value(((spec.get("resources") or {}).get("requests") or {}).get("storage"))
pvc_requested += requested
pvc_rows.append({
"namespace": meta.get("namespace"),
"name": meta.get("name"),
"storage_class": spec.get("storageClassName"),
"requested_bytes": requested,
"phase": (item.get("status") or {}).get("phase"),
})
workload_rows = sorted(workloads.values(), key=lambda row: (row["namespace"], row["workload"]))
requested_cpu = scheduled_cpu
requested_memory = scheduled_memory
pending_cpu = sum(item["cpu_millicores"] for item in pending_unscheduled)
pending_memory = sum(item["memory_bytes"] for item in pending_unscheduled)
preflight_nodes = []
for node in nodes:
allocated = node_allocated.get(node["name"], {"cpu_millicores": 0, "memory_bytes": 0})
preflight_nodes.append({
"name": node["name"],
"ready": node["ready"],
"unschedulable": False,
"allocatable_cpu_m": node["allocatable"]["cpu_millicores"],
"allocatable_memory_bytes": node["allocatable"]["memory_bytes"],
"allocated_cpu_m": allocated["cpu_millicores"],
"allocated_memory_bytes": allocated["memory_bytes"],
})
return {
"schema_version": "railiance.cluster-resource-observation.v1",
"record_type": "usage_observation",
"resource_id": "resource:railiance:reef-railiance:k3s",
"source": "railiance-cluster",
"workplan_id": "RCLUSTER-WP-0014",
"reef": "reef-railiance",
"captured_at": captured_at,
"capacity": {
"nodes": nodes,
"totals": {"cpu_millicores": alloc_cpu, "memory_bytes": alloc_memory, "pods": alloc_pods, "pv_bytes": pv_capacity},
},
"utilization": {"workloads": workload_rows},
"storage": {"classes": storage_classes, "claims": pvc_rows},
"failure_domain": {
"current_contract": "single Ready node on railiance01; local storage is correlated with node loss",
"independent_domains_claimed": 1 if nodes else 0,
"threephoenix_target_is_live": False,
},
"allocation": {
"method_version": "cluster-raw-drivers-v1",
"drivers": ["cpu_requests", "memory_requests", "pvc_requested_bytes", "observed_usage"],
"denominators": {"cpu_requested_millicores": requested_cpu, "memory_requested_bytes": requested_memory, "pvc_requested_bytes": pvc_requested},
"pending_unscheduled": {
"cpu_millicores": pending_cpu,
"memory_bytes": pending_memory,
"pods": pending_unscheduled,
},
"residual_capacity": {"cpu_millicores": max(alloc_cpu - requested_cpu, 0), "memory_bytes": max(alloc_memory - requested_memory, 0), "pv_bytes": max(pv_capacity - pvc_requested, 0)},
"not_a_scheduling_guarantee": True,
"selection_owner": "resource-control",
},
"state_hub_preflight_observation": {
"schema": "state-hub.release-headroom-preflight.v1-input",
"observed_at": captured_at,
"freshness_seconds": 0,
"nodes": preflight_nodes,
"pending_unrelated": [
{
"name": f"{item['namespace']}/{item['name']}",
"cpu_m": item["cpu_millicores"],
"memory_bytes": item["memory_bytes"],
}
for item in pending_unscheduled
],
},
"measurement_gaps": sorted(set(gaps)),
"provenance": {
"commands": [
"kubectl get nodes -o json", "kubectl get pods -A -o json",
"kubectl get pvc -A -o json", "kubectl get pv -o json",
"kubectl get storageclass -o json", "kubectl top nodes --no-headers",
"kubectl top pods -A --no-headers",
],
"secret_surfaces_read": False,
},
}
def run_json(command: list[str]) -> dict[str, Any]:
return json.loads(subprocess.check_output(command, text=True))
def run_optional(command: list[str]) -> str | None:
completed = subprocess.run(command, text=True, capture_output=True, check=False)
return completed.stdout if completed.returncode == 0 else None
def collect(kubectl: str, remote: str | None = None) -> dict[str, Any]:
base = ["ssh", "-o", "BatchMode=yes", remote, kubectl] if remote else [kubectl]
snapshots = {
"nodes": run_json(base + ["get", "nodes", "-o", "json"]),
"pods": run_json(base + ["get", "pods", "-A", "-o", "json"]),
"pvcs": run_json(base + ["get", "pvc", "-A", "-o", "json"]),
"pvs": run_json(base + ["get", "pv", "-o", "json"]),
"storageclasses": run_json(base + ["get", "storageclass", "-o", "json"]),
"node_top": run_optional(base + ["top", "nodes", "--no-headers"]),
"pod_top": run_optional(base + ["top", "pods", "-A", "--no-headers"]),
}
captured_at = datetime.now(UTC).replace(microsecond=0).isoformat().replace("+00:00", "Z")
return build_observation(snapshots, captured_at)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--kubectl", default="kubectl")
parser.add_argument("--remote", help="Optional SSH host on which kubectl runs")
parser.add_argument("--output-dir", type=Path)
args = parser.parse_args()
try:
observation = collect(args.kubectl, args.remote)
except (OSError, subprocess.CalledProcessError, json.JSONDecodeError, ValueError) as exc:
print(f"cluster observation failed: {exc}", file=sys.stderr)
return 1
rendered = json.dumps(observation, indent=2) + "\n"
if args.output_dir:
args.output_dir.mkdir(parents=True, exist_ok=True)
stamp = observation["captured_at"].replace(":", "").replace("-", "")
destination = args.output_dir / f"{stamp}.json"
destination.write_text(rendered, encoding="utf-8")
(args.output_dir / "latest.json").write_text(rendered, encoding="utf-8")
print(destination)
else:
sys.stdout.write(rendered)
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -1,153 +0,0 @@
#!/usr/bin/env python3
"""CUST-WP-0071-T01: join live cluster allocation with owner/service/tenant."""
from __future__ import annotations
import argparse
import importlib.util
import json
import sys
from datetime import UTC, datetime
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[1]
CLUSTER_OBS = Path.home() / "railiance-cluster" / "tools" / "observe_cluster_resources.py"
HEADROOM = Path.home() / "state-hub" / "scripts" / "release_headroom_preflight.py"
OWNERS = ROOT / "docs" / "evidence" / "namespace-ownership.yaml"
def load_module(path: Path, name: str):
spec = importlib.util.spec_from_file_location(name, path)
if spec is None or spec.loader is None:
raise RuntimeError(f"cannot load {path}")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def join(observation: dict[str, Any], owners: dict[str, Any]) -> dict[str, Any]:
mapping = owners.get("namespaces") or {}
rows = []
unknown = []
zero_request = []
for workload in observation.get("utilization", {}).get("workloads") or []:
ns = workload["namespace"]
meta = mapping.get(ns, {"owner": "unknown", "service": "unknown", "tenant": "unknown"})
row = {
"namespace": ns,
"workload": workload["workload"],
"pods": workload["pods"],
"cpu_request_m": workload["requests"]["cpu_millicores"],
"memory_request_bytes": workload["requests"]["memory_bytes"],
"owner": meta.get("owner", "unknown"),
"service": meta.get("service", "unknown"),
"tenant": meta.get("tenant", "unknown"),
}
rows.append(row)
if ns not in mapping:
unknown.append(ns)
if workload["requests"]["cpu_millicores"] == 0:
zero_request.append(f"{ns}/{workload['workload']}")
pending = observation.get("allocation", {}).get("pending_unscheduled") or {}
alloc = observation.get("allocation") or {}
capacity = observation.get("capacity", {}).get("totals") or {}
residual = (alloc.get("residual_capacity") or {}).get("cpu_millicores", 0)
requested = (alloc.get("denominators") or {}).get("cpu_requested_millicores", 0)
return {
"schema": "custodian.allocation-reconcile.v1",
"workplan_id": "CUST-WP-0071-T01",
"captured_at": observation.get("captured_at"),
"cluster_observation_schema": observation.get("schema_version"),
"count_host_and_cluster_cpus_once": True,
"host": owners.get("host"),
"capacity_cpu_m": capacity.get("cpu_millicores"),
"scheduled_request_cpu_m": requested,
"pending_unscheduled_cpu_m": pending.get("cpu_millicores", 0),
"residual_cpu_m": residual,
"not_a_scheduling_guarantee": True,
"workloads": sorted(rows, key=lambda r: (-r["cpu_request_m"], r["namespace"], r["workload"])),
"unknown_namespaces": sorted(set(unknown)),
"zero_request_workloads": sorted(zero_request),
"pending_unscheduled": pending.get("pods") or [],
"measurement_gaps": observation.get("measurement_gaps") or [],
"state_hub_preflight_observation": observation.get("state_hub_preflight_observation"),
"signal_notes": [
"Host resource:hosteurope:railiance01 and cluster resource:railiance:reef-railiance:k3s are the same 4 vCPU; do not add them.",
"Prometheus namespace_cpu:kube_pod_container_resource_requests:sum omitted Forgejo 100m on 2026-09-11; Kubernetes API is the scheduler-effective source.",
"node-exporter was disabled; host CPU usage is not a reservation and is not treated as spare capacity.",
"Zero-request pods are demand, not zero. Missing metrics are listed as gaps, not zeros.",
"STATE-WP-0091 preflight consumes state_hub_preflight_observation; residual millicores are not admission.",
],
}
def render_markdown(report: dict[str, Any], preflight: dict[str, Any] | None) -> str:
lines = [
f"# Railiance allocation reconcile — {report['captured_at']}",
"",
"CUST-WP-0071-T01. Scheduler-effective requests from the Kubernetes API",
"via `railiance-cluster` observe (RCLUSTER-WP-0014 / RAIL-BS-WP-0014).",
"Host and cluster are the same 4 vCPU and are counted once.",
"",
f"- Capacity: {report['capacity_cpu_m']}m",
f"- Scheduled requests: {report['scheduled_request_cpu_m']}m",
f"- Pending unscheduled: {report['pending_unscheduled_cpu_m']}m",
f"- Residual (not a guarantee): {report['residual_cpu_m']}m",
f"- Unknown namespaces: {', '.join(report['unknown_namespaces']) or 'none'}",
f"- Zero-request workloads: {len(report['zero_request_workloads'])}",
"",
"| Namespace | Workload | Owner | Tenant | CPU request (m) | Pods |",
"|---|---|---|---|---:|---:|",
]
for row in report["workloads"]:
lines.append(
f"| {row['namespace']} | {row['workload']} | {row['owner']} | {row['tenant']} | {row['cpu_request_m']} | {row['pods']} |"
)
lines += ["", "## STATE-WP-0091 preflight", ""]
if preflight:
lines.append(f"- ok: `{preflight.get('ok')}`")
lines.append(f"- remaining_cpu_m: {preflight.get('remaining_cpu_m')}")
for reason in preflight.get("reasons") or []:
lines.append(f"- refuse: {reason}")
for note in preflight.get("notes") or []:
lines.append(f"- note: {note}")
lines += ["", "## Signal notes", ""]
for note in report["signal_notes"]:
lines.append(f"- {note}")
lines.append("")
return "\n".join(lines)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--observation", type=Path, help="Existing cluster observation JSON")
parser.add_argument("--kubectl", default="kubectl")
parser.add_argument("--output-dir", type=Path, default=ROOT / "docs" / "evidence")
args = parser.parse_args()
cluster = load_module(CLUSTER_OBS, "cluster_resources")
if args.observation:
observation = json.loads(args.observation.read_text())
else:
observation = cluster.collect(args.kubectl)
import yaml # main only: join() stays stdlib-only for the in-cluster runner
owners = yaml.safe_load(OWNERS.read_text())
report = join(observation, owners)
preflight = None
if observation.get("state_hub_preflight_observation"):
headroom = load_module(HEADROOM, "release_headroom")
preflight = headroom.evaluate(observation["state_hub_preflight_observation"])
report["state_hub_preflight"] = preflight
stamp = datetime.now(UTC).strftime("%Y-%m-%d")
out_json = args.output_dir / f"{stamp}-allocation-reconcile.json"
out_md = args.output_dir / f"{stamp}-allocation-reconcile.md"
args.output_dir.mkdir(parents=True, exist_ok=True)
out_json.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
out_md.write_text(render_markdown(report, preflight), encoding="utf-8")
print(out_json)
print(out_md)
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -1,63 +0,0 @@
{
"schema_version": "0.3", "record_scope": "inventory",
"id": "resource:railiance:reef-railiance:k3s",
"financial_entity_id": null,
"procuring_entity_id": "entity:railiance",
"entity_gap": "no allocation driver; cost remains a Railiance residual",
"description": "Single-node k3s runtime on railiance01. Cost and consumption view of reef-railiance; not a copy of IPs or kubeconfig.",
"decision": {
"status": "gap",
"chosen": "Live discovered cluster; no formal procurement decision in this repo",
"rejected": [],
"approved_by": null,
"approved_on": null,
"ref": "/home/worsch/railiance-cluster/docs/rail-kubernetes-substrate-profile.md"
},
"operational_refs": [
"reef:railiance/declarations/reef.yaml"
],
"credential_handles": [],
"consumers": {
"potential": [
"rapp-postgres",
"rapp-qonto",
"rapp-openbao",
"coulomb-social",
"helix-forge",
"railiance-platform"
],
"actual": [
"rapp-postgres",
"rapp-qonto",
"coulomb-social",
"helix-forge",
"railiance-platform",
"state-hub"
]
},
"resource_class": "kubernetes_capacity",
"status": "active", "management_model": "self_managed",
"provider": {"name": "Railiance", "account_ref": null, "product_ref": "k3s", "provider_resource_id": "k3s://239.62.205.92.host.secureserver.net"},
"service": {"name": "reef-railiance k3s", "service_id": "reef-railiance", "class": "single-node Kubernetes runtime", "capacity_model": "shared"},
"location": {"region": null, "country": null, "failure_domains": ["provider:host-europe", "host:railiance01", "cluster:reef-railiance"], "residency": null},
"capacity": [
{"metric": "cpu", "value": 4, "unit": "vCPU", "kind": "usable", "observed_at": "2026-08-11"},
{"metric": "memory", "value": 15.62, "unit": "GiB", "kind": "usable", "observed_at": "2026-08-11"},
{"metric": "ephemeral_storage", "value": 183.06, "unit": "GiB", "kind": "usable", "observed_at": "2026-08-11"},
{"metric": "pods", "value": 110, "unit": "count", "kind": "limit", "observed_at": "2026-08-11"},
{"metric": "nodes", "value": 1, "unit": "count", "kind": "observed", "observed_at": "2026-08-11"},
{"metric": "cpu_usage", "value": 0.564, "unit": "vCPU", "kind": "observed", "observed_at": "2026-08-11"},
{"metric": "memory_usage", "value": 5.83, "unit": "GiB", "kind": "observed", "observed_at": "2026-08-11"}
],
"ownership": {"owner": "railiance-cluster", "environment": "production", "workload_ids": ["helix-forge", "coulomb-social", "rapp-qonto", "railiance-platform", "state-hub"], "tenant_id": null, "allocation": {"mode": "unattributed", "cost_attribution_key": null, "driver": null, "method_version": null}},
"cost": {"currency": "EUR", "tax_status": "unknown", "billing_model": "allocated share of railiance01 plus cluster operations labor; method not established", "commitment_ref": null, "price_evidence": null},
"lifecycle": {"proposed_on": null, "ordered_on": null, "commissioned_on": "2026-03-10", "renews_on": null, "cancel_by": null, "retired_on": null, "exit_path": "Deploy a replacement cluster, restore stateful services, promote workloads and ingress, verify health, then remove the old node and credentials."},
"relationships": [{"type": "hosted_on", "resource_id": "resource:hosteurope:railiance01"}],
"requirements": [{"kind": "service_objective", "ref": "/home/worsch/railiance-cluster/INTENT.md"}, {"kind": "recovery", "ref": "/home/worsch/railiance-cluster/workplans/RAIL-BS-WP-0007-threephoenix-ha-cluster.md"}],
"evidence": [
{"kind": "telemetry", "ref": "kubernetes:reef-railiance/node/239.62.205.92.host.secureserver.net@2026-08-11T09:05:25Z", "observed_at": "2026-08-11", "authority": "reef-railiance Kubernetes API"},
{"kind": "workload", "ref": "/home/worsch/railiance-cluster/docs/rail-kubernetes-substrate-profile.md", "observed_at": "2026-08-11", "authority": "railiance-cluster"},
{"kind": "workload", "ref": "STATE-WP-0091: state-hub MCP CPU request 50m→10m after partial telemetry (p99 2.35m, peak 30.45m, 1078 samples). 105m node remaining is not reef admission and does not authorize procurement or scaling.", "observed_at": "2026-09-14", "authority": "state-hub"},
{"kind": "workload", "ref": "docs/evidence/2026-10-19-custodian-allocation-review.md", "observed_at": "2026-10-19", "authority": "resource-control review of Custodian8e67cfb and Vergabe11f7c8a; reservation snapshot and provisional synthetic evidence, not admission"}
]
}

View file

@ -1,16 +0,0 @@
{
"schema": "custodian.weekly-allocation-queries.v1",
"note": "CUST-WP-0071-T05. Namespace-level aggregates proven on 2026-09-28 against telemetry/telemetry-prometheus. They populate namespace_context only; workload rows stay 'investigate' until workload-labelled queries exist (kube_pod_labels is not exported by the current kube-state-metrics). Changing any query text changes the report query_signature and breaks week-on-week comparability for one week.",
"window": "7d",
"step": "5m",
"queries": {
"cpu_p95_m": "quantile_over_time(0.95, (sum by(namespace) (rate(container_cpu_usage_seconds_total{container!=\"\",container!=\"POD\",namespace!=\"\"}[5m])) * 1000)[7d:5m])",
"cpu_peak_m": "max_over_time((sum by(namespace) (rate(container_cpu_usage_seconds_total{container!=\"\",container!=\"POD\",namespace!=\"\"}[5m])) * 1000)[7d:5m])",
"cpu_sample_count": "count_over_time((sum by(namespace) (rate(container_cpu_usage_seconds_total{container!=\"\",container!=\"POD\",namespace!=\"\"}[5m])) * 1000)[7d:5m])",
"memory_p95_bytes": "quantile_over_time(0.95, (sum by(namespace) (container_memory_working_set_bytes{container!=\"\",container!=\"POD\",namespace!=\"\"}))[7d:5m])",
"memory_peak_bytes": "max_over_time((sum by(namespace) (container_memory_working_set_bytes{container!=\"\",container!=\"POD\",namespace!=\"\"}))[7d:5m])",
"memory_sample_count": "count_over_time((sum by(namespace) (container_memory_working_set_bytes{container!=\"\",container!=\"POD\",namespace!=\"\"}))[7d:5m])",
"cpu_throttle_ratio": "sum by(namespace) (increase(container_cpu_cfs_throttled_periods_total{container!=\"\",container!=\"POD\",namespace!=\"\"}[7d])) / sum by(namespace) (increase(container_cpu_cfs_periods_total{container!=\"\",container!=\"POD\",namespace!=\"\"}[7d]))",
"restarts": "sum by(namespace) (increase(kube_pod_container_status_restarts_total[7d]))"
}
}

View file

@ -1,335 +0,0 @@
#!/usr/bin/env python3
"""CUST-WP-0071-T05 in-cluster weekly runner: read-only collection, join, retain.
Standard library only, so it runs on a pinned python:3.12-slim image with the
scripts mounted from a ConfigMap. It reads the Kubernetes API with the pod's
ServiceAccount (get/list only), queries Prometheus over plain HTTP, reuses the
existing owner code unchanged (railiance-cluster build_observation, custodian
reconcile join, weekly_allocation_report build_report/retain) and writes only
under the data directory. It never resizes, creates tasks or delivers messages.
Week identity is the ISO week of (run time - 6h); the telemetry window always
ends at Monday 06:00 UTC of that week, so consecutive reports are exactly
604800 s apart regardless of DST or scheduler jitter. 06:00 UTC is at or before
the Monday 08:00 Europe/Berlin schedule in both CET and CEST and keeps the
window start as close as possible to the seven-day Prometheus retention edge. A week whose report is already
retained is a no-op success (idempotent retry); a failed attempt leaves no
report, so the Job's retry or the next manual run collects again.
Delivery (founder decision 2026-10-21, "Hub message + repo tasks"): after a
week is retained, one State Hub message goes from custodian-allocation-review
to the-custodian with a short summary, report path and sha256. The message id
is recorded in receipts.jsonl; a week that already has a message id is never
posted again, and a retained-but-undelivered week is posted by the next run.
"""
from __future__ import annotations
import argparse
from datetime import datetime, timedelta, timezone
import hashlib
import importlib.util
import json
import os
from pathlib import Path
import re
import ssl
import sys
import urllib.error
import urllib.parse
import urllib.request
HERE = Path(__file__).resolve().parent
SA_DIR = Path("/var/run/secrets/kubernetes.io/serviceaccount")
WEEK = re.compile(r"^(\d{4})-W(\d{2})\.json$")
# Exactly the reads the collector performs; mirrored by the ClusterRole.
API_PATHS = {
"nodes": "/api/v1/nodes",
"pods": "/api/v1/pods",
"pvcs": "/api/v1/persistentvolumeclaims",
"pvs": "/api/v1/persistentvolumes",
"storageclasses": "/apis/storage.k8s.io/v1/storageclasses",
}
METRICS_PATHS = {
"node_top": "/apis/metrics.k8s.io/v1beta1/nodes",
"pod_top": "/apis/metrics.k8s.io/v1beta1/pods",
}
def load(path: Path, name: str):
spec = importlib.util.spec_from_file_location(name, path)
if spec is None or spec.loader is None:
raise RuntimeError(f"cannot load {path}")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def iso(value: datetime) -> str:
return value.astimezone(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
WINDOW_END_HOUR = 6
def week_of(now: datetime) -> tuple[str, datetime]:
"""ISO week label and its Monday 06:00 UTC (the telemetry window end)."""
shifted = now.astimezone(timezone.utc) - timedelta(hours=WINDOW_END_HOUR)
year, week, weekday = shifted.isocalendar()
monday = (shifted - timedelta(days=weekday - 1)).replace(hour=0, minute=0, second=0, microsecond=0)
return f"{year}-W{week:02d}", monday + timedelta(hours=WINDOW_END_HOUR)
def missed_weeks(previous: str | None, current: str) -> list[str]:
if previous is None:
return []
def monday(label):
year, week = int(label[:4]), int(label[6:8])
return datetime.fromisocalendar(year, week, 1).replace(tzinfo=timezone.utc, hour=WINDOW_END_HOUR)
out, cursor = [], monday(previous) + timedelta(days=7)
while cursor < monday(current):
out.append(week_of(cursor)[0])
cursor += timedelta(days=7)
return out
def previous_report(reports: Path, label: str) -> Path | None:
earlier = sorted(p for p in reports.glob("*.json") if WEEK.match(p.name) and p.stem < label)
return earlier[-1] if earlier else None
class Kube:
"""Minimal in-cluster API reader; the token is never logged or persisted."""
def __init__(self, base: str, token_path: Path, ca_path: Path):
self.base = base.rstrip("/")
self.token_path = token_path
self.context = ssl.create_default_context(cafile=str(ca_path))
def get(self, path: str):
request = urllib.request.Request(self.base + path, headers={
"Authorization": "Bearer " + self.token_path.read_text().strip(),
"Accept": "application/json"})
with urllib.request.urlopen(request, context=self.context, timeout=60) as response:
return json.load(response)
def top_lines(metrics: dict, *, pods: bool, observer) -> str:
"""Render metrics.k8s.io lists in the `kubectl top` shape parse_top reads."""
lines = []
for item in metrics.get("items") or []:
meta = item.get("metadata") or {}
if pods:
cpu = sum(observer.cpu_millicores((c.get("usage") or {}).get("cpu")) for c in item.get("containers") or [])
mem = sum(observer.bytes_value((c.get("usage") or {}).get("memory")) for c in item.get("containers") or [])
lines.append(f"{meta.get('namespace')} {meta.get('name')} {cpu}m {mem}")
else:
usage = item.get("usage") or {}
cpu = observer.cpu_millicores(usage.get("cpu"))
mem = observer.bytes_value(usage.get("memory"))
lines.append(f"{meta.get('name')} {cpu}m 0% {mem} 0%")
return "\n".join(lines) + ("\n" if lines else "")
def collect_snapshots(get, observer) -> dict:
snapshots = {name: get(path) for name, path in API_PATHS.items()}
for name, path in METRICS_PATHS.items():
try:
snapshots[name] = top_lines(get(path), pods=name == "pod_top", observer=observer)
except (OSError, ValueError, urllib.error.URLError):
snapshots[name] = None # recorded by build_observation as a measurement gap
return snapshots
def prometheus_query(base: str, promql: str, at: datetime, opener=urllib.request.urlopen) -> dict:
"""Return Prometheus' JSON response, including error bodies, never raise on 4xx/5xx."""
url = base.rstrip("/") + "/api/v1/query?" + urllib.parse.urlencode(
{"query": promql, "time": f"{at.timestamp():.3f}"})
try:
with opener(url, timeout=120) as response:
return json.load(response)
except urllib.error.HTTPError as exc:
try:
return json.loads(exc.read())
except ValueError:
return {"status": "error", "errorType": "http", "error": f"HTTP {exc.code}"}
except (OSError, ValueError) as exc:
return {"status": "error", "errorType": "transport", "error": type(exc).__name__}
def write_json(path: Path, value) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
temporary = path.with_name(path.name + ".tmp")
temporary.write_text(json.dumps(value, indent=2, sort_keys=True) + "\n")
os.replace(temporary, path)
def append_receipt(data: Path, receipt: dict) -> None:
data.mkdir(parents=True, exist_ok=True)
with (data / "receipts.jsonl").open("a") as stream:
stream.write(json.dumps(receipt, sort_keys=True) + "\n")
stream.flush()
os.fsync(stream.fileno())
print(json.dumps(receipt, sort_keys=True))
def delivered_message(data: Path, week: str) -> str | None:
path = data / "receipts.jsonl"
if not path.exists():
return None
for line in reversed(path.read_text().splitlines()):
try:
entry = json.loads(line)
except ValueError:
continue
if entry.get("week") == week and entry.get("message_id"):
return entry["message_id"]
return None
def summary(receipt: dict) -> dict:
report = json.loads(Path(receipt["report"]).read_text())
decisions: dict[str, int] = {}
for item in report["recommendations"]:
decisions[item["decision"]] = decisions.get(item["decision"], 0) + 1
return {"week": receipt["week"], "report": receipt["report"], "report_sha256": receipt["report_sha256"],
"capture_end": report["period"]["capture_end"], "workloads": len(report["workloads"]),
"namespaces_with_metrics": len(report["namespace_context"]),
"recommendations": dict(sorted(decisions.items())),
"comparison_compatible": report["comparison"]["compatible"],
"measurement_gaps": len(report["measurement_gaps"]),
"failed_queries": sorted(n for n, q in report["queries"].items() if q.get("response_status") != "success"),
"next_step": "Custodian session reviews and records owner-repo tasks (ADR-001); no automatic resizing."}
def hub_post(hub: str, body: dict) -> str:
request = urllib.request.Request(hub.rstrip("/") + "/messages/", data=json.dumps(body).encode(),
headers={"Content-Type": "application/json"}, method="POST")
with urllib.request.urlopen(request, timeout=30) as response:
return str(json.load(response)["id"])
def deliver(data: Path, receipt: dict, hub: str | None, post=hub_post) -> tuple[dict, bool]:
"""Post one hub message per retained week; returns (receipt, ok)."""
if receipt.get("status") not in {"retained", "already-retained"}:
return receipt, True
existing = delivered_message(data, receipt["week"])
if existing:
return {**receipt, "delivery": "already-posted", "message_id": existing}, True
if not hub:
return {**receipt, "delivery": "not configured"}, True
content = summary(receipt)
body = {"from_agent": "custodian-allocation-review", "to_agent": "the-custodian",
"subject": f"Weekly allocation assessment {receipt['week']}",
"body": json.dumps(content, sort_keys=True)}
try:
return {**receipt, "delivery": "posted", "message_id": post(hub, body)}, True
except (OSError, ValueError, KeyError) as exc:
return {**receipt, "delivery": f"failed: {type(exc).__name__}"}, False
def run(args, *, now: datetime, get, query) -> dict:
sources = {
"weekly_allocation_report": args.source_dir / "weekly_allocation_report.py",
"reconcile_railiance_allocation": args.source_dir / "reconcile_railiance_allocation.py",
"observe_cluster_resources": args.observer,
"weekly_allocation_incluster": Path(__file__).resolve(),
"queries": args.queries, "ownership": args.ownership, "inventory": args.inventory,
}
weekly = load(sources["weekly_allocation_report"], "weekly_allocation_report")
reconcile = load(sources["reconcile_railiance_allocation"], "reconcile_railiance_allocation")
observer = load(sources["observe_cluster_resources"], "observe_cluster_resources")
label, window_end = week_of(now)
reports = args.data_dir / "reports"
target = reports / f"{label}.json"
receipt = {"schema": "custodian.weekly-allocation-receipt.v1", "week": label,
"run_at": iso(now), "report": str(target), "delivery": "not emitted",
"acknowledgment": None}
if target.exists():
return {**receipt, "status": "already-retained", "report_sha256": sha256(target)}
previous_path = previous_report(reports, label)
previous_label = previous_path.stem if previous_path else None
observation = observer.build_observation(collect_snapshots(get, observer), iso(now))
owners = json.loads(args.ownership.read_text())
allocation = reconcile.join(observation, owners)
spec = json.loads(args.queries.read_text())
if spec.get("schema") != "custodian.weekly-allocation-queries.v1":
raise ValueError("unsupported query specification")
telemetry = {"captured_at": window_end.isoformat(), "window": spec["window"], "step": spec["step"],
"source": args.prometheus_url,
"queries": {name: {"promql": promql, "response": query(promql, window_end)}
for name, promql in sorted(spec["queries"].items())}}
provenance = {
"schema": "custodian.allocation-capture-provenance.v1", "workplan": "CUST-WP-0071-T05",
"source_observation": observation["captured_at"], "read_only": True,
"secret_surfaces_read": False, "executor": args.executor,
"kubernetes_reads": sorted(API_PATHS.values()) + sorted(METRICS_PATHS.values()),
"prometheus": args.prometheus_url, "telemetry_window_end": window_end.isoformat(),
"inputs": [{"name": n, "path": str(p), "sha256": sha256(p)} for n, p in sorted(sources.items())],
}
inventory = json.loads(args.inventory.read_text())
inputs = {"cluster": observation, "allocation": allocation, "telemetry": telemetry,
"inventory": inventory, "provenance": provenance}
attempt = args.data_dir / "runs" / label / iso(now).replace(":", "")
for name, value in inputs.items():
write_json(attempt / f"{name}.json", value)
report = weekly.build_report(
**inputs, as_of=iso(now), max_age_hours=args.max_age_hours,
previous=json.loads(previous_path.read_text()) if previous_path else None,
reviews=json.loads(args.reviews.read_text()) if args.reviews and args.reviews.exists() else None)
weekly.retain(target, report)
failed = sorted(n for n, q in telemetry["queries"].items() if q["response"].get("status") != "success")
return {**receipt, "status": "retained", "report_sha256": sha256(target),
"inputs_dir": str(attempt), "previous_week": previous_label,
"missed_weeks": missed_weeks(previous_label, label),
"comparison_compatible": report["comparison"]["compatible"],
"workloads": len(report["workloads"]), "failed_queries": failed,
"measurement_gaps": len(report["measurement_gaps"])}
def parser() -> argparse.ArgumentParser:
env = os.environ.get
p = argparse.ArgumentParser(description=__doc__)
p.add_argument("--data-dir", type=Path, default=Path(env("ASSESSMENT_DATA_DIR", "/data")))
p.add_argument("--source-dir", type=Path, default=Path(env("ASSESSMENT_SOURCE_DIR", str(HERE))))
p.add_argument("--observer", type=Path, default=Path(env("ASSESSMENT_OBSERVER",
str(Path(env("ASSESSMENT_SOURCE_DIR", str(HERE))) / "observe_cluster_resources.py"))))
p.add_argument("--queries", type=Path, default=Path(env("ASSESSMENT_QUERIES", str(HERE.parent / "config/weekly-allocation-queries.json"))))
p.add_argument("--ownership", type=Path, default=env("ASSESSMENT_OWNERSHIP") and Path(env("ASSESSMENT_OWNERSHIP")))
p.add_argument("--inventory", type=Path, default=env("ASSESSMENT_INVENTORY") and Path(env("ASSESSMENT_INVENTORY")))
p.add_argument("--reviews", type=Path, default=env("ASSESSMENT_REVIEWS") and Path(env("ASSESSMENT_REVIEWS")))
p.add_argument("--prometheus-url", default=env("PROMETHEUS_URL", "http://telemetry-prometheus.telemetry.svc:9090"))
p.add_argument("--hub-url", default=env("STATE_HUB_URL"), help="State Hub API; unset disables delivery")
p.add_argument("--executor", default=env("ASSESSMENT_EXECUTOR", "unspecified"))
p.add_argument("--max-age-hours", type=float, default=float(env("ASSESSMENT_MAX_AGE_HOURS", "48")))
return p
def main() -> int:
args = parser().parse_args()
now = datetime.now(timezone.utc)
label = week_of(now)[0]
try:
if not args.ownership or not args.inventory:
raise ValueError("ownership and inventory inputs must be bound")
host = os.environ["KUBERNETES_SERVICE_HOST"]
port = os.environ.get("KUBERNETES_SERVICE_PORT", "443")
kube = Kube(f"https://{host}:{port}", SA_DIR / "token", SA_DIR / "ca.crt")
receipt = run(args, now=now, get=kube.get,
query=lambda promql, at: prometheus_query(args.prometheus_url, promql, at))
except Exception as exc: # noqa: BLE001 - every failure must leave a receipt
append_receipt(args.data_dir, {"schema": "custodian.weekly-allocation-receipt.v1",
"week": label, "run_at": iso(now), "status": "failed",
"error": f"{type(exc).__name__}: {exc}"[:500]})
return 1
receipt, delivered = deliver(args.data_dir, receipt, args.hub_url)
append_receipt(args.data_dir, receipt)
return 0 if delivered else 1 # undelivered: Job retry posts it (report already retained)
if __name__ == "__main__":
sys.exit(main())

View file

@ -1,321 +0,0 @@
#!/usr/bin/env python3
"""Retain CUST-WP-0071 sizing evidence; no network, resizing or task emission."""
from __future__ import annotations
import argparse
from datetime import datetime, timedelta, timezone
import hashlib
import json
import math
import os
from pathlib import Path
import re
import tempfile
SCHEMA = "custodian.weekly-allocation.v1"
METRICS = (
"cpu_p95_m", "cpu_peak_m", "memory_p95_bytes", "memory_peak_bytes",
"cpu_throttle_ratio", "latency_p95_seconds", "errors", "request_count",
"restarts", "oom_events", "cpu_sample_count", "memory_sample_count",
)
REQUIRED = tuple(m for m in METRICS if not m.endswith("sample_count"))
def timestamp(value):
result = datetime.fromisoformat(value.replace("Z", "+00:00"))
if result.tzinfo is None:
raise ValueError("timestamps must include timezone")
return result.astimezone(timezone.utc)
def duration(value):
match = re.fullmatch(r"([1-9][0-9]*)([smhdw])", value)
if not match:
raise ValueError(f"unsupported duration: {value}")
return int(match[1]) * {"s": 1, "m": 60, "h": 3600, "d": 86400, "w": 604800}[match[2]]
def digest(value):
return hashlib.sha256(json.dumps(value, sort_keys=True, allow_nan=False).encode()).hexdigest()
def finite(value):
number = float(value)
if not math.isfinite(number) or number < 0:
raise ValueError("metric must be finite and nonnegative")
return number
def metric_series(telemetry):
"""Do not distribute namespace aggregates over workloads, or sum percentiles."""
series, queries = {}, {}
seconds = duration(telemetry["window"])
for name, query in sorted(telemetry.get("queries", {}).items()):
response = query.get("response", {})
data = response.get("data", {})
queries[name] = {
"promql": query.get("promql"), "response_status": response.get("status"),
"result_type": data.get("resultType"), "warnings": response.get("warnings", []),
"error": response.get("error"), "response_sha256": digest(response),
"evaluation_times": [],
}
if name not in METRICS or response.get("status") != "success" or data.get("resultType") != "vector":
continue
for sample in data.get("result", []):
labels = sample.get("metric", {})
namespace, workload = labels.get("namespace"), labels.get("workload")
# Additional dimensions are ambiguous: do not silently collapse pod/job series.
if not namespace or set(labels) - {"namespace", "workload", "__name__"}:
queries[name].setdefault("rejected", []).append("unaggregated or absent identity labels")
continue
key = (namespace, workload)
values = series.setdefault(key, {})
if name in values:
values[name] = {"value": None, "reason": "ambiguous duplicate series"}
continue
try:
epoch, raw = sample["value"]
end = datetime.fromtimestamp(finite(epoch), timezone.utc)
number = finite(raw)
except (KeyError, ValueError, TypeError, OverflowError):
values[name] = {"value": None, "reason": "invalid sample"}
continue
queries[name]["evaluation_times"].append(end.isoformat())
values[name] = {
"value": number, "window_start": (end - timedelta(seconds=seconds)).isoformat(),
"window_end": end.isoformat(), "query": name,
"capture_alignment_seconds": (end - timestamp(telemetry["captured_at"])).total_seconds(),
}
return series, queries
def recommendation_id(resource, namespace, workload):
return "allocation-" + digest([resource, namespace, workload, "sizing-review"])[:24]
def build_report(cluster, allocation, telemetry, inventory, provenance, *, as_of,
previous=None, reviews=None, max_age_hours=48):
now = timestamp(as_of)
if cluster.get("schema_version") != "railiance.cluster-resource-observation.v1":
raise ValueError("unsupported cluster schema")
if allocation.get("schema") != "custodian.allocation-reconcile.v1":
raise ValueError("unsupported allocation schema")
resource = cluster["resource_id"]
if inventory.get("id") != resource or allocation.get("host", {}).get("cluster_resource_id") != resource:
raise ValueError("resource identity mismatch")
if allocation.get("count_host_and_cluster_cpus_once") is not True:
raise ValueError("allocation must deduplicate host and cluster CPUs")
if cluster["captured_at"] != allocation["captured_at"]:
raise ValueError("cluster and reconcile snapshots differ")
if provenance.get("source_observation") != cluster["captured_at"]:
raise ValueError("provenance does not identify cluster snapshot")
seconds = duration(telemetry["window"])
if seconds != 604800:
raise ValueError("weekly report requires a seven-day telemetry window")
step = duration(telemetry["step"])
series, queries = metric_series(telemetry)
captured = {"cluster": cluster["captured_at"], "telemetry": telemetry["captured_at"]}
freshness = {}
for name, value in captured.items():
age = (now - timestamp(value)).total_seconds()
freshness[name] = {"age_seconds": age, "status": "future" if age < 0 else "stale" if age > max_age_hours * 3600 else "fresh"}
query_signature = digest({name: q["promql"] for name, q in queries.items()})
end = timestamp(telemetry["captured_at"])
comparison_reason = "no previous retained report"
compatible = False
if previous:
prev_end = timestamp(previous["period"]["capture_end"])
compatible = (
previous.get("schema") == SCHEMA and previous.get("resource_id") == resource
and previous["period"].get("seconds") == seconds
and previous["period"].get("step_seconds") == step
and previous.get("query_signature") == query_signature
and abs((end - prev_end).total_seconds() - seconds) <= step
)
comparison_reason = "comparable consecutive capture windows" if compatible else "nonconsecutive windows or changed query/resource/step contract"
previous_rows = {(r["namespace"], r["workload"]): r for r in (previous or {}).get("workloads", [])}
owners = {(r["namespace"], r["workload"]): r for r in allocation.get("workloads", [])}
if len(owners) != len(allocation.get("workloads", [])):
raise ValueError("duplicate allocation workload identity")
reviews = reviews or {}
rows, recommendations = [], []
seen = set()
for workload in sorted(cluster.get("utilization", {}).get("workloads", []), key=lambda x: (x["namespace"], x["workload"])):
key = workload["namespace"], workload["workload"]
if key in seen:
raise ValueError("duplicate cluster workload identity")
seen.add(key)
owner = owners.get(key, {})
measurements = {m: series.get(key, {}).get(m, {"value": None, "reason": "no workload series"}) for m in METRICS}
gaps = [m for m in REQUIRED if measurements[m]["value"] is None]
metric_stale = [m for m, v in measurements.items() if v["value"] is not None and not 0 <= (now - timestamp(v["window_end"])).total_seconds() <= max_age_hours * 3600]
metric_unaligned = [m for m, v in measurements.items() if v["value"] is not None and abs(v["capture_alignment_seconds"]) > step]
rid = recommendation_id(resource, *key)
reasons = []
if gaps:
reasons.append("missing workload metrics: " + ", ".join(gaps))
if metric_stale or any(v["status"] != "fresh" for v in freshness.values()):
reasons.append("stale or future observation")
if metric_unaligned:
reasons.append("metric evaluation windows differ from capture by more than one step")
if owner.get("owner", "unknown") == "unknown":
reasons.append("unknown owner")
if workload.get("requests", {}).get("cpu_millicores") == 0:
reasons.append("zero CPU request requires owner review")
review = reviews.get(rid)
decision, confidence, evidence = "investigate", "low", []
if review:
if review.get("decision") not in {"keep", "increase", "decrease", "investigate"}:
raise ValueError("invalid owner review decision")
required = ("owner", "evidence_refs", "receiving_task", "configuration", "rollback_ref", "reviewed_at", "period_end", "coverage_ref", "service_acceptance_ref", "margin_ref")
if any(not review.get(k) for k in required):
raise ValueError("owner review missing evidence, receiving task or configuration/rollback")
if review["owner"] != owner.get("owner") or review["period_end"] != telemetry["captured_at"]:
reasons.append("owner review does not match this owner/window")
elif timestamp(review["reviewed_at"]) > now:
reasons.append("owner review is future-dated")
elif not reasons:
decision, confidence, evidence = review["decision"], "owner-reviewed", review["evidence_refs"]
else:
reasons.append("owner proposal retained; incomplete evidence prevents accepted sizing recommendation")
else:
reasons.append("no owner-reviewed sizing decision")
row = {
"namespace": key[0], "workload": key[1], "owner": owner.get("owner", "unknown"),
"service": owner.get("service", "unknown"), "tenant": owner.get("tenant", "unknown"),
"pods": workload.get("pods"), "requests": workload.get("requests"),
"limits": workload.get("limits"), "instantaneous_observed": workload.get("observed"),
"metrics": measurements, "missing_metrics": gaps,
"namespace_context_ref": key[0] if (key[0], None) in series else None,
"raw_scrape_coverage": "unknown; evaluation counts do not prove source coverage",
"recommendation_id": rid, "week_on_week": {}, "comparison_reasons": {},
}
prior = previous_rows.get(key)
for metric in METRICS:
current = measurements[metric]["value"]
old_measurement = (prior or {}).get("metrics", {}).get(metric, {})
old = old_measurement.get("value")
metric_compatible = False
reason = comparison_reason
if compatible and current is not None and old is not None:
try:
old_end, old_start = timestamp(old_measurement["window_end"]), timestamp(old_measurement["window_start"])
new_end, new_start = timestamp(measurements[metric]["window_end"]), timestamp(measurements[metric]["window_start"])
metric_compatible = (
(old_end - old_start).total_seconds() == seconds
and (new_end - new_start).total_seconds() == seconds
and abs((new_end - old_end).total_seconds() - seconds) <= step
and abs((new_start - old_end).total_seconds()) <= step
and abs(measurements[metric]["capture_alignment_seconds"]) <= step
and abs((old_end - timestamp(previous["period"]["capture_end"])).total_seconds()) <= step
)
reason = "comparable consecutive metric windows" if metric_compatible else "metric evaluation windows are not consecutive or capture-aligned"
except (KeyError, ValueError, TypeError):
reason = "missing or invalid previous metric window"
elif compatible:
reason = "missing current or previous metric"
row["week_on_week"][metric] = current - old if metric_compatible else None
row["comparison_reasons"][metric] = reason
rows.append(row)
prior_recommendation = next((r for r in (previous or {}).get("recommendations", []) if r["id"] == rid), {})
recommendations.append({
"id": rid, "decision": decision, "confidence": confidence, "owner": row["owner"],
"reasons": reasons, "evidence_refs": evidence, "owner_proposal": review,
"receiving_task": review.get("receiving_task") if review else prior_recommendation.get("receiving_task"),
"previous_decision": prior_recommendation.get("decision"),
"owner_outcome": review.get("outcome") if review else None,
"delivery": "not emitted", "acknowledgment": None,
})
if seen != set(owners):
raise ValueError("cluster and allocation workload inventories differ")
return {
"schema": SCHEMA, "workplan": "CUST-WP-0071-T05", "as_of": as_of,
"adapter_sha256": hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),
"resource_id": resource, "period": {"capture_end": telemetry["captured_at"], "seconds": seconds, "step_seconds": step,
"note": "Actual per-query windows are retained on samples; capture time is not a common query evaluation time."},
"freshness": freshness, "max_age_hours": max_age_hours, "query_signature": query_signature,
"queries": queries, "collector_provenance": provenance,
"source_sha256": {"cluster": digest(cluster), "allocation": digest(allocation), "telemetry": digest(telemetry), "inventory": digest(inventory)},
"node": {"capacity": cluster.get("capacity"), "allocation": cluster.get("allocation"),
"scheduled_request_cpu_m": allocation.get("scheduled_request_cpu_m"),
"pending_unscheduled_cpu_m": allocation.get("pending_unscheduled_cpu_m"),
"reservation_residual_cpu_m": allocation.get("residual_cpu_m"),
"release_preflight": allocation.get("state_hub_preflight_observation"),
"operating_margin": None, "release_margin_acceptance": "not established by reservation residual",
"host_cluster_counted_once": True},
"portfolio": {k: inventory.get(k) for k in ("id", "schema_version", "ownership", "capacity", "relationships", "evidence")},
"workloads": rows,
"namespace_context": {ns: values for (ns, workload), values in sorted(series.items(), key=lambda x: str(x[0])) if workload is None},
"comparison": {"compatible": compatible, "reason": comparison_reason,
"previous_report_sha256": digest(previous) if previous else None,
"disappeared_workloads": [list(key) for key in sorted(set(previous_rows) - seen)]},
"measurement_gaps": cluster.get("measurement_gaps", []) + allocation.get("measurement_gaps", []),
"recommendations": recommendations,
"limitations": ["No representative pilot acceptance inferred from telemetry.",
"Namespace percentiles and peaks are not workload metrics or additive node peaks.",
"Portfolio capacity is dated evidence, not additional host capacity.",
"No procurement, sizing changes, owner-task creation, delivery or acknowledgment performed.",
"Installed executor, scoped reads, independent missed-run handling and first unattended receipt remain T05 gates."],
}
def retain(path, report):
"""Never overwrite a retained week; identical retries succeed."""
encoded = json.dumps(report, indent=2, sort_keys=True, allow_nan=False) + "\n"
path.parent.mkdir(parents=True, exist_ok=True)
temporary = None
try:
with tempfile.NamedTemporaryFile(mode="w", dir=path.parent, delete=False) as stream:
temporary = Path(stream.name)
stream.write(encoded)
stream.flush()
os.fsync(stream.fileno())
try:
os.link(temporary, path)
except FileExistsError:
if path.is_symlink() or path.read_text() != encoded:
raise ValueError("retained report differs; choose a new explicit revision path")
finally:
if temporary is not None:
temporary.unlink(missing_ok=True)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--request", type=Path, help="Executor-owned JSON request; paths relative to its directory")
for name in ("cluster", "allocation", "telemetry", "inventory", "provenance"):
parser.add_argument("--" + name, type=Path)
parser.add_argument("--previous", type=Path)
parser.add_argument("--reviews", type=Path)
parser.add_argument("--as-of")
parser.add_argument("--max-age-hours", type=float, default=48)
parser.add_argument("--output", type=Path)
args = parser.parse_args()
try:
if args.request:
request = json.loads(args.request.read_text())
if request.get("schema") != "custodian.weekly-allocation-request.v1":
raise ValueError("unsupported executor request schema")
if any(getattr(args, key) for key in ("cluster", "allocation", "telemetry", "inventory", "provenance", "previous", "reviews", "as_of", "output")):
raise ValueError("request cannot be mixed with input/output overrides")
for key in ("cluster", "allocation", "telemetry", "inventory", "provenance", "previous", "reviews", "output"):
value = request.get(key)
setattr(args, key, args.request.parent / value if value else None)
args.as_of = request.get("as_of")
if any(not getattr(args, key) for key in ("cluster", "allocation", "telemetry", "inventory", "provenance", "as_of", "output")):
raise ValueError("executor input paths, as_of and retention output must be explicitly bound")
if not math.isfinite(args.max_age_hours) or args.max_age_hours <= 0:
raise ValueError("max-age-hours must be positive and finite")
inputs = {name: json.loads(getattr(args, name).read_text()) for name in ("cluster", "allocation", "telemetry", "inventory", "provenance")}
report = build_report(**inputs, as_of=args.as_of, max_age_hours=args.max_age_hours,
previous=json.loads(args.previous.read_text()) if args.previous else None,
reviews=json.loads(args.reviews.read_text()) if args.reviews else None)
retain(args.output, report)
except (OSError, ValueError, KeyError, TypeError) as exc:
parser.exit(2, f"weekly allocation report refused: {exc}\n")
print(json.dumps({"status": "retained", "path": str(args.output), "sha256": digest(report),
"workloads": len(report["workloads"]), "delivery": "not emitted"}))
if __name__ == "__main__":
main()

View file

@ -1,55 +0,0 @@
{
"files": {
"namespace-ownership.json": {
"commit": "9edd3151ac5c93e5319a9581c0df145f8d93c68f",
"path": "docs/evidence/namespace-ownership.yaml",
"repo": "the-custodian",
"source_sha256": "a3663c03f685db35d5e0eabca59e1d74810b1b56c39db0892a58903302bb771f",
"vendored_sha256": "dd73079defcc26db460ee1ed0ef17a5eab39b04d9dbc209cb6d02064df8f3f05"
},
"observe_cluster_resources.py": {
"commit": "a5948408a565d383a99f43d414145277a43ce233",
"path": "tools/observe_cluster_resources.py",
"repo": "railiance-cluster",
"source_sha256": "7a613f7ba692169ec9ddf498f7250ee2a56f8fb1088585498af6df2302e15002",
"vendored_sha256": "7a613f7ba692169ec9ddf498f7250ee2a56f8fb1088585498af6df2302e15002"
},
"reconcile_railiance_allocation.py": {
"commit": "9edd3151ac5c93e5319a9581c0df145f8d93c68f",
"path": "scripts/reconcile_railiance_allocation.py",
"repo": "the-custodian",
"source_sha256": "3e118023397bbb6621116f6f3009cb6ec7f894809671efeb5c165dd2c3e32a3a",
"vendored_sha256": "3e118023397bbb6621116f6f3009cb6ec7f894809671efeb5c165dd2c3e32a3a"
},
"reef-railiance-k3s.json": {
"commit": "3d9b1bd08043735e71246213e46c91f98f41d6ce",
"path": "data/resources/reef-railiance-k3s.json",
"repo": "resource-control",
"source_sha256": "c97cb2c505cc52dcf1e1f394d35a8d87dfec09a9033f6cd4c0c05717111f913f",
"vendored_sha256": "c97cb2c505cc52dcf1e1f394d35a8d87dfec09a9033f6cd4c0c05717111f913f"
},
"weekly-allocation-queries.json": {
"commit": "9edd3151ac5c93e5319a9581c0df145f8d93c68f",
"path": "config/weekly-allocation-queries.json",
"repo": "the-custodian",
"source_sha256": "83bde8a6e53c4b86b9624195b9a00297310eefb1e271ba4506622c1efdd7e7a9",
"vendored_sha256": "83bde8a6e53c4b86b9624195b9a00297310eefb1e271ba4506622c1efdd7e7a9"
},
"weekly_allocation_incluster.py": {
"commit": "9edd3151ac5c93e5319a9581c0df145f8d93c68f",
"path": "scripts/weekly_allocation_incluster.py",
"repo": "the-custodian",
"source_sha256": "de21c18111f818bf31d9d02763e170a83d1e7581152759f7e1962c4402120d77",
"vendored_sha256": "de21c18111f818bf31d9d02763e170a83d1e7581152759f7e1962c4402120d77"
},
"weekly_allocation_report.py": {
"commit": "9edd3151ac5c93e5319a9581c0df145f8d93c68f",
"path": "scripts/weekly_allocation_report.py",
"repo": "the-custodian",
"source_sha256": "93f546243331308d7e60f8db36e988d9847537a2fe46cce49ee2fffba1f7f247",
"vendored_sha256": "93f546243331308d7e60f8db36e988d9847537a2fe46cce49ee2fffba1f7f247"
}
},
"schema": "railiance-platform.vendor-lock.v1",
"workplan": "CUST-WP-0071-T05"
}

View file

@ -1,29 +0,0 @@
# Scoped ops-mason builder (founder decision 2026-10-22, MASON-IN-0006 (b)).
# Kinds kubernetes_plane applies, plus get/list/watch on services for dependency
# reads, plus create on self-subject access reviews so kubectl auth can-i works.
# Excluded: pods, secrets, configmaps, serviceaccounts/token, impersonate,
# roles, rolebindings, clusterroles, clusterrolebindings, nodes/proxy, pods/exec,
# escalate, bind, wildcards.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: ops-mason-builder
labels:
app.kubernetes.io/part-of: railiance-gitops
railiance-platform/component: ops-mason-builder
rules:
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["create", "get", "list", "watch", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["serviceaccounts"]
verbs: ["create", "get", "list", "watch", "update", "patch", "delete"]
- apiGroups: ["networking.k8s.io"]
resources: ["networkpolicies"]
verbs: ["create", "get", "list", "watch", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "watch"]
- apiGroups: ["authorization.k8s.io"]
resources: ["selfsubjectaccessreviews", "selfsubjectrulesreviews"]
verbs: ["create"]

View file

@ -1,15 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: ops-mason-builder
labels:
app.kubernetes.io/part-of: railiance-gitops
railiance-platform/component: ops-mason-builder
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: ops-mason-builder
subjects:
- kind: User
name: ops-mason-builder
apiGroup: rbac.authorization.k8s.io

View file

@ -1,11 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# MASON-IN-0006 choice (b), decision 30d29060-8d14-4a8e-9cde-34aa826eb8d1.
# Scoped builder for ops-mason. User ops-mason-builder, no ServiceAccount token,
# no Secret. Applied by argocd/railiance01/applications/ops-mason-builder.application.yaml.
# Manual sync. targetRevision is pinned to the commit that introduced these manifests.
resources:
- clusterrole.yaml
- clusterrolebinding.yaml

View file

@ -12,8 +12,3 @@ with the founder's go-ahead: no `automated` block, no finalizer, and a pinned
`targetRevision`. A merge here plus a hand sync of the root is a live
production change on railiance01. Drafts wait in `../drafts/`, which no root
syncs.
A release that changes a child's `targetRevision` commits that pin here in the
same change window, whether or not the live Application was patched directly.
Before any hand sync of the root, run `make argocd-pin-drift`; any `DRIFT`
line means the sync would roll that child back, so stop and reconcile first.

View file

@ -1,7 +1,4 @@
# ACTIVITY-WP-0041 / RPF-WP-0048: founder-authorized adoption, 2026-09-27.
# Pin = live revision of the 2026-10-20 founder-approved release
# (activity-core docs/evidence/2026-10-20-release-health-probe-release.json).
# Every activity-core release commits its pin here; see RPF-WP-ADHOC-2026-10-19.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
@ -13,7 +10,7 @@ spec:
project: activity-core
source:
repoURL: https://forgejo.coulomb.social/coulomb/activity-core.git
targetRevision: 1fe2fcc0d51c086db00d5f4d8b4e2fadb1d89a1d
targetRevision: ce9a8f8edf4ec162bae3aa087803bd57a1b33a66
path: k8s/gitops
destination:
server: https://kubernetes.default.svc

View file

@ -1,8 +1,6 @@
# railiance01 child Application (RPF-WP-0047, founder request 2026-09-23 via
# user-engine 7d8982c2). Static notice only; OpenBao stays non-public.
# No automated sync, no finalizer; targetRevision is the reviewed commit.
# Manifests moved to rapp-openbao on 2026-10-23. This Application stays the
# GitOps attachment. Sync it by hand after that commit is on the remote.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
@ -16,9 +14,9 @@ metadata:
spec:
project: railiance-tenants
source:
repoURL: https://forgejo.coulomb.social/coulomb/rapp-openbao.git
targetRevision: a1f5851e8ad16df5689ef8c91273d77590863089
path: manifests/bao-notice
repoURL: https://forgejo.coulomb.social/coulomb/railiance-platform.git
targetRevision: 9ee20967ca7b50a8926c99ae86fa027f8bfb0fa3
path: argocd/platform-addons/bao-notice
destination:
server: https://kubernetes.default.svc
namespace: bao-notice

View file

@ -1,26 +0,0 @@
# railiance01 child Application (RPF-WP-0062 / CUST-WP-0073-T02, founder decision 2026-10-20).
# Read-only custodian agent observer identity. No automated sync, no finalizer;
# targetRevision is the reviewed commit that introduced the manifests.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: custodian-agents
namespace: argocd
labels:
app.kubernetes.io/part-of: railiance-gitops
railiance-platform/component: custodian-agent-identity
annotations:
argocd.argoproj.io/sync-wave: "10"
spec:
project: railiance-platform-addons
source:
repoURL: https://forgejo.coulomb.social/coulomb/railiance-platform.git
targetRevision: 567d2c671bb0e36bef2f7f2767414ea76aa8cf9a
path: argocd/platform-addons/custodian-agents
destination:
server: https://kubernetes.default.svc
namespace: custodian-agents
syncPolicy:
syncOptions:
- ApplyOutOfSyncOnly=true
- PruneLast=true

View file

@ -7,7 +7,7 @@ spec:
project: informed-decision-lifecycle
source:
repoURL: https://forgejo.coulomb.social/coulomb/informed-decision.git
targetRevision: 312ab97e876e17341bf16f9e95397e14c2ac2720
targetRevision: 9207a5134f92904f3843c200645ced91f247af72
path: deploy/lifecycle
destination:
server: https://kubernetes.default.svc

View file

@ -1,27 +0,0 @@
# railiance01 child Application (MASON-IN-0006 choice (b), decision
# 30d29060-8d14-4a8e-9cde-34aa826eb8d1). Scoped ops-mason builder identity.
# No automated sync, no finalizer; targetRevision is the commit that
# introduced the manifests.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: ops-mason-builder
namespace: argocd
labels:
app.kubernetes.io/part-of: railiance-gitops
railiance-platform/component: ops-mason-builder
annotations:
argocd.argoproj.io/sync-wave: "10"
spec:
project: railiance-platform-addons
source:
repoURL: https://forgejo.coulomb.social/coulomb/railiance-platform.git
targetRevision: bd6660a91672c76b78e017c22394705381d8767d
path: argocd/platform-addons/ops-mason-builder
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
syncOptions:
- ApplyOutOfSyncOnly=true
- PruneLast=true

View file

@ -14,7 +14,7 @@ spec:
project: railiance-tenants
source:
repoURL: https://forgejo.coulomb.social/coulomb/target-revenue.git
targetRevision: f110a4ea96be42335586d2921a987db73fa6b930
targetRevision: a3a8a27a8cda32ae3c7b55353ee16a131c50a0a0
path: k8s/railiance
destination:
server: https://kubernetes.default.svc

View file

@ -12,7 +12,6 @@ spec:
- https://forgejo.coulomb.social/coulomb/*.git
# Emergency rollback only (T12 Option A through 2026-08-07)
- https://gitea.coulomb.social/coulomb/*.git
- ssh://git@forgejo.coulomb.social:30022/coulomb/rapp-issue-core.git
destinations:
- server: https://kubernetes.default.svc
namespace: "*"

View file

@ -46,12 +46,6 @@ spec:
kind: Deployment
- group: batch
kind: CronJob
# CUST-WP-0071-T05 (founder decision 2026-10-21): custodian-allocation-review
# needs its retained-report claim and default-deny egress policy.
- group: ""
kind: PersistentVolumeClaim
- group: networking.k8s.io
kind: NetworkPolicy
- group: rbac.authorization.k8s.io
kind: Role
- group: rbac.authorization.k8s.io

View file

@ -1,29 +0,0 @@
# DRAFT — not synced by any root (drafts/ is never referenced). CUST-WP-0071-T05.
# Activation (founder go-ahead, after CUST-WP-0071-T04 and review): add
# PersistentVolumeClaim to the railiance-platform-addons AppProject, set
# targetRevision to the reviewed commit, move this file to ../applications/,
# run `make argocd-pin-drift`, then hand-sync the root and this Application.
# See argocd/platform-addons/custodian-allocation-review/README.md.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: custodian-allocation-review
namespace: argocd
labels:
app.kubernetes.io/part-of: railiance-gitops
railiance-platform/component: custodian-allocation-review
annotations:
argocd.argoproj.io/sync-wave: "10"
spec:
project: railiance-platform-addons
source:
repoURL: https://forgejo.coulomb.social/coulomb/railiance-platform.git
targetRevision: REPLACE_WITH_REVIEWED_COMMIT_SHA
path: argocd/platform-addons/custodian-allocation-review
destination:
server: https://kubernetes.default.svc
namespace: custodian-assessment
syncPolicy:
syncOptions:
- ApplyOutOfSyncOnly=true
- PruneLast=true

View file

@ -1,5 +1,5 @@
# DRAFT for railiance01 (RPF-WP-0044). Not synced by any root: move to
# ../applications/ only after predecessor automated proof under accepted D2.
# ../applications/ only in this app's adoption task, with the founder's go-ahead.
# No automated sync, no finalizer. CRDs survive Application pruning/deletion.
apiVersion: argoproj.io/v1alpha1
kind: Application
@ -27,15 +27,6 @@ spec:
serviceAccount:
create: true
name: external-secrets
# Wave-2 requests (RPF-WP-0063-T01); mirrors helm/external-secrets-resources.values.yaml
resources:
requests: {cpu: 10m, memory: 64Mi}
certController:
resources:
requests: {cpu: 10m, memory: 64Mi}
webhook:
resources:
requests: {cpu: 10m, memory: 80Mi}
destination:
server: https://kubernetes.default.svc
namespace: external-secrets

View file

@ -1,6 +1,6 @@
# railiance01 issue-core (RPF-WP-0044-T04, adopted 2026-10-20 under D2 89f1058f + founder go).
# No automated sync, no finalizer. Read-only SSH deploy key: argocd/rapp-issue-core-repository.
# targetRevision: rapp-issue-core 11af3cb (wave-2 requests 5m/48Mi, ISSUE-WP-0008-T01; on top of 805e124 forge-http URL).
# DRAFT for railiance01 (RPF-WP-0044). Not synced by any root: move to
# ../applications/ only in this app's adoption task, with the founder's go-ahead.
# No automated sync, no finalizer. targetRevision: rapp-issue-core commit diffed 2026-09-21.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
@ -14,8 +14,8 @@ metadata:
spec:
project: railiance-tenants
source:
repoURL: ssh://git@forgejo.coulomb.social:30022/coulomb/rapp-issue-core.git
targetRevision: 11af3cbf359ba850c8a807d7cc8b369c037862df
repoURL: https://forgejo.coulomb.social/coulomb/rapp-issue-core.git
targetRevision: a78f38cfd8afb29ed23d150a4ea3e3dcd1f35381
path: manifests
destination:
server: https://kubernetes.default.svc

View file

@ -1,30 +0,0 @@
# Owner-review candidate only. Not a kubectl resource and not activated.
# Cluster owner must merge with existing authenticators, validate natively and
# preserve existing admin/service-account authentication and rollback access.
apiVersion: apiserver.config.k8s.io/v1
kind: AuthenticationConfiguration
jwt:
- issuer:
url: https://kc.coulomb.social
audiences:
- playwright-kubernetes
audienceMatchPolicy: MatchAny
claimValidationRules:
- expression: "claims.aud == 'playwright-kubernetes' || claims.aud == ['playwright-kubernetes']"
message: dedicated Playwright audience required
- expression: "has(claims.scope) && 'playwright:connect' in claims.scope.split(' ')"
message: Playwright access scope required
- expression: "claims.principal_type == 'human' || (claims.principal_type == 'service' && has(claims.roles) && 'coding-agent' in claims.roles)"
message: identified human or registered coding agent required
- expression: "claims.sub != '' && claims.exp > claims.iat && claims.exp - claims.iat <= 3600"
message: nonempty subject and bounded token lifetime required
claimMappings:
username:
expression: "'keycape:' + claims.principal_type + ':' + claims.sub"
groups:
expression: "['playwright-consumers']"
userValidationRules:
- expression: "!user.username.startsWith('system:')"
message: reserved Kubernetes identity prefix refused
- expression: "user.groups == ['playwright-consumers']"
message: only the Playwright consumer group may be mapped

View file

@ -1,9 +0,0 @@
{
"clientId": "playwright-cli",
"audience": "playwright-kubernetes",
"displayName": "Playwright platform consumer CLI",
"redirectUris": ["http://127.0.0.1:18764/callback"],
"allowedScopes": ["openid", "profile", "groups", "playwright:connect"],
"grantTypes": ["authorization_code"],
"clientType": "public"
}

View file

@ -1,10 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIBeDCCAR2gAwIBAgIBADAKBggqhkjOPQQDAjAjMSEwHwYDVQQDDBhrM3Mtc2Vy
dmVyLWNhQDE3NzMxMDM2MjAwHhcNMjYwMzEwMDA0NzAwWhcNMzYwMzA3MDA0NzAw
WjAjMSEwHwYDVQQDDBhrM3Mtc2VydmVyLWNhQDE3NzMxMDM2MjAwWTATBgcqhkjO
PQIBBggqhkjOPQMBBwNCAAT44CzyUUGic6xYSLOApGq8A61OcoP/v+nns47p3F1z
M89iyNSZqtIHRpg0BxiMtTcdwXKart6aPIC8/8PfpVyAo0IwQDAOBgNVHQ8BAf8E
BAMCAqQwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUG/vhGaKEp4uD2KaSPDTU
249sq2EwCgYIKoZIzj0EAwIDSQAwRgIhALUc7a0LcGfMiAI+auFTByXlL4hTH0Cr
DxsOiRhV9D0vAiEAjTVKYP/KFPe68cAL8S76mLKdKhd564XiVAeISh5NkG4=
-----END CERTIFICATE-----

View file

@ -5,7 +5,7 @@ request_type: workload-kv-read
title: Railiance offsite backup lane (Nextcloud WebDAV + age recovery)
status: active
created: '2026-07-07'
updated: '2026-10-07'
updated: '2026-09-05'
requester:
agent: grok
reason: Move railiance-backup / forgejo-backup credentials from hardcoded script
@ -20,12 +20,6 @@ review:
decision: approved
comment: "Approved in chat \u2014 establish backup lane credentials in OpenBao\
\ for workstation forgejo-backup and railiance-backup tooling (Option A Nextcloud)."
- at: '2026-10-07'
reviewer: platform-operator (founder Bernd Worsch)
decision: approved
comment: Founder decision 73fff3d3-49fd-4535-9040-b2754a3e2024 approves removing
the retired bao.coulomb.social OIDC callback from allowed_redirect_uris and
one delegated re-apply. Nothing else changes.
target:
domain: financials
tenant: railiance
@ -46,6 +40,7 @@ openbao:
mount: netkingdom
role: railiance-backup-workload-kv-read
allowed_redirect_uris:
- https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback
- http://localhost:8250/oidc/callback
- http://127.0.0.1:8250/oidc/callback
oidc_scopes:
@ -77,13 +72,13 @@ risk:
classification: high
notes:
- NC_WEBDAV_TOKEN grants upload to the offsite backup file drop.
- Since 2026-09-05 the file drop belongs to Backup, folder /railiance-backups, with
create-only permissions and a server-enforced 10 GiB account quota.
- Operator credentials remain in KVv2 operators/nextcloud/backup, fields BACKUP_USERNAME
and BACKUP_PASSWORD; never deliver them to production.
- Existing Bernd-owned retained backups and recovery access remain separate; predecessor
file-drop unshared 2026-09-15 by operator attestation; age-key exposure remains
open.
- Since 2026-09-05 the file drop belongs to Backup, folder /railiance-backups,
with create-only permissions and a server-enforced 10 GiB account quota.
- Operator credentials remain in KVv2 operators/nextcloud/backup, fields
BACKUP_USERNAME and BACKUP_PASSWORD; never deliver them to production.
- Existing Bernd-owned retained backups and recovery access remain separate;
predecessor file-drop unshared 2026-09-15 by operator attestation; age-key
exposure remains open.
- "AGE_PRIVATE_KEY decrypts all age-encrypted backup artifacts \u2014 recovery escrow\
\ only."
- Credentials must not be stored on production hosts with delete permission.
@ -122,20 +117,13 @@ verification:
kind: capabilities_safe_verify
result: passed
details:
- 'WP-0026 T07: capabilities-safe re-verify on bao.coulomb.social (no secret values
read).'
- "Positive: token with policy workload-kv-read-railiance-backup-offsite-lane\
\ \u2192 capabilities read on platform/data/workloads/railiance/backup/offsite-lane."
- "Negative: default-policy token \u2192 deny on the same data path; token revoked\
\ after check."
- "Agent boundary: policy agent-high-risk-boundary \u2192 deny on data path, read\
\ on metadata path."
- Field presence via JSON key inventory only (NC_WEBDAV_TOKEN, NC_WEBDAV_URL,
AGE_PRIVATE_KEY); lengths recorded, values not printed.
- 'EXPOSED taint set: custom_metadata exposed_at=2026-07-16T00:00:00Z exposed_version=2
(WP-0026 T05).'
- "ops-warden catalog promoted draft\u2192active; fetch_command pinned to NC_WEBDAV_TOKEN;\
\ resolvable=true; risk=high."
- 'WP-0026 T07: capabilities-safe re-verify on bao.coulomb.social (no secret values read).'
- 'Positive: token with policy workload-kv-read-railiance-backup-offsite-lane → capabilities read on platform/data/workloads/railiance/backup/offsite-lane.'
- 'Negative: default-policy token → deny on the same data path; token revoked after check.'
- 'Agent boundary: policy agent-high-risk-boundary → deny on data path, read on metadata path.'
- 'Field presence via JSON key inventory only (NC_WEBDAV_TOKEN, NC_WEBDAV_URL, AGE_PRIVATE_KEY); lengths recorded, values not printed.'
- 'EXPOSED taint set: custom_metadata exposed_at=2026-07-16T00:00:00Z exposed_version=2 (WP-0026 T05).'
- 'ops-warden catalog promoted draft→active; fetch_command pinned to NC_WEBDAV_TOKEN; resolvable=true; risk=high.'
- at: '2026-09-05'
actor: codex
kind: dedicated_backup_account_cutover
@ -143,8 +131,8 @@ verification:
details:
- User authorized dedicated Backup account and operator custody coordinates.
- Verified 10737418240-byte quota and Backup-owned create-only share.
- Encrypted fixture PUT 201, owner download and escrow decryption passed; runtime
GET and DELETE returned 405 on the actual upload endpoint.
- Encrypted fixture PUT 201, owner download and escrow decryption passed;
runtime GET and DELETE returned 405 on the actual upload endpoint.
- CAS advanced workload KV version 2 to 3, preserving age escrow and other fields.
- Evidence docs/evidence/RPF-WP-0029-backup-account-2026-09-05.json.
- at: '2026-09-15'
@ -156,15 +144,7 @@ verification:
- No predecessor value, fingerprint, length or shape was recorded.
- HTTP 401/403 probe not run; predecessor must not be reconstructed.
- Evidence docs/evidence/2026-09-15-rpf-wp-0029-predecessor-share-invalidated.json.
- at: '2026-10-07T13:10:39+00:00'
actor: founder
kind: delegated_metadata_apply
result: passed
details:
- Delegated metadata applier ran as founder using local bao CLI ambient authority.
- 'Policy metadata write: sys/policies/acl/workload-kv-read-railiance-backup-offsite-lane'
- 'Auth role metadata write: auth/netkingdom/role/railiance-backup-workload-kv-read'
- No secret values were read, written, printed, or accepted in argv.
lifecycle:
deactivate: Disable ops-warden catalog entry and detach OIDC role policy; rotate
Nextcloud file-drop token at provider.

View file

@ -5,7 +5,7 @@ request_type: workload-kv-read
title: secrets-engine client-side read of its approval client secret
status: applied
created: '2026-09-09'
updated: '2026-10-07'
updated: '2026-09-13'
requester:
agent: claude
reason: RPF-WP-0035-T06 residual from the completed verifier custody (CCR-2026-0017).
@ -55,12 +55,6 @@ review:
not a fabricated separate owner vote or approval-engine human decision. Live
delivery verification remains pending and the credential front door remains
non-resolvable.
- at: '2026-10-07'
reviewer: platform-operator, secrets-engine-owner (founder Bernd Worsch)
decision: approved
comment: Founder decision 73fff3d3-49fd-4535-9040-b2754a3e2024 approves removing
the retired bao.coulomb.social OIDC callback from allowed_redirect_uris and
one delegated re-apply. Nothing else changes.
target:
domain: financials
tenant: platform
@ -81,6 +75,7 @@ openbao:
mount: netkingdom
role: secrets-engine-approval-client-workload-kv-read
allowed_redirect_uris:
- https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback
- http://localhost:8250/oidc/callback
- http://127.0.0.1:8250/oidc/callback
oidc_scopes:
@ -162,15 +157,6 @@ verification:
- 'Policy metadata write: sys/policies/acl/workload-kv-read-secrets-engine-approval-client'
- 'Auth role metadata write: auth/netkingdom/role/secrets-engine-approval-client-workload-kv-read'
- No secret values were read, written, printed, or accepted in argv.
- at: '2026-10-07T13:10:43+00:00'
actor: founder
kind: delegated_metadata_apply
result: passed
details:
- Delegated metadata applier ran as founder using local bao CLI ambient authority.
- 'Policy metadata write: sys/policies/acl/workload-kv-read-secrets-engine-approval-client'
- 'Auth role metadata write: auth/netkingdom/role/secrets-engine-approval-client-workload-kv-read'
- No secret values were read, written, printed, or accepted in argv.
lifecycle:
deactivate: Detach the policy from role secrets-engine-approval-client-workload-kv-read
and disable the ops-warden catalog entry. The KeyCape verifier lane and its delivery

View file

@ -3,62 +3,29 @@ kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: KeyCape secrets-engine-openbao issuer workload reader and authoritative custody
status: applied
status: proposed
created: '2026-10-04'
updated: '2026-10-07'
updated: '2026-10-06'
requester:
agent: key-cape
reason: 'KEY-WP-0037-T02 owner packet; issuer-side read of the authoritative secrets-engine-openbao
client secret. Revised 2026-10-06 per founder direction (the secret lives only
in OpenBao) and ops-mason plan keycape-secrets-engine-openbao-client-custody (658cdbc,
MASON-WP-0009): consumer file and secrets-engine D1-D4 removed.'
client secret. Revised 2026-10-06 per founder direction (the secret lives only in OpenBao)
and ops-mason plan keycape-secrets-engine-openbao-client-custody (658cdbc, MASON-WP-0009):
consumer file and secrets-engine D1-D4 removed.'
review:
required: true
required_approvers:
- platform-owner
- key-cape-owner
- net-kingdom-owner
comments:
- at: '2026-10-07'
reviewer: platform-owner (founder Bernd Worsch)
decision: approved
comment: Founder decision 2ab717e1-7881-491e-aed4-28946550c289 (resolved 2026-10-06)
approves CCR-2026-0033 as platform-owner. Covers A1 (ops-mason design 658cdbc),
A2 (this CCR and CCR-2026-0034 at 4a284f0, bound claims of both readers confirmed)
and A3 (exactly one attended custody-child provision run, cas=0, KV version
1). Does not approve the policy/role/ESO apply, the NetKingdom env patch, the
KeyCape registration, the restart or any rotation.
- at: '2026-10-07'
reviewer: key-cape-owner (founder Bernd Worsch)
decision: approved
comment: Founder decision 2ab717e1-7881-491e-aed4-28946550c289 (resolved 2026-10-06)
approves CCR-2026-0033 as key-cape-owner. Covers A1 (ops-mason design 658cdbc),
A2 (this CCR and CCR-2026-0034 at 4a284f0, bound claims of both readers confirmed)
and A3 (exactly one attended custody-child provision run, cas=0, KV version
1). Does not approve the policy/role/ESO apply, the NetKingdom env patch, the
KeyCape registration, the restart or any rotation.
- at: '2026-10-07'
reviewer: net-kingdom-owner (founder Bernd Worsch)
decision: approved
comment: Founder decision 2ab717e1-7881-491e-aed4-28946550c289 (resolved 2026-10-06)
approves CCR-2026-0033 as net-kingdom-owner. Covers A1 (ops-mason design 658cdbc),
A2 (this CCR and CCR-2026-0034 at 4a284f0, bound claims of both readers confirmed)
and A3 (exactly one attended custody-child provision run, cas=0, KV version
1). Does not approve the policy/role/ESO apply, the NetKingdom env patch, the
KeyCape registration, the restart or any rotation.
- at: '2026-10-07'
reviewer: platform-owner, key-cape-owner, net-kingdom-owner (founder Bernd Worsch)
decision: approved
comment: Founder decision 1fdd993b-8899-4f87-8a8e-636346bb9692 approves the token_self_lifecycle
amendment (lookup-self and revoke-self on the ESO reader's own token) and one
delegated re-apply of this CCR's policy and role. Nothing else changes.
comments: []
target:
domain: infotech
tenant: platform
workload: keycape
environment: production
purpose: Hold the secrets-engine-openbao client secret authoritatively in platform
KV and deliver it to the KeyCape issuer through sso-only ESO
purpose: Hold the secrets-engine-openbao client secret authoritatively in platform KV and
deliver it to the KeyCape issuer through sso-only ESO
openbao:
mount: platform
kv_path: platform/workloads/keycape/secrets-engine-openbao
@ -67,7 +34,6 @@ openbao:
policy_name: workload-kv-read-keycape-secrets-engine-openbao-eso
policy_file: openbao/policies/workload-kv-read-keycape-secrets-engine-openbao-eso.hcl
metadata_read: false
token_self_lifecycle: true
auth:
method: kubernetes
mount: kubernetes
@ -84,7 +50,7 @@ openbao:
- external-secrets
service_account_namespaces:
- external-secrets
bound_claims_confirmed: true
bound_claims_confirmed: false
kv_metadata:
cas_required: true
max_versions: 2
@ -107,47 +73,27 @@ risk:
classification: high
notes:
- Reads the whole path holding the secrets-engine-openbao confidential value
- Only ExternalSecrets in namespace sso may use the store; sso namespace writers
are within this boundary
- 'One value, two copies: KV (authoritative) and the sso Secret (issuer delivery).
The consumer holds no copy at rest; it reads in memory through CCR-2026-0034.'
- 'Must stay on kubernetes auth: a KeyCape-issued identity would make KeyCape startup
depend on KeyCape.'
- Only ExternalSecrets in namespace sso may use the store; sso namespace writers are within
this boundary
- 'One value, two copies: KV (authoritative) and the sso Secret (issuer delivery). The consumer
holds no copy at rest; it reads in memory through CCR-2026-0034.'
- 'Must stay on kubernetes auth: a KeyCape-issued identity would make KeyCape startup depend
on KeyCape.'
verification:
positive:
- Owner-reviewed native exact identity and exact path proof required
negative:
- Owner-reviewed wrong identity, sibling GET and parent LIST denials required
- Metadata shows cas_required=true, max_versions=2 and the declared custom_metadata
(value-free).
- Metadata shows cas_required=true, max_versions=2 and the declared custom_metadata (value-free).
activation_conditions:
- Exact CCR approval, confirmed identity, attended custody and native receipt acceptance
required
evidence:
- at: '2026-10-07T00:03:08+00:00'
actor: founder
kind: delegated_metadata_apply
result: passed
details:
- Delegated metadata applier ran as founder using local bao CLI ambient authority.
- 'Policy metadata write: sys/policies/acl/workload-kv-read-keycape-secrets-engine-openbao-eso'
- 'Auth role metadata write: auth/kubernetes/role/external-secrets-keycape-secrets-engine-openbao'
- No secret values were read, written, printed, or accepted in argv.
- at: '2026-10-07T00:10:09+00:00'
actor: founder
kind: delegated_metadata_apply
result: passed
details:
- Delegated metadata applier ran as founder using local bao CLI ambient authority.
- 'Policy metadata write: sys/policies/acl/workload-kv-read-keycape-secrets-engine-openbao-eso'
- 'Auth role metadata write: auth/kubernetes/role/external-secrets-keycape-secrets-engine-openbao'
- No secret values were read, written, printed, or accepted in argv.
lifecycle:
deactivate: Owner-reviewed delivery withdrawal and token revocation; exact rollback
receipt required
rotate: 'Roll-forward only: custody child writes a new version with cas=<current>
under a standing rotation approval for this CCR (event-driven + 90d); never a
read or restore of an old version'
deactivate: Owner-reviewed delivery withdrawal and token revocation; exact rollback receipt
required
rotate: 'Roll-forward only: custody child writes a new version with cas=<current> under
a standing rotation approval for this CCR (event-driven + 90d); never a read or restore
of an old version'
compromised: Owner incident process; no automatic read, reseed or cleanup
scaffold:
draft_only: true
@ -161,65 +107,64 @@ state_hub:
related_workplan: KEY-WP-0037
delivery:
surface: external-secrets
target: sso/keycape-secrets-engine-openbao-client key client-secret; issuer-side
delivery only
target: sso/keycape-secrets-engine-openbao-client key client-secret; issuer-side delivery
only
review_bundle: docs/provisioning-windows/2026-10-04-1000/keycape
consumer_delivery: none at rest; attended operator OIDC reader CCR-2026-0034 reads
into secrets-engine process memory per command
consumer_delivery: none at rest; attended operator OIDC reader CCR-2026-0034 reads into
secrets-engine process memory per command
boundary_decision:
id: 632a8893-0d6a-4591-a50b-40bbdb798f32
approves: custody boundary only (platform authoritative KV and least-privilege issuer
delivery; secrets-engine-owned mode-0600 consumer file outside every worktree)
approves: custody boundary only (platform authoritative KV and least-privilege issuer delivery;
secrets-engine-owned mode-0600 consumer file outside every worktree)
does_not_approve: generation, KV write, fetch, delivery, registration or deployment
note: 'The design narrows this boundary: the secrets-engine consumer file it approved
is withdrawn (founder direction 2026-10-06).'
path_correction: 'Candidate path platform/services/keycape/secrets-engine-openbao
from the KeyCape packet is outside the platform-mount applier scope (WARDEN-WP-0028:
platform/workloads/ only). Corrected to platform/workloads/keycape/secrets-engine-openbao,
same field CLIENT_SECRET. The issuer binds to the sso Secret; the operator reader
(CCR-2026-0034) reads the exact data path.'
note: 'The design narrows this boundary: the secrets-engine consumer file it approved is
withdrawn (founder direction 2026-10-06).'
path_correction: 'Candidate path platform/services/keycape/secrets-engine-openbao from the
KeyCape packet is outside the platform-mount applier scope (WARDEN-WP-0028: platform/workloads/
only). Corrected to platform/workloads/keycape/secrets-engine-openbao, same field CLIENT_SECRET.
The issuer binds to the sso Secret; the operator reader (CCR-2026-0034) reads the exact
data path.'
custody_child:
script: scripts/keycape_secrets_engine_openbao_custody.py
tests: tests/test_keycape_secrets_engine_openbao_custody.py
status: source-only; never executed
requires:
- --approval-decision <A3-decision-id>; refused when absent, malformed, equal to
the boundary decision, or not recorded in an approved review comment of this CCR
- this CCR and sibling CCR-2026-0034 approved with platform-owner, key-cape-owner
and net-kingdom-owner approvals and bound_claims_confirmed true
- --approval-decision <A3-decision-id>; refused when absent, malformed, equal to the boundary
decision, or not recorded in an approved review comment of this CCR
- this CCR and sibling CCR-2026-0034 approved with platform-owner, key-cape-owner and net-kingdom-owner
approvals and bound_claims_confirmed true
- attended warden platform-admin envelope; no BAO_TOKEN/VAULT_TOKEN in environment
- umask 077, shell tracing off
- --rotate for any run after the first (cas=<observed current version>); without
it an existing entry is refused
generation: one secrets.token_urlsafe(48) value in process memory (64 chars); never
in argv, environment, stdout, stderr, logs, Git or the hub
authoritative_write: KV-v2 write with options.cas=0 (absent) or cas=<observed current
version> (rotation, --rotate); value only in process memory and the bao stdin/HTTP
body; metadata-only preflight
- --rotate for any run after the first (cas=<observed current version>); without it an existing
entry is refused
generation: one secrets.token_urlsafe(48) value in process memory (64 chars); never in argv,
environment, stdout, stderr, logs, Git or the hub
authoritative_write: KV-v2 write with options.cas=0 (absent) or cas=<observed current version>
(rotation, --rotate); value only in process memory and the bao stdin/HTTP body; metadata-only
preflight
receipt: O_EXCL 0600 JSON with booleans, KV version, request id, CAS, metadata applied/unchanged,
CCR/decision ids and CCR digest only
failure_cleanup: nothing to clean locally; a written KV version is retained and
recorded; recovery is a fresh roll-forward run with --rotate (cas=<current>),
never a KV read
metadata_write: kv metadata put of openbao.kv_metadata, compared before write against
a metadata-only read; skipped when equal
failure_cleanup: nothing to clean locally; a written KV version is retained and recorded;
recovery is a fresh roll-forward run with --rotate (cas=<current>), never a KV read
metadata_write: kv metadata put of openbao.kv_metadata, compared before write against a
metadata-only read; skipped when equal
rotation_revocation:
generation_and_authoritative_version: railiance-platform custody child, version-guarded,
roll-forward
issuer_pickup: 'key-cape / net-kingdom: ESO refresh then KeyCape restart immediately
(exchanges fail closed 401 between KV write and restart)'
consumer_pickup: 'automatic: the next secrets-engine command reads the latest version
in memory (CCR-2026-0034)'
disable_on_compromise: key-cape / net-kingdom remove registration first; platform
rotates and detaches the CCR-2026-0034 role policy
issuer_pickup: 'key-cape / net-kingdom: ESO refresh then KeyCape restart immediately (exchanges
fail closed 401 between KV write and restart)'
consumer_pickup: 'automatic: the next secrets-engine command reads the latest version in
memory (CCR-2026-0034)'
disable_on_compromise: key-cape / net-kingdom remove registration first; platform rotates
and detaches the CCR-2026-0034 role policy
retire_versions: railiance-platform per separate destroy approval
live_order:
- 0 founder approvals A1 design, A2 this CCR + CCR-2026-0034 (three reviews, bound
claims confirmed for both readers), A3 custody-child decision id
- 0 founder approvals A1 design, A2 this CCR + CCR-2026-0034 (three reviews, bound claims
confirmed for both readers), A3 custody-child decision id
- 1 attended custody child (this repo) writes KV v1 and metadata; non-secret receipt
- 2 platform applies policies/roles (this CCR and CCR-2026-0034) and the sso-only
ClusterSecretStore + ExternalSecret; Secret sso/keycape-secrets-engine-openbao-client
exists (metadata receipt only)
- 2 platform applies policies/roles (this CCR and CCR-2026-0034) and the sso-only ClusterSecretStore
+ ExternalSecret; Secret sso/keycape-secrets-engine-openbao-client exists (metadata receipt
only)
- 3 net-kingdom env patch secrets-engine-openbao-client.proposed.patch.yaml (NK-WP-0046)
- 4 KeyCape registration merge (KEY-WP-0037-T03)
- 5 KeyCape restart immediately after step 4
@ -227,19 +172,19 @@ live_order:
- 7 platform OpenBao JWT role/policy secrets-engine-openbao and login proof (RPF-WP-0035-T02)
rollback:
before_step_3: delete the CCR-2026-0034 OIDC role, then its policy; delete ExternalSecret
(uid precondition), then the ESO role/policy; KV entry retained pending separate
destroy approval
after_step_3: net-kingdom rollout helper restores prior config/Deployment first,
then the before_step_3 sequence
cleanup_receipts: native, value-free, per step owner; a failed cleanup is recorded
as open evidence
(uid precondition), then the ESO role/policy; KV entry retained pending separate destroy
approval
after_step_3: net-kingdom rollout helper restores prior config/Deployment first, then the
before_step_3 sequence
cleanup_receipts: native, value-free, per step owner; a failed cleanup is recorded as open
evidence
founder_confirmation_items:
- 'bound_claims_confirmed stays false until the founder confirms BOTH readers: this
CCR (kubernetes, external-secrets/external-secrets, audience openbao) and sibling
CCR-2026-0034 (OIDC, groups=net-kingdom-admins and /assurance/level=aal2).'
- 'Approvals: A1 ops-mason design; A2 this CCR plus CCR-2026-0034 with platform-owner,
key-cape-owner and net-kingdom-owner reviews; A3 the exact custody-child decision
id (must differ from boundary decision 632a8893-0d6a-4591-a50b-40bbdb798f32).'
- 'bound_claims_confirmed stays false until the founder confirms BOTH readers: this CCR (kubernetes,
external-secrets/external-secrets, audience openbao) and sibling CCR-2026-0034 (OIDC, groups=net-kingdom-admins
and /assurance/level=aal2).'
- 'Approvals: A1 ops-mason design; A2 this CCR plus CCR-2026-0034 with platform-owner, key-cape-owner
and net-kingdom-owner reviews; A3 the exact custody-child decision id (must differ from
boundary decision 632a8893-0d6a-4591-a50b-40bbdb798f32).'
runbook: docs/provisioning-windows/2026-10-04-1000/keycape/founder-runbook.md
related:
sibling_ccr: CCR-2026-0034

View file

@ -3,16 +3,16 @@ kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: KeyCape secrets-engine-openbao attended operator reader (in-memory, per command)
status: applied
status: proposed
created: '2026-10-06'
updated: '2026-10-07'
updated: '2026-10-06'
requester:
agent: railiance-platform
reason: Founder direction 2026-10-06 - the secrets-engine-openbao client secret
lives only in OpenBao; ops-mason plan keycape-secrets-engine-openbao-client-custody
reason: Founder direction 2026-10-06 - the secrets-engine-openbao client secret lives
only in OpenBao; ops-mason plan keycape-secrets-engine-openbao-client-custody
(658cdbc, MASON-WP-0009) section 3.3. Sibling of CCR-2026-0033 (issuer ESO reader);
one auth role per CCR is this repo's convention (cf. CCR-2026-0019). Replaces
the superseded secrets-engine consumer file (SECRETS-WP-0012-T02 D1-D4).
one auth role per CCR is this repo's convention (cf. CCR-2026-0019). Replaces the
superseded secrets-engine consumer file (SECRETS-WP-0012-T02 D1-D4).
review:
required: true
required_approvers:
@ -23,39 +23,12 @@ review:
- at: '2026-10-06'
reviewer: key-cape (owner packet, key-cape 7d96ee4)
decision: claim_shape_reported
comment: KeyCape reports the groups claim is always emitted, so groups_claim groups
bound to net-kingdom-admins works as in CCR-2026-0019. MFA is not pinned on
the openbao-admin client; it holds only through the provider default requireForAll.
KeyCape asks that the role also bind the assurance claim (/assurance/level =
aal2, issued only after a verified factor). This is an owner report, not an
approval and not the founder confirmation of bound claims.
- at: '2026-10-07'
reviewer: platform-owner (founder Bernd Worsch)
decision: approved
comment: Founder decision 2ab717e1-7881-491e-aed4-28946550c289 (resolved 2026-10-06)
approves CCR-2026-0034 as platform-owner. Covers A2 for this operator OIDC reader
(groups=net-kingdom-admins and /assurance/level=aal2 confirmed). Applying the
role and policy needs its own go.
- at: '2026-10-07'
reviewer: key-cape-owner (founder Bernd Worsch)
decision: approved
comment: Founder decision 2ab717e1-7881-491e-aed4-28946550c289 (resolved 2026-10-06)
approves CCR-2026-0034 as key-cape-owner. Covers A2 for this operator OIDC reader
(groups=net-kingdom-admins and /assurance/level=aal2 confirmed). Applying the
role and policy needs its own go.
- at: '2026-10-07'
reviewer: net-kingdom-owner (founder Bernd Worsch)
decision: approved
comment: Founder decision 2ab717e1-7881-491e-aed4-28946550c289 (resolved 2026-10-06)
approves CCR-2026-0034 as net-kingdom-owner. Covers A2 for this operator OIDC
reader (groups=net-kingdom-admins and /assurance/level=aal2 confirmed). Applying
the role and policy needs its own go.
- at: '2026-10-07'
reviewer: platform-owner, key-cape-owner, net-kingdom-owner (founder Bernd Worsch)
decision: approved
comment: Founder decision 73fff3d3-49fd-4535-9040-b2754a3e2024 approves removing
the retired bao.coulomb.social OIDC callback from allowed_redirect_uris and
one delegated re-apply. Nothing else changes.
comment: 'KeyCape reports the groups claim is always emitted, so groups_claim groups
bound to net-kingdom-admins works as in CCR-2026-0019. MFA is not pinned on the
openbao-admin client; it holds only through the provider default requireForAll.
KeyCape asks that the role also bind the assurance claim (/assurance/level = aal2,
issued only after a verified factor). This is an owner report, not an approval
and not the founder confirmation of bound claims.'
target:
domain: infotech
tenant: platform
@ -78,6 +51,7 @@ openbao:
mount: netkingdom
role: keycape-secrets-engine-openbao-operator-workload-kv-read
allowed_redirect_uris:
- https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback
- http://localhost:8250/oidc/callback
- http://127.0.0.1:8250/oidc/callback
oidc_scopes:
@ -92,7 +66,7 @@ openbao:
groups:
- net-kingdom-admins
/assurance/level: aal2
bound_claims_confirmed: true
bound_claims_confirmed: false
policies:
- workload-kv-read-keycape-secrets-engine-openbao-operator
ttl: 5m
@ -104,8 +78,8 @@ openbao:
founder_confirmation_items:
- bound_claims_confirmed stays false until the founder confirms BOTH readers' bindings
(this role's groups=net-kingdom-admins plus /assurance/level=aal2, and CCR-2026-0033's
external-secrets/external-secrets ServiceAccount). An unconfirmed binding blocks
the applier (applier-dry-run refuses).
external-secrets/external-secrets ServiceAccount). An unconfirmed binding blocks the
applier (applier-dry-run refuses).
- The aal2 binding is the only MFA guarantee on this lane; the openbao-admin client
itself does not pin MFA (KeyCape 7d96ee4).
access_frontdoor:
@ -145,25 +119,6 @@ verification:
- Founder approvals A1 (ops-mason design), A2 (this CCR and CCR-2026-0033 with three
reviews and bound_claims_confirmed true) and a completed CCR-2026-0033 custody
run (A3).
evidence:
- at: '2026-10-07T00:03:24+00:00'
actor: founder
kind: delegated_metadata_apply
result: passed
details:
- Delegated metadata applier ran as founder using local bao CLI ambient authority.
- 'Policy metadata write: sys/policies/acl/workload-kv-read-keycape-secrets-engine-openbao-operator'
- 'Auth role metadata write: auth/netkingdom/role/keycape-secrets-engine-openbao-operator-workload-kv-read'
- No secret values were read, written, printed, or accepted in argv.
- at: '2026-10-07T13:10:47+00:00'
actor: founder
kind: delegated_metadata_apply
result: passed
details:
- Delegated metadata applier ran as founder using local bao CLI ambient authority.
- 'Policy metadata write: sys/policies/acl/workload-kv-read-keycape-secrets-engine-openbao-operator'
- 'Auth role metadata write: auth/netkingdom/role/keycape-secrets-engine-openbao-operator-workload-kv-read'
- No secret values were read, written, printed, or accepted in argv.
lifecycle:
deactivate: Delete the OIDC role first, then the policy; value-free receipt.
rotate: Not applicable to the reader; the value rotates through the CCR-2026-0033

View file

@ -1,211 +0,0 @@
{
"id": "CCR-2026-0036",
"kind": "credential-change-request",
"schema_version": 1,
"request_type": "workload-kv-read",
"title": "coulomb-social primary archive node-delivery reader",
"status": "applied",
"created": "2026-10-08",
"updated": "2026-10-27",
"requester": {
"agent": "codex",
"reason": "User asked to arrange platform credential delivery; preparation under RPF-WP-0053, no live grant inferred."
},
"review": {
"required": true,
"required_approvers": [
"platform-owner",
"coulomb-social-owner"
],
"comments": [
{
"at": "2026-10-08T12:40:00Z",
"reviewer": "User (platform-owner)",
"decision": "approved",
"comment": "Founder accepted this reader on 2026-10-08. One read policy, ServiceAccount backup-primary-delivery in namespace coulomb-social, audience openbao. Node delivery is not admitted.",
"source_ref": "founder-acceptance-2026-10-08"
},
{
"at": "2026-10-08T12:40:00Z",
"reviewer": "User (coulomb-social-owner)",
"decision": "approved",
"comment": "Founder accepted the coulomb-social reader record. Node delivery of its pair was not requested.",
"source_ref": "founder-acceptance-2026-10-08"
},
{
"at": "2026-10-27",
"reviewer": "User (platform-owner)",
"decision": "approved",
"comment": "User explicitly approved the scoped Coulomb-only node executor and governed pair delivery. Native reader/provider denials verified before delivery; independent recovery/retention and timer activation remain separately gated.",
"source_ref": "9557e2b8-2b37-5b94-a5e6-e5fe08d0351e"
},
{
"at": "2026-10-27",
"reviewer": "User (coulomb-social-owner)",
"decision": "approved",
"comment": "User explicitly approved the scoped Coulomb-only node executor and governed pair delivery. Native reader/provider denials verified before delivery; independent recovery/retention and timer activation remain separately gated.",
"source_ref": "9557e2b8-2b37-5b94-a5e6-e5fe08d0351e"
}
]
},
"target": {
"domain": "communication",
"tenant": "platform",
"workload": "coulomb-social",
"environment": "production",
"purpose": "Read this application backup publisher key pair into the admitted node delivery process; no other workload credential or escrow."
},
"openbao": {
"mount": "platform",
"kv_path": "platform/workloads/coulomb-social/backup-primary",
"fields": [
"AWS_ACCESS_KEY_ID",
"AWS_SECRET_ACCESS_KEY"
],
"policy_name": "workload-kv-read-coulomb-social-backup-primary",
"policy_file": "openbao/policies/workload-kv-read-coulomb-social-backup-primary.hcl",
"metadata_read": false,
"token_self_lifecycle": true,
"auth": {
"method": "kubernetes",
"mount": "kubernetes",
"role": "coulomb-social-backup-primary-workload-kv-read",
"audience": "openbao",
"ttl": "5m",
"token_max_ttl": "5m",
"token_explicit_max_ttl": "5m",
"token_no_default_policy": true,
"token_num_uses": 4,
"token_type": "service",
"policies": [
"workload-kv-read-coulomb-social-backup-primary"
],
"bound_claims": {
"service_account_names": [
"backup-primary-delivery"
],
"service_account_namespaces": [
"coulomb-social"
]
},
"bound_claims_confirmed": true
}
},
"access_frontdoor": {
"type": "platform-owner",
"catalog_id": "coulomb-social-backup-primary-delivery",
"readiness": "applied-pending-verify",
"resolvable": false
},
"delivery": {
"surface": "node-private-file",
"host": "railiance01",
"cluster_uid": "a553c742-0115-43d4-99a4-a5ca56fe0786",
"target": "/etc/coulomb-social-backup/primary.env",
"owner": "root:root",
"mode": "0600",
"directory_mode": "0700",
"procedure": "scripts/application_backup_credentials.py",
"admission": {
"node_executor_approved": true,
"provider_scope_verified": true,
"reader_boundary_verified": true,
"source_ref": "9557e2b8-2b37-5b94-a5e6-e5fe08d0351e"
}
},
"provider": {
"endpoint": "https://s3.nl-ams.scw.cloud",
"bucket": "railiance-platform-pg-backup",
"prefix": "platform-pg/application-archives/coulomb-social/",
"identity": "IAM application coulomb-social-backup-primary 681d00bf-b965-4323-b657-97bfde423511; key only in OpenBao; bucket policy limits the prefix and withholds object delete",
"retained_delete": false
},
"risk": {
"classification": "high",
"notes": [
"Admitted node root may request the dedicated ServiceAccount JWT; no RBAC grant or mounted token for this account. Namespace writers and node administrators are inside this credential boundary.",
"TokenRequest JWT and five-minute, four-use OpenBao token stay in process memory; only the provider pair is delivered at rest.",
"No worker receives IAM/bucket administration, native Barman access, sibling archive access or decryption identity.",
"Policy/role construction does not seed values or activate capture, timers, retention, deletion or a production restore."
]
},
"verification": {
"positive": [
"Exact namespace/ServiceAccount/audience authenticates with this one policy, no default/identity policy, five-minute maximum TTL and four-use bound.",
"One own-path KV read followed by self-revocation yields only these two fields at a positive pinned KV version.",
"Node delivery produces root-owned 0600 file in 0700 directory; contained readback equality and metadata-only receipt; no secrets in outputs/argv.",
"Provider caller conditional multipart publication and version-pinned readback within exact archive prefix work."
],
"negative": [
"Wrong namespace, ServiceAccount and audience cannot log in.",
"Sibling application data GET, parent LIST, metadata GET, any KV write and platform admin endpoints are denied.",
"Provider denies sibling/native prefixes, retained object/version deletion and IAM/bucket administration.",
"Draft/unconfirmed/source-changed contract, wrong cluster, symlinks, hard links, insecure modes, changed KV version, unconfirmed token revocation and different existing file refuse delivery."
],
"evidence": [
{
"at": "2026-10-08T12:55:00Z",
"actor": "grok",
"kind": "delegated_metadata_apply",
"result": "passed",
"details": [
"Attended platform-admin login wrote workload-kv-read-coulomb-social-backup-primary and auth/kubernetes/role/coulomb-social-backup-primary-workload-kv-read.",
"Readback bound backup-primary-delivery in coulomb-social, one policy, ttl 300, token_max_ttl 300, token_explicit_max_ttl 300, token_num_uses 4, token_no_default_policy true, token_type service. Node delivery remains unadmitted."
]
},
{
"at": "2026-10-27",
"actor": "codex",
"kind": "native_reader_provider_boundary_proof",
"result": "passed",
"details": {
"completed_objects_created": false,
"credential_values_printed": false,
"kv_version": 2,
"negative_login_denials": {
"wrong_audience": 403,
"wrong_serviceaccount": 403
},
"own_multipart_create_abort": true,
"positive_reader_and_revoke": true,
"provider_denials": {
"bucket_policy": 403,
"delete": 403,
"iam_list": 403,
"native_prefix": 403,
"sibling_prefix": 403
},
"reader_denials": {
"admin": 403,
"list": 403,
"metadata": 403,
"sibling": 403,
"write_cas0_existing": 403
},
"schema": "coulomb.backup-boundary-proof.v1",
"status": "verified"
},
"source_ref": "9557e2b8-2b37-5b94-a5e6-e5fe08d0351e"
}
],
"activation_conditions": [
"Named owner reviews and confirmed native reader bindings, applied policy/role with live positive/negative evidence, provider prefix boundary proof and explicit node execution admission.",
"Separate platform-owned attended pair provision with initial KV CAS=0, no partial/second single-field PUT; retain only version metadata. Current reader does not create or rotate values.",
"Pin the final reviewed CCR SHA256 and expected KV version; initialise designated root directory only after admission. Initial delivery may resume identical bytes but cannot rotate.",
"Independent age escrow and metadata receipt custody, retention/lifecycle/capacity, first capture/recovery and timer activation remain separate RPF-WP-0053 gates."
]
},
"lifecycle": {
"deactivate": "Owner detaches this Kubernetes role first, revokes tokens, and removes only admitted local delivery through an explicit withdrawal record. Do not delete retained archives.",
"rotate": "Separate owner-approved CAS rotation and atomic host replacement contract required. This helper refuses different existing bytes; retain old provider identity until new publication/recovery acceptance, then revoke predecessor at provider.",
"compromised": "Fence this worker and disable its provider identity first. Owner revokes reader tokens, withdraws reader, rotates pair and reviews retained-object access under incident authority; no automated archive deletion."
},
"state_hub": {
"workplan_id": "RPF-WP-0053",
"task_id": "RPF-WP-0053-T02"
},
"related": {
"consumer_workplan": "CSOC-WP-0008",
"design": "docs/credential-lane-designs/application-backup-delivery-2026-10-08.md"
}
}

View file

@ -1,173 +0,0 @@
{
"id": "CCR-2026-0037",
"kind": "credential-change-request",
"schema_version": 1,
"request_type": "workload-kv-read",
"title": "email-connect primary archive node-delivery reader",
"status": "applied",
"created": "2026-10-08",
"updated": "2026-10-08",
"requester": {
"agent": "codex",
"reason": "User asked to arrange platform credential delivery; preparation under RPF-WP-0053, no live grant inferred."
},
"review": {
"required": true,
"required_approvers": [
"platform-owner",
"email-connect-owner"
],
"comments": [
{
"at": "2026-10-08T12:40:00Z",
"reviewer": "User (platform-owner)",
"decision": "approved",
"comment": "Founder accepted this reader on 2026-10-08. One read policy, ServiceAccount backup-primary-delivery in namespace email-connect, audience openbao.",
"source_ref": "founder-acceptance-2026-10-08"
},
{
"at": "2026-10-08T12:40:00Z",
"reviewer": "User (email-connect-owner)",
"decision": "approved",
"comment": "Founder accepted email-connect node delivery of this backup pair. Retention, escrow, capture and the timer stay separate.",
"source_ref": "founder-acceptance-2026-10-08"
}
]
},
"target": {
"domain": "communication",
"tenant": "platform",
"workload": "email-connect",
"environment": "production",
"purpose": "Read this application backup publisher key pair into the admitted node delivery process; no other workload credential or escrow."
},
"openbao": {
"mount": "platform",
"kv_path": "platform/workloads/email-connect/backup-primary",
"fields": [
"AWS_ACCESS_KEY_ID",
"AWS_SECRET_ACCESS_KEY"
],
"policy_name": "workload-kv-read-email-connect-backup-primary",
"policy_file": "openbao/policies/workload-kv-read-email-connect-backup-primary.hcl",
"metadata_read": false,
"token_self_lifecycle": true,
"auth": {
"method": "kubernetes",
"mount": "kubernetes",
"role": "email-connect-backup-primary-workload-kv-read",
"audience": "openbao",
"ttl": "5m",
"token_max_ttl": "5m",
"token_explicit_max_ttl": "5m",
"token_no_default_policy": true,
"token_num_uses": 4,
"token_type": "service",
"policies": [
"workload-kv-read-email-connect-backup-primary"
],
"bound_claims": {
"service_account_names": [
"backup-primary-delivery"
],
"service_account_namespaces": [
"email-connect"
]
},
"bound_claims_confirmed": true
}
},
"access_frontdoor": {
"type": "platform-owner",
"catalog_id": "email-connect-backup-primary-delivery",
"readiness": "applied-pending-verify",
"resolvable": false
},
"delivery": {
"surface": "node-private-file",
"host": "railiance01",
"cluster_uid": "a553c742-0115-43d4-99a4-a5ca56fe0786",
"target": "/etc/email-connect-backup/primary.env",
"owner": "root:root",
"mode": "0600",
"directory_mode": "0700",
"procedure": "scripts/application_backup_credentials.py",
"admission": {
"node_executor_approved": true,
"provider_scope_verified": true,
"reader_boundary_verified": true,
"source_ref": "backup-reader-boundary-2026-10-08"
}
},
"provider": {
"endpoint": "https://s3.nl-ams.scw.cloud",
"bucket": "railiance-platform-pg-backup",
"prefix": "platform-pg/application-archives/email-connect/",
"identity": "IAM application email-connect-backup-primary 0eb0f958-77a4-4c70-9aa7-14ba46411415; key only in OpenBao; bucket policy limits the prefix and withholds object delete",
"retained_delete": false
},
"risk": {
"classification": "high",
"notes": [
"Admitted node root may request the dedicated ServiceAccount JWT; no RBAC grant or mounted token for this account. Namespace writers and node administrators are inside this credential boundary.",
"TokenRequest JWT and five-minute, four-use OpenBao token stay in process memory; only the provider pair is delivered at rest.",
"No worker receives IAM/bucket administration, native Barman access, sibling archive access or decryption identity.",
"Policy/role construction does not seed values or activate capture, timers, retention, deletion or a production restore."
]
},
"verification": {
"positive": [
"Exact namespace/ServiceAccount/audience authenticates with this one policy, no default/identity policy, five-minute maximum TTL and four-use bound.",
"One own-path KV read followed by self-revocation yields only these two fields at a positive pinned KV version.",
"Node delivery produces root-owned 0600 file in 0700 directory; contained readback equality and metadata-only receipt; no secrets in outputs/argv.",
"Provider caller conditional multipart publication and version-pinned readback within exact archive prefix work."
],
"negative": [
"Wrong namespace, ServiceAccount and audience cannot log in.",
"Sibling application data GET, parent LIST, metadata GET, any KV write and platform admin endpoints are denied.",
"Provider denies sibling/native prefixes, retained object/version deletion and IAM/bucket administration.",
"Draft/unconfirmed/source-changed contract, wrong cluster, symlinks, hard links, insecure modes, changed KV version, unconfirmed token revocation and different existing file refuse delivery."
],
"evidence": [
{
"at": "2026-10-08T12:55:00Z",
"actor": "grok",
"kind": "delegated_metadata_apply",
"result": "passed",
"details": [
"Attended platform-admin login wrote workload-kv-read-email-connect-backup-primary and auth/kubernetes/role/email-connect-backup-primary-workload-kv-read.",
"Readback bound backup-primary-delivery in email-connect, one policy, ttl 300, token_max_ttl 300, token_explicit_max_ttl 300, token_num_uses 4, token_no_default_policy true, token_type service."
]
},
{
"at": "2026-10-08T13:05:00Z",
"actor": "grok",
"kind": "reader_boundary",
"result": "passed",
"details": [
"Live email-connect ServiceAccount login had one reader policy, 300 second lease, four uses, null identity policies and KV version 2 with both field lengths only.",
"Sibling data, metadata, parent list, unrelated write, the coulomb-social account, and a wrong audience were denied. The token was revoked. Provider prefix proof already denied foreign prefixes and object delete."
]
}
],
"activation_conditions": [
"Named owner reviews and confirmed native reader bindings, applied policy/role with live positive/negative evidence, provider prefix boundary proof and explicit node execution admission.",
"Separate platform-owned attended pair provision with initial KV CAS=0, no partial/second single-field PUT; retain only version metadata. Current reader does not create or rotate values.",
"Pin the final reviewed CCR SHA256 and expected KV version; initialise designated root directory only after admission. Initial delivery may resume identical bytes but cannot rotate.",
"Independent age escrow and metadata receipt custody, retention/lifecycle/capacity, first capture/recovery and timer activation remain separate RPF-WP-0053 gates."
]
},
"lifecycle": {
"deactivate": "Owner detaches this Kubernetes role first, revokes tokens, and removes only admitted local delivery through an explicit withdrawal record. Do not delete retained archives.",
"rotate": "Separate owner-approved CAS rotation and atomic host replacement contract required. This helper refuses different existing bytes; retain old provider identity until new publication/recovery acceptance, then revoke predecessor at provider.",
"compromised": "Fence this worker and disable its provider identity first. Owner revokes reader tokens, withdraws reader, rotates pair and reviews retained-object access under incident authority; no automated archive deletion."
},
"state_hub": {
"workplan_id": "RPF-WP-0053",
"task_id": "RPF-WP-0053-T05"
},
"related": {
"consumer_workplan": "EMAIL-WP-0007",
"design": "docs/credential-lane-designs/application-backup-delivery-2026-10-08.md"
}
}

View file

@ -1,130 +0,0 @@
id: CCR-2026-0038
kind: credential-change-request
schema_version: 1
request_type: service-jwt-login
title: secrets-engine-openbao KeyCape service-JWT login (isolated mount, login-only
policy)
status: applied
created: '2026-10-09'
updated: '2026-10-08'
requester:
agent: railiance-platform
reason: RPF-WP-0035-T02. KEY-WP-0037 finished 2026-10-07 (registration live, issuance
verified 10/10, key-cape e0b5fae); CCR-2026-0033/0034 applied. Design docs/credential-lane-designs/secrets-engine-service-jwt.md;
source bundle prepared 2026-09-27. ops-mason MASON-WP-0009-T05 waits on this lane
to recheck invariant (ii).
review:
required: true
required_approvers:
- platform-owner
- key-cape-owner
- secrets-engine-owner
comments:
- at: '2026-10-08T22:42:07+00:00'
reviewer: platform-owner, key-cape-owner, secrets-engine-owner (founder Bernd
Worsch)
decision: approved
comment: 'Approve service-JWT login lane: mount keycape-services, role secrets-engine-openbao,
policy secrets-engine-login-self (self endpoints + deny on the client-secret
path).'
- at: '2026-10-08T22:42:07+00:00'
reviewer: founder
decision: binding_confirmed
comment: 'Bound claims match KeyCape registration: aud secrets-engine-openbao,
sub service:secrets-engine, principal_type service, tenant tenant:platform,
roles secrets-engine, scope openbao:login; issuer https://kc.coulomb.social
RS256.'
target:
domain: infotech
tenant: platform
workload: secrets-engine
environment: production
purpose: Let secrets-engine log in to OpenBao with a KeyCape-issued service JWT
and receive a 5-minute, 8-use token that can only inspect and revoke itself.
openbao:
policy_name: secrets-engine-login-self
policy_file: openbao/policies/secrets-engine-login-self.hcl
guarded_paths:
- platform/data/workloads/keycape/secrets-engine-openbao
- platform/metadata/workloads/keycape/secrets-engine-openbao
auth:
method: jwt
mount: keycape-services
config_file: openbao/auth/keycape-services-config.json
role: secrets-engine-openbao
role_file: openbao/auth/secrets-engine-jwt-role.json
bound_claims_confirmed: true
founder_confirmation_items:
- bound_claims_confirmed stays false until the founder confirms the binding against
the KeyCape registration (aud secrets-engine-openbao, sub service:secrets-engine,
principal_type service, tenant tenant:platform, roles secrets-engine, scope openbao:login;
issuer https://kc.coulomb.social, RS256). applier-dry-run refuses until then.
- The isolated mount keycape-services is new (ops-mason survey 2026-10-07 found no
auth/jwt or keycape-services mount). The applier refuses if the path exists with
a type other than jwt.
access_frontdoor:
type: none
catalog_id: none
readiness: pending-review
resolvable: false
delivery:
surface: none
target: No value is delivered. Before the login proof, the founder may write a local,
non-secret probe input from the applied values (mount keycape-services, role
secrets-engine-openbao, bound_issuer https://kc.coulomb.social; secrets-engine
docs/keycape-openbao-consumer-custody.md, e94dc07). That file is probe input,
not the platform contract. railiance-platform publishes the consumer contract
file, or confirms the probe input as it, only after the login proof passes.
risk:
classification: medium
notes:
- Login identity only. The policy holds lookup-self, capabilities-self and revoke-self,
plus explicit deny on the client-secret data and metadata paths (MASON-WP-0009
invariant (ii)); no KV read, policy/auth write, SecretID or child token.
- A JWT may be replayed while valid (15m); residual access is bounded by the 5m/8-use
OpenBao token. In an incident, delete the role, revoke issued tokens; do not disable
a shared mount.
verification:
positive:
- Readback after apply matches source for the policy text, mount type jwt, config
and role (done by applier-apply; prints nothing).
- A secrets-engine --auth service-jwt run (SECRETS-WP-0012-T03 probe-openbao) logs
in; the token's policies are exactly [secrets-engine-login-self]; revoke-self
succeeds.
negative:
- sys/capabilities-self for the service token returns deny on both guarded paths
and on sys/policies/acl/*, auth/*/role/*, auth/token/create.
- Wrong audience, subject, tenant, principal_type, role or scope, a coding-agent
JWT and an expired JWT are refused.
- A failed login never falls back to bootstrap/env providers.
activation_conditions:
- Founder approval of this CCR (three reviews) and bound_claims_confirmed true.
evidence:
- at: '2026-10-08T22:42:13+00:00'
actor: founder
kind: delegated_metadata_apply
result: passed
details:
- Delegated metadata applier ran as founder using local bao CLI ambient authority.
- 'Policy metadata write: sys/policies/acl/secrets-engine-login-self'
- 'JWT auth mount ensured: sys/auth/keycape-services (type jwt)'
- 'Auth config write: auth/keycape-services/config'
- 'Auth role metadata write: auth/keycape-services/role/secrets-engine-openbao'
- Readback matched source for policy, mount type, config and role.
- No secret values were read, written, printed, or accepted in argv.
lifecycle:
deactivate: Delete auth/keycape-services/role/secrets-engine-openbao, then the policy;
removing the exclusively owned empty mount is a later review.
rotate: Not applicable; the client secret rotates through the CCR-2026-0033 custody
child.
compromised: KeyCape disables the registration; platform deletes the role and revokes
issued tokens; tokens are 5m/8-use and self-revoked.
state_hub:
workplan_id: RPF-WP-0035
task_id: RPF-WP-0035-T02
related_workplan: KEY-WP-0037
related:
design: docs/credential-lane-designs/secrets-engine-service-jwt.md
sibling_ccrs:
- CCR-2026-0033
- CCR-2026-0034

View file

@ -1,138 +0,0 @@
id: CCR-2026-0039
kind: credential-change-request
schema_version: 1
request_type: workload-kv-operator-matrix
title: fluid-telegram hall-of-helix attended operator KV lane (per-path matrix, OIDC
aal2)
status: applied
created: '2026-10-09'
updated: '2026-10-09'
requester:
agent: railiance-platform
reason: RPF-WP-0035-T03. Founder approved ops-mason plan fluid-telegram-operator-credential-lane
2026-10-09 (approved_spec_sha256 41a0ca2746aa93b6ee9d1ca631517d680ade95c56e7a38b75a55209ef2316b73)
and ruled the tenant segment stays coulomb (ops-mason message b72ff77d). Design
docs/credential-lane-designs/fluid-telegram-operator-kv.md; key-cape confirmed
groups and aal2 claims (message b2ed890f, key-cape e0b5fae).
review:
required: true
required_approvers:
- platform-owner
- fluid-telegram-owner
- key-cape-owner
comments:
- at: '2026-10-09T10:29:55+00:00'
reviewer: platform-owner, fluid-telegram-owner, key-cape-owner (founder Bernd
Worsch)
decision: approved
comment: 'Approve ops-mason plan fluid-telegram-operator-credential-lane (spec
41a0ca27): four-entry matrix at coulomb/fluid-telegram/hall-of-helix, OIDC role
on netkingdom, agent denies.'
- at: '2026-10-09T10:29:55+00:00'
reviewer: founder
decision: binding_confirmed
comment: groups=[fluid-telegram-operators] (LLDAP, member tegwick) and /assurance/level=aal2
match key-cape b2ed890f.
target:
domain: infotech
tenant: coulomb
workload: fluid-telegram
environment: production
purpose: Let a member of fluid-telegram-operators with a verified second factor
run the attended provisioner for campaign hall-of-helix with exactly the four-entry
matrix.
openbao:
mount: platform
campaign_path: platform/workloads/coulomb/fluid-telegram/hall-of-helix
policy_name: workload-kv-fluid-telegram-hall-of-helix-operator
policy_file: openbao/policies/workload-kv-fluid-telegram-hall-of-helix-operator.hcl
boundary_overlay_file: openbao/fluid-telegram/agent-deny-overlay.hcl
matrix:
- entry: operator-app
fields:
- api_id
- api_hash
capabilities:
- read
- entry: operator-session
fields:
- session_b64
capabilities:
- create
- read
- update
- entry: bot-token
fields:
- token
capabilities:
- create
- read
- update
- entry: redaction-salt
fields:
- salt
capabilities:
- create
- read
auth:
method: oidc
mount: netkingdom
role: fluid-telegram-hall-of-helix-workload-kv
role_file: openbao/auth/fluid-telegram-hall-of-helix-oidc-role.json
bound_claims_confirmed: true
founder_confirmation_items:
- bound_claims_confirmed stays false until the founder confirms groups=[fluid-telegram-operators]
(LLDAP group, single member tegwick) and /assurance/level=aal2 against the key-cape
confirmation b2ed890f. applier-dry-run refuses until then.
- The matrix grants no metadata, list, patch or delete. The salt is create-only by
ACL; first-write CAS 0 is the consumer's job (FT-WP-0002) and is verified by an
owner-run probe, not by ACL parameters.
- The apply appends exact coding-agent denies on all four data and metadata paths
to the live agent-high-risk-boundary policy, preserving its other content verbatim,
and refuses if only some of those denies are already present.
access_frontdoor:
type: none
catalog_id: none
risk:
level: high
notes:
- operator-session is a full Telegram account session. Membership of fluid-telegram-operators
is the whole access decision; adding a member widens this lane's reach.
- Tokens are 15m (30m maximum), no default policy, num_uses 0 so MTProto session
persistence is not interrupted; the provisioner self-revokes at completion.
verification:
positive:
- Readback after apply matches source for boundary denies, policy text and role
(done by applier-apply; prints nothing).
- An approved member's token reports exactly the matrix capabilities on each entry.
negative:
- Parent, sibling workload and sibling campaign paths report deny.
- A non-member, or a member without aal2, cannot obtain the role.
- Coding-agent identities report deny on all four data and metadata paths.
- Owner-run disposable probe: salt create succeeds, a second write is denied.
activation_conditions:
- Founder approval (three reviews) and bound_claims_confirmed true.
- Custody moved from the infotech prefix with scripts/fluid_telegram_custody_move.py.
evidence:
- at: '2026-10-09T10:30:01+00:00'
actor: founder
kind: delegated_metadata_apply
result: passed
details:
- Delegated metadata applier ran as founder using local bao CLI ambient authority.
- 'Agent boundary denies ensured: sys/policies/acl/agent-high-risk-boundary (8
exact paths)'
- 'Policy metadata write: sys/policies/acl/workload-kv-fluid-telegram-hall-of-helix-operator'
- 'OIDC role metadata write: auth/netkingdom/role/fluid-telegram-hall-of-helix-workload-kv'
- Readback matched source for boundary denies, policy and role.
- No secret values were read, written, printed, or accepted in argv.
lifecycle:
deactivate: Delete auth/netkingdom/role/fluid-telegram-hall-of-helix-workload-kv
first, then the policy; KV entries stay in custody pending an explicit retention
decision.
rotate: Session compromise needs Telegram-side session revocation; bot-token rotation
needs provider replacement; salt rotation needs a data-owner decision.
compromised: Delete the role, revoke issued tokens, revoke the Telegram session.
state_hub:
workplan: RPF-WP-0035
task: RPF-WP-0035-T03

View file

@ -1,105 +0,0 @@
id: CCR-2026-0040
kind: credential-change-request
schema_version: 1
request_type: custody-generate-grant
title: Broker grant keycape/client-secret-generate (create-only, generate in custody)
status: applied
created: '2026-10-09'
updated: '2026-10-10'
requester:
agent: railiance-platform
reason: RPF-WP-0054-T04. net-kingdom SecurityCaseType oidc-browser-relying-party
step mint-secret (lane keycape-client-secret-generate); ops-warden d9f889b1, ops-mason
789feb39. Founder chose the templated create-only grant 2026-10-09 (T02 option
A).
review:
required: true
required_approvers:
- platform-owner
- key-cape-owner
comments:
- at: '2026-10-09T17:18:53+00:00'
reviewer: platform-owner, key-cape-owner (founder Bernd Worsch)
decision: approved
comment: Approve create-only broker grant keycape/client-secret-generate (RPF-WP-0054-T02
option A).
target:
domain: infotech
tenant: platform
workload: keycape
environment: production
purpose: Let the credential broker create a new KeyCape confidential-client secret
at platform/workloads/keycape/<client_id> once, without any value leaving custody.
openbao:
grant_id: keycape/client-secret-generate
kv_prefix: platform/workloads/keycape
reserved_entries:
- secrets-engine-openbao
policy_name: keycape-client-secret-generate
policy_file: openbao/policies/keycape-client-secret-generate.hcl
issuer_policy: credential-broker-keycape-client-secret-generate-issuer
issuer_policy_file: openbao/policies/credential-broker-keycape-client-secret-generate-issuer.hcl
token_role: keycape-client-secret-generate
token_ttl: 5m
founder_confirmation_items:
- This apply writes two policies and one token role. It binds the issuer policy to
no identity, so nothing can mint the token until a separate review binds it to the
caller named in RPF-WP-0054-T01.
- Create-only on keycape/+ means a token can create a new client entry once and can
never change an existing one. Its widest misuse is an unused entry for a client
that does not exist.
access_frontdoor:
type: none
catalog_id: none
risk:
level: medium
notes:
- No data read, no update, no metadata write, no list, no delete. Exact denies on
reserved entries take precedence over the + glob.
- The token lives at most 5m and only inside the broker process.
verification:
positive:
- Readback after apply matches source for both policies and the token role (done
by applier-apply; prints nothing).
- Disposable probe (T05) creates probe-secret-generate-<date> at version 1.
negative:
- A second create on the probe path, a data read, update, delete and any path outside
keycape/ are denied for the grant token (sys/capabilities-self).
- The reserved entry keycape/secrets-engine-openbao reports deny on data and metadata.
activation_conditions:
- Founder approval (two reviews).
- Issuer binding reviewed after RPF-WP-0054-T01; probe passed (T05).
evidence:
- at: '2026-10-09T17:19:06+00:00'
actor: founder
kind: delegated_metadata_apply
result: passed
details:
- Delegated metadata applier ran as founder using local bao CLI ambient authority.
- 'Policy metadata write: sys/policies/acl/keycape-client-secret-generate'
- 'Issuer policy metadata write: sys/policies/acl/credential-broker-keycape-client-secret-generate-issuer'
- 'Token role metadata write: auth/token/roles/keycape-client-secret-generate'
- Readback matched source for both policies and the token role.
- Issuer policy is not bound to any identity by this apply.
- No secret values were read, written, printed, or accepted in argv.
- at: '2026-10-10T09:15:46+00:00'
actor: founder
kind: delegated_metadata_apply
result: passed
details:
- Delegated metadata applier ran as founder using local bao CLI ambient authority.
- 'Policy metadata write: sys/policies/acl/keycape-client-secret-generate'
- 'Issuer policy metadata write: sys/policies/acl/credential-broker-keycape-client-secret-generate-issuer'
- 'Token role metadata write: auth/token/roles/keycape-client-secret-generate'
- Readback matched source for both policies and the token role.
- Issuer policy is not bound to any identity by this apply.
- No secret values were read, written, printed, or accepted in argv.
lifecycle:
deactivate: Delete auth/token/roles/keycape-client-secret-generate, then both policies;
generated entries stay in custody with their cases.
rotate: Not applicable; rotation is a separate lane (RPF-WP-0054-T07).
compromised: Delete the token role and revoke outstanding tokens by accessor; review
metadata for entries without an accepted case.
state_hub:
workplan: RPF-WP-0054
task: RPF-WP-0054-T04

View file

@ -1,111 +0,0 @@
id: CCR-2026-0041
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: 'forgejo-netkingdom-oidc: keycape-reader-policy (oidc-browser-relying-party@1.0)'
status: applied
created: '2026-10-09'
updated: '2026-10-09'
requester:
agent: ops-mason
reason: 'MASON-WP-0010-T03 build of SecurityCase forgejo-netkingdom-oidc (oidc-browser-relying-party@1.0),
render sha256:fb8b114d465341a46bb492259cb9c6b77bc0731670edace2e13a1f58758c6e23.
Accepted by owner, founder on 2026-10-08: key-cape docs/forgejo-oidc-client-request.yaml
(status accepted-2026-10-08, KEY-WP-0038-T01); type acknowledged via informed-decision
memo nk-wp-0048-t05-oidc-browser-relying-party-r4, hub decision e418263e. The
case acceptance replaces a fresh executive summary.'
review:
required: true
required_approvers:
- platform-owner
comments:
- at: '2026-10-09T18:09:10+00:00'
reviewer: platform-owner (founder Bernd Worsch)
decision: approved
comment: Approve ESO reader for SecurityCase forgejo-netkingdom-oidc (accepted
2026-10-08).
- at: '2026-10-09T18:09:10+00:00'
reviewer: founder
decision: binding_confirmed
comment: Bound to external-secrets/external-secrets, mirrors CCR-2026-0033.
target:
domain: infotech
tenant: platform
workload: keycape
environment: production
purpose: KeyCape reads its client secret only through ESO. No metadata, list, parent
or sibling access.
openbao:
mount: platform
kv_path: platform/workloads/keycape/forgejo
fields:
- CLIENT_SECRET
policy_name: workload-kv-read-keycape-forgejo-eso
policy_file: openbao/policies/workload-kv-read-keycape-forgejo-eso.hcl
metadata_read: false
token_self_lifecycle: true
auth:
method: kubernetes
mount: kubernetes
role: external-secrets-keycape-forgejo
audience: openbao
ttl: 15m
token_max_ttl: 15m
token_explicit_max_ttl: 15m
token_no_default_policy: true
policies:
- workload-kv-read-keycape-forgejo-eso
bound_claims:
service_account_names:
- external-secrets
service_account_namespaces:
- external-secrets
bound_claims_confirmed: true
access_frontdoor:
type: external-secrets
catalog_id: forgejo-keycape-client
readiness: pending-review
resolvable: false
risk:
classification: high
notes:
- Rendered from an acknowledged SecurityCaseType; shape reviewed with the type,
names checked against live structure.
- Exact read on one KV path; no metadata, list, parent or sibling access.
verification:
positive:
- policy workload-kv-read-keycape-forgejo-eso grants read on platform/data/workloads/keycape/forgejo
only (capabilities check)
negative:
- a default-only identity has no read capability on the path
- sibling GET and parent LIST are denied
activation_conditions:
- case acceptance present
- bound claims confirmed by the platform owner
evidence:
- at: '2026-10-09T18:09:18+00:00'
actor: founder
kind: delegated_metadata_apply
result: passed
details:
- Delegated metadata applier ran as founder using local bao CLI ambient authority.
- 'Policy metadata write: sys/policies/acl/workload-kv-read-keycape-forgejo-eso'
- 'Auth role metadata write: auth/kubernetes/role/external-secrets-keycape-forgejo'
- No secret values were read, written, printed, or accepted in argv.
lifecycle:
deactivate: delete the role, then policy workload-kv-read-keycape-forgejo-eso (case
rollback)
rotate: roll-forward through the credential broker (mint-secret); never a read of
the old value
compromised: owner incident process; no automatic read, reseed or cleanup
security_case:
case: forgejo-netkingdom-oidc
type: oidc-browser-relying-party@1.0
render_digest: sha256:fb8b114d465341a46bb492259cb9c6b77bc0731670edace2e13a1f58758c6e23
acceptance_record: key-cape docs/forgejo-oidc-client-request.yaml (status accepted-2026-10-08,
KEY-WP-0038-T01); type acknowledged via informed-decision memo nk-wp-0048-t05-oidc-browser-relying-party-r4,
hub decision e418263e
accepted_at: '2026-10-08'
accepted_by:
- owner
- founder

View file

@ -1,81 +0,0 @@
id: CCR-2026-0042
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: 'forgejo-netkingdom-oidc: consumer-reader-policy (oidc-browser-relying-party@1.0)'
status: in_flight
created: '2026-10-09'
updated: '2026-10-09'
requester:
agent: ops-mason
reason: 'MASON-WP-0010-T03 build of SecurityCase forgejo-netkingdom-oidc (oidc-browser-relying-party@1.0), render
sha256:fb8b114d465341a46bb492259cb9c6b77bc0731670edace2e13a1f58758c6e23. Accepted by owner, founder on 2026-10-08:
key-cape docs/forgejo-oidc-client-request.yaml (status accepted-2026-10-08, KEY-WP-0038-T01); type acknowledged
via informed-decision memo nk-wp-0048-t05-oidc-browser-relying-party-r4, hub decision e418263e. The case acceptance
replaces a fresh executive summary.'
review:
required: true
required_approvers:
- platform-owner
comments: []
target:
domain: infotech
tenant: platform
workload: keycape
environment: production
purpose: The consuming service reads the same secret. No metadata, list, parent or sibling access.
openbao:
mount: platform
kv_path: platform/workloads/keycape/forgejo
fields:
- CLIENT_SECRET
policy_name: workload-kv-read-keycape-forgejo-forgejo
policy_file: openbao/policies/workload-kv-read-keycape-forgejo-forgejo.hcl
metadata_read: false
token_self_lifecycle: true
access_frontdoor:
type: none
catalog_id: forgejo-keycape-client
readiness: pending-review
resolvable: false
risk:
classification: high
notes:
- Rendered from an acknowledged SecurityCaseType; shape reviewed with the type, names checked against live structure.
- Exact read on one KV path; no metadata, list, parent or sibling access.
verification:
positive:
- policy workload-kv-read-keycape-forgejo-forgejo grants read on platform/data/workloads/keycape/forgejo only
(capabilities check)
negative:
- a default-only identity has no read capability on the path
- sibling GET and parent LIST are denied
activation_conditions:
- case acceptance present
- bound claims confirmed by the platform owner
lifecycle:
deactivate: delete the role, then policy workload-kv-read-keycape-forgejo-forgejo (case rollback)
rotate: roll-forward through the credential broker (mint-secret); never a read of the old value
compromised: owner incident process; no automatic read, reseed or cleanup
security_case:
case: forgejo-netkingdom-oidc
type: oidc-browser-relying-party@1.0
render_digest: sha256:fb8b114d465341a46bb492259cb9c6b77bc0731670edace2e13a1f58758c6e23
acceptance_record: key-cape docs/forgejo-oidc-client-request.yaml (status accepted-2026-10-08, KEY-WP-0038-T01);
type acknowledged via informed-decision memo nk-wp-0048-t05-oidc-browser-relying-party-r4, hub decision e418263e
accepted_at: '2026-10-08'
accepted_by:
- owner
- founder
in_flight:
missing_fields:
- openbao.auth
owner: railiance-apps
blocking_reason: >-
railiance-apps must choose and review the consumer delivery/auth identity. Native
forgejo-gitea and forgejo-runner both use forgejo/default; that shared identity
must not be filled as a dedicated consumer binding. RAPPS-WP-0016 intends ESO
delivery; current type1.1 templates only the KeyCape ESO role, so a consumer
role/delivery refinement needs explicit owner review. Historical type1.0 CCR
provenance remains unchanged; reconcile exact current1.1 structure before
application. See docs/evidence/2026-10-11-forgejo-first-case/owner-return.md.

View file

@ -1,106 +0,0 @@
id: CCR-2026-0043
kind: credential-change-request
schema_version: 1
request_type: broker-issuer-binding
title: Operator OIDC role for the keycape-client-secret-generate broker issuer (net-kingdom-admins,
aal2)
status: applied
created: '2026-10-10'
updated: '2026-10-10'
requester:
agent: railiance-platform
reason: RPF-WP-0054-T05. ops-warden (23985370) names the caller as the human operator
who runs warden case run, using their own OIDC session. Founder chose net-kingdom-admins
+ aal2 on 2026-10-10. The issuer policy is live from CCR-2026-0040.
review:
required: true
required_approvers:
- platform-owner
- key-cape-owner
comments:
- at: '2026-10-10T08:27:32+00:00'
reviewer: platform-owner, key-cape-owner (founder Bernd Worsch)
decision: approved
comment: 'Approve issuer binding for keycape-client-secret-generate: net-kingdom-admins
+ aal2 (RPF-WP-0054).'
- at: '2026-10-10T08:27:32+00:00'
reviewer: founder
decision: binding_confirmed
comment: groups=[net-kingdom-admins] and /assurance/level=aal2, same binding as
CCR-2026-0034.
target:
domain: infotech
tenant: platform
workload: credential-broker
environment: production
purpose: Let a net-kingdom-admins member with a verified second factor log in for
15 minutes with only the broker issuer policy, so credential.py generate can mint
the create-only keycape/client-secret-generate token.
openbao:
issuer_policy: credential-broker-keycape-client-secret-generate-issuer
auth:
method: oidc
mount: netkingdom
role: credential-broker-keycape-client-secret-generate
role_file: openbao/auth/credential-broker-keycape-client-secret-generate-oidc-role.json
bound_claims_confirmed: true
founder_confirmation_items:
- bound_claims_confirmed stays false until the founder confirms groups=[net-kingdom-admins]
and /assurance/level=aal2, the same binding as CCR-2026-0034 (key-cape b2ed890f).
- The role carries only the issuer policy. It cannot read or write KV; it can mint
keycape-client-secret-generate tokens through that token role, and look up and revoke
tokens by accessor.
- Loopback CLI callbacks only (no UI callback); 15m with unlimited uses inside that
lifetime (a limited-use token cannot create the child grant token), no default policy.
access_frontdoor:
type: none
catalog_id: none
risk:
level: medium
notes:
- Membership of net-kingdom-admins is the access decision; that group already holds
the KeyCape operator reader (CCR-2026-0034).
- Worst case within 15m is unused keycape/<client_id> entries; existing entries
cannot be overwritten (CCR-2026-0040 is create-only).
verification:
positive:
- Readback after apply matches source for the role (done by applier-apply; prints
nothing).
- A member with aal2 logs in with role credential-broker-keycape-client-secret-generate
and the token's policies are exactly [credential-broker-keycape-client-secret-generate-issuer].
negative:
- A non-member or a member without aal2 cannot obtain the role.
- The issuer token is denied on platform/* and on token creation through any other
role.
activation_conditions:
- Founder approval (two reviews) and bound_claims_confirmed true.
- CCR-2026-0040 applied (done 2026-10-09).
evidence:
- at: '2026-10-10T08:27:38+00:00'
actor: founder
kind: delegated_metadata_apply
result: passed
details:
- Delegated metadata applier ran as founder using local bao CLI ambient authority.
- 'Issuer policy confirmed live and matching source: sys/policies/acl/credential-broker-keycape-client-secret-generate-issuer'
- 'OIDC role metadata write: auth/netkingdom/role/credential-broker-keycape-client-secret-generate'
- Readback matched source for the role.
- No secret values were read, written, printed, or accepted in argv.
- at: '2026-10-10T09:15:56+00:00'
actor: founder
kind: delegated_metadata_apply
result: passed
details:
- Delegated metadata applier ran as founder using local bao CLI ambient authority.
- 'Issuer policy confirmed live and matching source: sys/policies/acl/credential-broker-keycape-client-secret-generate-issuer'
- 'OIDC role metadata write: auth/netkingdom/role/credential-broker-keycape-client-secret-generate'
- Readback matched source for the role.
- No secret values were read, written, printed, or accepted in argv.
lifecycle:
deactivate: Delete auth/netkingdom/role/credential-broker-keycape-client-secret-generate.
rotate: Not applicable (no stored credential).
compromised: Delete the role, revoke issued tokens; review keycape/ metadata for
entries without an accepted case.
state_hub:
workplan: RPF-WP-0054
task: RPF-WP-0054-T05

View file

@ -1,144 +0,0 @@
id: CCR-2026-0044
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: Name the dedicated reuse-surface hub-writer caller
status: applied
created: '2026-10-14'
updated: '2026-10-14'
requester:
agent: ops-mason
reason: CUST-WP-0081 / State Hub message 08b254af. CCR-2026-0005 is active and does
not name a caller. Entity 4a1f663c-0621-0483-edd0-f852274f4b6b carries default,
operator-custody and platform-admin, so its token can reach the data path, the
metadata path and siblings. Do not attach workload-kv-read-reuse-surface-runtime
to that entity. Bind a new OIDC role to a different principal. No secret value
is rotated or read by this request.
review:
required: true
required_approvers:
- platform-operator
- reuse-surface-owner
comments:
- at: '2026-10-14T16:40:00Z'
reviewer: grok
decision: filed_proposed
comment: Filed from ops-mason fd2c5a5 (State Hub 3642bdc4, MASON-WP-0011). Status
stays proposed. bound_claims_confirmed stays false until key-cape confirms group
reuse-surface-hub-writers and a member entity other than 4a1f663c-0621-0483-edd0-f852274f4b6b.
The caller policy file is unchanged; apply, after platform-operator and reuse-surface-owner
approval, is what adds lookup-self and revoke-self. CCR-2026-0005 remains the
KV and ESO authority. No KV write and no attachment to the platform-admin entity.
- at: '2026-10-14T17:44:49+00:00'
reviewer: platform-operator, reuse-surface-owner (founder Bernd Worsch)
decision: approved
comment: Founder go-ahead 2026-10-14. Approves CCR-2026-0044 as platform-operator
and reuse-surface-owner. Group reuse-surface-hub-writers, member bernd.worsch-3,
second factor enrolled, entity f94b7c34-e151-30bb-b473-df3ccdbb9bf1. That entity
is not 4a1f663c-0621-0483-edd0-f852274f4b6b. No KV write. Do not attach the
policy to the admin entity.
- at: '2026-10-14T17:44:49+00:00'
reviewer: founder Bernd Worsch
decision: binding_confirmed
comment: Bound claims are groups=reuse-surface-hub-writers and /assurance/level=aal2.
Member is bernd.worsch-3, entity f94b7c34-e151-30bb-b473-df3ccdbb9bf1. The apply
checks that entity's policy names and stops if platform-admin or operator-custody
is present.
target:
domain: financials
tenant: reuse
workload: reuse-surface
environment: production
purpose: Attended read of the existing hub writer token by one dedicated caller.
openbao:
mount: platform
kv_path: platform/workloads/reuse/reuse-surface/runtime-secrets
fields:
- REUSE_SURFACE_TOKEN
- REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET
policy_name: workload-kv-read-reuse-surface-runtime
policy_file: openbao/policies/workload-kv-read-reuse-surface-runtime.hcl
metadata_read: true
token_self_lifecycle: true
auth:
method: oidc
mount: netkingdom
role: reuse-surface-hub-writer-workload-kv-read
allowed_redirect_uris:
- http://localhost:8250/oidc/callback
- http://127.0.0.1:8250/oidc/callback
- http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback
oidc_scopes:
- openid
- profile
- email
- groups
user_claim: sub
groups_claim: groups
bound_claims_type: string
bound_claims:
groups:
- reuse-surface-hub-writers
/assurance/level: aal2
bound_claims_confirmed: true
policies:
- workload-kv-read-reuse-surface-runtime
ttl: 15m
token_max_ttl: 30m
token_explicit_max_ttl: 30m
token_no_default_policy: true
token_type: service
access_frontdoor:
type: ops-warden
catalog_id: reuse-surface-hub-write-token
selector: reuse-surface federation hub write bearer token
command: After a passing member receipt, run warden access reuse-surface-hub-write-token
--exec --field REUSE_SURFACE_TOKEN -- <silent helper> with the unprinted lane
token. Do not use --fetch.
resolvable: false
readiness: pending-review
risk:
classification: high
notes:
- REUSE_SURFACE_TOKEN writes to the production federation hub. This request does
not read or rotate it.
- The platform-admin entity must not receive this policy. OpenBao merges entity
policies into every token for that entity.
- The ESO policy workload-kv-read-reuse-surface-runtime-eso stays on its Kubernetes
role. This role does not attach it.
- bound_claims_confirmed stays false until key-cape confirms group reuse-surface-hub-writers
and a member entity other than 4a1f663c-0621-0483-edd0-f852274f4b6b.
- Public bao.coulomb.social ingress is not a callback. Use the workstation tunnel.
verification:
positive:
- A member token's policy set is exactly workload-kv-read-reuse-surface-runtime.
- That token's capabilities are read on the exact data path and the exact metadata
path.
negative:
- The same token is deny on platform/data/workloads/reuse/reuse-surface, its metadata
parent, and platform/data/workloads/issue-core/issue-core/issue-core-runtime.
- A principal outside reuse-surface-hub-writers is refused by the role.
- Entity 4a1f663c-0621-0483-edd0-f852274f4b6b is not the member token's entity.
activation_conditions:
- platform-operator and reuse-surface-owner approve this caller binding.
- key-cape confirms the group and that the member entity has no platform-admin or
operator-custody identity policy.
- Founder applies policy and role through the delegated applier. No KV write.
- scripts/verify-reuse-surface-hub-writer.py member and non-member both pass. Receipts
stay mode 0600 and contain no token or field value.
evidence:
- at: '2026-10-14T17:47:43+00:00'
actor: founder
kind: delegated_metadata_apply
result: passed
details:
- Delegated metadata applier ran as founder using local bao CLI ambient authority.
- 'Policy metadata write: sys/policies/acl/workload-kv-read-reuse-surface-runtime'
- 'Auth role metadata write: auth/netkingdom/role/reuse-surface-hub-writer-workload-kv-read'
- No secret values were read, written, printed, or accepted in argv.
lifecycle:
deactivate: delete auth/netkingdom/role/reuse-surface-hub-writer-workload-kv-read.
Leave the KV path, the ESO role, and CCR-2026-0005 delivery in place.
rotate: unchanged from CCR-2026-0005. This request does not rotate field values.
compromised: detach the role, revoke outstanding tokens for the role, and follow
the CCR-2026-0005 compromise procedure if a field value was exposed.

View file

@ -1,158 +0,0 @@
id: CCR-2026-0045
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: Let the reuse-surface hub-writer caller query its own capabilities
status: applied
created: '2026-10-14'
updated: '2026-10-15'
supersedes: CCR-2026-0044
requester:
agent: ops-mason
reason: NK-WP-0051-T06 / MASON-WP-0012. The CCR-2026-0044 member proof called sys/capabilities
with a root or platform-admin token because the lane policy has no capabilities-self
grant. This request adds a generated sys/capabilities-self grant (update; the
endpoint is a POST and returns capability names only) so the lane token proves
its own reach. Policy, role, bound claims and token bounds are otherwise unchanged
from CCR-2026-0044. Do not attach workload-kv-read-reuse-surface-runtime to entity
4a1f663c-0621-0483-edd0-f852274f4b6b. No secret value is read, written or rotated.
review:
required: true
required_approvers:
- platform-operator
- reuse-surface-owner
comments:
- at: '2026-10-14T21:00:00Z'
reviewer: claude
decision: filed_proposed
comment: Filed for NK-WP-0051-T06 (RPF-WP-0057). Repo policy file now carries
the generated sys/capabilities-self grant; the live policy is unchanged until
approval and a delegated apply. Bound claims are those confirmed under CCR-2026-0044
(groups=reuse-surface-hub-writers, /assurance/level=aal2); this request does
not change them.
- at: '2026-10-15T08:55:59+00:00'
reviewer: Bernd Worsch (founder; platform-operator and reuse-surface-owner)
decision: approved
comment: 'Approved in the NetKingdom NK-WP-0051 attended session on 2026-10-15,
in response to the explicit request covering both approval roles, CCR-2026-0045
capabilities-self metadata apply, and the prepared RPF-WP-0057 login-page deployment.
Negative fixture selected: tegwick; founder confirms MFA enrolled and railiance-admins
membership only.'
target:
domain: financials
tenant: reuse
workload: reuse-surface
environment: production
purpose: Attended read of the existing hub writer token by one dedicated caller.
openbao:
mount: platform
kv_path: platform/workloads/reuse/reuse-surface/runtime-secrets
fields:
- REUSE_SURFACE_TOKEN
- REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET
policy_name: workload-kv-read-reuse-surface-runtime
policy_file: openbao/policies/workload-kv-read-reuse-surface-runtime.hcl
metadata_read: true
token_self_lifecycle: true
auth:
method: oidc
mount: netkingdom
role: reuse-surface-hub-writer-workload-kv-read
allowed_redirect_uris:
- http://localhost:8250/oidc/callback
- http://127.0.0.1:8250/oidc/callback
- http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback
oidc_scopes:
- openid
- profile
- email
- groups
user_claim: sub
groups_claim: groups
bound_claims_type: string
bound_claims:
groups:
- reuse-surface-hub-writers
/assurance/level: aal2
bound_claims_confirmed: true
policies:
- workload-kv-read-reuse-surface-runtime
ttl: 15m
token_max_ttl: 30m
token_explicit_max_ttl: 30m
token_no_default_policy: true
token_type: service
token_capabilities_self: true
access_frontdoor:
type: ops-warden
catalog_id: reuse-surface-hub-write-token
selector: reuse-surface federation hub write bearer token
command: After a passing member receipt, run warden access reuse-surface-hub-write-token
--exec --field REUSE_SURFACE_TOKEN -- <silent helper> with the unprinted lane
token. Do not use --fetch.
resolvable: false
readiness: pending-review
risk:
classification: high
notes:
- sys/capabilities-self reports capability names for paths the caller names. It
returns no secret value and grants no access.
- The platform-admin entity must not receive this policy. OpenBao merges entity
policies into every token for that entity.
- The ESO policy workload-kv-read-reuse-surface-runtime-eso stays on its Kubernetes
role and is unchanged.
- Until this apply, the live lane policy differs from openbao/policies/workload-kv-read-reuse-surface-runtime.hcl
by this one stanza; CCR-2026-0044 member proofs stay on the earlier operator-token
path.
verification:
positive:
- A member token's policy set is exactly workload-kv-read-reuse-surface-runtime.
- That token's capabilities are read on the exact data path and the exact metadata
path.
- The member token can call sys/capabilities-self on the five proof paths without
any operator or root token.
negative:
- The same token is deny on platform/data/workloads/reuse/reuse-surface, its metadata
parent, and platform/data/workloads/issue-core/issue-core/issue-core-runtime.
- A principal outside reuse-surface-hub-writers is refused by the role.
- Entity 4a1f663c-0621-0483-edd0-f852274f4b6b is not the member token's entity.
activation_conditions:
- platform-operator and reuse-surface-owner approve this request.
- Founder applies the policy through the delegated applier. No KV write, no role
change beyond re-asserting the CCR-2026-0044 role.
- ops-mason scripts/verify-reuse-surface-hub-writer.py member passes using capabilities-self
only, and non-member records the OpenBao refusal verbatim. Receipts stay mode
0600 with no token or field value.
evidence:
- at: '2026-10-15T17:13:16+00:00'
actor: Bernd Worsch (founder), via codex conduit
kind: delegated_metadata_apply
result: passed
details:
- Delegated metadata applier ran as Bernd Worsch (founder), via codex conduit
using local bao CLI ambient authority.
- 'Policy metadata write: sys/policies/acl/workload-kv-read-reuse-surface-runtime'
- 'Auth role metadata write: auth/netkingdom/role/reuse-surface-hub-writer-workload-kv-read'
- No secret values were read, written, printed, or accepted in argv.
- at: '2026-10-15T17:22:52+00:00'
actor: Bernd Worsch (founder), via codex conduit
kind: positive_verification
result: passed
details:
- Attended bernd.worsch-3 diagnosis aal2 and hub-writers group confirmed; exact
workload policy, intended reads, parent/sibling denial and self-revocation passed
using capabilities-self. Receipt docs/evidence/2026-10-15-sign-in-diagnosis/reuse-surface-hub-writer-member.txt
- at: '2026-10-15T17:22:52+00:00'
actor: Bernd Worsch (founder), via codex conduit
kind: negative_verification
result: passed
details:
- 'Founder-selected tegwick diagnosis showed railiance-admins only and aal2 before
the workload role refused the groups bound claim. Native sanitized receipt names
same uid and preserves error: docs/evidence/2026-10-15-sign-in-diagnosis/reuse-surface-hub-writer-non-member.txt'
lifecycle:
deactivate: Re-apply the CCR-2026-0044 policy body (drop the sys/capabilities-self
stanza). Leave the role, KV path and ESO lane in place.
rotate: unchanged from CCR-2026-0005. This request does not rotate field values.
compromised: detach the role, revoke outstanding tokens for the role, and follow
the CCR-2026-0005 compromise procedure if a field value was exposed.

View file

@ -1,155 +0,0 @@
id: CCR-2026-0046
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: Reuse Surface exact seven private-source repository reader
status: approved
created: '2026-10-15'
updated: '2026-10-15'
requester:
agent: platform-reader-owner delegated preparation worker
reason: CUST-WP-0081 hosted63/70; founder metadata consent excludes credential provisioning.
Prepare separate seven-source reader.
review:
required: true
required_approvers:
- platform-operator
- railiance-forge-owner
- reuse-surface-owner
- railiance-apps-owner
comments: []
target:
domain: infotech
tenant: reuse
workload: reuse-surface
environment: production
purpose: Native acquisition of seven owner-consented pinned capability indexes.
Entire private repository code becomes readable by this dedicated reader; publication
remains only approved metadata.
openbao:
mount: platform
kv_path: platform/workloads/reuse/reuse-surface/forgejo-source-read
fields:
- FORGEJO_SOURCE_TOKEN
- API_USER
- API_BASE_URL
- TOKEN_SCOPES
- GENERATED_AT
- CCR_ID
policy_name: workload-kv-read-reuse-surface-runtime-eso
policy_file: openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl
auth:
method: kubernetes
mount: kubernetes
role: reuse-surface-runtime-eso
bound_claims:
service_account_names:
- reuse-surface-eso
service_account_namespaces:
- reuse
bound_claims_confirmed: true
policies:
- workload-kv-read-reuse-surface-runtime-eso
ttl: 15m
audience: openbao
access_frontdoor:
type: external-secrets
catalog_id: reuse-surface-forgejo-source-read
readiness: pending-review
resolvable: false
delivery:
surface: external-secrets
bootstrap_command: warden access openbao-platform-admin-login --exec -- python3
scripts/provision-reuse-surface-forgejo-source.py --approval <approved-revision-receipt>
--receipt <0600-durable-receipt>
provisioning_script: scripts/provision-reuse-surface-forgejo-source.py
target: Existing namespace-limited store openbao-reuse. Apps owner adds only proposed
data item into ExternalSecret reuse/reuse-surface-runtime. Shared target reuse-surface-env
remains intact.
forgejo_identity: reuse-surface-source
forgejo_team: reuse-surface-source-readers
repositories:
- coulomb/rapp-openbao
- coulomb/rapp-policy-nexus
- coulomb/rapp-postgres
- coulomb/rapp-sbom-nexus
- coulomb/rapp-telemetry
- coulomb/rapp-tenant-engine
- coulomb/rapp-user-engine
token_scopes:
- read:repository
consumer_gate: Reader must not be delivered until reuse-surface owner supplies verified
image digest containing BOTH authenticated redirect rejection/HTTPS enforcement
AND runtime-controlled exact URL allowlist checked before credential lookup. Apps
owner must deploy REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST mapping the reader environment
to exactly fourteen consented pinned index/entry URLs and inventory existing authenticated
sources for migration. Current image and redirect-only fix are not approved. Native
owner helper must verify all seven pinned indexes and entry hashes after rollout.
risk:
classification: high
cost_estimate_eur: 0
notes:
- Repository grants disclose entire source of these seven repositories to hub; PAT
cannot restrict access to index files.
- No all-repositories membership, unrelated private grants, admin privileges or
repository writes.
- Existing ESO role expands by one exact data-read path; writer OIDC caller remains
unchanged and denied new path.
- Current consumer accepts arbitrary authenticated source URLs and redirects, allowing
bearer disclosure. Delivery barred until full reviewed redirect/HTTPS and runtime-controlled
exact URL allowlist patch image plus config is verified.
- PAT has no server-side expiry; rotate quarterly and immediately upon incident.
- Bootstrap writes only exact new identity/team/PAT/KV. Policy apply, ESO delivery,
consumer rollout and registrations require their separate reviewed owner steps.
- R1 pagination defect preserved; R2 pages until empty with duplicate/count/bound
checks. Deployed-version live complete-grant inventory remains required; code
review is not live scope proof.
- Quarterly rotation is a proposed manual requirement, not deployed expiry enforcement;
due-date owner task/receipt and separate exact-token recovery approval must be
established before activation.
verification:
positive:
- All seven repositories private; exact team/code-read flags, one dedicated team
member, exact seven memberships, effective permissions read and exactly one read:repository
PAT.
- Each consented index digest matches from authenticated Forgejo read; final hosted
native owner helper verifies pinned entry hashes as well.
- OpenBao stdin CAS=0 field write readback matches without printing or persisting
value.
- ESO binding and namespace condition surveyed and confirmed; native fixed consumer
image verified before delivery; final hosted70/70 exactly once.
negative:
- Known excluded private rapp-core-hub reader request denied (403/404); administrative
endpoint and Actions secrets denied.
- No additional private effective repositories or teams; no write probing that could
itself cause a change.
- Scoped writer and default OpenBao identities denied new data path; ESO role denied
unrelated siblings and wrong SA/namespace/audience login refused.
- Missing/anonymous native authentication refused with no privileged fallback.
activation_conditions:
- Independent platform/Forgejo/reuse/apps owner reviews of exact artifacts and founder
CCR provisioning disposition recorded; no approval inferred from metadata consent.
- Confirm existing ESO Kubernetes binding before any delegated metadata apply.
- Consumer security patch image digest verified and apps rollout artifacts reviewed/approved
before delivery.
- Durable bounded bootstrap and positive/negative receipts pass; owner native fetch
proof passes all seven pinned sources.
- Separate scoped writer registration batch followed by fresh hosted70/70 completion
audit.
evidence: []
lifecycle:
deactivate: Separate explicitly approved silent scripts/revoke-reuse-surface-forgejo-source.py
validates reviewed single PAT ID/name, admin PATCH only dedicated user to fresh
process-memory password, Basic-auth DELETE exact token, then reconciles complete
inventory absence. Admin PAT/Sudo alone cannot delete. KV/shared runtime Secret
preserved. Apps/platform owners remove only new ESO item/policy stanza through
their reviewed path; preserve writer/webhook/public registrations. Recovery helper
approval is separate from mint/bootstrap approval.
rotate: Attended owner revokes old identified PAT then governed bootstrap remints
and verifies; never overwrite differing/orphan KV through bootstrap. Explicit
scoped CAS update plan required for rotation.
compromised: Revoke all dedicated service-user PATs, remove delivery data item,
roll fixed consumer with owners, retain failed sanitized evidence; investigate
all seven private source exposure. Do not rotate unrelated credentials without
separate scope.
revision: 2

View file

@ -1,133 +0,0 @@
id: CCR-2026-0047
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: rapp-qonto collector exact audit sender custody and delivery
status: applied
created: '2026-10-15'
updated: '2026-10-15'
requester:
agent: codex
reason: NK-WP-0035 operational receiver admission requires actual Qonto delivery. This proposed new
grant requires explicit owner approval of the concrete packet; prior timing rollout approval does
not approve it.
review:
required: true
required_approvers:
- platform-operator
- audit-core-owner
- rapp-qonto-owner
- qonto-assistant-owner
comments:
- at: '2026-10-15T19:27:15.042944+00:00'
reviewer: user explicit session approval (platform-operator, audit-core-owner, rapp-qonto-owner,
qonto-assistant-owner)
decision: approved
comment: User selected "Approve packet and proceed" for the complete exact packet including CCR-2026-0047
new bearer/ESO lane, compatible source/receiver upgrades and migrations, exact registry admission,
collector and one-hour observation. This approval applies to reviewed exact scopes; prerequisites,
attended custody and independent receipts remain required.
target:
domain: financials
tenant: friendly:binky
workload: rapp-qonto
environment: production
purpose: Sender rapp-qonto-collector, sole source qonto-assistant, sole tenant tenant:friendly:binky,
may_write=true, may_read=false, secret_policy=redact. Envelope admission load-bearing; preserve
the source declaration per-class distinction. One source heartbeat maps to audit-core.heartbeat
covering only audit.deny. Wait 2f=7200s, effective deadline3f=10800s. No bank calls or authority
changes.
openbao:
mount: platform
kv_path: platform/workloads/rapp-qonto/audit-sender
fields:
- AUDIT_TOKEN
- CUSTODY_REQUEST
policy_name: workload-kv-read-rapp-qonto-audit
policy_file: openbao/policies/workload-kv-read-rapp-qonto-audit.hcl
metadata_read: false
auth:
method: kubernetes
mount: kubernetes
role: external-secrets-rapp-qonto-audit
bound_claims:
service_account_names:
- external-secrets
service_account_namespaces:
- external-secrets
bound_claims_confirmed: true
policies:
- workload-kv-read-rapp-qonto-audit
ttl: 15m
access_frontdoor:
type: external-secrets
catalog_id: rapp-qonto-audit
readiness: pending-review
resolvable: false
delivery:
surface: external-secrets
target: ClusterSecretStore openbao-rapp-qonto-audit restricted to namespace rapp-qonto; ExternalSecret
and Secret rapp-qonto/rapp-qonto-audit-sender key bearer. Source manifests/rapp-qonto-audit-sender.yaml.
No namespace is created.
risk:
classification: high
notes:
- A bearer permits append only for exact collector sender/source/tenant. Own-source reconciliation
is available as receiver contract, cross-source and other read routes remain refused.
- The registry update reads existing sender credentials inside the attended platform process only.
No registry is delivered to a producer.
- OpenBao login TTL limits the ESO reader session, not the audit bearer. Revocation must remove the
sender token from the receiver registry and prove refusal.
- New paths extend the existing coding-agent deny boundary. No existing high-risk grant is widened.
verification:
positive:
- CAS=0 first provision; durable provenance-bound version1 resume without rotation; exact sender registry
CAS merge preserves all existing credentials/senders/other fields.
- ESO copies only AUDIT_TOKEN to the named bearer key, exact source/delivery readback, receiver registry
delivery and compatible reload.
- Actual capture, periodic real elapsed frame, per-instance reconciliation and independent observer
acceptance remain required after provisioning.
negative:
- Proposed or missing owner approval refuses before native calls; drifted custody, sender or source
hashes refuse.
- Reader cannot read registry, sibling custody, metadata or parent listing; wrong service account/namespace
refused; coding-agent boundary deny wins.
- Wrong sender source/tenant and non-own-source read routes refused. Audit bearer revocation is separate
from reader-session revoke.
activation_conditions:
- Explicit approval of this complete concrete packet from platform-operator, audit-core-owner, rapp-qonto-owner
and qonto-assistant-owner.
- Compatible immutable receiver release with cadence parser and per-instance reconciliation is deployed/ready
before credential reads.
- Use attended Warden platform-admin --exec envelope, no shell-exported credentials; keep nonsecret
independent receipt and self-revoke.
- Apply only reviewed exact policy/role/projection; retain custody on failure and resume explicitly;
no destructive rollback or blind rotation.
- No claim of operational completion until native positive/negative and independent observer receipts.
evidence:
- at: '2026-10-15T19:40:23.546597+00:00'
actor: codex via attended user platform-admin (root execution)
kind: cadence_receiver_custody_and_native_sender
result: passed
details:
- "Native custody receipt receiver-custody-live.json: protected registry CAS15\u219216, version1\
\ new sender custody, Approval credentials and all other senders preserved; exact readers/negative\
\ checks/revocation, ESO values and loaded receiver registry matched."
- Independent attended wrapper exit0/session self-revocation confirmed by root; initial rejected
OPENBAO_TOKEN-ambient attempt made no credential mutation and session self-revocation was confirmed.
Retry launch removed all three token aliases before OIDC.
- 'Native collector source/tenant admission: heartbeat1 and deny1 reconciled for one exact instance;
duplicate200, wrong-source400, wrong-tenant400, archive-read403, invalid-bearer401; pending0.
Original202 receipts persisted across actual collector pod restart.'
- Source receipts copied unchanged to docs/evidence/2026-10-15-cadence-receiver/. Status applied
only; elapsed-hour periodic observation, independent protected observer readback and complete
native matrix remain pending.
lifecycle:
deactivate: Stop only collector; remove only its bearer from receiver via CAS and prove refusal; detach
exact reader role and ESO projection; retain custody versions and evidence.
rotate: Separate reviewed overlap-first change; this helper only provisions version1 and resumes without
rotation.
compromised: Stop collector and revoke its exact receiver bearer first; preserve evidence and perform
separate reviewed rotation.
state_hub:
related_workplan: NK-WP-0035

View file

@ -1,174 +0,0 @@
{
"id": "CCR-2026-0048",
"kind": "credential-change-request",
"schema_version": 1,
"request_type": "workload-kv-read",
"title": "NetKingdom identity backup publisher delivery",
"status": "verified",
"created": "2026-10-16",
"updated": "2026-10-16",
"requester": {
"agent": "codex",
"reason": "Operator requested implementation of missing full identity backup coverage under NK-WP-0049."
},
"review": {
"required": true,
"required_approvers": [
"platform-owner",
"net-kingdom-owner",
"backup-share-owner"
],
"comments": [
{
"reviewer": "User (platform-owner)",
"decision": "approved",
"source_ref": "statehub:decision:5edd5203-fae7-461b-8775-5d22b7873971",
"comment": "Operator approved initial live packet and subsequently explicitly approved provider-added folder-only re-sharing (publisher21/retention25). Native admission and independent recovery gates remain.",
"reviewed_at": "2026-10-15T23:51:21.372915+00:00"
},
{
"reviewer": "User (net-kingdom-owner)",
"decision": "approved",
"source_ref": "statehub:decision:5edd5203-fae7-461b-8775-5d22b7873971",
"comment": "Operator approved initial live packet and subsequently explicitly approved provider-added folder-only re-sharing (publisher21/retention25). Native admission and independent recovery gates remain.",
"reviewed_at": "2026-10-15T23:51:21.372938+00:00"
},
{
"reviewer": "User (backup-share-owner)",
"decision": "approved",
"source_ref": "statehub:decision:5edd5203-fae7-461b-8775-5d22b7873971",
"comment": "Operator approved initial live packet and subsequently explicitly approved provider-added folder-only re-sharing (publisher21/retention25). Native admission and independent recovery gates remain.",
"reviewed_at": "2026-10-15T23:51:21.372944+00:00"
}
]
},
"target": {
"domain": "infotech",
"tenant": "platform",
"workload": "net-kingdom-identity-backup",
"environment": "production",
"purpose": "publisher limited to new encrypted identity bundle folder; no age private key or Backup account password"
},
"openbao": {
"mount": "platform",
"kv_path": "platform/workloads/net-kingdom/identity-backup/publisher",
"fields": [
"NC_WEBDAV_TOKEN"
],
"policy_name": "net-kingdom-backup-publisher-eso",
"policy_file": "openbao/policies/net-kingdom-backup-publisher-eso.hcl",
"metadata_read": false,
"token_self_lifecycle": true,
"auth": {
"method": "kubernetes",
"mount": "kubernetes",
"role": "net-kingdom-backup-publisher-eso",
"audience": "openbao",
"ttl": "15m",
"token_max_ttl": "15m",
"token_explicit_max_ttl": "15m",
"token_no_default_policy": true,
"policies": [
"net-kingdom-backup-publisher-eso"
],
"bound_claims": {
"service_account_names": [
"net-kingdom-backup-publisher-eso"
],
"service_account_namespaces": [
"sso"
]
},
"bound_claims_confirmed": true
}
},
"provider": {
"host": "nx4069.your-storageshare.de",
"account_owner": "Backup",
"folder": "/railiance-backups/net-kingdom-identity",
"share_type": 3,
"share_permissions": 21,
"label": "net-kingdom-identity-publisher",
"age_private_key_delivered": false,
"backup_account_password_delivered": false,
"provider_added_resharing_approved": true,
"scope_approval_ref": "statehub:decision:5edd5203-fae7-461b-8775-5d22b7873971"
},
"delivery": {
"surface": "ESO-managed Secret",
"namespace": "sso",
"secret": "net-kingdom-backup-publisher",
"key": "token",
"service_account": "net-kingdom-backup-publisher-eso",
"reader": "owner-controlled root backup publisher process on railiance01; no agent credential delivery",
"cluster_uid": "a553c742-0115-43d4-99a4-a5ca56fe0786"
},
"risk": {
"classification": "high",
"notes": [
"Secret-bearing capture is limited to the four reviewed identity services; source/cluster/PVC bindings and private staging are mandatory.",
"Publisher can read/create in only this folder; retention can read/delete in only this folder. Both also include explicitly owner-approved provider-added re-sharing, but neither grants update, other folders or account administration.",
"Compromise of the retention token can destroy backups in this folder; keep it separate from the publisher and retain independent custody/recovery points.",
"Node root and ESO administration are inside this credential boundary. Coding agents must be denied the new data/metadata paths.",
"KV initial CAS=0 only; preserve existing values and refuse drift. No rotation of service keys, age keys, operator credentials or existing shares.",
"Nextcloud adds permission16 to read-enabled public links. User approved exact normalized21/25 on2026-10-16; tokens remain confined to the encrypted backup folder. Original5/9 attempt failed before credential publication."
]
},
"verification": {
"positive": [
"Exact share path/permissions and bounded KV/ESO role readback",
"Ciphertext create/readback/hash and exact runtime token delivery",
"Retention protects recovered point/newest/seven daily/two weekly and uses ETag-conditional deletion"
],
"negative": [
"Publisher overwrite/delete denied; retention create/overwrite denied",
"Sibling namespace/path/audience and KV write/metadata access denied",
"Unapproved or changed CCR/source, wrong cluster, resource drift and malformed archive refuse execution"
],
"activation_conditions": [
"Explicit platform, NetKingdom and Backup-share owner approval of this exact request and source packet.",
"Native exact namespace/ServiceAccount/audience verification and bounded role readback; no coding-agent access.",
"First real encrypted capture, off-host readback, independent-key recovery and cleanup before enabling timers.",
"Retention protects the independently recovered point and newest backup; no automatic deletion of pre-existing backups."
],
"actual_native_receipt": {
"repository": "net-kingdom",
"path": "docs/evidence/2026-10-16-identity-backup-live/publisher-native-delivery-verified.json",
"source_ccr_sha256": "a7d5c356ce0c4df9de439a58ad3d36e1578f27cda327c9387e97cfc2e9dc7cdc",
"status": "reader_and_delivery_verified",
"kv_version": 1,
"wrong_service_account_denied": true,
"wrong_namespace_denied": true,
"wrong_audience_denied": true,
"bounded_policy_ttl": true,
"eso_equality_verified": true,
"temporary_reader_revoked": true
},
"actual_archive_and_runtime_acceptance": {
"owner_repo": "net-kingdom",
"archive_recovery": "docs/evidence/2026-10-16-identity-backup-live/actual-full-recovery-review.json",
"activation": "docs/evidence/2026-10-16-identity-backup-live/activation-review.json",
"recurring_job": "docs/evidence/2026-10-16-identity-backup-live/recurring-job-review.json",
"independent_owner_retrieval_and_key_recovery_passed": true,
"systemd_job_result": "success",
"timers_enabled": true,
"retention_pinned_and_newest_protected": true,
"native_full_service_authentication_claimed": false
}
},
"state_hub": {
"workplan_id": "NK-WP-0049",
"task_id": "NK-WP-0049-T03"
},
"access_frontdoor": {
"type": "platform-owner",
"catalog_id": "net-kingdom-identity-backup-publisher",
"readiness": "pending-review",
"resolvable": false
},
"lifecycle": {
"deactivate": "Stop the corresponding timer, withdraw only this reader and disable its dedicated share under owner control; retain archives and recovery keys.",
"rotate": "Separate owner-approved share replacement and KV CAS rotation, preserved old access until new readback and recovery pass; no silent replacement by this setup tool.",
"compromised": "Fence this executor and revoke its dedicated share/reader. Treat retention-token compromise as possible loss of this folder; preserve independent recovery points and rotate only under incident authority."
}
}

View file

@ -1,174 +0,0 @@
{
"id": "CCR-2026-0049",
"kind": "credential-change-request",
"schema_version": 1,
"request_type": "workload-kv-read",
"title": "NetKingdom identity backup retention delivery",
"status": "verified",
"created": "2026-10-16",
"updated": "2026-10-16",
"requester": {
"agent": "codex",
"reason": "Operator requested implementation of missing full identity backup coverage under NK-WP-0049."
},
"review": {
"required": true,
"required_approvers": [
"platform-owner",
"net-kingdom-owner",
"backup-share-owner"
],
"comments": [
{
"reviewer": "User (platform-owner)",
"decision": "approved",
"source_ref": "statehub:decision:5edd5203-fae7-461b-8775-5d22b7873971",
"comment": "Operator approved initial live packet and subsequently explicitly approved provider-added folder-only re-sharing (publisher21/retention25). Native admission and independent recovery gates remain.",
"reviewed_at": "2026-10-15T23:51:21.374014+00:00"
},
{
"reviewer": "User (net-kingdom-owner)",
"decision": "approved",
"source_ref": "statehub:decision:5edd5203-fae7-461b-8775-5d22b7873971",
"comment": "Operator approved initial live packet and subsequently explicitly approved provider-added folder-only re-sharing (publisher21/retention25). Native admission and independent recovery gates remain.",
"reviewed_at": "2026-10-15T23:51:21.374033+00:00"
},
{
"reviewer": "User (backup-share-owner)",
"decision": "approved",
"source_ref": "statehub:decision:5edd5203-fae7-461b-8775-5d22b7873971",
"comment": "Operator approved initial live packet and subsequently explicitly approved provider-added folder-only re-sharing (publisher21/retention25). Native admission and independent recovery gates remain.",
"reviewed_at": "2026-10-15T23:51:21.374039+00:00"
}
]
},
"target": {
"domain": "infotech",
"tenant": "platform",
"workload": "net-kingdom-identity-backup",
"environment": "production",
"purpose": "retention limited to new encrypted identity bundle folder; no age private key or Backup account password"
},
"openbao": {
"mount": "platform",
"kv_path": "platform/workloads/net-kingdom/identity-backup/retention",
"fields": [
"NC_WEBDAV_TOKEN"
],
"policy_name": "net-kingdom-backup-retention-eso",
"policy_file": "openbao/policies/net-kingdom-backup-retention-eso.hcl",
"metadata_read": false,
"token_self_lifecycle": true,
"auth": {
"method": "kubernetes",
"mount": "kubernetes",
"role": "net-kingdom-backup-retention-eso",
"audience": "openbao",
"ttl": "15m",
"token_max_ttl": "15m",
"token_explicit_max_ttl": "15m",
"token_no_default_policy": true,
"policies": [
"net-kingdom-backup-retention-eso"
],
"bound_claims": {
"service_account_names": [
"net-kingdom-backup-retention-eso"
],
"service_account_namespaces": [
"sso"
]
},
"bound_claims_confirmed": true
}
},
"provider": {
"host": "nx4069.your-storageshare.de",
"account_owner": "Backup",
"folder": "/railiance-backups/net-kingdom-identity",
"share_type": 3,
"share_permissions": 25,
"label": "net-kingdom-identity-retention",
"age_private_key_delivered": false,
"backup_account_password_delivered": false,
"provider_added_resharing_approved": true,
"scope_approval_ref": "statehub:decision:5edd5203-fae7-461b-8775-5d22b7873971"
},
"delivery": {
"surface": "ESO-managed Secret",
"namespace": "sso",
"secret": "net-kingdom-backup-retention",
"key": "token",
"service_account": "net-kingdom-backup-retention-eso",
"reader": "owner-controlled root backup retention process on railiance01; no agent credential delivery",
"cluster_uid": "a553c742-0115-43d4-99a4-a5ca56fe0786"
},
"risk": {
"classification": "high",
"notes": [
"Secret-bearing capture is limited to the four reviewed identity services; source/cluster/PVC bindings and private staging are mandatory.",
"Publisher can read/create in only this folder; retention can read/delete in only this folder. Both also include explicitly owner-approved provider-added re-sharing, but neither grants update, other folders or account administration.",
"Compromise of the retention token can destroy backups in this folder; keep it separate from the publisher and retain independent custody/recovery points.",
"Node root and ESO administration are inside this credential boundary. Coding agents must be denied the new data/metadata paths.",
"KV initial CAS=0 only; preserve existing values and refuse drift. No rotation of service keys, age keys, operator credentials or existing shares.",
"Nextcloud adds permission16 to read-enabled public links. User approved exact normalized21/25 on2026-10-16; tokens remain confined to the encrypted backup folder. Original5/9 attempt failed before credential publication."
]
},
"verification": {
"positive": [
"Exact share path/permissions and bounded KV/ESO role readback",
"Ciphertext create/readback/hash and exact runtime token delivery",
"Retention protects recovered point/newest/seven daily/two weekly and uses ETag-conditional deletion"
],
"negative": [
"Publisher overwrite/delete denied; retention create/overwrite denied",
"Sibling namespace/path/audience and KV write/metadata access denied",
"Unapproved or changed CCR/source, wrong cluster, resource drift and malformed archive refuse execution"
],
"activation_conditions": [
"Explicit platform, NetKingdom and Backup-share owner approval of this exact request and source packet.",
"Native exact namespace/ServiceAccount/audience verification and bounded role readback; no coding-agent access.",
"First real encrypted capture, off-host readback, independent-key recovery and cleanup before enabling timers.",
"Retention protects the independently recovered point and newest backup; no automatic deletion of pre-existing backups."
],
"actual_native_receipt": {
"repository": "net-kingdom",
"path": "docs/evidence/2026-10-16-identity-backup-live/retention-native-delivery-verified.json",
"source_ccr_sha256": "d391018abbda0fbc1acec4b5a22c0f99ce65525de940431df376a7552b3746cf",
"status": "reader_and_delivery_verified",
"kv_version": 1,
"wrong_service_account_denied": true,
"wrong_namespace_denied": true,
"wrong_audience_denied": true,
"bounded_policy_ttl": true,
"eso_equality_verified": true,
"temporary_reader_revoked": true
},
"actual_archive_and_runtime_acceptance": {
"owner_repo": "net-kingdom",
"archive_recovery": "docs/evidence/2026-10-16-identity-backup-live/actual-full-recovery-review.json",
"activation": "docs/evidence/2026-10-16-identity-backup-live/activation-review.json",
"recurring_job": "docs/evidence/2026-10-16-identity-backup-live/recurring-job-review.json",
"independent_owner_retrieval_and_key_recovery_passed": true,
"systemd_job_result": "success",
"timers_enabled": true,
"retention_pinned_and_newest_protected": true,
"native_full_service_authentication_claimed": false
}
},
"state_hub": {
"workplan_id": "NK-WP-0049",
"task_id": "NK-WP-0049-T03"
},
"access_frontdoor": {
"type": "platform-owner",
"catalog_id": "net-kingdom-identity-backup-retention",
"readiness": "pending-review",
"resolvable": false
},
"lifecycle": {
"deactivate": "Stop the corresponding timer, withdraw only this reader and disable its dedicated share under owner control; retain archives and recovery keys.",
"rotate": "Separate owner-approved share replacement and KV CAS rotation, preserved old access until new readback and recovery pass; no silent replacement by this setup tool.",
"compromised": "Fence this executor and revoke its dedicated share/reader. Treat retention-token compromise as possible loss of this folder; preserve independent recovery points and rotate only under incident authority."
}
}

View file

@ -1,149 +0,0 @@
id: CCR-2026-0050
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: Retained flex-auth lifecycle PDP Ed25519 seed ESO read lane
status: proposed
created: '2026-10-16'
updated: '2026-10-21'
requester:
agent: railiance-platform
reason: CUST-WP-0080 / RPF-WP-0035-T12 / FLEX-WP-0024-T06 source proposal. Exact
new custody/read scope requires founder review; no approval or native binding
is inferred.
review:
required: true
required_approvers:
- founder
- railiance-platform-owner
- access-engine-owner
- secrets-engine-owner
- cluster-runtime-storage-owner
comments:
- reviewer: railiance-platform-owner (agent railiance-platform, codex)
decision: approved
comment: "INTENT Secure Custody/Stable Interfaces/Operable and Observable; RPF-WP-0035-T12. Approved Platform source/plan review only for frozen P4 manifest sha256:9eec861838a8e9138f2c821cff3014998fbe74bb01447642bf50d44e8b3b7cb3 and R2 manifest sha256:5be7a7f438624dcb35b7dbc96e7c9e5a8e2d5d1b1966cb4338aa5aecbe3a8b59; own bounded review artifact docs/evidence/2026-10-16-flex-auth-native-plan-review.json; sanitized supplement sha256:f02a9b595d9246a358d19b37bdad8fde158b7f0c9657a6ff088874732511a32c. Exact read policy/role/ESO proposal and one-use CAS0 writer stay separate. Identified manual Applications and recovery gates are mandatory. No founder decision, native execution, confirmed bindings or completed admission assent; current proposed status unchanged."
- reviewer: secrets-engine-owner (agent secrets-engine, hub message e73c9ffc-8a6d-4a17-9604-84324c678dc0)
decision: approved
comment: "2026-10-21, consumer side only. Consumer has no KV read role and receives no seed; public trust is admitted through a founder ssh-keygen -Y signed trust file (secrets-engine ebd8e49, docs/founder-admission.md, SECRETS-WP-0014-T04, decision e4eee965). Conditions: (1) the public receipt (kid, Ed25519 public key as 43-char unpadded base64url, evaluator/mode/policy_package/policy_version/policy_package_digest) is emitted by the attended writer at creation time or derived from its public output, never from a later read of the KV document; (2) the receipt carries the KV version and source/image provenance hashes. Not founder approval, native execution or a binding."
- reviewer: access-engine-owner (agent access-engine, hub message 6529bacf-6065-4fb9-a9a2-a41b7ed8fa83)
decision: approved
comment: "2026-10-21, Access scope only (retained workload/release source, Helm values, candidate image 84c78e0 / sha256:09a83b27...981f, signer delivery/mount, process/outbox fencing/rollback). Reviewed CCR sha256:66b3ea12d3f3bb870fa0aa0e7598e32abdb9bf39efe1adcfac286407abeb98e4 (before the 2026-10-21 review-comment edits), policy sha256:b9f816da25c7770863a5fc7b3b873e9da90b087e71a9052e766bf0a187ff85ef, native plan sha256:22306d50e082fc35c5ff22c31e739a53c7584176969063f7907bc01d77e506e5, R2 manifest sha256:5be7a7f438624dcb35b7dbc96e7c9e5a8e2d5d1b1966cb4338aa5aecbe3a8b59. Notes: seed read once at startup so rotation stays a fenced restart; bound_claims_confirmed needs native readback before activation; consumer policy pin comes from access-engine docs/native-signer-public-pin.json and the public receipt is prepared with access-engine tools/prepare_signer_public_receipt.py (public fields only); keep writer receipt fields kid/public_key/public_fingerprint/kv_version/status/path/source_ccr_sha256 stable. Access rollout founder go is separate: decision 4fab263d-686b-4a06-9cea-8df619c87c03 (FLEX-WP-0024-T07). Not founder approval or native execution."
- reviewer: railiance-platform-owner (agent railiance-platform, claude)
decision: approved
comment: "2026-10-21 runbook review: docs/runbooks/flex-auth-envelope-signing-attended-creation.md prepared. Secrets condition 1 is met for kid/public key/fingerprint/kv_version/source/image hashes by deriving the consumer receipt from the writer receipt (no KV read); the evaluator/mode/policy_package/policy_version/policy_package_digest fields are not in the writer receipt and must come from Access's published pin. Review-scope digest no longer binds expires_at so owner reviews can precede the 120 s window. Pending: cluster-runtime-storage-owner (railiance-cluster, requested f8b4b229), founder. Nothing executed."
target:
domain: infotech
tenant: platform
workload: flex-auth-secrets-engine
environment: production
purpose: Deliver the Ed25519 seed only to the retained lifecycle PDP startup file.
No taskpilot enablement, caller policy changes or private consumer trust discovery.
openbao:
mount: platform
kv_path: platform/workloads/flex-auth/envelope-signing
fields:
- ED25519_PRIVATE_KEY
metadata_read: false
token_self_lifecycle: true
policy_name: workload-kv-read-flex-auth-envelope-signing
policy_file: openbao/policies/workload-kv-read-flex-auth-envelope-signing.hcl
auth:
method: kubernetes
mount: kubernetes
role: external-secrets-flex-auth-envelope-signing
bound_claims:
service_account_names:
- flex-auth-envelope-signing-eso
service_account_namespaces:
- flex-auth
bound_claims_confirmed: false
policies:
- workload-kv-read-flex-auth-envelope-signing
ttl: 15m
audience: openbao
token_max_ttl: 15m
token_explicit_max_ttl: 15m
token_no_default_policy: true
access_frontdoor:
type: external-secrets
catalog_id: flex-auth-decision-envelope-signing-key
selector: Retained flex-auth/flex-auth-secrets-engine signer only
readiness: pending-review
resolvable: false
delivery:
surface: external-secrets
target: Namespace flex-auth; dedicated SA flex-auth-envelope-signing-eso; SecretStore
openbao-flex-auth-envelope-signing; ExternalSecret/Secret flex-auth-secrets-engine-envelope-signing;
seed property only -> startup file /var/run/secrets/decision-envelope-signing/seed0440,
readOnly uid/gid/fsGroup65532; no envFrom
bootstrap_command: Attended writer is a distinct exact-source action proposal in
docs/credential-lane-designs/flex-auth-envelope-signing-native-plan.json; this
read CCR authorizes neither seed writes nor any execution by itself.
risk:
classification: high
notes:
- Possession of seed can forge lifecycle PDP envelopes; independent consumer public
trust admission and fixed policy pins remain required.
- 'KV read is document-granular: dedicated ESO identity can read all three fields;
only private field is projected. Signer and Secrets consumer have no KV read role.'
- No parent/list/metadata/write/delete capability on ESO role. No token/Secret read
or ExternalSecret mutation granted to signer/consumer.
- Dedicated role is new scope; founder approval required. Canonical local draft
rapp-flex-auth-secrets-engine declaration committed d52f811ed5ec0be27c9c367e71d476125b3228be
(tree d4fbad71bdc8ef870603526ccd1f53195d0322af) resolves locally. Remote publication,
placement and actual runtime/package join remain open; no missing-declaration waiver.
- Controlled outage fence stops every signer pod and disables autoscaling before
creation/rotation. Recreate single writer and retained PVC remain Access-owned
review gates.
- fields declares only projected ED25519_PRIVATE_KEY. The same KV document also
has public key/kid, and OpenBao data-read is not field-filtered; do not claim
field-level authorization.
verification:
positive:
- Exact SA/namespace/audience login; native exact data read; ESO Ready/SecretSynced;
safe metadata resourceVersion and admitted KV version correspondence.
- All retained signer replicas restart with admitted kid/source/image and seed/public
correspondence; signed non-effect lifecycle allow/deny with durable originals
and independent consumer signature verification.
negative:
- Wrong SA/namespace/audience login refused; sibling/list/metadata/write/delete
denied; signer/consumer TokenRequest and Secret/ExternalSecret mutation denied.
- Unknown/old kid, wrong policy, tamper, expired/unsigned envelope, wrong caller
and missing seed refuse; no taskpilot activation.
- Receipt write failure/lost backend response retained unknown; no blind retry/remint;
stale CAS cannot overwrite version.
activation_conditions:
- All five required approvers independently dispose the exact CCR and native writer/fence
proposal; founder exact new custody scope approved.
- Exact proposed candidate source Git revision 84c78e072c8f3daf2bc3b90f8efc8fbeb16c3cf0 and immutable
OCI reference forgejo.coulomb.social/coulomb/flex-auth@sha256:09a83b273740133518cc7e7915fb211381e06545d347d2dd1974042525ac981f
published by successful ordinary CI402; source OS-lock/64MiB/startup hardening present.
Access reviews exact workload/Helm image/mount/outbox source and actual stopped fence;
Platform reviews existing GitOps Application sync/freeze; cluster reviews runtime,
headroom and new PVC1Gi/local-path actual PV retention. Local draft package d52
remains source2b/current05a, not candidate84 adoption; remote placement/runtime join
and native mounted-runtime acceptance remain open. Enablement only if separately
proposed ArgoCD installation/controller configuration changes; none granted here.
- Exact native bound claims verified without printing JWT/Secret; read grant policy
and role native readback match source.
- Protected child source pinned, silent attended OIDC context verified, target cluster/backend
pinned, fence verified and durable receipt custody installed.
- Consumer independently admits owner-authenticated public receipt and exact policy/image/trust
pins before protected acceptance.
evidence: []
lifecycle:
deactivate: Fence all retained signer processes, stop consumers, disable delivery
role/ESO and online trust; retain private KV and audit receipts under explicit
owner retention until reviewed destruction.
rotate: Pre-admit successor public receipt offline; fence consumers/all signer replicas/no
HPA; CAS exact version with new in-memory seed; wait exact ESO version, restart
single writer, verify native allow/deny and old-key refusal before reopening.
compromised: Immediately remove old online trust and stop all signer processes;
forward-only CAS recovery with independently approved successor. Never restore
compromised seed; archive historical public keys for offline audit only.
state_hub:
workplan_id: RPF-WP-0035
task_id: RPF-WP-0035-T14
decision_id: 18731e84-a6ab-4ac5-bf0e-7e7c3a9c4508
decision_api_url: http://127.0.0.1:8000/decisions/18731e84-a6ab-4ac5-bf0e-7e7c3a9c4508

View file

@ -1,152 +0,0 @@
id: CCR-2026-0051
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: Temporary MAS Scaleway TEM relay SMTP ESO read lane
status: applied
created: '2026-10-18'
updated: '2026-10-18'
requester:
agent: ops-mason
reason: ops-mason plans/mas-tem-relay-smtp.md (MASON-WP-0014, reviewed 2026-10-18T10:13:55Z)
for consumer mail-address-semantics MAS-WP-0003. Hub messages daa119e6, a77e5879
(kv_path correction) and 049afa1f. No approval is implied by this filing.
review:
required: true
required_approvers:
- founder
- railiance-platform-owner
- mail-address-semantics-owner
comments:
- at: '2026-10-18T10:36:53+00:00'
reviewer: railiance-platform-owner (policy-as-code ccr-standard-lane v1)
decision: approved
comment: 'Standard lane: passes ccr-standard-lane v1 sha256:5481be201456739b9e01b14b0b11211923d7d838a2e71e2e417af038a05aec95.
Platform-owner source review only; founder approval and confirm-binding still
required.'
- at: '2026-10-18T10:42:00+00:00'
reviewer: founder (Bernd Worsch)
decision: approved
comment: 'Approved 2026-10-18 for ops-mason plans/mas-tem-relay-smtp.md (MASON-WP-0014):
build policy workload-kv-read-mas-tem-relay and kubernetes role external-secrets-mas-tem-relay
under ops-mason-construct. Temporary; teardown with namespace mas.'
- at: '2026-10-18T10:46:25+00:00'
reviewer: founder
decision: approved
comment: 'State Hub decision e633607e-ff3e-46ec-bb6f-143e8b44f1af: APPROVE: CCR-2026-0051
path=platform/suppliers/scaleway/mas-tem-relay policy=workload-kv-read-mas-tem-relay
auth_role=auth/kubernetes/role/external-secrets-mas-tem-relay; rationale=temporary
MAS TEM relay lane, read-only, namespace mas, torn down with mas'
- at: '2026-10-18T10:58:26+00:00'
reviewer: railiance-platform-owner (claude, native evidence)
decision: binding_confirmed
comment: 'Native 2026-10-18: SA mas-tem-relay-eso exists in ns mas (consumer mail-address-semantics
deploy/railiance/manifests.yaml 2e9a074, automount false). SecretStore mas/openbao-mas-tem-relay
references mount kubernetes, role external-secrets-mas-tem-relay, SA mas-tem-relay-eso,
audience openbao, and validated Ready at 10:49:56Z, i.e. a successful login
with exactly the CCR bound claims. ESO reads the path; the 10:56:56Z failure
is consumer-side admission, not auth.'
target:
domain: infotech
tenant: platform
workload: mas-tem-relay
environment: production
purpose: Deliver Scaleway TEM SMTP relay username/password to namespace mas only.
Temporary lane; torn down with namespace mas.
openbao:
mount: platform
kv_path: platform/suppliers/scaleway/mas-tem-relay
fields:
- username
- password
metadata_read: false
token_self_lifecycle: true
policy_name: workload-kv-read-mas-tem-relay
policy_file: openbao/policies/workload-kv-read-mas-tem-relay.hcl
auth:
method: kubernetes
mount: kubernetes
role: external-secrets-mas-tem-relay
bound_claims:
service_account_names:
- mas-tem-relay-eso
service_account_namespaces:
- mas
bound_claims_confirmed: true
policies:
- workload-kv-read-mas-tem-relay
ttl: 15m
audience: openbao
token_max_ttl: 15m
token_explicit_max_ttl: 15m
token_no_default_policy: true
access_frontdoor:
type: external-secrets
catalog_id: mas-tem-relay-smtp
selector: Namespace mas, SA mas-tem-relay-eso only
readiness: pending-review
resolvable: false
delivery:
surface: external-secrets
target: Namespace mas; dedicated SA mas-tem-relay-eso; ExternalSecret projecting
username and password only.
bootstrap_command: Not applicable. KV value already written by the founder (v2);
this CCR creates no value and authorizes no value read.
risk:
classification: medium
notes:
- Supplier SMTP credential can send mail as the TEM sending domain; exposure is
limited to namespace mas.
- New read scope on a supplier path; founder approval required. No other lane reads
this path (ops-mason live survey 2026-10-18T10:13:55Z).
- Apply may be run by ops-mason under ops-mason-construct (MASON-IN-0005) once installed
(MASON-WP-0015-T02) and this CCR is approved; that grant covers workload-kv-read-*
policies and external-secrets-* roles only.
verification:
positive:
- Exact SA/namespace/audience login succeeds; ESO Ready/SecretSynced; metadata version
matches admitted KV version 2 without printing values.
negative:
- Wrong SA, namespace or audience login refused; sibling paths under platform/suppliers/scaleway,
list, metadata, write and delete denied.
activation_conditions:
- Founder, platform owner and consumer owner approve the exact CCR.
- Native bound claims confirmed (confirm-binding) and native policy/role readback
matches source.
evidence:
- at: '2026-10-18T10:44:23+00:00'
actor: ops-mason (claude-MASON-WP-0014-T03, ops-mason-construct)
kind: apply
result: success
details:
- '2026-10-18: policy workload-kv-read-mas-tem-relay written from openbao/policies/workload-kv-read-mas-tem-relay.hcl;
read back byte-equal'
- 'kubernetes role external-secrets-mas-tem-relay written: SA mas-tem-relay-eso,
ns mas, audience openbao, token_policies [workload-kv-read-mas-tem-relay], ttl/max/explicit_max
15m, token_no_default_policy true; read back matches CCR'
- KV platform/suppliers/scaleway/mas-tem-relay present (v2, founder-written);
no value read
- 'pending: SA mas-tem-relay-eso not yet applied (bound_claims_confirmed false);
confirm-binding and ESO sync next'
- at: '2026-10-18T10:58:27+00:00'
actor: railiance-platform (claude)
kind: native-binding
result: success
details:
- SecretStoreValid 2026-10-18T10:49:56Z with the exact CCR SA/namespace/audience/role;
ESO provider read succeeded; ops-mason apply readback 102668f.
- 'Open: ExternalSecret mas/mas-relay refused by admission policy reject-secret-last-applied
(client-side apply annotation copied to target); fix sent to mail-address-semantics
1a76a832. Negative checks (wrong SA/namespace/audience, sibling, list, metadata,
write) not yet run.'
lifecycle:
deactivate: Delete ExternalSecret and role external-secrets-mas-tem-relay, then
policy workload-kv-read-mas-tem-relay, when namespace mas is torn down.
rotate: Founder writes a new KV version via UI; ESO refresh picks it up; verify
metadata version only.
compromised: Revoke the SMTP credential at Scaleway, disable the role, rotate the
KV value, then re-enable after review.
state_hub:
decision_id: e633607e-ff3e-46ec-bb6f-143e8b44f1af
decision_api_url: http://127.0.0.1:8000/decisions/e633607e-ff3e-46ec-bb6f-143e8b44f1af
decision_resolved_at: '2026-10-18T10:46:17.864665Z'

View file

@ -1,141 +0,0 @@
id: CCR-2026-0052
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: NetKingdom identity-provisioner directory bind (svc-provisioner) delivery
status: verified
created: '2026-10-30'
updated: '2026-10-30'
requester:
agent: claude-code
reason: 'NK-WP-0056-T04 pilot: move the first LLDAP service bind out of the founder''s
personal SOPS custody into platform custody, so later rotations need neither a
decrypted custody copy nor a person handling the value. svc-provisioner is the
simplest consumer: its own Secret, two keys, no embedded config.'
review:
required: true
required_approvers:
- platform-owner
- net-kingdom-owner
comments:
- at: '2026-10-30T22:04:42+00:00'
reviewer: User (platform-owner)
decision: approved
comment: 'Approved as proposed: read-only svc-provisioner lane, CAS=0 from live
value, ESO Merge of password only.'
- at: '2026-10-30T22:04:43+00:00'
reviewer: User (net-kingdom-owner)
decision: approved
comment: Approved as proposed for NK-WP-0056-T04 pilot.
- at: '2026-10-30T22:06:09+00:00'
reviewer: User (platform-owner)
decision: binding_confirmed
comment: 'Binding confirmed: ServiceAccount net-kingdom-svc-provisioner-bind-eso
in namespace sso only, audience openbao, as declared in net-kingdom sso-mfa/k8s/identity-provisioner/bind-eso.yaml.'
target:
domain: infotech
tenant: platform
workload: net-kingdom-identity-provisioner
environment: production
purpose: Hold the svc-provisioner LLDAP bind password in platform custody and project
only that value into the existing Secret sso/identity-provisioner-ldap; no ld-admin,
admin or other service bind
openbao:
mount: platform
kv_path: platform/workloads/net-kingdom/directory-binds/svc-provisioner
fields:
- LDAP_BIND_PASSWORD
policy_name: net-kingdom-svc-provisioner-bind-eso
policy_file: openbao/policies/net-kingdom-svc-provisioner-bind-eso.hcl
metadata_read: false
token_self_lifecycle: true
auth:
method: kubernetes
mount: kubernetes
role: net-kingdom-svc-provisioner-bind-eso
audience: openbao
ttl: 15m
token_max_ttl: 15m
token_explicit_max_ttl: 15m
token_no_default_policy: true
policies:
- net-kingdom-svc-provisioner-bind-eso
bound_claims:
service_account_names:
- net-kingdom-svc-provisioner-bind-eso
service_account_namespaces:
- sso
bound_claims_confirmed: true
access_frontdoor:
type: platform-owner
catalog_id: net-kingdom-svc-provisioner-bind
readiness: pending-review
resolvable: false
lifecycle:
deactivate: Delete the ExternalSecret (deletionPolicy Retain keeps the working Secret),
then withdraw the role and policy; the KV entry stays as custody until the founder's
copy is confirmed current.
rotate: Change the svc-provisioner password in LLDAP and write the new value as
the next KV version with CAS on the current version; ESO projects it and the provisioner
restarts. Until NK-WP-0056 executors exist this is founder-run.
compromised: Disable or reset svc-provisioner in LLDAP first (it is in lldap_admin),
then rotate as above and review directory changes made under that account; fence
the reader role if its boundary is in doubt.
delivery:
surface: ESO-managed Secret key (Merge into existing Secret)
namespace: sso
secret: identity-provisioner-ldap
key: password
service_account: net-kingdom-svc-provisioner-bind-eso
reader: identity-provisioner Deployment in sso (existing secretKeyRef, unchanged);
no agent credential delivery
manifest: 'net-kingdom: sso-mfa/k8s/identity-provisioner/bind-eso.yaml'
cluster_uid: a553c742-0115-43d4-99a4-a5ca56fe0786
risk:
classification: medium
notes:
- 'svc-provisioner is a member of lldap_admin (founder decision 2026-10-28, exception
recorded in the Service Identity standard): a leak grants directory administration.
Treat the path as high-value even though the lane itself is a single-field read.'
- Initial KV write is CAS=0 with the value already live in the Secret and the founder's
custody; no rotation happens in this request. An existing different KV value refuses.
- 'ESO uses creationPolicy Merge so the existing Secret (created by apply-service-secrets.py)
keeps its username key and owner; only password is projected. deletionPolicy Retain:
removing the ExternalSecret leaves the working Secret in place.'
- Coding agents are fenced from the data and metadata paths in agent-high-risk-boundary
before the first write.
- Node root, ESO administration and platform-admin remain inside this credential
boundary.
verification:
positive:
- Exact policy, role and bound claims read back; native reader login as the ESO
ServiceAccount gets only this policy with a lease of at most 15m
- KV value equals the live Secret password before ESO is applied (setup) and after
ESO sync (verify), compared in memory only
- ExternalSecret Ready/SecretSynced; identity-provisioner readiness (includes directory
login) stays Ready after a restart
negative:
- Reader cannot read metadata, a sibling path, the other directory-bind paths or
create tokens
- Wrong ServiceAccount, wrong audience and wrong namespace logins are denied
- Coding-agent boundary denies data and metadata of the path
activation_conditions:
- Explicit platform-owner and net-kingdom-owner approval of this exact request (sha256-bound
by the setup tool).
- Attended platform-admin (aal2) session for setup; the founder runs it, value read
from the decrypted custody copy without display.
- Policy, role and KV in place and equal to live before the ExternalSecret is applied.
- Positive and negative results recorded in value-free receipts.
evidence:
- at: '2026-10-30T22:23:46+00:00'
actor: founder (attended), claude-code guiding
kind: verifier_custody_and_rollout
result: passed
details:
- '2026-10-30: bind-lane.py setup (custody equal to live, coding agents fenced,
KV v1 CAS=0, policy+role) and verify (exact policy/TTL, wrong SA and audience
denied, metadata/sibling/token-create denied, KV equals delivered Secret, reader
revoked) passed; identity-provisioner rolled out Ready. First ESO apply refused
by reject-secret-last-applied, fixed with target.template.metadata {} (net-kingdom
985cfc3). Evidence: net-kingdom docs/evidence/2026-10-30-nk56-svc-provisioner-bind/
(87e57aa).'

View file

@ -1,141 +0,0 @@
id: CCR-2026-0053
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: NetKingdom Authelia directory bind (svc-authelia) delivery
status: verified
created: '2026-10-31'
updated: '2026-10-31'
requester:
agent: claude-code
reason: 'NK-WP-0056-T04, second lane after the svc-provisioner pilot (CCR-2026-0052):
move the svc-authelia LLDAP bind out of the founder''s personal SOPS custody into
platform custody, so its rotation needs neither a decrypted custody copy nor a
person handling the value.'
review:
required: true
required_approvers:
- platform-owner
- net-kingdom-owner
comments:
- at: '2026-10-31T09:03:46+00:00'
reviewer: platform-owner
decision: approved
comment: 'Approved as proposed: read-only svc-authelia lane, CAS=0 from live value,
ESO Merge of ldap_password only.'
- at: '2026-10-31T09:03:53+00:00'
reviewer: net-kingdom-owner
decision: approved
comment: Approved as proposed for NK-WP-0056-T04.
- at: '2026-10-31T09:04:03+00:00'
reviewer: platform-owner
decision: binding_confirmed
comment: 'Binding confirmed: ServiceAccount net-kingdom-svc-authelia-bind-eso
in namespace sso only, audience openbao, as declared in net-kingdom sso-mfa/k8s/authelia/bind-eso.yaml.'
target:
domain: infotech
tenant: platform
workload: net-kingdom-authelia
environment: production
purpose: Hold the svc-authelia LLDAP bind password in platform custody and project
only that value into the key ldap_password of the existing Secret sso/authelia-secrets;
no session, storage or OIDC key, no ld-admin, admin or other service bind
openbao:
mount: platform
kv_path: platform/workloads/net-kingdom/directory-binds/svc-authelia
fields:
- LDAP_BIND_PASSWORD
policy_name: net-kingdom-svc-authelia-bind-eso
policy_file: openbao/policies/net-kingdom-svc-authelia-bind-eso.hcl
metadata_read: false
token_self_lifecycle: true
auth:
method: kubernetes
mount: kubernetes
role: net-kingdom-svc-authelia-bind-eso
audience: openbao
ttl: 15m
token_max_ttl: 15m
token_explicit_max_ttl: 15m
token_no_default_policy: true
policies:
- net-kingdom-svc-authelia-bind-eso
bound_claims:
service_account_names:
- net-kingdom-svc-authelia-bind-eso
service_account_namespaces:
- sso
bound_claims_confirmed: true
access_frontdoor:
type: platform-owner
catalog_id: net-kingdom-svc-authelia-bind
readiness: pending-review
resolvable: false
lifecycle:
deactivate: Delete the ExternalSecret (deletionPolicy Retain keeps the working Secret),
then withdraw the role and policy; the KV entry stays as custody until the founder's
copy is confirmed current.
rotate: Change the svc-authelia password in LLDAP and write the new value as the
next KV version with CAS on the current version; ESO projects it and Authelia
is restarted, because it reads ldap_password only at start-up. Until NK-WP-0056
executors exist this is founder-run.
compromised: Reset svc-authelia in LLDAP first (read-only bind, lldap_strict_readonly),
then rotate as above; fence the reader role if its boundary is in doubt.
delivery:
surface: ESO-managed Secret key (Merge into existing Secret)
namespace: sso
secret: authelia-secrets
key: ldap_password
service_account: net-kingdom-svc-authelia-bind-eso
reader: Authelia Deployment in sso (existing secret volume, file /run/secrets/authelia/ldap_password,
unchanged); no agent credential delivery
manifest: 'net-kingdom: sso-mfa/k8s/authelia/bind-eso.yaml'
cluster_uid: a553c742-0115-43d4-99a4-a5ca56fe0786
risk:
classification: medium
notes:
- svc-authelia is a read-only directory bind (lldap_strict_readonly); a leak exposes
directory attributes (users, groups, emails), not write access. Authelia's users
filter excludes svc-* accounts from login.
- authelia-secrets also holds the session, storage-encryption and OIDC keys. ESO
Merge projects only ldap_password; the reader policy cannot reach any other path.
- Initial KV write is CAS=0 with the value already live in the Secret and the founder's
custody; no rotation happens in this request. An existing different KV value refuses.
- Setup also refuses unless the live authelia-config binds uid=svc-authelia, so
the projected value cannot belong to another account.
- Coding agents are fenced from the data and metadata paths in agent-high-risk-boundary
before the first write.
- Node root, ESO administration and platform-admin remain inside this credential
boundary.
verification:
positive:
- Exact policy, role and bound claims read back; native reader login as the ESO
ServiceAccount gets only this policy with a lease of at most 15m
- KV value equals the live Secret ldap_password before ESO is applied (setup) and
after ESO sync (verify), compared in memory only
- ExternalSecret Ready/SecretSynced; Authelia stays Ready and a first-factor login
works after a restart
negative:
- Reader cannot read metadata, a sibling path, the other directory-bind paths or
create tokens
- Wrong ServiceAccount, wrong audience and wrong namespace logins are denied
- Coding-agent boundary denies data and metadata of the path
activation_conditions:
- Explicit platform-owner and net-kingdom-owner approval of this exact request (sha256-bound
by the setup tool).
- Attended platform-admin (aal2) session for setup; the founder runs it, value read
from the decrypted custody copy without display.
- Policy, role and KV in place and equal to live before the ExternalSecret is applied.
- Positive and negative results recorded in value-free receipts.
evidence:
- at: '2026-10-31T09:11:39+00:00'
actor: founder (attended), claude-code guiding
kind: verifier_custody_and_rollout
result: passed
details:
- '2026-10-31: bind-lane.py setup (live authelia-config binds svc-authelia, custody
equal to live, coding agents fenced, KV v1 CAS=0, policy+role) and verify (exact
policy/TTL, wrong SA and audience denied, metadata/sibling/token-create denied,
KV equals delivered ldap_password, reader revoked) passed; ExternalSecret Ready
on first apply; Authelia restarted and admin signed in with OTP. Evidence: net-kingdom
docs/evidence/2026-10-31-nk56-svc-authelia-bind/ (7f37e25).'

View file

@ -1,148 +0,0 @@
id: CCR-2026-0054
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: NetKingdom KeyCape directory bind (svc-keycape) delivery
status: verified
created: '2026-10-31'
updated: '2026-10-31'
requester:
agent: claude-code
reason: 'NK-WP-0056-T04, third lane (after CCR-2026-0052 svc-provisioner and CCR-2026-0053
svc-authelia): move the svc-keycape LLDAP bind out of the founder''s personal
SOPS custody into platform custody. KeyCape reads it from KEYCAPE_LLDAP_BIND_PW
(key-cape KEY-WP-0046), so ESO owns a dedicated Secret instead of one field inside
keycape-config.'
review:
required: true
required_approvers:
- platform-owner
- net-kingdom-owner
comments:
- at: '2026-10-31T09:36:13+00:00'
reviewer: platform-owner
decision: approved
comment: 'Approved as proposed: read-only svc-keycape lane, CAS=0 from live value,
ESO-owned dedicated Secret keycape-lldap-bind.'
- at: '2026-10-31T09:36:25+00:00'
reviewer: net-kingdom-owner
decision: approved
comment: Approved as proposed for NK-WP-0056-T04, including the KeyCape image
main-e2b5fa1.
- at: '2026-10-31T09:36:35+00:00'
reviewer: platform-owner
decision: binding_confirmed
comment: 'Binding confirmed: ServiceAccount net-kingdom-svc-keycape-bind-eso in
namespace sso only, audience openbao, as declared in net-kingdom sso-mfa/k8s/keycape/bind-eso.yaml.'
target:
domain: infotech
tenant: platform
workload: net-kingdom-keycape
environment: production
purpose: Hold the svc-keycape LLDAP bind password in platform custody and deliver
it as the dedicated Secret sso/keycape-lldap-bind (key password), read by KeyCape
as KEYCAPE_LLDAP_BIND_PW; no signing key, client or other keycape-config value,
no ld-admin, admin or other service bind
openbao:
mount: platform
kv_path: platform/workloads/net-kingdom/directory-binds/svc-keycape
fields:
- LDAP_BIND_PASSWORD
policy_name: net-kingdom-svc-keycape-bind-eso
policy_file: openbao/policies/net-kingdom-svc-keycape-bind-eso.hcl
metadata_read: false
token_self_lifecycle: true
auth:
method: kubernetes
mount: kubernetes
role: net-kingdom-svc-keycape-bind-eso
audience: openbao
ttl: 15m
token_max_ttl: 15m
token_explicit_max_ttl: 15m
token_no_default_policy: true
policies:
- net-kingdom-svc-keycape-bind-eso
bound_claims:
service_account_names:
- net-kingdom-svc-keycape-bind-eso
service_account_namespaces:
- sso
bound_claims_confirmed: true
access_frontdoor:
type: platform-owner
catalog_id: net-kingdom-svc-keycape-bind
readiness: pending-review
resolvable: false
lifecycle:
deactivate: Restore bindPW in keycape-config (directory-bind-rollout.py --operation
switch), remove the KEYCAPE_LLDAP_BIND_PW env from the Deployment, then delete
the ExternalSecret (deletionPolicy Retain keeps the Secret) and withdraw the role
and policy.
rotate: Change the svc-keycape password in LLDAP and write the new value as the
next KV version with CAS on the current version; ESO projects it and KeyCape is
restarted. Only after bindPW is cleared from keycape-config, because KeyCape refuses
differing values. Until NK-WP-0056 executors exist this is founder-run.
compromised: Reset svc-keycape in LLDAP first (read-only bind, lldap_strict_readonly),
then rotate as above; fence the reader role if its boundary is in doubt.
delivery:
surface: ESO-owned Secret (creationPolicy Owner, new dedicated Secret)
namespace: sso
secret: keycape-lldap-bind
key: password
service_account: net-kingdom-svc-keycape-bind-eso
reader: KeyCape Deployment in sso, env KEYCAPE_LLDAP_BIND_PW from secretKeyRef keycape-lldap-bind/password
(image with key-cape KEY-WP-0046); no agent credential delivery
manifest: 'net-kingdom: sso-mfa/k8s/keycape/bind-eso.yaml'
cluster_uid: a553c742-0115-43d4-99a4-a5ca56fe0786
risk:
classification: medium
notes:
- svc-keycape is a read-only directory bind (lldap_strict_readonly); a leak exposes
directory attributes (users, groups, emails), not write access.
- keycape-config also holds the signing key and client configuration; this request
delivers none of it, and setup compares only lldap.bindDN/bindPW in memory.
- Initial KV write is CAS=0 with the value already live in keycape-config and the
founder's custody; no rotation happens in this request. An existing different
KV value refuses.
- Setup refuses unless the live config binds uid=svc-keycape. KeyCape refuses to
start if KEYCAPE_LLDAP_BIND_PW and bindPW differ, so a wrong delivery cannot be
used silently.
- Coding agents are fenced from the data and metadata paths in agent-high-risk-boundary
before the first write.
- Node root, ESO administration and platform-admin remain inside this credential
boundary.
verification:
positive:
- Exact policy, role and bound claims read back; native reader login as the ESO
ServiceAccount gets only this policy with a lease of at most 15m
- KV value equals the live keycape-config bindPW before ESO is applied (setup),
and the delivered Secret (and bindPW while present) after ESO sync (verify), compared
in memory only
- ExternalSecret Ready/SecretSynced; KeyCape with the env rolls out Ready and a
portal login works, again after bindPW is cleared from keycape-config
negative:
- Reader cannot read metadata, a sibling path, the other directory-bind paths or
create tokens
- Wrong ServiceAccount, wrong audience and wrong namespace logins are denied
- Coding-agent boundary denies data and metadata of the path
activation_conditions:
- Explicit platform-owner and net-kingdom-owner approval of this exact request (sha256-bound
by the setup tool).
- Attended platform-admin (aal2) session for setup; the founder runs it, value read
from the decrypted custody copy without display.
- Policy, role and KV in place and equal to live before the ExternalSecret is applied;
the KeyCape image with KEY-WP-0046 deployed before the env is added.
- Positive and negative results recorded in value-free receipts.
evidence:
- at: '2026-10-31T09:52:22+00:00'
actor: founder (attended), claude-code guiding
kind: verifier_custody_and_rollout
result: passed
details:
- '2026-10-31: bind-lane.py setup (live keycape-config binds svc-keycape, custody
equal to bindPW, coding agents fenced, KV v1 CAS=0, policy+role) and verify
passed with env and config, then again env-only after directory-bind-rollout.py
clear-password; KeyCape main-e2b5fa1 (KEY-WP-0046) rolled out with KEYCAPE_LLDAP_BIND_PW;
admin signed in with OTP before and after. Evidence: net-kingdom docs/evidence/2026-10-31-nk56-svc-keycape-bind/
(a656739).'

View file

@ -1,109 +0,0 @@
id: CCR-2026-0055
kind: credential-change-request
schema_version: 1
request_type: attended-service-credential-delivery
title: NetKingdom privacyIDEA resolver directory bind (svc-privacyidea) custody
status: verified
created: '2026-10-31'
updated: '2026-10-31'
requester:
agent: claude-code
reason: 'NK-WP-0056-T04, fourth lane (after CCR-2026-0052/0053/0054): move the svc-privacyidea
LLDAP bind out of the founder''s personal SOPS custody into platform custody.
privacyIDEA stores the resolver password encrypted in its own database and accepts
it only through its pi-admin API; an unattended writer would need pi-admin''s
password standing in the cluster, so no workload reads it: delivery is an attended
write-through.'
review:
required: true
required_approvers:
- platform-owner
- net-kingdom-owner
comments:
- at: '2026-10-31T10:10:37+00:00'
reviewer: platform-owner
decision: approved
comment: 'Approved as proposed: svc-privacyidea held in OpenBao, no workload reader,
attended write-through.'
- at: '2026-10-31T10:10:44+00:00'
reviewer: net-kingdom-owner
decision: approved
comment: Approved as proposed for NK-WP-0056-T04.
target:
domain: infotech
tenant: platform
workload: net-kingdom-privacyidea
environment: production
purpose: Hold the svc-privacyidea LLDAP bind password in platform custody so that
its rotation needs no decrypted custody copy; privacyIDEA receives it only through
the attended resolver write. No other privacyIDEA, LLDAP or service credential.
openbao:
mount: platform
kv_path: platform/workloads/net-kingdom/directory-binds/svc-privacyidea
fields:
- LDAP_BIND_PASSWORD
auth:
method: attended-oidc
mount: netkingdom
role: platform-admin
runtime_access: false
initial_cas: 0
access_frontdoor:
type: attended-service-config
catalog_id: openbao-platform-admin-login
readiness: pending-review
resolvable: false
lifecycle:
deactivate: Delete the KV entry only after the founder custody copy is confirmed
current; privacyIDEA keeps its stored resolver password.
rotate: Change the svc-privacyidea password in LLDAP, write the next KV version
with CAS on the current version, then run realm_bootstrap.py --rotate-bind --from-openbao
(attended pi-admin; --verify-user MFA login required) and verify with an OTP login.
Founder-run.
compromised: Reset svc-privacyidea in LLDAP first (read-only bind, lldap_strict_readonly),
then rotate as above.
delivery:
surface: attended-service-config
service: privacyIDEA (sso), LDAP resolver netkingdom-lldap
target: resolver BINDPW, stored encrypted in the privacyIDEA database
procedure: net-kingdom sso-mfa/k8s/privacyidea/realm_bootstrap.py --rotate-bind
--from-openbao
gate: attended platform-admin (aal2) OpenBao session, attended pi-admin password,
and a fresh MFA login through the resolver (--verify-user) that proves the write
risk:
classification: medium
notes:
- svc-privacyidea is a read-only directory bind (lldap_strict_readonly); a leak
exposes directory attributes, not write access.
- No workload, Kubernetes or ESO reader exists; only the attended platform-admin
session reads the value, in memory.
- Initial KV write is CAS=0 from custody after the value is proven current by an
LLDAP sign-in as svc-privacyidea; no rotation happens in this request.
- Coding agents are fenced from the data and metadata paths in agent-high-risk-boundary
before the first write.
verification:
positive:
- The KV value signs in to LLDAP as svc-privacyidea (current password), compared
without display
- An OTP login resolves the user through the resolver after any write-through
negative:
- Coding-agent boundary denies data and metadata of the path
- No auth role or policy grants the path to a workload
activation_conditions:
- Explicit platform-owner and net-kingdom-owner approval of this exact request (sha256-bound
by the setup tool).
- Attended platform-admin (aal2) session; the founder runs setup with the decrypted
custody copy and the write-through with pi-admin and an MFA login through the
resolver.
- Positive and negative results recorded in value-free receipts.
evidence:
- at: '2026-10-31T10:19:36+00:00'
actor: founder (attended), claude-code guiding
kind: verifier_custody_and_write_through
result: passed
details:
- '2026-10-31: bind-lane.py setup (custody signs in to LLDAP as svc-privacyidea,
coding agents fenced, KV v1 CAS=0, no workload role) and verify passed; realm_bootstrap.py
--rotate-bind --from-openbao wrote BINDPW from OpenBao and admin passed token-backed
MFA through the resolver. Evidence: net-kingdom docs/evidence/2026-10-31-nk56-svc-privacyidea-bind/
(ad7d1cd).'

View file

@ -1,89 +0,0 @@
id: CCR-2026-0056
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: Email-connect platform SMTP password projection
status: proposed
created: '2026-10-31'
updated: '2026-10-31'
requester:
agent: coulomb-social
reason: CSOC-WP-0015-T02 platform sender migration; user selected platform-root with Bernd as the human reply and bounce operator. This ownership choice is not credential admission.
review:
required: true
required_approvers:
- founder
- railiance-platform-owner
- email-connect-owner
comments: []
target:
domain: communication
tenant: platform
workload: email-connect
environment: production
purpose: Fixed admitted registration and invitation templates from platform@coulomb.social; no new caller or arbitrary notification authority.
openbao:
mount: platform
kv_path: platform/workloads/railiance-telemetry/smtp
fields:
- SMTP_PASSWORD
metadata_read: false
token_self_lifecycle: true
policy_name: email-connect-platform-smtp-eso
policy_file: openbao/policies/email-connect-platform-smtp-eso.hcl
auth:
method: kubernetes
mount: kubernetes
role: email-connect-platform-smtp-eso
bound_claims:
service_account_names:
- email-connect-platform-smtp-eso
service_account_namespaces:
- email-connect
bound_claims_confirmed: false
policies:
- email-connect-platform-smtp-eso
ttl: 15m
audience: openbao
token_max_ttl: 15m
token_explicit_max_ttl: 15m
token_no_default_policy: true
access_frontdoor:
type: external-secrets
catalog_id: email-connect-platform-smtp
selector: Namespace email-connect and ServiceAccount email-connect-platform-smtp-eso only
readiness: pending-review
resolvable: false
delivery:
surface: external-secrets
namespace: email-connect
service_account: email-connect-platform-smtp-eso
secret_store: openbao-email-connect-platform-smtp
external_secret: email-connect-platform-smtp
target_secret: email-connect-platform-smtp
target_key: password
target: Dedicated namespace SecretStore and password-only ExternalSecret; caller tokens unchanged.
bootstrap_command: Existing mailbox is reused; this request does not create or rotate a value.
risk:
classification: high
notes:
- Shared mailbox reader outside the automatic single-reader profile; existing telemetry and Authelia grants must be preserved.
- OpenBao authorizes the whole exact KV record, not individual fields. ESO projects only SMTP_PASSWORD; this is not field-level OpenBao access control.
- SMTP credential can send as the platform mailbox. No IMAP access or mailbox forwarding operation is included.
- No new application sender, mailbox scan, suppression operator or timer is activated by admission alone.
verification:
positive:
- Exact scoped role and policy readback; certificate-verified STARTTLS AUTH without mail submission.
- Native service account binding confirmation precedes apply; actual workload login and ESO Ready are verified separately after projection.
negative:
- Changed policy, role, settings, approval, source digest or unconfirmed binding refuses.
- Agent boundary denies both KV data and metadata; workload token denies metadata, sibling and mutation capabilities.
activation_conditions:
- Exact CCR approval and native bound claims confirmed through the owner workflow.
- Attended owner helper receipt verified with unchanged mailbox version and other consumers; Warden session revocation confirmed.
- Native workload positive and negative login, scoped projection Ready and separate supervised application sender rollout accepted.
evidence: []
lifecycle:
deactivate: Stop the admitted consumer and reconcile pending sends, revoke only this dedicated reader role/policy, and remove only its projection after owner verification. Preserve the mailbox and other consumers.
rotate: Coordinate provider rotation across telemetry, Authelia and email-connect; verify all projections and consumers before retiring the predecessor. Never rotate a single consumer copy independently.
compromised: Pause affected sending and coordinate shared-mailbox credential rotation through the owner; preserve ledger evidence and investigate all admitted consumers. Do not auto-resend unknown outcomes.

View file

@ -1,125 +0,0 @@
id: CCR-2026-0057
kind: credential-change-request
schema_version: 1
request_type: attended-service-credential-delivery
title: NetKingdom synthetic journey identity (syn-journey) password and TOTP seed
custody
status: verified
created: '2026-10-31'
updated: '2026-10-31'
requester:
agent: claude-code
reason: 'NK-WP-0056-T09: a synthetic, unprivileged sign-in identity whose MFA journey
through KeyCape, Authelia and privacyIDEA replaces the founder''s sign-in as the
check between consumers in a bounded bind rotation (NK-WP-0056-T06). Its password
and TOTP seed are generated in memory and held only in OpenBao; LLDAP and privacyIDEA
receive them through one attended procedure. No workload reads them until the
executor identity exists (T03/T10), which needs its own request.'
review:
required: true
required_approvers:
- platform-owner
- net-kingdom-owner
comments:
- at: '2026-10-31T11:50:37+00:00'
reviewer: platform-owner
decision: approved
comment: Approved by the founder in session 2026-10-31 ('i accept the ccr') for
NK-WP-0056-T09.
- at: '2026-10-31T11:50:37+00:00'
reviewer: net-kingdom-owner
decision: approved
comment: Approved by the founder in session 2026-10-31 ('i accept the ccr') for
NK-WP-0056-T09.
target:
domain: infotech
tenant: platform
workload: net-kingdom-synthetic-journey
environment: production
purpose: Hold the password and TOTP seed of the LLDAP user syn-journey (no groups,
no rights) so that a synthetic journey can prove sign-in with MFA without a person.
No other identity, directory, privacyIDEA or service credential.
openbao:
mount: platform
kv_path: platform/workloads/net-kingdom/synthetic-journey/syn-journey
fields:
- PASSWORD
- TOTP_SEED
auth:
method: attended-oidc
mount: netkingdom
role: platform-admin
runtime_access: false
initial_cas: 0
access_frontdoor:
type: attended-service-config
catalog_id: openbao-platform-admin-login
readiness: pending-review
resolvable: false
lifecycle:
deactivate: lane.py teardown deletes the privacyIDEA token SYNJOURNEY01, the LLDAP
user syn-journey (refused if it has gained any group) and the KV entry with all
versions; the KeyCape client nk-synthetic-journey is removed with keycape-client.py
--operation remove. The coding-agent fence stays.
rotate: Tear down and set up again (new password and seed, CAS=0 on the fresh path).
No in-place rotation path in this request.
compromised: Delete the privacyIDEA token first (the account then cannot pass MFA),
then tear down. The identity has no groups, so a leak grants a sign-in that reaches
no protected service.
delivery:
surface: attended-service-config
service: LLDAP (sso) user syn-journey; privacyIDEA (mfa) TOTP token SYNJOURNEY01
in realm coulomb
target: the LLDAP user's password (set with the pinned lldap_set_password, OPAQUE)
and the privacyIDEA token's OTP key (stored encrypted in the privacyIDEA database)
procedure: net-kingdom sso-mfa/k8s/synthetic-journey/lane.py setup
gate: attended platform-admin (aal2) OpenBao session, the human admin's LLDAP password,
the attended pi-admin password, and a passing synthetic journey (lane.py verify)
against the live issuer
risk:
classification: medium
notes:
- The identity is new and holds no group. Authelia admits it (it is not svc-* or
ld-admin), KeyCape issues it tokens, but no consuming service grants it anything.
KeyCape has no per-client group gate, so every client that admits any authenticated
user would admit it too; none of the registered clients grants rights without
a group.
- KeyCape marks its tokens principal_type human (key-cape token.go); the account
is declared SERVICE in the service-identity declarations. Recorded, not changed
here.
- Values are generated in memory by lane.py and written CAS=0; neither is printed,
logged or written to disk. The journey's receipts are value-free.
- Coding agents are fenced from the data and metadata paths in agent-high-risk-boundary
before the first write.
- No workload, Kubernetes or ESO reader exists; T10 needs a separate workload-kv-read
request for the executor identity.
verification:
positive:
- The KV password signs in to LLDAP as syn-journey (compared without display)
- The synthetic journey passes against https://kc.coulomb.social at aal2 with the
KV values (lane.py verify), with a value-free receipt
negative:
- Coding-agent boundary denies data and metadata of the path
- No auth role or policy grants the path to a workload
- setup refuses if the KV path, the LLDAP user or the privacyIDEA token already
exist
activation_conditions:
- Explicit platform-owner and net-kingdom-owner approval of this exact request (sha256-bound
by lane.py).
- The KeyCape client nk-synthetic-journey registered through security-cases/nk-synthetic-journey.yaml
before lane.py verify.
- Attended platform-admin (aal2) session; the founder runs setup and verify.
- Positive and negative results recorded in value-free receipts.
evidence:
- at: '2026-10-31T12:33:37+00:00'
actor: founder (attended), claude-code guiding
kind: verifier_identity_custody_and_journey
result: passed
details:
- '2026-10-31: KeyCape client nk-synthetic-journey registered (SecurityCase accepted,
net-kingdom d654f8a); lane.py setup (coding agents fenced, syn-journey with
no groups, TOTP token SYNJOURNEY01 with a privacyIDEA-generated key confirmed
by code, KV v1 CAS=0, no workload role) and verify (KV current by LLDAP sign-in,
live synthetic journey passed at aal2 with otp) passed. One partial setup removed
by lane.py teardown first. Evidence: net-kingdom docs/evidence/2026-10-31-nk56-t09-synthetic-identity/
(661ab6a).'

View file

@ -1,188 +0,0 @@
id: CCR-2026-0058
kind: credential-change-request
schema_version: 1
request_type: workload-kv-write-next-version
title: NetKingdom bounded executor writes the next svc-authelia bind version (pilot)
status: approved
created: '2026-10-31'
updated: '2026-10-31'
requester:
agent: claude-code
reason: 'NK-WP-0056-T02 owner request (hub 9d08288d), for NK-WP-0056-T06: the bounded
executor service:nk-bind-executor rotates the svc-authelia LLDAP bind unattended
under one founder admission (Bounded Execution Admission v0.1, proposed). The
executor must write the next KV version of the bind with CAS and read it back
for its checks and revert. Drafted by the railiance-platform worker; not approved.'
review:
required: true
required_approvers:
- platform-owner
- net-kingdom-owner
comments:
- at: '2026-10-31T14:26:48+00:00'
reviewer: platform-owner
decision: approved
comment: Approved by the founder in session 2026-10-31 as amended (window-scoped
role, bind self-rotation) for NK-WP-0056-T06.
- at: '2026-10-31T14:27:33+00:00'
reviewer: net-kingdom-owner
decision: approved
comment: Approved by the founder in session 2026-10-31 as amended (window-scoped
role, bind self-rotation) for NK-WP-0056-T06. Recorded by hand in the approve
tool's format; the tool refuses the second approval for this request type once
the first sets status approved.
- at: '2026-10-31T15:46:26+00:00'
reviewer: platform-owner
decision: approved
comment: Re-approved by the founder in session 2026-10-31 with the corrected policy
(required_parameters removed; NK-WP-0056-T12 synthetic pilot evidence).
- at: '2026-10-31T15:46:26+00:00'
reviewer: net-kingdom-owner
decision: approved
comment: Re-approved by the founder in session 2026-10-31 with the corrected policy
(required_parameters removed; NK-WP-0056-T12 synthetic pilot evidence).
- at: '2026-10-31T18:38:22+00:00'
reviewer: platform-owner
decision: binding_confirmed
comment: 'Binding confirmed: ServiceAccount nk-bind-executor in namespace sso
only (uid bd15569c-8a85-4625-b001-d9f799cb857b), audience openbao, from rapp-bounded-executor.'
target:
domain: infotech
tenant: platform
workload: net-kingdom-bind-executor
environment: production
purpose: Let only the executor ServiceAccount sso/nk-bind-executor read and write
the next version of platform/workloads/net-kingdom/directory-binds/svc-authelia,
with CAS on the current version; no create, metadata, delete, destroy, list or
sibling path
admission:
standard: net-kingdom canon/standards/bounded-execution-admission_v0.1.md (proposed)
executor_identity: service:nk-bind-executor
example: net-kingdom examples/bounded-execution-admission/bind-rotation-svc-authelia.yaml
note: The OpenBao role does not evaluate the admission. The admission (approval-engine
consume, window, plan digest) is enforced by the executor and its owners; this
request bounds what a compromised or unadmitted executor can reach to one value.
openbao:
mount: platform
kv_path: platform/workloads/net-kingdom/directory-binds/svc-authelia
fields:
- LDAP_BIND_PASSWORD
policy_name: net-kingdom-bind-executor-svc-authelia-write
policy_file: openbao/policies/net-kingdom-bind-executor-svc-authelia-write.hcl
write:
mode: next-version
cas_required: true
create: false
metadata_read: false
delete: false
token_self_lifecycle: true
auth:
method: kubernetes
mount: kubernetes
role: net-kingdom-bind-executor-svc-authelia-write
audience: openbao
ttl: 15m
token_max_ttl: 15m
token_explicit_max_ttl: 15m
token_no_default_policy: true
policies:
- net-kingdom-bind-executor-svc-authelia-write
bound_claims:
service_account_names:
- nk-bind-executor
service_account_namespaces:
- sso
bound_claims_confirmed: true
access_frontdoor:
type: platform-owner
catalog_id: net-kingdom-bind-executor-svc-authelia-write
readiness: pending-review
resolvable: false
agent_fence:
data_and_metadata_denied: true
note: The svc-authelia data and metadata paths are already denied to coding agents
since CCR-2026-0053; setup re-checks the deny before the role is created.
amendments:
- at: '2026-10-31'
by: claude-code
change: 'Policy file corrected: removed required_parameters = ["options"]. OpenBao
2.5.4 applies it to reads as well, so the executor could not read the version
it must name for CAS (net-kingdom NK-WP-0056-T12 synthetic pilot, docs/evidence/2026-10-31-nk56-t12-synthetic-pilot/).
Writes without options.cas are still refused by cas_required on the key; metadata,
delete, sibling and new paths stay denied (tested). Status reset to proposed:
the approved policy file changed, re-approval needed.'
lifecycle:
role_window: 'Window-scoped (founder decision 2026-10-31, hub c29c6eda): the policy
is applied once and grants nothing on its own; the Kubernetes-auth role is created
by the attended platform-admin when an admission naming this lane is approved,
bounded to that admission''s window, and deleted after the run''s receipt or at
window end, whichever comes first. No write role exists between admitted runs.'
deactivate: Delete the role, then the policy; the KV entry and the ESO reader lane
(CCR-2026-0053) are unchanged. The bind falls back to founder-run rotation.
rotate: Executor reads the current version N, signs in to LLDAP as svc-authelia
with it and changes that account's own password (self-rotation, founder decision
2026-10-31, hub 3d0c8f86; no lldap_password_manager account), writes version N+1
with options.cas=N; ESO projects it, Authelia restarts, the synthetic journey
proves sign-in. Revert changes the password back the same way and writes the prior
value as version N+2 with options.cas=N+1.
compromised: Delete the role first (stops new logins; existing tokens expire within
15m, or revoke by accessor), then reset svc-authelia in LLDAP and rotate attended
as in CCR-2026-0053.
delivery:
surface: native-workload-login
namespace: sso
service_account: nk-bind-executor
reader: Executor runtime (net-kingdom source, rapp-* package per ADR-0015, NK-WP-0056-T03);
logs in with its projected ServiceAccount token, audience openbao; no Secret is
created by this request
risk:
classification: medium
notes:
- A write role on the svc-authelia bind is new; until now only the attended platform-admin
wrote this path. It exists only during an admitted window. Within it, a compromised
executor ServiceAccount can replace the bind value (Authelia sign-in outage on
next restart) but cannot read any other path, create paths, delete versions or
read metadata; outside it, the ServiceAccount cannot log in to this role at all.
- cas_required on the key makes every write state the version it replaces; a racing
or replayed write is refused by the engine, not by the executor.
- The executor also reads the current value (needed for CAS and revert). svc-authelia
is a read-only directory bind (lldap_strict_readonly).
- The role is a separate role from the ESO reader (CCR-2026-0053) and from the journey
read lane (NK-WP-0056-T10); policies are never combined in one role.
- Node root, ESO administration, cluster admins able to mint tokens for sso/nk-bind-executor
and platform-admin remain inside this credential boundary.
verification:
positive:
- Exact policy and role read back; login as sso/nk-bind-executor (audience openbao)
gets only this policy with a lease of at most 15m
- Write of the next version with options.cas equal to the current version succeeds
in a rehearsal on a synthetic sibling-free test path or under the T06 admission;
the value is compared in memory only
negative:
- Write without options or with a stale cas is refused
- Create of a new sibling path, metadata read, delete, destroy, undelete and list
are refused
- Wrong ServiceAccount, wrong namespace and wrong audience logins are denied
- Coding-agent boundary denies data and metadata of the path
activation_conditions:
- Explicit platform-owner and net-kingdom-owner approval of this exact request.
- ServiceAccount sso/nk-bind-executor exists from the reviewed rapp-* package (bound_claims_confirmed
then set to true by the platform-owner).
- Attended platform-admin (aal2) session sets cas_required on the key and applies
the policy once from a reviewed commit.
- Per admitted run, the attended platform-admin creates the role for the admission's
window and deletes it after the receipt (or at window end); both steps are recorded
with the admission id.
- Positive and negative results recorded in value-free receipts.
evidence:
- at: '2026-10-31T18:48:55+00:00'
actor: founder (attended), claude-code guiding
kind: executor_lane_policy_setup
result: passed
details:
- '2026-10-31: policy applied and cas_required set on the svc-authelia key (metadata
write naming only cas_required; max_versions, delete_version_after and custom_metadata
unchanged) by net-kingdom tools/service-binds/executor-lanes.py; the window-scoped
role was not created and its login is denied (verified). Role creation remains
per admitted window. Evidence: net-kingdom docs/evidence/2026-10-31-nk56-t10-executor-lanes/
(ef6284c).'

View file

@ -1,169 +0,0 @@
id: CCR-2026-0059
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: NetKingdom bounded executor reads the synthetic journey identity (syn-journey)
status: verified
created: '2026-10-31'
updated: '2026-10-31'
requester:
agent: claude-code
reason: 'NK-WP-0056-T10/T06: the bounded executor service:nk-bind-executor runs
the synthetic MFA journey (KeyCape, Authelia, privacyIDEA) as the check after
each consumer restart, with no person signing in. It needs the syn-journey password
and TOTP seed held under CCR-2026-0057. Lane design: railiance-platform docs/credential-lane-designs/bounded-executor.md
"Journey secrets (T10)". Drafted by net-kingdom; not approved.'
review:
required: true
required_approvers:
- platform-owner
- net-kingdom-owner
comments:
- at: '2026-10-31T18:06:02+00:00'
reviewer: platform-owner
decision: approved
comment: 'Approved by the founder 2026-10-31: standing journey reader for NK-WP-0056-T10,
accepting the CCR-2026-0057 runtime_access widening.'
- at: '2026-10-31T18:06:12+00:00'
reviewer: net-kingdom-owner
decision: approved
comment: Approved by the founder 2026-10-31 for NK-WP-0056-T10/T06.
- at: '2026-10-31T18:38:23+00:00'
reviewer: platform-owner
decision: binding_confirmed
comment: 'Binding confirmed: ServiceAccount nk-bind-executor in namespace sso
only (uid bd15569c-8a85-4625-b001-d9f799cb857b), audience openbao, from rapp-bounded-executor.'
supersedes_boundary:
ccr: CCR-2026-0057
field: openbao.runtime_access
was: false
change: 'This request gives one workload reader to the path CCR-2026-0057 holds
with runtime_access: false. CCR-2026-0057''s attended setup, teardown and rotation
stay valid and unchanged; its rotate (teardown + setup) does not need this role.'
target:
domain: infotech
tenant: platform
workload: net-kingdom-bind-executor
rapp: rapp-bounded-executor
environment: production
purpose: Let only the executor ServiceAccount sso/nk-bind-executor read the syn-journey
PASSWORD and TOTP_SEED to run the synthetic journey; no write, metadata, list,
sibling path, or other identity
admission:
standard: net-kingdom canon/standards/bounded-execution-admission_v0.1.md (proposed)
executor_identity: service:nk-bind-executor
example: net-kingdom examples/bounded-execution-admission/bind-rotation-svc-authelia.yaml
note: The journey is a check step under an admission. The OpenBao role does not
evaluate the admission; it bounds what the ServiceAccount can read to one synthetic,
group-less identity.
openbao:
mount: platform
kv_path: platform/workloads/net-kingdom/synthetic-journey/syn-journey
fields:
- PASSWORD
- TOTP_SEED
policy_name: net-kingdom-bind-executor-syn-journey-read
policy_file: openbao/policies/net-kingdom-bind-executor-syn-journey-read.hcl
metadata_read: false
token_self_lifecycle: true
auth:
method: kubernetes
mount: kubernetes
role: net-kingdom-bind-executor-syn-journey-read
audience: openbao
ttl: 15m
token_max_ttl: 15m
token_explicit_max_ttl: 15m
token_no_default_policy: true
policies:
- net-kingdom-bind-executor-syn-journey-read
bound_claims:
service_account_names:
- nk-bind-executor
service_account_namespaces:
- sso
bound_claims_confirmed: true
access_frontdoor:
type: platform-owner
catalog_id: net-kingdom-bind-executor-syn-journey-read
readiness: pending-review
resolvable: false
agent_fence:
data_and_metadata_denied: true
note: The syn-journey data and metadata paths are denied to coding agents since
CCR-2026-0057; setup re-checks the deny before the role is created.
lifecycle:
role_window: Standing (founder decision 2026-10-31, net-kingdom session). Unlike
the CCR-2026-0058 write role, this role only reads a group-less synthetic identity,
and a standing reader lets the journey run as a scheduled check (T10) without
a platform-admin per run.
deactivate: Delete the role, then the policy. The KV entry, the LLDAP user and the
privacyIDEA token stay under CCR-2026-0057.
rotate: As in CCR-2026-0057 (lane.py teardown and setup); the reader needs no change,
because the path stays the same.
compromised: Delete the privacyIDEA token SYNJOURNEY01 first (the account then cannot
pass MFA), delete this role, then tear down and set up as in CCR-2026-0057. The
identity has no groups, so a leak grants a sign-in that reaches no protected service.
delivery:
surface: native-workload-login
namespace: sso
service_account: nk-bind-executor
reader: Executor runtime (net-kingdom bounded-executor/, deployed by rapp-bounded-executor);
logs in with its projected ServiceAccount token, audience openbao, separately
from the CCR-2026-0058 write role; values stay in memory and never reach a Secret,
log or receipt
manifest: 'rapp-bounded-executor: manifests/rbac.yaml, templates/job.yaml'
risk:
classification: low
notes:
- Reachable value is one synthetic identity with no groups. Authelia and KeyCape
admit it, but no consuming service grants it anything (CCR-2026-0057 risk notes).
- A reader holding both values can complete an MFA sign-in as syn-journey, which
is the purpose. privacyIDEA refuses a reused TOTP time step, so a parallel use
and the journey can race; the journey then fails closed.
- The role is separate from the CCR-2026-0058 write role and from the ESO reader
(CCR-2026-0053); policies are never combined in one role.
- Node root, cluster admins able to mint tokens for sso/nk-bind-executor and platform-admin
remain inside this credential boundary.
verification:
positive:
- Exact policy and role read back; login as sso/nk-bind-executor (audience openbao)
gets only this policy with a lease of at most 15m
- The synthetic journey passes at aal2 with the values read through this role, with
a value-free receipt
negative:
- Write, metadata read, list, delete and sibling paths (including directory-binds)
are refused
- Wrong ServiceAccount, wrong namespace and wrong audience logins are denied
- Coding-agent boundary denies data and metadata of the path
activation_conditions:
- Explicit platform-owner and net-kingdom-owner approval of this exact request,
which includes the change to CCR-2026-0057's runtime_access boundary.
- ServiceAccount sso/nk-bind-executor exists from the reviewed rapp-bounded-executor
package (bound_claims_confirmed then set to true by the platform-owner).
- Attended platform-admin (aal2) session applies the policy and role from a reviewed
commit.
- Positive and negative results recorded in value-free receipts.
evidence:
- at: '2026-10-31T18:48:55+00:00'
actor: founder (attended), claude-code guiding
kind: executor_lane_setup_and_proof
result: passed
details:
- '2026-10-31: policy and standing role applied by net-kingdom tools/service-binds/executor-lanes.py
setup (lanes_prepared); verify (lanes_verified): sso/nk-bind-executor audience
openbao gets exactly this policy, lease <=15m, reads only syn-journey data (fields
checked in memory), metadata/directory-binds/token-create denied, default SA,
wrong audience and write-role login denied, coding-agent fence effective. The
positive journey run through this role is the open part of NK-WP-0056-T10. Evidence:
net-kingdom docs/evidence/2026-10-31-nk56-t10-executor-lanes/ (ef6284c).'
- at: '2026-10-31T19:50:05+00:00'
actor: founder started the Job; no person in the journey
kind: executor_journey_live
result: passed
details:
- '2026-10-31: rapp-bounded-executor make journey, Job sso/bounded-executor-journey-20261031-194719
(image sha256:71da4a51), logged in as sso/nk-bind-executor with this role only
(openbao audience), read syn-journey and passed the synthetic journey at aal2
(pwd+otp, signature verified), effects false. Evidence: net-kingdom docs/evidence/2026-10-31-nk56-t10-journey-only/live-receipt.json
(a020758).'

View file

@ -1,98 +0,0 @@
id: CCR-2026-0060
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: 'nk-bind-executor-approval: keycape-reader-policy and eso-role (oidc-service-client@0.1)'
status: proposed
created: '2026-10-31'
updated: '2026-10-31'
requester:
agent: railiance-platform
reason: 'NK-WP-0056-T14 first case of SecurityCaseType oidc-service-client 0.1 (net-kingdom
5d5f15d, proposed; hub decision f8044f81; memo nk-wp-0056-t14-oidc-service-client-r1).
Drafted by the platform owner in its review (docs/credential-lane-designs/oidc-service-client-review.md,
R6). KeyCape reads the client secret of nk-bind-executor-approval only through ESO, as
CCR-2026-0041 for the browser type. Not approved.'
review:
required: true
required_approvers:
- platform-owner
comments: []
target:
domain: infotech
tenant: platform
workload: keycape
environment: production
purpose: KeyCape reads the nk-bind-executor-approval client secret only through ESO. No metadata,
list, parent or sibling access.
openbao:
mount: platform
kv_path: platform/workloads/keycape/nk-bind-executor-approval
fields:
- CLIENT_SECRET
policy_name: workload-kv-read-keycape-nk-bind-executor-approval-eso
policy_file: openbao/policies/workload-kv-read-keycape-nk-bind-executor-approval-eso.hcl
metadata_read: false
token_self_lifecycle: true
auth:
method: kubernetes
mount: kubernetes
role: external-secrets-keycape-nk-bind-executor-approval
audience: openbao
ttl: 15m
token_max_ttl: 15m
token_explicit_max_ttl: 15m
token_no_default_policy: true
policies:
- workload-kv-read-keycape-nk-bind-executor-approval-eso
bound_claims:
service_account_names:
- external-secrets
service_account_namespaces:
- external-secrets
bound_claims_confirmed: false
access_frontdoor:
type: external-secrets
catalog_id: bounded-executor-keycape-nk-bind-executor-approval
readiness: pending-review
resolvable: false
risk:
classification: high
notes:
- Rendered from a proposed SecurityCaseType; the type is not yet acknowledged and the case
is not yet accepted.
- Exact read on one KV path; no metadata, list, parent or sibling access.
- The value lets its holder obtain approval-engine tokens as service:nk-bind-executor
(approval:create/read/consume, tenant:platform).
- Only ExternalSecrets in namespace sso may use the store; sso namespace writers are
within this boundary (as CCR-2026-0033/0041).
verification:
positive:
- policy workload-kv-read-keycape-nk-bind-executor-approval-eso grants read on
platform/data/workloads/keycape/nk-bind-executor-approval only (capabilities check)
- ExternalSecret sso/keycape-nk-bind-executor-approval-client reports Ready=True (status
only)
negative:
- a default-only identity has no read capability on the path
- sibling GET, parent LIST and metadata read are denied
- wrong ServiceAccount and wrong audience logins are denied
activation_conditions:
- oidc-service-client 0.1 acknowledged by the founder (informed-decision memo, hub decision
f8044f81).
- SecurityCase nk-bind-executor-approval accepted by owner and founder.
- Custody entry minted at version 1 by keycape-client-secret-generate, after the broker
admits the type (review R1).
- platform-owner approval of this exact request and bound_claims_confirmed set to true.
lifecycle:
deactivate: delete ExternalSecret sso/keycape-nk-bind-executor-approval-client, then role
external-secrets-keycape-nk-bind-executor-approval, then the policy (case rollback)
rotate: no rotation lane exists yet (RPF-WP-0055 residual); until then a rotation is an
attended platform-admin write with CAS=current, an ESO refresh and a KeyCape restart
compromised: owner incident process; delete the role first; no automatic read, reseed or
cleanup
security_case:
case: nk-bind-executor-approval
type: oidc-service-client@0.1
type_status: proposed
type_source: net-kingdom security-case-types/oidc-service-client/type.yaml (5d5f15d)
acceptance_record: pending

View file

@ -1,127 +0,0 @@
id: CCR-2026-0061
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: 'nk-bind-executor-approval: consumer reader for sso/nk-bind-executor (oidc-service-client@0.1)'
status: proposed
created: '2026-10-31'
updated: '2026-10-31'
requester:
agent: railiance-platform
reason: 'NK-WP-0056-T14 first case of SecurityCaseType oidc-service-client 0.1 (net-kingdom
5d5f15d, proposed; hub decision f8044f81). The bounded executor service:nk-bind-executor
reads its approval-engine client secret natively per call to file, read and consume its
own approvals. Drafted by the platform owner in its review
(docs/credential-lane-designs/oidc-service-client-review.md, R2-R6). Not approved.'
review:
required: true
required_approvers:
- platform-owner
- net-kingdom-owner
comments: []
target:
domain: infotech
tenant: platform
workload: net-kingdom-bind-executor
rapp: rapp-bounded-executor
environment: production
purpose: Let only the executor ServiceAccount sso/nk-bind-executor read the
nk-bind-executor-approval CLIENT_SECRET in memory for its client_credentials exchange;
no write, metadata, list, sibling path, Secret copy or other identity
admission:
standard: net-kingdom canon/standards/bounded-execution-admission_v0.1.md (proposed)
executor_identity: service:nk-bind-executor
note: The role does not evaluate the admission. It bounds what the ServiceAccount can read
to one client secret whose tokens carry approval:create/read/consume at approval-engine.
openbao:
mount: platform
kv_path: platform/workloads/keycape/nk-bind-executor-approval
fields:
- CLIENT_SECRET
policy_name: workload-kv-read-keycape-nk-bind-executor-approval-bounded-executor
policy_file: openbao/policies/workload-kv-read-keycape-nk-bind-executor-approval-bounded-executor.hcl
metadata_read: false
token_self_lifecycle: true
auth:
method: kubernetes
mount: kubernetes
# Review R4: role name ends with -workload-kv-read so the delegated applier admits it;
# the type r1 template (role name = policy name) would be refused.
role: keycape-nk-bind-executor-approval-bounded-executor-workload-kv-read
audience: openbao
ttl: 15m
token_max_ttl: 15m
token_explicit_max_ttl: 15m
token_no_default_policy: true
policies:
- workload-kv-read-keycape-nk-bind-executor-approval-bounded-executor
bound_claims:
service_account_names:
- nk-bind-executor
service_account_namespaces:
- sso
bound_claims_confirmed: false
access_frontdoor:
type: platform-owner
catalog_id: bounded-executor-keycape-nk-bind-executor-approval
readiness: pending-review
resolvable: false
agent_fence:
data_and_metadata_denied: true
note: Coding agents are denied data and metadata of platform/workloads/keycape/nk-bind-executor-approval;
setup checks the deny before the role is created.
lifecycle:
role_window: Standing, as CCR-2026-0059. The executor exchanges a fresh token per call and
keeps neither the secret nor a token beyond the call.
deactivate: Delete the role, then the policy. The custody entry and the ESO reader stay
under CCR-2026-0060 until the case rollback.
rotate: No rotation lane exists yet (RPF-WP-0055 residual). The reader needs no change on
rotation because the path stays the same.
compromised: Remove client nk-bind-executor-approval from KeyCape (register-client rollback,
attended), delete this role, then rotate attended. Approvals already created by the
client still need a human entry under human_control.
delivery:
surface: native-workload-login
namespace: sso
service_account: nk-bind-executor
reader: Executor runtime (net-kingdom bounded-executor/, deployed by rapp-bounded-executor);
logs in with its projected ServiceAccount token, audience openbao, separately from the
CCR-2026-0058 and CCR-2026-0059 roles; the value stays in memory and never reaches a
Secret, log or receipt
risk:
classification: high
notes:
- Reachable value is one confidential client secret; its tokens act as service:nk-bind-executor
at approval-engine (create approvals naming itself, read and consume approvals of
tenant:platform). Approvals still need a human entry under human_control.
- The role is separate from the CCR-2026-0058 write role, the CCR-2026-0059 journey reader
and the CCR-2026-0060 ESO reader; policies are never combined in one role.
- No Secret copy in namespace sso beyond KeyCape's own ESO target; other workloads in sso
without the ServiceAccount cannot log in.
- Node root, cluster admins able to mint tokens for sso/nk-bind-executor and platform-admin
remain inside this credential boundary.
verification:
positive:
- Exact policy and role read back; login as sso/nk-bind-executor (audience openbao) gets
only this policy with a lease of at most 15m
- The executor reports the claims of one client_credentials exchange per scope (sub
service:nk-bind-executor, aud approval-engine, tenant:platform, lifetime <= 15m), value-free
negative:
- Write, metadata read, list, delete and sibling paths (including keycape/secrets-engine-openbao
and directory-binds) are refused
- Wrong ServiceAccount, wrong namespace and wrong audience logins are denied
- Coding-agent boundary denies data and metadata of the path
activation_conditions:
- oidc-service-client acknowledged by the founder and case nk-bind-executor-approval accepted.
- Custody entry minted (CCR-2026-0040 lane, after broker admission R1).
- Explicit platform-owner and net-kingdom-owner approval of this exact request.
- bound_claims_confirmed set to true by the platform-owner (ServiceAccount sso/nk-bind-executor
already exists, uid bd15569c, CCR-2026-0058/0059).
- Attended platform-admin (aal2) session or the delegated applier applies policy and role
from a reviewed commit; positive and negative results recorded in value-free receipts.
security_case:
case: nk-bind-executor-approval
type: oidc-service-client@0.1
type_status: proposed
type_source: net-kingdom security-case-types/oidc-service-client/type.yaml (5d5f15d)
acceptance_record: pending

View file

@ -104,82 +104,6 @@ grants:
audit: {openbao_audit_required: true, state_hub_metadata_allowed: true, record_secret_values: false}
revocation: {required: true, by_accessor: true, on_exec_exit: true, on_denied_request: false}
- id: keycape/client-secret-generate
title: KeyCape confidential-client secret generation in custody
status: pilot
grant_class: approval-required
credential_type: openbao-token
issuer: openbao
audience: railiance-platform-credential-broker
description: >
RPF-WP-0054 / CCR-2026-0040. Short-lived token the broker uses inside its
own process to create platform/workloads/keycape/<client_id> once (CAS 0)
for an accepted oidc-browser-relying-party case. Create-only: it cannot
update, read values, write metadata, list or delete. The generated value
never leaves the broker process and the bao child's stdin; the caller gets a
receipt only. Founder decision 2026-10-09 (RPF-WP-0054-T02 option A).
openbao:
namespace: openbao
token_role: keycape-client-secret-generate
issuer_policy: credential-broker-keycape-client-secret-generate-issuer
policies:
- keycape-client-secret-generate
disallowed_policies:
- root
- platform-admin
mount_paths:
- platform/data/workloads/keycape/+
- platform/metadata/workloads/keycape/+
ttl:
default: 5m
max: 5m
renewable: false
requires_human_above: 5m
actors:
allowed_types:
- human-operator
required_subject_binding: auth/netkingdom/role/credential-broker-keycape-client-secret-generate
# CCR-2026-0043: human net-kingdom-admins membership and /assurance/level=aal2.
authorization:
flex_auth_required: false
flex_auth_mode: optional-preflight
approval_required: true
purpose_required: true
allowed_purpose_examples:
- oidc-browser-relying-party-mint-secret
delivery:
allowed:
- exec-env
preferred: exec-env
denied:
- chat
- state-hub-body
- git
- command-line-token-argument
- llm-prompt
exec_env:
variable: VAULT_TOKEN
child_only: true
redact_logs: true
audit:
openbao_audit_required: true
state_hub_metadata_allowed: true
record_secret_values: false
metadata_fields:
- grant_id
- actor
- subject
- purpose
- case_ref
- client_id
- kv_version
- lease_accessor
- status
revocation:
required: true
by_accessor: true
on_exec_exit: true
on_denied_request: false
- id: ops-warden/warden-sign
title: Ops Warden OpenBao SSH signing smoke token
status: pilot

View file

@ -54,45 +54,3 @@ receipts, concurrent release, restart, failed health and successful rollback.
Run destructive failure fixtures in an isolated environment; production must not
be intentionally broken to manufacture proof. Admit only after tests and the
observation gate pass. Access, budget or scope expansion stays a monthly decision.
## Deterministic platform readiness gate
`python3 scripts/activity_promotion_readiness.py --live --observation observation.json
--receipt readiness.json` verifies only the current bounded adoption and reports
an explicit owner-proof wait until ACTIVITY-WP-0041-T03 acceptance exists. This
read-only check neither creates a principal nor activates a release worker. D2
removes the founder reapproval; the identity/custody and runtime proofs above
remain required. ACTIVITY-WP-0042 scheduling acceptance does not supply them.
Later activation checks require operator-owned `--trusted-owner-key` (raw
32-byte Ed25519 public key) and `--trusted-owner-principal`, supplied separately
from `--owner-acceptance`. No signer key supplied inside a receipt is trusted.
The acceptance envelope has exactly `payload` and `signature` (base64 Ed25519
signature of sorted compact JSON). Its payload uses
`schema_version: activity-core-release-acceptance/v1`, binds the existing
`owner_task: ACTIVITY-WP-0041-T03`, issuer, dedicated release principal,
`authority: ACTIVITY-WP-0041-image-only-v1`, application, exact estate cluster UID,
adoption revision and full broker implementation revision. It expires within
five minutes; the operator owns signer admission/revocation and actual native
evidence verification. An agent must not synthesize this production acceptance
from test fixtures or a producer's booleans.
The `targets` mapping is the exact single platform child
`targetRevision` path on `main`, selective root sync of only activity-core,
child activity-core and prune false, as named in the checker. `checks` must
contain exactly its named acceptance checks, all actual owner-verified: bounded
identity/custody/revocation/denials, independent authenticated receipts/CI digest
binding, continuous observer, image-only scope, source CAS/single-flight,
restart/lost-response recovery, retention, real failed-health rollback, Temporal
registration, durable audit sink and report/schedule invariants.
`continuous_health` binds the adoption revision and a current actual 24h sampled
interval with at most 90-second gaps. A source timestamp or snapshot is not this
proof. Signatures authenticate the accepted owner's assertions; the owner still
must ground them in actual runtime receipts.
Native adoption mismatch or malformed/forged/stale/widened owner acceptance
returns unverified. Missing owner acceptance/trust binding returns waiting on
the existing owner task, with its cross-owner coordination case. Only ready
returns exit code zero. No automatic-sync source candidate is generated:
ACTIVITY-WP-0041's current broker contract still relies on explicit selective
manual child/root sync and forbids an automated child policy.

View file

@ -1,54 +0,0 @@
---
id: RPLAT-ADR-0006
title: "ADR-0006 — Give Playwright consumers a dedicated namespace-scoped group"
status: accepted
revision: "1.0"
owner: railiance-platform
date: "2026-10-13"
last_reviewed: "2026-10-13"
review_interval: 6m
---
## Context
rapp-playwright's published private pilot has a namespace Role but deliberately
no consumer binding. The user requested establishing a group during the Platform
open-work review. Existing cluster documentation provides administrative access;
the live binding inventory contains no non-system consumer group.
## Decision
Establish Kubernetes RBAC group subject `playwright-consumers`, bound only to
the package-owned `playwright-consumer` Role in namespace `playwright`. Platform
owns the RoleBinding; rapp-playwright retains the Role and workload source.
The initial membership policy includes all identified platform humans and coding
agents, as explicitly directed by the user. No per-use-case membership restriction
is introduced now. The identity/cluster owners must implement an authenticated
group claim for those entities before general consumer availability is accepted.
## Consequences
The Role permits pod get/list, pod port-forward creation and get of Deployment
`playwright`. It grants no Secrets, pod exec, workload writes, identity
impersonation or other namespaces. The namespace must remain dedicated to this
service because pod discovery and forwarding cover all its pods. Membership
confers browser-job submission for the reviewed trusted targets; it does not
authorize private targets, hostile pages or mutually untrusted tenant workloads.
Kubernetes has no separate Group object to create. This establishes an RBAC
subject and permissions, not a KeyCape group, login integration or membership.
Administrator impersonation can verify the permission boundary but cannot prove
consumer authentication. RPF-WP-0056 retains those outstanding acceptance steps.
Rollback removes only the Platform RoleBinding after reviewing its exact live
identity; it does not delete the Namespace, workload or artifact PVC. Existing
port-forwards require separate termination when revoking a consumer's active
access. Do not treat removal of a binding as proof that a running tunnel closed.
## Alternatives considered
Binding all authenticated users would broaden browser access beyond the human/coding-agent
identity population. Sharing the administrator kubeconfig would convey unrelated cluster
authority. Both are rejected. Selecting OIDC configuration or issuing client
certificates without an owner-supported membership route remains outside this
bounded group-binding change.

View file

@ -25,11 +25,5 @@
"synthetic_load": {
"contract_id": null,
"driver_revision": null
},
"synthetic_sender": {
"identity": null,
"source": null,
"tenant": null,
"expires_at": null
}
}

View file

@ -11,7 +11,7 @@ pod. It does not authorize a live revocation.
| Boundary | Exact scope | Responsible owner |
| --- | --- | --- |
| Consumer | Deployment `audit-core/audit-core`; Secret `audit-core/audit-core-database`; ExternalSecret `audit-core/audit-core-database` | `audit-core` |
| Lease | `database/creds/audit-core-runtime`; the one handle issued for the current ExternalSecret refresh (older predecessors are left alone) | `railiance-platform` custody operator |
| Lease | `database/creds/audit-core-runtime`; exactly one live handle whose issue time coheres with the current ExternalSecret refresh | `railiance-platform` custody operator |
| Database | CNPG `databases/platform-pg`, database `audit_core`, runtime group `audit_core_app` | `rapp-postgres` |
| Load | One separately approved synthetic sender/tenant contract; no production event or identity | `audit-core` + load owner |
| Coordination and evidence | `RAILIANCE-WP-0024-T02` | `railiance-platform` |
@ -48,124 +48,6 @@ The separately approved load driver is an executable that accepts `baseline`,
shape. Unexpected keys—including a bearer accidentally returned as `token`—
make the harness fail without echoing driver output.
## Synthetic sender projection lane
The load driver (`audit-core` `scripts/t02_synthetic_load_driver.py`, registered
at `8c8bcf4`) needs exactly one temporary audit-core sender identity. The
reviewed, reusable lane is `scripts/audit_core_synthetic_sender_lane.py`. It
reuses the live-proven registry/reload helpers of the dated Whitehat `-02`
procedure by import and the T02 receipt, attest-chain and preflight gates of
the lease harness; the dated `-01`/`-02` procedures are not copied.
**Receipt binding.** The identity is named only in the approved T02 receipt
(see `docs/audit-core-database-lease-approval.example.json`), in a
`synthetic_sender` block with exactly these keys:
```json
"synthetic_sender": {
"identity": "t02-synthetic-<window>",
"source": "<named synthetic source, contains 'synthetic'>",
"tenant": "tenant:trial:<name>",
"expires_at": "<RFC3339 UTC, not later than window.end>"
}
```
The lane accepts the receipt only if it also passes the lease harness's own
approval validation (approved status, ≤15-minute window, abort operator, all
three owner acknowledgements) and `synthetic_load.contract_id` equals the
registered driver interface `RAILIANCE-WP-0024-T02-SYNTHETIC-01`. The registry
entry it adds is `sources=[source]`, `tenants=[tenant]`, `may_write=true`,
`may_read=true`, `expires_at=<receipt value>`; projection refuses when the
name, source or tenant already appears on any existing identity. The same
`identity`, `source` and `tenant` values are what the operator passes to the
driver as `AUDIT_T02_SOURCE`/`AUDIT_T02_TENANT`; the confirm string is
`audit-core-synthetic-sender:<approval_id>:attended` (printed by preflight).
**Ordering.** The bearer reaches audit-core only through the registry at
`platform/workloads/audit-core/senders` → ExternalSecret `audit-core-senders`
→ env `AUDIT_CORE_SENDERS`, so both projection and cleanup restart
`deploy/audit-core`. The restarts must stay outside the restart-free proof:
project → restart → T02 exercise → evidence → cleanup → restart.
**Contained OpenBao access only.** Every live command (lane `project` and
`cleanup`, harness `exercise`) requires `--contained` and refuses unless it runs
as the child of `scripts/openbao-attended-exec.py`, i.e. inside `warden access
openbao-platform-admin-login --exec`: `WARDEN_ROUTING_CATALOG` must be set,
`BAO_ADDR` must be the operator tunnel `http://127.0.0.1:18200`, and
`BAO_TOKEN`/`VAULT_TOKEN`/`OPENBAO_TOKEN` must be absent; `bao` authenticates
through Warden's isolated token helper. There is no token-file path any more:
the harness's former default `https://bao.coulomb.social` was retracted on
2026-09-15 (RMASTER-WP-0020-T09), and a standing 0600 platform-admin token file
is outside the accepted custody path. Outside the login, the lane's `preflight`
runs the cluster checks only; `--contained` adds registry metadata and
`--inspect-registry` (attended, in-memory) adds the conflict check.
**One login per window.** `scripts/t02-attended-window.py` is the single reviewed
child of the contained login, so the ≤15-minute window needs one MFA login:
```bash
make audit-t02-attended-window \
AUDIT_DATABASE_LEASE_APPROVAL=/path/to/approved-receipt.json \
AUDIT_DATABASE_LEASE_DRIVER=/path/to/audit-core/scripts/t02_synthetic_load_driver.py \
AUDIT_SYNTHETIC_SENDER_CONFIRM=audit-core-synthetic-sender:<approval_id>:attended \
AUDIT_T02_TOKEN_FILE=/abs/0700-dir/t02.bearer \
AUDIT_T02_EVIDENCE_DIR=/abs/0700-dir/evidence
```
It runs, in order:
1. lane `project` — only within the first 3 minutes of the window and with
≥10 minutes of identity lifetime left. Writes the bearer to a new mode-`0600`
file (absolute, operator-owned `0700` directory outside every Git work tree;
never stdout), adds the one identity, forces `audit-core-senders` sync, runs
`rollout restart deploy/audit-core`, and waits for one Ready restarted pod.
Any failure removes the identity (restarting again) and shreds the file.
2. port-forward — the lane and harness reach the cluster only via
`ssh railiance01 kubectl`, so the window uses the same path: `ssh -L
127.0.0.1:<port>:127.0.0.1:<port> railiance01 timeout 900 kubectl -n
audit-core port-forward --address 127.0.0.1 deploy/audit-core <port>:8080`
(default port 18081, `AUDIT_T02_PORT` to override). No local kubeconfig is
needed; the remote forward is bounded by `timeout` even if SSH is cut.
audit-core's NetworkPolicies admit only named namespaces, which is why the
operator-side driver needs the forward.
3. harness `exercise --contained` with the registered driver; the driver env is
derived from the receipt: `AUDIT_T02_BASE_URL=http://127.0.0.1:<port>`,
`AUDIT_T02_TOKEN_FILE`, `AUDIT_T02_SOURCE`/`AUDIT_T02_TENANT` from
`synthetic_sender`, `AUDIT_T02_FIXTURE_ID=t02-<approval_id>`,
`AUDIT_T02_OCCURRED_AT=window.start`. Its baseline captures the
post-projection pod UID.
4. evidence — the exercise JSON is written `0600` as
`<evidence-dir>/<approval_id>-exercise.json`; a value-safe window summary is
written as `<approval_id>-window.json` on success and on abort.
5. lane `cleanup` with that evidence — refuses unless it names this
`approval_id`. Removes only the exact named identity (refusing if the name
carries a different scope), restarts audit-core only if something was
removed, shreds the bearer file, and confirms 1/1 Ready.
The port-forward is stopped before cleanup and on every exit. Any failure after
projection was attempted (including a failed `project`, whose registry write may
already have reached OpenBao) runs `cleanup --abort-reason` before the window
script exits non-zero. Children's stdout is captured and only their value-safe
JSON is re-emitted.
The single-step targets (`audit-synthetic-sender-project`,
`audit-database-lease-recovery-exercise`, `audit-synthetic-sender-cleanup`)
remain for the abort path; each costs its own contained login. A missed cleanup
is run as `make audit-synthetic-sender-cleanup
AUDIT_SYNTHETIC_SENDER_ABORT_REASON=...` and is a no-op if nothing remains.
If cleanup is missed, audit-core's `expires_at` enforcement makes the identity
inert at window end; the attended cleanup is still required to remove it.
Projection and cleanup each restart audit-core once (Recreate, no surge), so
production senders see two short retryable gaps per window; announce them with
the window.
The contained login discards child output and fails closed (exit 12, token
revoked) on any byte the child prints. The window script therefore prints
nothing: its summary, exercise evidence or `window-error-*.json` land only in the
evidence directory. Single-step targets require `AUDIT_T02_RESULT_FILE` so the
lane/harness write their JSON there instead of stdout.
## Attended sequence
Keep one terminal attached from baseline through recovery. A value-handling
@ -181,15 +63,10 @@ bodies and clear all transient JSON from memory or mode-`0600` temporary files.
3. Inside the attended process, list only lease handles below
`sys/leases/lookup/database/creds/audit-core-runtime`. OpenBao's lease lookup
returns issue/expiry/TTL metadata but not the leased username, so exact
selection is deliberately strict: at most 8 handles may exist under that
exact runtime prefix; exactly one must have been issued within 5 seconds of
the current database Secret refresh (the mounted credential), with at least
five minutes of TTL left; every other handle must be an older predecessor.
ESO refreshes every 15 minutes while the role TTL is 15 minutes (max 1 hour),
so a predecessor can legitimately still be live; it is never revoked. Abort
if no handle, several handles, or a newer unmatched handle match the refresh.
(2026-10-14: the first attended window aborted here under the earlier
"exactly one handle" rule with two live handles.) Retain only a one-way
selection is deliberately stricter: there must be exactly one live handle
under that exact runtime prefix, and its issue/expiry interval must contain
the current database Secret refresh time. Abort for database-owner
disambiguation if zero or multiple handles exist. Retain only a one-way
handle fingerprint in evidence and clear the lookup response immediately.
4. Recheck that the Secret `resourceVersion`, pod UID, and selected handle have
not changed. Revoke only that handle. Do not revoke the role prefix and do
@ -205,16 +82,17 @@ bodies and clear all transient JSON from memory or mode-`0600` temporary files.
7. Prove recovery: `/readyz` returns 200, the retrying synthetic request reaches
accepted or duplicate, the pre-exercise event remains readable, pod UID is
unchanged, and restart count is unchanged.
8. Stop the load, confirm the predecessor lease is revoked, record the
evidence, then remove the identity with the synthetic sender lane cleanup
(which restarts audit-core after the evidence is captured). The replacement remains managed by ESO
8. Stop the load, remove its separately approved fixture/identity, and confirm
the predecessor lease is revoked. The replacement remains managed by ESO
and expires through its normal database role TTL.
Only inside the approved window, with the receipt and reviewed driver, run the
whole window through `make audit-t02-attended-window` (see the synthetic sender
section above); the exercise step alone is
`make audit-database-lease-recovery-exercise`, which also runs inside the
contained login.
Only inside the approved window, with the receipt and reviewed driver:
```bash
make audit-database-lease-recovery-exercise \
AUDIT_DATABASE_LEASE_APPROVAL=/path/to/approved-receipt.json \
AUDIT_DATABASE_LEASE_DRIVER=/path/to/approved-load-driver
```
The exact confirmation string is embedded in the target. The harness rechecks
the Secret/pod/lease immediately before revocation, requires at least five

View file

@ -1,154 +0,0 @@
# Hourly cadence receiver admission and Qonto sender custody
NK-WP-0035 / RPF-WP-0059 / CCR-2026-0047. User explicitly approved this complete production packet on 2026-10-15
with "Approve packet and proceed", including the new sender grant. The founder's timing-rollout approval did not approve this new bearer
or ESO reader. The approved scope binds platform operator, Audit Core, rapp-qonto and
qonto-assistant owner roles; native prerequisites still apply.
The prepared change preserves every existing sender credential, source/tenant
scope and other registry field. It updates only Approval Engine's canonical
cadence and four heartbeat waits (2f=7200s; receiver grace1.5 gives3f=10800s),
and adds one new exact sender:
| Coordinate | Proposed value |
| --- | --- |
| Sender | rapp-qonto-collector |
| Source | qonto-assistant |
| Tenant | tenant:friendly:binky |
| Permission | write=true, read=false; receiver own-source reconciliation remains contract-defined |
| Evidence | envelope load-bearing/redact; complete source cadence retains each class's kind |
| Heartbeat | one source beat maps once to audit-core.heartbeat, data.class=audit.deny |
| Custody | platform/workloads/rapp-qonto/audit-sender: AUDIT_TOKEN,CUSTODY_REQUEST |
| Reader policy | workload-kv-read-rapp-qonto-audit; exact data path read only, no metadata/list |
| Kubernetes role | external-secrets-rapp-qonto-audit; external-secrets/external-secrets,15m |
| Delivery | namespace-restricted openbao-rapp-qonto-audit store; rapp-qonto/rapp-qonto-audit-sender:bearer |
Plan without credentials or network:
```bash
python3 scripts/credential-change.py validate CCR-2026-0047
python3 scripts/cadence_receiver_custody.py plan
```
The plan hashes CCR, exact policy/projection, executable helpers and enforcing
receiver parser sources, raw source declarations and Approval metadata proposal.
Record owner approvals in the CCR, regenerate the plan, and review its resulting
SHA256. Any subsequent source change requires a newly reviewed packet. The
runner verifies the exact packet hash; a proposed CCR refuses before native
calls. The immutable deployed receiver must support full cadence parsing and
per-instance reconciliation, verified with fixture-only native sender parsing
before any credential read. A source compatibility test alone cannot satisfy
that deployed gate.
Run `apply` only inside the existing attended Warden platform-admin `--exec`
envelope. Use credential routing first; do not export a token into a shell.
Before starting OIDC, remove all three ambient token aliases from the launcher
environment without reading or printing their values:
```bash
env -u BAO_TOKEN -u VAULT_TOKEN -u OPENBAO_TOKEN \
warden access openbao-platform-admin-login --exec -- \
python3 /home/worsch/railiance-platform/scripts/cadence_receiver_custody.py apply <reviewed-arguments>
```
This is required for the custody and fresh observer-readback launches. The
initial native attempt demonstrated that OPENBAO_TOKEN is not scrubbed by the
existing Warden wrapper; the contained helper correctly refused it. Session
self-revocation was confirmed and no custody mutation occurred in that attempt.
Do not inspect, copy or relax the guard for an ambient credential.
The owner wrapper must preserve its independent exit/self-revocation receipt.
The contained command is:
```bash
python3 scripts/cadence_receiver_custody.py apply \
--kubeconfig '<owner-selected-kubeconfig>' --server '<verified-primary-server>' \
--expected-receiver-image 'forgejo.coulomb.social/coulomb/audit-core@sha256:<reviewed-digest>' \
--confirm-packet-sha256 '<reviewed-post-approval-plan-sha256>' \
--receipt '<new-private-receipt-path>'
```
The command is silent; all subprocess outputs are captured, exceptions expose
fixed diagnostics only, and the0600 O_EXCL receipt contains metadata. It checks
the primary cluster UID, single ready receiver/pinned image, existing registry,
exact projection and custody provenance before reader grant writes. It extends
the coding-agent deny boundary for only the new data/metadata paths, provisions
CAS0 version1 with request provenance, and merges with exact registry-version
CAS. A concurrent edit fails; it never retries against an unseen registry.
It verifies exact reader path and scope/TTL, denied registry/sibling/metadata/list,
wrong service identity, revoked reader session and deny-boundary precedence;
then delivers ESO, compares bearer values privately, refreshes the receiver
registry secret, restarts the receiver and verifies loaded registry equality.
The new store is restricted to rapp-qonto, and source projection shape forbids
extra data or arbitrary templates. API-observed default fields are explicitly
pinned so a legitimate resumed projection remains equal.
A partial failure retains custody/version1 for investigation. Inspect its
nonsecret receipt, confirm unchanged reviewed source, and rerun with a new
receipt path plus `--resume`. The same request's durable token is reused;
conflicting versions, provenance, registry scope or role/policy drift refuse.
No helper rotation, secret deletion or broad rollback occurs. A restart after
successful delivery may be repeated by an explicitly resumed attempt; it
preserves the token and registry content.
Success is `delivered_pending_native_producer_and_independent_acceptance`.
It does not prove collector source capture, denial refusal, bearer revocation,
independent receiver reads, elapsed-hour heartbeat observation, or profile
acceptance. Read-only observer custody must remain independent; do not upgrade
the collector bearer. Keep the runtime active using the owner's authorized
health polling for the real hour, without changing scale-to-zero. A session
lasting15m must close normally; use a fresh attended session for hour-end reads.
Source-global missing-heartbeat findings must be interpreted alongside actual
active-instance inventory: intended scale-to-zero is not unexpected active
process silence. Preserve shutdown/restart scope separately.
Residual native checks include isolated wrong-namespace store denial and
former-valid collector bearer revocation, alongside the source/tenant/read
refusal and per-instance observer matrix. The runner's wrong-service-identity
probes do not separately isolate wrong namespace while preserving the correct
service account name. Record that check honestly rather than treating the
combined refusal as the complete namespace test.
## Independent hour-end custody readback
Use a fresh attended platform-admin envelope after the observation hour; the
initial session must not be held open. `scripts/cadence_observer_readback.py`
reuses the existing uniquely selected independent read-only full-tenant
operator credential in process memory. It changes no credential or workload
and never grants the collector archive read permission. Its only transport
is a temporary primary-cluster localhost port-forward that is terminated on
exit, with redirects and environment HTTP proxies disabled.
```bash
python3 scripts/cadence_observer_readback.py \
--source qonto-assistant --tenant tenant:friendly:binky \
--instance '<exact-source-snapshot-instance>' \
--since '<acceptance-window-start-with-timezone>' \
--until '<acceptance-window-end-with-timezone>' \
--event-id '<exact-collector-event-id-1>' \
--event-id '<exact-collector-event-id-2>' \
--kubeconfig '<owner-selected-kubeconfig>' --server '<verified-primary-server>' \
--expected-receiver-image 'forgejo.coulomb.social/coulomb/audit-core@sha256:<reviewed-digest>' \
--receipt '<new-private-readback-receipt-path>'
```
Provide all capture event IDs (maximum256, unique). The reader only opens
those IDs, the exact source/tenant/instance reconciliation query, and the
protected global stream-findings surface. It outputs only findings for the
selected source; no unrelated findings or request payload is persisted.
Every event must match exact ID/source/tenant/instance and acceptance window.
The sanitized event projection includes instance/sequence, action, observed
and accepted timestamps, and exact heartbeat coverage. Actual custody
accepted_at can distinguish a duplicate delivery receipt from original custody
time; a collector duplicate200 is not itself first-acceptance timing proof.
Approval Engine source/tenant is also allowed; omit --instance for its
instance-free lifecycle records. The helper does not invent process identity
or sequence for Approval and labels its conclusion as a bounded source
lifecycle acceptance window. Qonto still requires an explicit exact instance.
Receiver-normalized timezone spelling is compared by timestamp semantics;
source, tenant and actual instance bindings remain exact.
Exit0 reports `independent_custody_readback_complete`, which proves only the
bounded supplied custody reads. The source snapshot, contiguous startup/
periodic capture, process inventory and negative observation matrix remain
independent inputs to Kings Guard's assessment.

View file

@ -55,8 +55,8 @@ coordinates:
| --- | --- |
| `aud` / client id | `codex-railiance-platform` |
| `sub` | `service:codex:railiance-platform` |
| `principal_type` | `service` today; `agent` after the KeyCape change (RPF-WP-0065). The role accepts both during the transition |
| `tenant` | `tenant:platform` |
| `principal_type` | `service` |
| `tenant` | `tenant:coulomb` |
| `roles` | contains `coding-agent` |
| requested scope | `openbao:login` |
@ -115,16 +115,3 @@ check. Revoke the issued OpenBao token if it remains live.
differs from the reviewed body or its verifier fails.
- Revoke the test OpenBao token, disable the KeyCape client, and rotate its
confidential secret after suspected disclosure.
## Source clarification — 2026-10-11
The tenant table now follows current `coding-agent-jwt-role.json`, KeyCape
`config/service-clients.example.yaml` and the 2026-09-27 Platform tenant review.
This correction grants no additional policy or custody access. Historical
AppRole verification and earlier migration examples above do not establish
current named coding-client registration, confidential-secret delivery or a
KEY39 producer admission. The source-only candidate in
`proposals/2026-10-11-key39-coding-client-custody-proposal.json` is not an installed
CCR, grant, catalogue entry or credential request. Follow its separate owner
and founder prerequisites before any client-secret retrieval or JWT exchange.

View file

@ -1,8 +0,0 @@
# Consumer contract for secrets-engine service-JWT login to OpenBao (CCR-2026-0038).
# Published by railiance-platform 2026-10-10 after the login proof passed
# (secrets-engine docs/evidence/2026-10-10-keycape-openbao-probe.json, a963a77).
# Non-secret. Point SECRETS_ENGINE_OPENBAO_JWT_LOGIN at a copy of this file.
# Identical to the founder's probe input; that local copy may be kept as is.
mount: keycape-services
role: secrets-engine-openbao
bound_issuer: https://kc.coulomb.social

View file

@ -303,70 +303,3 @@ Use the whynot-design npm token lane as the pilot:
Once that path feels good, reuse it for the sibling workload-KV lanes and the
credential broker's OpenBao token-role gates.
## Self-Service Intake (RPF-WP-0061)
Clients file standard CCRs without a railiance-platform worker session. The
platform's judgement is in [`registry/ccr-standard-profile.yaml`](../registry/ccr-standard-profile.yaml);
`review-standard` applies it, and `.forgejo/workflows/ccr-intake.yaml` re-checks
it in CI. Allowed submitters: **ops-mason** only (profile `submitters`).
**In profile** means: a `workload-kv-read` lane on mount `platform`; one exact path
`platform/workloads/<a>/<b>[/<c>]` or `platform/suppliers/<supplier>/<account>`;
`metadata_read: false`; a policy file equal to the generated HCL; Kubernetes auth
with role `external-secrets-*`, audience `openbao`, all ttls 15m or less,
`token_no_default_policy: true`; one service account in one non-platform
namespace; risk `low` or `medium`; and no other live CCR reading the path.
Everything else (OIDC roles, AppRole, token roles, second readers, high risk)
still goes to the `railiance-platform` inbox.
### Client steps
Run these in a railiance-platform checkout that is up to date with `main`:
```bash
# 0. Generate the policy instead of writing it, so it is byte-equal by construction.
python3 scripts/credential-change.py render-policy --kv-path <platform/...> > <policy.hcl>
# Optional: plan_ref: {repo, path, commit} in the draft links the client's
# construction plan; submit keeps it and review-standard only checks its shape.
# 1. File the draft. Allocates the CCR id, copies the policy, runs the review.
python3 scripts/credential-change.py submit \
--ccr <draft.yaml> --policy <policy.hcl> --slug <slug> \
--requester ops-mason --push --notify
# [NEEDS-PLATFORM] lines mean out of profile: stop. --notify messaged
# railiance-platform. Do not edit the review comment to get past it.
# 2. Wait for the "CCR intake gate" run on ccr/<id>-<slug> to pass, then
# fast-forward main to it. Never merge a red or unchecked ccr/* branch.
git switch main && git merge --ff-only ccr/<id>-<slug> && git push origin main
# 3. Open the founder decision and commit the decision id.
python3 scripts/credential-change.py request-decision <id>
git commit -m "<id>: request founder decision" -- credential-change-requests/<id>-<slug>.yaml
git push origin main
```
After step 3, the client waits and does not edit `status` or `review.comments`
itself. Approvals reach the file only through `sync-decision`, even when the
founder approved the client's plan elsewhere. The hub decision is the single
approval record for both the client plan and the CCR; clients copy their plan
approval *from* it after the sync. The **founder** resolves the State Hub decision.
Then a **platform session or the founder** runs `sync-decision <id>`. Clients do
not run `sync-decision` on their own CCRs. State Hub does not authenticate who
resolved a decision (`decided_by` is free text). Until founder approval can be
verified (RPF-WP-0061 follow-up B), the sync must come from someone other than
the requester. After the CCR is `approved`, the usual steps apply:
`confirm-binding` against native state, apply (ops-mason under
ops-mason-construct for in-profile lanes, otherwise the applier), then
`record-evidence`.
### Known limits
- All workstation agents push to Forgejo as the same account, so `ccr/*`
cannot yet be restricted to ops-mason. The submitter list and the
merge-only-green rule are enforced by tooling and convention, not by Forgejo.
The main-branch `check-ids` job and `check-branch` detect violations after the
fact (follow-up C).
- The automated review is a source review only. It never sets `approved` and
never touches OpenBao.

View file

@ -1,17 +1,10 @@
# Activity Core repo-manager Forgejo read lane (no CCR filed)
# Activity Core repo-manager Forgejo read lane (CCR-2026-0034, number to confirm)
Status: **proposed design only**, drafted 2026-10-04 for STATE-WP-0094 memo 3
Status: **proposed**, drafted 2026-10-04 for STATE-WP-0094 memo 3
(`freshness-runner-deploy-trial-e77f8b4`, accepted by the founder 2026-10-04,
approval `4ae04c8e`) and RMGR-WP-0020-T04. Nothing here is approved, applied or
provisioned. No Forgejo, OpenBao or cluster object was created.
An earlier draft of this page cited CCR-2026-0034. That number is the KeyCape
secrets-engine operator reader
(`credential-change-requests/CCR-2026-0034.yaml`, applied 2026-10-07). No
Forgejo-read CCR exists for the Activity Core worker. CCR-2026-0029 and
CCR-2026-0030 are ops_run worker tokens, and CCR-2026-0031 is the core-hub
Forgejo source read bound to another namespace. Do not treat this design as a
CCR id. Assign the next free id only when this lane is admitted.
provisioned. No Forgejo, OpenBao or cluster object was created. Confirm the CCR
number with the CCR tooling before registering; 0033 was the highest seen.
## Why
@ -54,7 +47,7 @@ worker's other secrets and is the default here.
## Draft manifests (land only after metadata apply)
```yaml
# No CCR id. Do not apply until this design is admitted, numbered and approved.
# CCR-2026-0034 (to confirm). Do not apply until approval and confirm-binding.
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:

View file

@ -1,178 +0,0 @@
# Application backup credential delivery — 2026-10-08
Prepared for RPF-WP-0053-T02/T05, CSOC-WP-0008-T02 and EMAIL-WP-0007-T03.
Both requests are **draft**, bindings unconfirmed and delivery admission false.
This packet creates no provider identity, OpenBao structure/value, ServiceAccount,
node file, backup capture or timer. Source preparation is not owner acceptance.
| Consumer | Reader request | Authoritative KV pair | Node destination |
| --- | --- | --- | --- |
| Coulomb paired archive | CCR-2026-0036 | platform/workloads/coulomb-social/backup-primary | /etc/coulomb-social-backup/primary.env |
| Email ledger archive | CCR-2026-0037 | platform/workloads/email-connect/backup-primary | /etc/email-connect-backup/primary.env |
Both records contain exactly AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. Each
has its own Scaleway IAM application/key, Kubernetes ServiceAccount namespace,
OpenBao role and read policy. The destinations are root:root0600 in root:root0700
directories. Workers receive provider credentials; the delivery reader alone
uses OpenBao, without a persistent bootstrap credential. Age decryption identities
belong to independent recovery custody.
## Public review sources
- credential-change-requests/CCR-2026-0036-coulomb-social-backup-primary.yaml
- credential-change-requests/CCR-2026-0037-email-connect-backup-primary.yaml
- openbao/policies/workload-kv-read-{coulomb-social,email-connect}-backup-primary.hcl
- openbao/backup-primary-serviceaccounts.yaml: two accounts, automount=false;
no RoleBinding, Secret, mounted JWT or workload deployment.
- scripts/application_backup_credentials.py: dependency-free node reader/delivery.
- scripts/render_backup_credential_contract.py: existing platform validator and
PyYAML export YAML source to a public JSON contract; the node needs no PyYAML.
- tests/test_application_backup_credentials.py: synthetic fixtures only.
The exporter verifies exact reader coordinates, delegated metadata-applier scope
and policy equality. It embeds source CCR/policy hashes. After every owner/status
update, regenerate and review the JSON export and pin **its** SHA256 in the node
command; an old draft export cannot execute. The CCR remains the source of truth.
## Required owner setup, in order
1. Admit the proposed railiance01 root executor under T01. It already needs k3s
administration for coherent capture; this design explicitly records that node
administrators and namespace writers can exercise the reader identity. This
is not a narrower alternative to root execution admission.
2. Survey the existing bucket policy, IAM principals and lifecycle. Preserve
native Barman/Forgejo principals and existing policy statements. Create two
separate IAM applications; another key on an existing broad application does
not establish isolation. Bind each caller to its exact application archive
prefix with IAM plus bucket policy, including explicit effective denial where
project-wide IAM would otherwise allow sibling access. Review the complete
merged policy before its replacement; this packet supplies no blind overwrite.
3. Admit each CCR with platform-owner and its named application-owner reviews,
confirmed namespace/account/audience, and a reviewed structure change. Use
railiance-platform's existing delegated metadata applier for policies/roles;
ops-mason now surveys read-only under ops-mason-survey (MASON-IN-0004), and
cannot apply structure. The existing kubernetes auth mount must use its admitted
reviewer; no TokenReview authority is granted to these delivery accounts.
4. Provision each provider **pair atomically** under an explicit platform custody
act, initial KV CAS=0. Retain only its version receipt. The generic Warden
paste-once desk currently runs a single-field KV PUT, replacing the record:
two desk runs would discard the first field. It is unsuitable for this pair.
A reviewed pair-aware custody child/front door is still required under T06;
neither the node reader nor the administration-login lane provisions values.
5. Verify positive reader identity and negative wrong namespace/account/audience,
sibling GET, metadata GET, parent LIST, KV write and administration denials.
Verify provider conditional multipart publication, version GET, foreign-prefix
and retained-delete denial. These are contained owner proofs, not regression
tests against production. Record native evidence before setting the CCR's
delivery admission booleans and source_ref; do not infer them from code tests.
6. Export the final admitted CCRs, review their hashes, create only the designated
private node directories and stage public source/export artifacts. Run delivery
at the pinned positive KV version; preserve the metadata receipt independently.
Admission covers initial delivery, not capture, service activation or rotation.
Scaleway destination is s3://railiance-platform-pg-backup with endpoint
https://s3.nl-ams.scw.cloud. Prefixes are
platform-pg/application-archives/coulomb-social/ and
platform-pg/application-archives/email-connect/. Publisher rights are conditional
multipart create/parts/completion, abort of its own unfinished uploads, HEAD,
version-pinned GET and prefix-constrained listing only if needed. Deny retained
object/version deletion, native/sibling prefixes, bucket/IAM administration and
escrow. Owner admission must also reconcile the existing 30-day lifecycle with
14 daily/4 weekly protected recovery points, capacity and independent receipts.
Scaleway's [IAM/bucket policy guide](https://www.scaleway.com/en/docs/object-storage/api-cli/combining-iam-and-object-storage/)
explains the project-wide IAM boundary and granular bucket policy. No effective
provider boundary is claimed before native proof under these specific callers.
## Routing verification
Installed Warden 0.1.2 was rebuilt from the corrected checkout, preserving
phase-memory. Bundled catalog hash 5e6b217aa1f6 matches current source. WARDEN-WP-0043
fixes keyword collisions without granting authority:
```bash
warden plan "attended OpenBao platform administration to configure read policies and auth roles for Coulomb and email backup delivery" --json
warden plan "provision a Scaleway object storage backup publisher key pair into OpenBao custody" --json
```
The first selects openbao-platform-admin-login, founder_required/oidc_login;
survey still selects the read-only ops-mason ceremony. The second selects
openbao-api-key, founder_required/paste_once_provision: this remains a generic
owner pointer, not an admitted pair writer. Do not run the single-field desk for
these pairs or use the administration identity as an unreviewed KV writer.
An attended OIDC command must use Warden's contained child envelope, including
its output and self-revocation requirements, for its exact reviewed operation.
## Preview and admitted delivery shape
Public export (platform environment with the existing PyYAML validator):
```bash
python3 scripts/render_backup_credential_contract.py \
--application coulomb-social \
--ccr credential-change-requests/CCR-2026-0036-coulomb-social-backup-primary.yaml \
--output /tmp/coulomb-backup-reader-review.json
python3 scripts/application_backup_credentials.py \
--application coulomb-social --ccr /tmp/coulomb-backup-reader-review.json
```
Repeat with email-connect/CCR-2026-0037 and its own output. Default delivery preview
has no network, TokenRequest or filesystem effects. --execute requires the exact
reviewed export hash, an applied/verified/active CCR, confirmed bindings, named
owner reviews with source references, native provider/reader proof and admitted
node execution. The implementation does not authenticate these source statements;
the owner must bind and approve the exact immutable export before the node run.
After those owner steps, the node command shape is:
```text
sudo python3 /opt/railiance-platform/scripts/application_backup_credentials.py \
--application APPLICATION \
--ccr /etc/APPLICATION-backup/credential-reader.json \
--expected-ccr-sha256 REVIEWED_JSON_EXPORT_SHA256 \
--expected-kv-version APPROVED_POSITIVE_KV_VERSION --execute
```
This is not yet an executable activation instruction: neither installed source
placement nor the approved values above exist. Source/export transfers contain
no credentials. Do not transport provider pairs through scp, chat or /tmp files.
The reader pins kube-system UID a553c742-0115-43d4-99a4-a5ca56fe0786 before issuing
a ten-minute TokenRequest JWT for backup-primary-delivery/openbao in its own
namespace. Kubernetes has a ten-minute minimum; the resulting OpenBao token has
a separate five-minute hard maximum and four-use limit, one policy and no default
or identity policy. JWT and token stay in process memory. The helper checks the
returned JWT binding/expiry, OpenBao token bounds and exact current KV version,
revokes its token **before** file publication and verifies contained readback.
Backend bodies, exception text and key hashes never enter receipts or outputs.
Proxy environment and HTTP redirects are disabled for the OpenBao exchange.
Initial file publication uses a private temporary file, fsync and no-overwrite
hard link. Symlink ancestors, unsafe target modes/owners, aliases and special
files refuse. A lock excludes overlapping deliveries. SIGTERM unwinds through
revocation; host loss/SIGKILL can leave a short-lived token until expiry and
requires metadata reconciliation, never an automatic success claim. A crash after
publication but before receipt can resume only by a new scoped read with exact
file equality. Different existing bytes require a separate version-guarded
rotation contract; this helper cannot overwrite them.
## Evidence and remaining gates
Thirty synthetic delivery cases and four YAML/export cases pass, alongside thirty primary-archive/retention
cases. They cover both profiles, draft/source/owner admission, narrower token
checks, revocation failure, private-file attacks, payload injection, version
changes, atomic failures and identical-file recovery. Current draft exports and
policies validate without errors; the expected unconfirmed-binding warning blocks
apply. Warden's complete regression gate passes, with the human stdout test now
explicitly modelling a human independently of an agent session environment.
Shared security orientation was retrieved as emergent-security 0.2.6, digest
8d1d6a9d4d07fec7b619618de7dfe2be2254c1790f85f4198c7fe19db9751c02. It was withheld
for source drift, so current ops-mason INTENT, custodian agent orientation and
informed-decision README were read directly. No card was treated as authority.
No live credential delivery, provider upload or timer activation is evidenced.
RPF-WP-0053-T02/T05/T06 retain the custody/admission work. T03/T04 retain the first
publication, independent application recovery, schedule and monitoring/retention
gates. CSOC-WP-0008 and CSOC-WP-0010 remain active; this preparation alone cannot
complete either plan.

View file

@ -1,50 +0,0 @@
# Backup publisher pair custody — preview, 2026-10-08
RPF-WP-0053-T06. The node reader in `scripts/application_backup_credentials.py`
can deliver an already stored pair. It cannot create one. Warden's paste-once
desk writes one field per PUT and would drop the other half of
`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`.
`scripts/application_backup_pair_custody.py` is the prepared front door for
both draft records:
| Application | CCR | KV path |
| --- | --- | --- |
| coulomb-social | CCR-2026-0036 | platform/workloads/coulomb-social/backup-primary |
| email-connect | CCR-2026-0037 | platform/workloads/email-connect/backup-primary |
Default command:
```bash
python3 scripts/application_backup_pair_custody.py --application email-connect
```
The preview reads the draft CCR and prints field names, CAS 0 and one intended
write. `writer_admitted` is false. There is no stdin, provider call, OpenBao
call, key generation, file write or timer change.
`provision()` is the future attended shape, covered by synthetic tests only:
- writer token policies must be exactly `backup-pair-writer`, with no default
policy and no `workload-kv-read-*-backup-primary` reader policy
- TTL at most 5 minutes and at most 2 uses, then revoke
- metadata absence is required; an existing version is not overwritten
- one HTTP-style body carries `cas: 0` and both fields
- the receipt keeps version and request id, never the values
The founder admitted this identity on 2026-10-08. The words used were
"bucket-pair-writer"; the OpenBao policy name is `backup-pair-writer`.
No implemented SecurityCaseType covers the pair: `workload-kv-read` is still
only an inventory candidate, and ops-mason can survey structure but cannot
write it. The attended install is `scripts/install-backup-pair-writer.sh`.
The installed policy matches the file
(`86e435900bc6912fada10c526bfa865127a0e28acd328cf5f5f69e8d62bda310`), so
`WRITER_ADMITTED` is true. The command-line preview still does not write.
Both KV paths are absent. The bootstrap key is denied IAM application and
policy list (HTTP 403), so it cannot create the two publisher applications.
The live bucket policy `resource-control-scoped-backup` allows one principal,
`application_id:ee80333e-5bab-4a04-85a9-5bdb46b7b3c1`, under `platform-pg/*`,
including object delete. No second application was added and no key was
stored. CCR-2026-0036 and CCR-2026-0037 stay draft. The next founder act is
creating the two IAM applications in the console, then one CAS=0 write each.

View file

@ -1,22 +0,0 @@
# Attended service credential delivery
Request type `attended-service-credential-delivery` (`scripts/attended_service_delivery.py`), first used by
CCR-2026-0055 (NetKingdom `svc-privacyidea`, NK-WP-0056-T04).
Some services keep a credential in their own encrypted store and accept it only through an administrator API
that a person operates. privacyIDEA's LDAP resolver is one: its `BINDPW` lives encrypted in the privacyIDEA
database and only the `pi-admin` API may change it. ESO cannot deliver such a value, and an unattended job would
need the administrator password standing in the cluster.
The contract therefore holds the value in OpenBao and nothing else:
- `openbao.auth` is the attended `platform-admin` OIDC session on `netkingdom`; `runtime_access: false`;
`initial_cas: 0`. No policy, auth role, ESO object or other access may be declared.
- The runtime frontdoor stays disabled (`access_frontdoor.resolvable: false`).
- `delivery` names the service, the target inside it, the owner's procedure and the gate (the attended person and
the proof they run).
- Approval follows `review.required_approvers`; from `applied` onward every listed role must have approved.
The owner's procedure reads the value in memory under the attended session and writes it into the service; the
person runs it and proves the write with an MFA login through the service. A rotation needs no decrypted custody copy. Modelled on
`attended-host-key-delivery` (CCR-2026-0028), which delivers to a host file instead of a service.

View file

@ -1,107 +0,0 @@
# Bounded executor: OpenBao lanes and audit sender (owner answer, NK-WP-0056-T02)
Owner answer from railiance-platform to NetKingdom's request
(net-kingdom `docs/proposals/2026-10-31-nk56-t02-owner-requests.md` §railiance-platform;
State Hub message `9d08288d`). Basis: Bounded Execution Admission v0.1 (proposed) and its pilot
example `bind-rotation-svc-authelia.yaml`. Workplan: `workplans/RPF-WP-0069-bounded-executor-lanes.md`.
Everything here is source and proposals. No role, policy, Secret or KV value was created or read.
CCR-2026-0058 is `proposed`; approvals and the founder's choices below stay open.
## Q1. Kubernetes auth role for `sso/nk-bind-executor`: which request types?
**Answer: one role per lane, all bound to the one ServiceAccount, two request types.**
The existing `workload-kv-read` contract requires a role to carry exactly its own policy
(`scripts/credential-change.py` `validate_workload_kv_read`, "openbao.auth.policies must contain exactly
the policy used by the auth method"; applier check "must contain only openbao.policy_name"). We keep that
rule. The executor logs in once per lane with the same projected ServiceAccount token (audience
`openbao`); each token carries one policy and lives at most 15 minutes. A combined role would make every
lane's review depend on every other lane.
| Executor need | Request type | Status |
| --- | --- | --- |
| Write the next version of `platform/workloads/net-kingdom/directory-binds/svc-authelia` (CAS) and read it for checks/revert (T06) | `workload-kv-write-next-version` (**new**, `scripts/workload_kv_write_next_version.py`) | Type in source; CCR-2026-0058 `proposed` |
| Read `platform/workloads/net-kingdom/synthetic-journey/syn-journey` (`PASSWORD`, `TOTP_SEED`) for the journey (T10) | `workload-kv-read`, native workload login (`token_self_lifecycle: true`, `metadata_read: false`), as the native reader proof in CCR-2026-0053 | Not drafted; see below |
| LLDAP credential to set `svc-authelia`'s password | `workload-kv-read` of a new custody entry, after secrets-engine answers its Q2 | Not drafted |
| audit-core sender bearer (receipts) | `workload-kv-read` with ESO delivery, as CCR-2026-0021/0022 | Not drafted; see Q2 |
### The new type `workload-kv-write-next-version`
Prepared the way `attended-service-credential-delivery` was added: module
`scripts/workload_kv_write_next_version.py`, tests `tests/test_workload_kv_write_next_version.py`, dispatch in
`scripts/credential-change.py` (validate, plan, operator-commands, runbook). It is **contract-only**: there
is no delegated applier (it is not in `LANE_MODULES`). The attended platform-admin applies policy and role
from a reviewed commit, like CCR-2026-0052..0055.
The contract refuses anything wider than:
- one exact `platform/workloads/...` path on mount `platform`; no wildcard, no sibling;
- policy (generated by `policy_hcl`, artifact `openbao/policies/<policy>.hcl`):
`read` + `update` on `platform/data/<path>` only (no `required_parameters`: OpenBao applies it to reads too, NK-WP-0056-T12),
`allowed_parameters = {data, options}`; plus `lookup-self`/`revoke-self`. No `create` (a missing path is
refused, so the executor cannot mint new entries), no `metadata/`, `delete/`, `destroy/`, `undelete/`, list;
- key-level `cas_required = true`, set by the attended platform-admin at setup. The engine then refuses
every write that does not name the version it replaces; races and replays fail closed in OpenBao, not in
executor code. (`required_parameters = ["options"]` alone cannot force `options.cas`; the key setting does.)
- Kubernetes auth: one ServiceAccount, one namespace, audience `openbao`, `ttl`/`token_max_ttl`/
`token_explicit_max_ttl` = 15m, `token_no_default_policy: true`, exactly one policy;
- `access_frontdoor.resolvable: false`; `agent_fence.data_and_metadata_denied: true` (coding agents fenced);
- `admission` names the standard and executor identity; full `verification` and `lifecycle`; explicit
approval by every `review.required_approvers` role from `approved` onward.
Revert (`restore-kv` in the example) is a further next-version write of the prior value with CAS. Reading
an older version is a `read` on `data/<path>?version=N`, which the policy already grants; no extra
capability is needed.
**What the role does not do.** OpenBao does not evaluate the admission. The role is standing: a compromised
`nk-bind-executor` ServiceAccount can replace the svc-authelia bind (sign-in outage on Authelia's next
restart) but reach nothing else. The admission window, consume-once and plan digest are enforced by the
executor, approval-engine and access-engine. If that is not enough, the founder can choose a
window-scoped role instead (created by the attended platform-admin at the window start, removed after);
that removes the "unattended" property of T06 for the platform side. Recommendation: standing role for the
pilot, because the reachable damage is one read-only directory bind already covered by an attended
recovery path (CCR-2026-0053 `lifecycle.compromised`).
### Journey secrets (T10)
CCR-2026-0057 is `attended-service-credential-delivery` with `runtime_access: false`; its contract forbids
any role or policy on that path. A `workload-kv-read` request for the executor therefore changes
CCR-2026-0057's stated boundary. It must say so, cite CCR-2026-0057, and be approved by both
platform-owner and net-kingdom-owner. CCR-2026-0057's attended procedure stays valid. Shape: role
`net-kingdom-bind-executor-syn-journey-read`, policy `read` on the data path only, native login, 15m.
Draft it when T10 is scheduled and the ServiceAccount exists.
## Q2. Can the executor pod receive an ESO-projected audit-core sender bearer?
**Yes, with the CCR-2026-0021/0022 shape, once audit-core admits the sender.** The receipt is an event to
audit-core; per RPF-WP-0067-T01 the wire is `POST /v1/events` to
`http://audit-core.audit-core.svc.cluster.local:8080`, with a per-sender bearer from the registry at
`platform/workloads/audit-core/senders`. `Idempotency-Key` is the envelope `id` and `correlation_id` is the
`run_id`. The envelope `source` equals the sender name.
Shape (as `docs/credential-lane-designs/factory-audit-senders-review.md`):
- custody `platform/workloads/net-kingdom/bounded-executor/audit-sender` (`AUDIT_TOKEN`), generated CAS=0 in
the attended platform process and merged into the registry as one exact sender (proposed name
`nk-bounded-executor`, `tenant:platform`, write true, read false, `evidence_kind: load-bearing`,
`secret_policy: redact`); no existing sender changes;
- exact-path policy, Kubernetes role for `external-secrets/external-secrets` only, ClusterSecretStore
restricted to namespace `sso`; ExternalSecret into `sso/nk-bind-executor-audit` key `audit-token`,
mounted as a file in the executor pod;
- coding-agent fence on the new data and metadata paths.
The bearer lets the executor append its own receipts only. It is not an OpenBao identity. The ESO
manifests belong in the `rapp-*` package, as in CCR-2026-0053, which points to net-kingdom
`bind-eso.yaml`. Prerequisites: audit-core says yes to its Q1 (net-kingdom request §audit-core) and
names the sender. Then a `workload-kv-read` CCR follows here.
## Open items for the founder and other owners
1. Review and approve or change CCR-2026-0058 (platform-owner, net-kingdom-owner). It stays `proposed`.
2. Standing vs window-scoped write role (recommendation: standing for the pilot).
3. Accept that a journey-read CCR widens CCR-2026-0057's `runtime_access: false`.
4. secrets-engine: the LLDAP credential lane shape (its Q2) before a custody CCR can be drafted.
5. audit-core: sender admission and name, before the sender CCR.
6. railiance-apps / rapp package: the ServiceAccount `sso/nk-bind-executor` must exist before
`bound_claims_confirmed` can be set.

View file

@ -1,75 +0,0 @@
# Coulomb paired-backup primary custody review — 2026-10-07
Pending admission under RPF-WP-0053-T01/T02; this is a concrete consumer request,
not an approved credential grant. No existing Secret, policy or grant is changed.
The revised consumer uses Scaleway primary storage, separate from native Barman
objects. The earlier Nextcloud overwrite-slot proposal is withdrawn: the current
Nextcloud workload uploader is create-only and remains the independent secondary.
| Coordinate | Requested contract |
| --- | --- |
| Consumer | Node-side coulomb-social-paired-backup.service; proposed root executor guarded by current cluster UID/app digest, subject to T01 review |
| Cluster | railiance01; kube-system UID a553c742-0115-43d4-99a4-a5ca56fe0786 |
| Destination | s3://railiance-platform-pg-backup/platform-pg/application-archives/coulomb-social/ |
| Provider | Scaleway Standard Multi-AZ, nl-ams; https://s3.nl-ams.scw.cloud |
| Source scope | coulomb_social_db on apps-pg plus the coulomb-social-content PVC, one quiesced capture |
| Publisher rights | Multipart creation/parts/completion, abort its own unfinished uploads, HEAD and version-pinned GET within the exact archive prefix; prefix-constrained listing if required for HEAD missing-object semantics |
| Publisher exclusions | No retained-object/version deletion, native Barman writes, other application prefixes, bucket/IAM configuration, escrow identity or bootstrap provider key |
| Worker delivery | Owner writes /etc/coulomb-social-backup/primary.env, root:root0600, parent0700; AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY only |
| Recovery custody | Independent read authority plus the age identity; public object key/version/hash/size receipt retained outside the node/workstation |
| Retention | Separate owner; proposed 14 daily/4 weekly recovery generations, deduplicated and protecting newest plus last successfully recovered point; not activated |
| Runtime | Dedicated /opt/railiance-platform/backup-venv, hash-locked requirements/backup-primary.lock; SDK conditional-completion field verified offline |
Existing CCR-2026-0012 identifies the custody owner and source
platform/workloads/railiance/backup/object-storage fields ACCESS_KEY/SECRET_KEY.
It authorizes the existing databases/platform-pg-backup-s3 delivery, not this
consumer. Do not copy that Secret, broaden its ESO namespace conditions, reuse the
bootstrap key or manufacture a successor grant. Owner must determine whether a
separate scoped IAM application/key is needed: Scaleway policies attach to the
application, so merely minting another key under a broad application does not
establish prefix isolation. A worker intentionally has no delete authority even
if an existing provider identity has wider capabilities.
Routing check: warden route show backup-object-storage returns unknown on this
workstation, despite that historical catalog id in CCR-2026-0012. Broad route
lookup returns the Nextcloud lane and generic OpenBao pointer. No secret retrieval
was attempted. Owner must reconcile the current primary front door and delivery;
record its review/grant reference here before execution. Node metadata-only check
on 2026-10-07: primary.env absent. Source presence is not custody admission.
Before first publication, prove bucket versioning and conditional create behavior
under this caller, missing-object HEAD behavior, denied foreign-prefix access and
retained-object deletion denial. Metadata/code tests do not prove those live
rights. The worker does not contact a provider during default preview. Its check
requires the SDK model and delivered fields, without printing values or contacting
Scaleway. Live transfer needs --execute; it never reads Kubernetes Secrets.
The platform documents a 30-day current/noncurrent lifecycle; exact prefix coverage
must be checked before accepting a 14/4 calendar contract. Four weekly selections,
protected recovery points and monthly drills can outlive an age-based expiration.
Do not activate an incompatible lifecycle or promise protected points survive it.
Owner must bind capacity/headroom, local encrypted-copy retention and independent
receipt retention before enabling an unattended timer. No live deletion/lifecycle
change is included in this request.
First run: approved current-image capture, app resumes, age sealing, immutable
conditional completion, version-pinned GET and ciphertext SHA-256 check. Receipt
explicitly says application_restore_verified=false. Recovery on a different
workspace uses the retained receipt with primary_application_archive.py retrieve,
then consumer recover-paired-backup.py and restore-paired-snapshot.py. Retrieve does
only version-pinned GET and needs no original archive. Decryption belongs to the
attended custodian, never the scheduled publisher. Complete rendering and denied
access checks and record full elapsed time before calling this recoverable service.
Provider contract reference: [Scaleway conditional writes](https://www.scaleway.com/en/docs/object-storage/api-cli/using-conditional-writes/).
If-None-Match is applied at multipart completion. The lock pins boto3 1.40.18 and
botocore 1.40.76; the actual botocore service model exposes IfNoneMatch. Synthetic
provider tests cover lost completion response, abort of own pending upload,
changed versions, corrupted readback, scope/hash binding, and node-loss retrieval.
Email-connect has a separate archive profile in the platform helper. That is a
transport capability only: this request neither captures its SQLite ledger nor
admits an email worker. EMAIL-WP-0006 deployment evidence is complete; ongoing
ledger recovery stays tracked by CSOC-WP-0010-T04/T05 and requires a separate
producer/custody/rehearsal contract.

View file

@ -1,73 +0,0 @@
{
"schema": "platform.coulomb-recovery-api.v1",
"project_id": "06d107bc-9d84-4672-b2a0-95701cc0c07f",
"region": "nl-ams",
"custody_owner": "railiance-platform/platform-root",
"public_recipient": "age1qm30jk8q0cezey652dk2dhcr3d9vs528v6lg8mavl6d4q3e3vgnqmrmyy3",
"materials": {
"age": {
"id": "634e4b9f-0b61-4329-a83c-1ed0e76c4636",
"revision": 1,
"name": "coulomb-social-age-recovery"
},
"reader-pair": {
"id": "241742f7-d3b5-4163-8d87-3a3d9585778d",
"revision": 1,
"name": "coulomb-social-recovery-reader-api"
},
"custodian-pair": {
"id": "cf44e9bd-9edc-450b-9f10-564ee3008305",
"revision": 1,
"name": "coulomb-social-recovery-custodian-api"
}
},
"applications": {
"custodian": "f80037a2-87c9-44d2-9163-3e6907af3ced",
"reader": "7a4055fc-a3f5-40bf-8013-cd0eec9028de"
},
"routine_key_expirations": {
"reader": "2027-10-28T19:24:59.647270Z",
"custodian": "2027-10-28T19:25:00.458446Z"
},
"buckets": {
"kits": {
"name": "railiance-coulomb-social-recovery",
"prefix": "kits/",
"automatic_expiry_enabled": false
},
"archives": {
"name": "railiance-coulomb-social-archives",
"prefix": "archives/",
"automatic_expiry_enabled": false
}
},
"native_application_recovery_verified": true,
"protected_recovered_point": {
"captured_at": "2026-10-31T07:13:52Z",
"primary_object_key": "platform-pg/application-archives/coulomb-social/2026/10/31/20261031T071352Z-abf5997f01a14d9cb05e4bba.tar.age",
"primary_version_id": "1793430889823443",
"ciphertext_sha256": "840e633047fddd88c6e6a1a3d6909d9af1ad80eaba8c90f83d8e81188dfc17a4",
"protected_kit": {
"schema": "platform.coulomb-protected-recovery-kit.v1",
"project_id": "06d107bc-9d84-4672-b2a0-95701cc0c07f",
"bucket": "railiance-coulomb-social-recovery",
"object_key": "kits/4166eb8ba97f7bd79e912c2efaee2622fcd472b05ed80070ccf7e6728fec5bd6/recovery-kit.tar",
"version_id": "1793432303941465",
"sha256": "4166eb8ba97f7bd79e912c2efaee2622fcd472b05ed80070ccf7e6728fec5bd6",
"bytes": 276019200,
"automatic_expiry": false
},
"recovery_seconds": 46.692,
"proof": "docs/evidence/2026-10-31-coulomb-provider-kit-post-date-recovery.json",
"protected_recovery_proof": {
"project_id": "06d107bc-9d84-4672-b2a0-95701cc0c07f",
"bucket": "railiance-coulomb-social-recovery",
"object_key": "kits/receipts/b93e3003627581eb510086ba207bf283085d88cb9b3dfd0e74ffa8ebf02c39f0/recovery-proof.json",
"version_id": "1793432409572654",
"sha256": "b93e3003627581eb510086ba207bf283085d88cb9b3dfd0e74ffa8ebf02c39f0",
"bytes": 4365,
"independent_readback_verified": true,
"automatic_expiry": false
}
}
}

View file

@ -1,84 +0,0 @@
# Coulomb recovery API custody
Owner: railiance-platform / platform-root. Admission decisions
27bbc1ac-e275-42be-ba34-6cf16eae4e78 and 6139e02b-3fb7-4eff-be8a-efe127c45ce5.
Non-secret coordinates: coulomb-recovery-api.json. Native evidence:
../evidence/2026-10-29-coulomb-recovery-api-bootstrap.json.
Routine access uses separate reader/custodian API pairs under an owner-private
directory outside Git, normally ~/.config/railiance-platform/coulomb-social-recovery/.
The worker holds only its original publisher pair and the dedicated public age
recipient. Never deliver the custodian pair, reader pair or private age identity
to that worker. No sibling credentials or private keys are reused.
Check independent retrieval without writing a private identity:
```sh
python3 scripts/application_recovery_access.py --execute --material age
```
For an isolated attended restore, add --output with a fresh absolute filename
under an existing owner-private directory outside Git. The helper validates exact
project/name/protection/revision/nonexpiry and public recipient before writing
mode0600. Materials reader-pair and custodian-pair use the same front door;
omit --output to verify retrieval without creating another secret copy.
No value goes to stdout, Git, State Hub, shell arguments or environment.
The reader performs only prefix/version reads and listing in the isolated project.
The custodian can also publish new kit versions and read the dedicated escrow;
neither can delete retained versions or administer buckets/IAM. Scaleway's current
2023-04-17 bucket policies intersect IAM with an explicit allowlist. Deletion is
absent while publisher multipart abort is included; native checks prove both.
Negative PUT probes use separate transports because a denied upload can leave
the provider connection unusable for its next request.
Both buckets are private and versioned; no automatic expiry has been enabled.
The protected bucket never participates in rolling expiry. First coherent app
capture, independent exact-version decryption and isolated application recovery
are now verified below. Rolling retention, capacity, copy disposition, monitoring
and timers remain WP-0053/CSOC-WP-0012 acceptance work. The earlier synthetic
fixtures remain boundary evidence, distinct from this real application backup.
The routine pairs and age identity each have a separate protected, enabled,
nonexpiring provider secret version. They survive node/workstation loss. If all
local routine credentials are lost, platform-root uses its existing independent
account/MFA recovery to retrieve only the custodian escrow coordinate in the
public contract, then returns to the API front door. Emergency owner recovery is
separate from routine drill access; preserve the platform-root recovery mechanism.
Routine API keys expire on 2027-10-28; exact timestamps are in the JSON contract.
Platform-root owns renewal and independently read-back escrow before those
deadlines, using a bounded attended construction window. No standing organization
administration key or publisher IAM administration is admitted. The construction
key was revoked, its temporary pair removed and the personal browser context
closed without saving state. Routine drills do not rerun identity bootstrap.
The first provider-only kit recovery is now verified in30.232s monotonic. The
public JSON contract records the complete protected kit, exact primary recovery
point and separately protected recovery-proof coordinates. Native receipt:
../evidence/2026-10-29-coulomb-provider-kit-recovery.json. All2098kit members,
13tables/112rows/sequences,82files and39rendered pages/access denials pass.
Images/tools/data came from the kit; the age identity came from separate escrow.
Held API callers and the existing Docker daemon were used. Account-recovery
latency, a fresh daemon and real-browser identity journeys are excluded.
application_recovery_drill.py previews without effects and executes only with
--execute, --kit-reference and a fresh --destination under an owner-private
directory outside Git. It never restores production. Retention/copy disposition,
monitoring and scheduled activation remain open; this receipt closes only the
consumer's first-paired-recovery task.
2026-10-31 corrected-date protected recovery refresh: the 07:13:52Z point is now
independently retained in kit4166eb8b/version1793432303941465 (276,019,200bytes)
and recovered using only the provider kit plus separate age escrow. All2098members,
13tables/114rows/sequences,86files and40owner renders/40outsider denials pass in
46.692s. The matching captured image is51281946; this point predates the revision19
image update but includes the verified source-date correction. Current image522bcd7f
has separate same-store export/view proof. The successful recovery proof is itself
protected and independently read back at version1793432409572654. The fresh drill's
temporary identity file was removed; pre-existing custody and historical copies
remain untouched. No production restore, new grants, expiry or timer activation.
Public API coordinates now identify this verified point; the former pointer still
named October29 and was stale. Historical kits/proofs remain retained. Receipt:
docs/evidence/2026-10-31-coulomb-provider-kit-post-date-recovery.json. Existing
retention/capacity/monitoring/copy-disposition obligations remain open.

View file

@ -1,205 +0,0 @@
# Attended native signer custody adapter source
RPF-WP-0035-T12 / CUST-WP-0080. This source implements the protected native
adapter described by the separate frozen native-preparation R1. It does not
admit an execution. Its CLI is silent and intended only as the exact reviewed
child of `warden access openbao-platform-admin-login --exec` in the founder's
attended OIDC envelope. The existing Warden owner contains the token helper,
checks child output and revokes/removes the session on exit. No new service,
credential broker, standing grant, API or key-returning interface is created.
## Current routing blocker
During preparation the required `warden plan` query for attended OpenBao
platform administration of signer CAS0/metadata selected `openbao-structure-survey`
with an approve ceremony, rather than `openbao-platform-admin-login` with
`oidc_login`. The installed CLI reports wheel-bundled catalog0.1.2 fallback.
This is preserved as a live refusal, not routed around. Exact `route show` names
the existing active attended login, but that pointer does not cure dispatch.
The adapter requires an independently retained routing receipt selecting the
correct lane/verdict/identity act. No founder execution step is ready until the
routing defect and all other native approvals are resolved by their owners.
## Independent input and authority boundary
The CLI requires an absolute owner-private admission file, its independently
approved exact SHA256, the exact private receipt directory selected in the
reviewed native proposal, and an original receipt only for recovery. No default
image/package/caller trust, founder identity, approval, route or source pin is
usable. The admission schema is closed in `validate_admission`; it includes the
immutable published producer image, retained caller, deployment projection,
selected consumer trust baseline, exact native plan/CCR/source bundle, fresh
UTC authority expiry (5..120seconds), founder entity and five distinct externally
reviewed owner dispositions. Each disposition binds the same approval-free
scope digest. An admission shape, local digest or review string never approves
itself: the founder and owners must independently review the exact supplied
command/file hash through existing governance before attended invocation.
The trust hash selects the independently reviewed consumer verifier/policy/trust
baseline for this custody action. It is not a claim that the newly generated
public key is already online: generation happens later, and independent delivery
and successor trust installation are required after the public receipt. The
writer never reads consumer private credentials or discovers trust from KV.
All successful custody states remain pending independent public admission.
`checked_sources` verifies the actual selected native action plan, exact proposed
kid, read CCR hash/status/binding, generated policy source, and source bundle.
The read CCR must independently be approved/binding-confirmed; the canonical
proposed CCR remains unapproved, so ordinary source checkout refuses. Source
bundle pins execution modules, not arbitrary request-selected code. The native
image/caller/seed projection is compared to the actual stopped Deployment.
The lifecycle policy digest/version stays unchanged; execute-ccr probes must
prove no ALLOW/effect. Current generic runtime guard can return HTTP503 before
the lifecycle evaluator, so signed unknown_action is not promised.
## Actual checks and effects
Before private work, the child checks OpenBao cluster ID, unsealed state, exact
non-root platform-admin OIDC entity, enabled audit, actual exact read-policy HCL
and role bindings/15m limits. The selected fixed loopback transport is the existing
owner-admin bridge; Kubernetes reads use fixed strict-host SSH to railiance01.
The OpenBao token lookup-self identifier stays only in the contained custody
process to authenticate fixed HTTP calls; it never reaches the agent, a receipt,
argv or another token file. This is a native protected-process implementation,
not evidence that agent runtime isolation has been promoted or granted.
The fence checks the pinned kube-system UID, stopped Deployment generation and
replica status, exact immutable image/caller/seed mount/kid projection, zero owned
ReplicaSet replicas, no live or terminating signer-SA/owned/seed-mount pods, and
no targeting HPA. It rechecks before metadata setup and immediately before CAS.
A private filesystem lock excludes a second local custody window. The lock and
observations do not provide atomic Kubernetes fencing: the independently reviewed
exclusive window must keep GitOps/Helm/other creators stopped. No rollout or
cluster mutation is performed by this adapter, and no concurrency guarantee is
inferred from source alone. Access/cluster owners retain that admission.
Bootstrap requires the exact OpenBao empty-errors HTTP404 metadata absence. A
durable private intent precedes exact CAS-required/max_versions2/no-auto-delete
metadata setup, followed by exact native readback. Only then does the shared
Ed25519 primitive generate one in-memory seed and CAS0 the fixed three-field
KV document. Independent version1 private readback and final metadata verification
precede the durable success receipt. The seed is never put in argv/env/local
files or output. The receipt carries public key, ASCII-base64url fingerprint,
version, bindings and closed phases only. The old fixture's live-dev-process gate
is preserved; its shared receipt initialization was extracted without weakening
that gate. Python cannot promise seed zeroization.
Every backend/body/close and tool call has a two-second absolute budget and
1MiB combined output bound. The complete operation is bounded by the supplied
fresh expiry. CLI validation and runtime failures remain silent. Existing0600/
0700 no-follow/ownership checks, exclusive creation, fsync/atomic replacement and
directory fsync retain evidence. Same-UID hostile code is not isolated by modes;
Warden's contained home, trusted runtime and independently reviewed custody
location are required, not inferred from approval frequency or shell prefixes.
## Process loss and independent read-only recovery
Any failed/ambiguous metadata/write/readback/final receipt outcome remains
unconfirmed. A success receipt is withheld until final metadata verification.
A child death immediately after the real native CAS leaves the earlier public
intent with its derived public key and no private value. A fresh `recover`
admission must bind that exact original receipt SHA and the same immutable scope.
It reserves a new durable recovery intent before private reads, verifies current
version1 metadata and exact private seed/public/kid correspondence, and records
separate public reconciliation. It never creates a writer, generates a seed,
POSTs metadata/data, restores a seed, consumes approval or dispatches business
operations. Original unknown evidence is never rewritten. Changed versions,
damaged/typed-invalid receipts, missing fresh reviews and denied reads refuse;
replaying a recovery ID cannot repeat even the private backend read.
This source implements bootstrap and read-only version1 reconciliation only.
Rotation needs a separately reviewed exact-version native action; the disposable
primitive's CAS1 test is not a native rotation grant. No automatic retry, remint,
rollback or general recovery API exists. Metadata-only setup uncertainty without
a public seed intent requires separate attended reconciliation, not this path.
Access observed zero PVCs in flex-auth. Its proposed new1Gi/local-path outbox,
PV Retain/reclaim disposition and runtime process-lock/capacity/startup bounds
remain separate reviews. This writer grants no PVC, audit egress, policy role,
SecretStore/Secret or producer/consumer deployment admission.
## R2 phase-aware recovery and source lineage
Frozen R1's metadata-only crash counterexample remains valid historical evidence.
R2 accepts a strict original metadata-setup receipt with no public key, or a
seed-cas receipt with a canonical derived public key. Fresh recovery reserves
durable evidence before target reads. Exact current metadata absence AND exact
version1 data absence yields `current-custody-absent`. Exact configured metadata
with current_version0 AND exact version1 data absence yields
`metadata-only-confirmed`. Both results have no public key/fingerprint/KV version,
key_custody_verified=false, historical_effect=unresolved, original_unknown_preserved,
and an explicit fresh independently reviewed action requirement. A locally
generated but unstored seed's public key is not promoted into public admission.
Neither observation means the original attempt historically made no effects.
Denied/malformed/misleading404 metadata or data, timeout, changed configuration,
unexpected versions, or version1 after a metadata-only/no-public phase keep the
new recovery attempt unconfirmed. They never grant generation, write, retry,
remint, consume or business authority. A subsequent metadata-only bootstrap is
not silently enabled: existing metadata still refuses CAS0 bootstrap admission.
A distinct exact reviewed action/source revision must choose any cleanup/reuse
or generation step. The original receipt remains byte-for-byte unchanged.
Native plan bootstrap.source_primitive_sha256 now equals the actual shared
primitive in the admitted source bundle and writer receipt; checked_sources
enforces the join. The prior8ed pin is explicitly historical. The plan pins the
shared primitive, not itself or the complete native executor, so no self-hash
cycle exists. The exact native executor is independently pinned by source_bundle
and its digest in bindings. A recover admission must additionally supply
original_execution (original receipt bindings, writer SHA and CCR SHA). Fresh
owner reviews bind both original and current lineages. This can describe an older
executed source/proposal without labeling it current. Image/deployment/policy/
consumer trust bindings must still match; recovery cannot change that authority.
## Exact120-second expiry and attended scheduling assessment
The unchanged validate_admission rule requires an absolute UTC expires_at with
5 < expires_at-now <=120seconds, and the entire child runs beneath that fixed
remaining absolute/monotonic budget. The five external reviews cover the scope
digest including expires_at. No deadline is generated, refreshed or widened by
the child; rechecking cannot extend it.
Existing ops-warden proxy.proxy_attended_login_exec runs the OIDC client before the child.
Its _contained_run captures output through subprocess.run without a timeout.
Therefore neither OIDC/MFA nor five external reviews have a demonstrated bound
that fits120seconds from preparing an admission. Creating a now+120seconds
admission before unbounded review/login is not a reliable operational ceremony.
Source success tests start from synthetic ready identities; they prove no human
OIDC/review timing SLO.
A coherent ceremony under the current rule is to pre-review one exact future
absolute expiry/window, including all artifact/source/image/trust/identity and
review references, then arrange attended login so the child starts within its
implicit [expires_at-120seconds, expires_at-5seconds) interval. Review may occur
before that interval; it does not change runtime authority. The founder must aim
for the beginning of the interval, not its last five seconds. The fixed path has
26 bounded native/tool requests before success (52seconds at their2second caps),
plus key/source/receipt work. Its expiry still wins if those operations cannot
finish; no completion or filesystem timing guarantee is inferred.
If login completes too early/late, a review changes any bytes, or adequate time
remains uncertain, refuse this revision. A new expires_at changes the approved
scope digest and needs explicit dispositions for the new revision; existing
source-only reviews cannot be copied into native approval. Do not silently
refresh after OIDC, reinterpret expiry as start-only, widen120seconds, skip
reviews or start a general window-grant service. An alternative authority/window
contract would itself require separate owner/founder design and is not supplied
by R2. Current native admission remains unapproved.
## R3 precise owner review boundary
The native review slot is now `cluster-runtime-storage-owner`, held by
railiance-cluster for runtime/storage/headroom only. The previous
`cluster-gitops-owner` slot conflated separate authorities and is rejected;
no historic review is reinterpreted as a combined assent. Platform's existing
review covers its custody/read lane and existing GitOps Applications/freeze
integration. Access retains workload/release/Helm-values and signer/outbox
ownership. Enablement review is needed only when the action actually changes
ArgoCD installation/controller configuration; this action does not authorize
that. Changed scope requires a separate exact revision and any affected owner.
This is a source-schema correction, not a new approval. Existing R1/R2 source
reviews and P1/P2 packets remain frozen. A successor packet must pin the new
executor/source-bundle/plan/CCR hashes and collect five exact fresh dispositions
under the corrected role set. Admission-file digest approval and CCR
approved/binding-confirmed status/native checks remain unchanged.

View file

@ -1,158 +0,0 @@
{
"schema": "railiance-platform.signer-native-action-proposal.v1",
"proposal_id": "RPF35-PDP-SIGNER-NATIVE-20261016-R1",
"status": "prepared-unapproved",
"read_ccr": "CCR-2026-0050",
"proposal_scope": "attended bootstrap of retained lifecycle PDP seed; no taskpilot enablement, rotation requires a new exact-version action revision",
"actor": "founder attended OIDC via warden openbao-platform-admin-login; reviewed silent Platform custody child",
"executor_ownership": "railiance-platform; founder executes exact child; agent receives closed metadata only",
"abort_owner": "founder; Platform preserves unknown custody/evidence, Access keeps signer/consumers fenced",
"target": {
"cluster": "primary/railiance01",
"kube_system_uid": "a553c742-0115-43d4-99a4-a5ca56fe0786",
"openbao_cluster_id": "fd28df5d-98ec-57dd-42ec-9b3e4f4e53bf",
"openbao_origin": "http://127.0.0.1:18200",
"namespace": "flex-auth",
"deployment": "flex-auth-secrets-engine",
"signer_sa": "flex-auth-secrets-engine"
},
"credential_route": "openbao-platform-admin-login; flex-auth-decision-envelope-signing-key remains draft/nonresolvable; route is not a seed vending service",
"bootstrap": {
"action": "CAS0 fresh in-memory Ed25519 seed",
"kid": "flex-auth-lifecycle-20261016-r1",
"expected_version": 0,
"data_path": "platform/data/workloads/flex-auth/envelope-signing",
"document_fields": [
"ED25519_PRIVATE_KEY",
"ED25519_PUBLIC_KEY",
"kid"
],
"metadata_path": "platform/metadata/workloads/flex-auth/envelope-signing",
"metadata_contract": {
"cas_required": true,
"max_versions": 2,
"delete_version_after": "0s"
},
"allowed_native_methods": [
{
"method": "POST",
"path": "platform/metadata/workloads/flex-auth/envelope-signing",
"purpose": "CAS-required/version-retention configuration after absence/no-drift validation"
},
{
"method": "GET",
"path": "platform/metadata/workloads/flex-auth/envelope-signing",
"purpose": "exact metadata readback, no private value output"
},
{
"method": "POST",
"path": "platform/data/workloads/flex-auth/envelope-signing",
"purpose": "single CAS0 seed document write"
},
{
"method": "GET",
"path": "platform/data/workloads/flex-auth/envelope-signing?version=1",
"purpose": "exact private readback in custody, closed public receipt only"
}
],
"source_primitive_sha256": "sha256:f169ad12f90d729b647085c3bed2c0d8d005db23a2341b6d3d5f1793bcfd110c",
"historical_source_primitive_sha256": "sha256:8ed281a49c1f1cdb23772efe3593e75c62572a12b8d7a6a4c574f27b38722e75",
"source_lineage_rule": "source_primitive_sha256 must equal current admitted source_bundle[scripts/flex_auth_signer_custody.py] and exact writer receipt; historical_source_primitive_sha256 is non-authoritative R1 evidence only"
},
"fencing": {
"before_key_generation": [
"Verify cluster/backend IDs, exact current source/CCR and founder disposition hashes.",
"Read exact deployment/ReplicaSets/pods by ownerReference and SA; refuse a second workload/pod using signer SA or selecting signer Secret.",
"Access stops protected consumers and scales retained deployment0 through its reviewed release path; this child does not mutate replicas.",
"Verify deployment spec replicas0, observed generation caught up, no live or terminating owned ReplicaSet/pods, no matching signer-SA pods and no HPA targeting deployment.",
"Reject a differing/previously existing KV entry at bootstrap. Hold exclusive owner action receipt lock and recheck fence immediately before CAS."
],
"after_cas": "Keep consumers/signer stopped until independent public admission and exact ESO/readback/source/image verification. Access published source Git revision 84c78e072c8f3daf2bc3b90f8efc8fbeb16c3cf0 with OS process lock,64MiB capacity/startup bounds and ordinary CI402 success, immutable OCI reference forgejo.coulomb.social/coulomb/flex-auth@sha256:09a83b273740133518cc7e7915fb211381e06545d347d2dd1974042525ac981f. This supplies reviewed-source/publication evidence, not native mounted-runtime/durable-record acceptance. Access owns Recreate1 activation; actual stopped fence, single writer, PV retention and protected consumer acceptance remain open.",
"concurrency": "Single attended owner window/exclusive receipt; Access owns workload/Helm release and freeze of competing workload writers; Platform owns existing GitOps Applications and sync/freeze integration; cluster owner reviews runtime/storage/headroom only. Enablement reviews actual ArgoCD installation/controller configuration changes only, which this action excludes. Actual competing creator IDs/revisions and stopped signer fence need separately reviewed authenticated observations. Plan is not a Kubernetes atomic fence or same-UID isolation."
},
"protected_adapter": {
"state": "Native adapter bootstrap/phase-aware read-only recovery source implemented and tested at published Platform a3e8eb64bae110393ba0aeab043de03a92b8421a; owner-boundary source R3/R4 is a successor exact review revision, not a native execution approval. Exact source/CCR, distinct owner/founder dispositions, entity/window/expiry and actual backend/fence remain mandatory. Fixture dev-process gate remains preserved.",
"reuse": [
"scripts/state_hub_preflight_lane.py attended silent child/cluster/platform-admin/fencing patterns",
"scripts/flex_auth_signer_custody.py protected Ed25519 CAS/readback and receipt state machine",
"scripts/task_execution_native_fixture.py absolute main-thread2s I/O budget",
"scripts/task_execution_recovery.py strict bounded JSON/no-follow/private custody patterns"
],
"requirements": [
"New fixed native adapter consumes exact approved CCR/source/native-action digest; cannot accept arbitrary URL/path/command/payload/seed.",
"Read OIDC token only within warden child from attended context; require platform-admin and reject root; never print/store bearer. No credential comes back to agent.",
"Pin exact kube-system/OpenBao cluster IDs before private calls, refuse ambient address override; enforce2s absolute reads and1MiB cap; no redirect/shell/tracing.",
"Verify metadata absence/exact admitted values before CAS-required change; any differing existing object refuses. Exact response contracts and duplicate/nonfinite JSON rejection.",
"Private seed is generated once, never imported or persisted outside OpenBao; no subprocess argv/env seed; readback compares exact version/fields and derived public correspondence.",
"Receipt intent is durable before native effect; final success requires exact KV/metadata correspondence and receipt fsync. Process loss/error remains unknown without remint/retry."
]
},
"receipts": {
"directory": "/home/worsch/.local/state/railiance-platform/flex-auth-envelope-signing",
"mode_directory": "0700",
"mode_file": "0600",
"native_execution_location": "founder workstation inside reviewed warden child; no agent-owned shared /tmp custody",
"basename": "RPF35-PDP-SIGNER-NATIVE-20261016-R1.json",
"maximum_bytes": 65536,
"atomic": "exclusive initial receipt, fsync-before-effect, atomic replacement and directory fsync, no-follow/owner/mode/nlink validation",
"public_fields": [
"proposal/executed source/CCR/identity/cluster/image/policy/trust digests",
"actor/time",
"closed phase/status",
"exact KV path/version/kid",
"public key and explicitly encoded fingerprint",
"fence/metadata/readback checks",
"rollback or unknown recovery disposition"
],
"fingerprint": "sha256: lowercase SHA256 of canonical unpadded base64url public-key ASCII, exactly43bytes; independent Secrets owner agreement before admission",
"delivery": "Founder/Platform separately authenticate metadata receipt to Secrets owner; public receipt shape/file does not create consumer trust; raw private receipt directory is not a shared agent intake"
},
"rollback": {
"before_cas": "Abort without private write; retain receipt/fence; restore prior Access image/values only via reviewed Access action.",
"after_cas_unknown": "Retain unknown and fence; independently attended owner reconciles exact metadata/version/public correspondence. Never rerun CAS0 or restore prior seed automatically.",
"compromise": "Remove online old public key immediately; stop signer, independent new reviewed forward-only action; old private seed never restored."
},
"requested_scope": {
"read_lane": "Exact CCR0050 policy/role/delivery source only after separate founder review. No extra ESO RBAC permitted.",
"custody_writer": "One attended exact-target CAS0 plus fixed KV metadata setup/readback; no standing writer grant or broker role. This authority is separate from read CCR.",
"kubernetes": "Metadata-only fence observations by child; Access separately reviews workload/Helm stop/restart/delivery; Platform owns GitOps Application sync/freeze integration; cluster owner reviews runtime/storage/headroom/PV retention; no ArgoCD installation/controller configuration mutation included.",
"new_infrastructure_spend_eur": 0,
"new_resources": "Platform creates no storage resources. Access proposes separate exact new flex-auth-secrets-engine-outbox1Gi local-path Helmkeep. Actual provisioning/PV identity, StorageClass Delete/WaitForFirstConsumer to reviewed PV Retain disposition and native bounded-runtime evidence remain open. Source lock/64MiB/startup bounds are published in candidate84, not evidence of native mounted-runtime acceptance.",
"risk_bound": "Controlled loss of retained PDP availability during explicit attended fence; seed compromise can forge lifecycle decisions until independent public trust withdrawal; founder must approve this concrete high-risk scope."
},
"reviews_required": [
{
"role": "railiance-platform-owner",
"disposition": "pending exact source/artifact review; no approval recorded"
},
{
"role": "access-engine-owner",
"disposition": "pending exact source/artifact review; no approval recorded"
},
{
"role": "secrets-engine-owner",
"disposition": "pending exact source/artifact review; no approval recorded"
},
{
"role": "cluster-runtime-storage-owner",
"disposition": "pending exact source/artifact review; no approval recorded"
},
{
"role": "founder",
"disposition": "pending exact source/artifact review; no approval recorded"
}
],
"authority_granted": false,
"candidate_delivery_amendments": {
"supersedes_unadmitted_R3_role": "external-secrets-flex-auth-envelope-signing replaces flex-auth-envelope-signing-eso to fit existing delegated-applier role convention; dedicated SA unchanged",
"read_ccr_field_projection": "Only ED25519_PRIVATE_KEY is delivered; public sibling fields remain visible to dedicated document-reader identity but never become consumer trust discovery",
"projection_source": "docs/credential-lane-designs/flex-auth-envelope-signing-native-projection.yaml.proposed",
"managed_declaration": "Canonical local rapp-flex-auth-secrets-engine declaration committed d52f811ed5ec0be27c9c367e71d476125b3228be, exact13file tree d4fbad71bdc8ef870603526ccd1f53195d0322af; tuple rapp-flex-auth-secrets-engine/flex-auth-secrets-engine/flex-auth-secrets-engine resolves locally as draft. Frozen source2b/current05a package baseline does not adopt candidate84/signing. Remote publication/placement and actual runtime/package join remain open. No missing-declaration claim or migration-debt waiver is applicable to the established local declaration."
},
"owner_review_scope": {
"railiance-platform-owner": "OpenBao custody/read lane; existing GitOps Applications and their sync/freeze/controller ownership; covers explicitly identified workload/Helm competing-writer integration with Access, not Access workload semantic ownership.",
"access-engine-owner": "Retained workload/release source, Helm values, candidate image, signer delivery/mount and process/outbox fencing/rollback.",
"cluster-runtime-storage-owner": "railiance-cluster: cluster identity/runtime/storage/headroom, local-path provisioning and actual PV retention; excludes workload and GitOps Application ownership.",
"enablement_condition": "railiance-enablement additional exact review required only if proposed action changes ArgoCD installation/controller configuration; this fixed custody action authorizes no such change. Changed scope needs a separately reviewed exact revision rather than blanket extra reviewer."
}
}

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