Compare commits
No commits in common. "main" and "csoc-authelia-mail-custody" have entirely different histories.
main
...
csoc-authe
681 changed files with 933 additions and 77754 deletions
|
|
@ -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
136
.custodian-brief.md
Normal 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.
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
{"version":1,"repo_slug":"railiance-platform"}
|
||||
|
|
@ -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
4
.gitignore
vendored
|
|
@ -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
133
Makefile
|
|
@ -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
|
||||
|
|
|
|||
45
SCOPE.md
45
SCOPE.md
|
|
@ -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.
|
||||
|
|
|
|||
137
WORK-RECORDS.md
137
WORK-RECORDS.md
|
|
@ -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 |
|
||||
|
|
|
|||
35
argocd/platform-addons/bao-notice/default.conf
Normal file
35
argocd/platform-addons/bao-notice/default.conf
Normal 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;
|
||||
}
|
||||
}
|
||||
19
argocd/platform-addons/bao-notice/index.html
Normal file
19
argocd/platform-addons/bao-notice/index.html
Normal 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>
|
||||
18
argocd/platform-addons/bao-notice/kustomization.yaml
Normal file
18
argocd/platform-addons/bao-notice/kustomization.yaml
Normal 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]
|
||||
177
argocd/platform-addons/bao-notice/manifests.yaml
Normal file
177
argocd/platform-addons/bao-notice/manifests.yaml
Normal 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}}
|
||||
|
|
@ -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"]
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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"}
|
||||
]
|
||||
}
|
||||
|
|
@ -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]))"
|
||||
}
|
||||
}
|
||||
|
|
@ -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())
|
||||
|
|
@ -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()
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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"]
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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: "*"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBeDCCAR2gAwIBAgIBADAKBggqhkjOPQQDAjAjMSEwHwYDVQQDDBhrM3Mtc2Vy
|
||||
dmVyLWNhQDE3NzMxMDM2MjAwHhcNMjYwMzEwMDA0NzAwWhcNMzYwMzA3MDA0NzAw
|
||||
WjAjMSEwHwYDVQQDDBhrM3Mtc2VydmVyLWNhQDE3NzMxMDM2MjAwWTATBgcqhkjO
|
||||
PQIBBggqhkjOPQMBBwNCAAT44CzyUUGic6xYSLOApGq8A61OcoP/v+nns47p3F1z
|
||||
M89iyNSZqtIHRpg0BxiMtTcdwXKart6aPIC8/8PfpVyAo0IwQDAOBgNVHQ8BAf8E
|
||||
BAMCAqQwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUG/vhGaKEp4uD2KaSPDTU
|
||||
249sq2EwCgYIKoZIzj0EAwIDSQAwRgIhALUc7a0LcGfMiAI+auFTByXlL4hTH0Cr
|
||||
DxsOiRhV9D0vAiEAjTVKYP/KFPe68cAL8S76mLKdKhd564XiVAeISh5NkG4=
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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."
|
||||
}
|
||||
}
|
||||
|
|
@ -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."
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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'
|
||||
|
|
@ -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).'
|
||||
|
|
@ -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).'
|
||||
|
|
@ -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).'
|
||||
|
|
@ -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).'
|
||||
|
|
@ -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.
|
||||
|
|
@ -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).'
|
||||
|
|
@ -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).'
|
||||
|
|
@ -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).'
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -25,11 +25,5 @@
|
|||
"synthetic_load": {
|
||||
"contract_id": null,
|
||||
"driver_revision": null
|
||||
},
|
||||
"synthetic_sender": {
|
||||
"identity": null,
|
||||
"source": null,
|
||||
"tenant": null,
|
||||
"expires_at": null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue