Consolidate platform workplans and assess intent gaps

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a06ecb-456a-71c2-b41e-0755d336e883
This commit is contained in:
codex 2026-09-05 11:14:42 +02:00
parent 9d958f8e09
commit 9f83e426c7
40 changed files with 2985 additions and 295 deletions

View file

@ -0,0 +1,50 @@
---
id: RPF-WP-ADHOC-2026-08-23
type: workplan
title: "Repair apps-pg tenancy evidence declaration"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-08-23"
updated: "2026-08-23"
state_hub_workstream_id: "50140ec5-5e07-5347-8e46-c1bca675a6e4"
---
# RPF-WP-ADHOC-2026-08-23 — apps-pg tenancy evidence declaration
## Attach evidence to the current apps-pg posture
```task
id: RPF-WP-ADHOC-2026-08-23-T01
status: done
priority: high
state_hub_task_id: "0b40ab2a-989b-5be1-b9bc-6048d1464004"
```
Added the existing authoritative backup/restore artifact to the top-level
`evidence` map for the current `apps-pg` R2 and V1 claims. The provider-axis
evidence remains in place because it supports the separate capability offer;
the new entries satisfy the current-posture evidence contract directly.
Verification runs the canonical NetKingdom tenancy-posture validator against
this repository's declaration and checks the repository diff.
## Stabilize expired-engagement broker readiness tests
```task
id: RPF-WP-ADHOC-2026-08-23-T02
status: done
priority: medium
state_hub_task_id: "dfa696e9-4c8e-5e5a-acb8-cdcbbcf46ec8"
```
Full test discovery exposed two WP0025 broker-readiness tests that built a
receipt with wall-clock time for a terminal prior-day engagement. Add an
optional explicit clock to the receipt builders and pin the unit fixtures to
the contract window so the production default stays current UTC while offline
tests remain deterministic.
The complete 146-test offline suite now passes on dates after the terminal
engagement window.

View file

@ -0,0 +1,481 @@
---
id: RPF-WP-0001
type: workplan
title: "Credential Request and Lease Broker"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
planning_priority: high
planning_order: 5
created: "2026-06-24"
updated: "2026-07-02"
depends_on_workplans:
- RAIL-PL-WP-0002
state_hub_workstream_id: "bcc11cf9-d008-5e42-8fff-e2b33a1d0ff2"
---
# RPF-WP-0001 - Credential Request and Lease Broker
## Goal
Provide a clean, secure, low-friction way for operators, agents, and approved
automations to request, generate, receive, use, renew, and revoke short-lived
credentials such as the OpenBao token needed for ops-warden vault-backed SSH
signing smoke.
The target experience is self-service for routine, policy-approved leases and
explicit human approval for high-risk grants, without ever pasting secret values
into Git, State Hub, chat, prompts, workplans, or shell history.
## Repository Decision
The primary owner is railiance-platform because OpenBao is the canonical
runtime secret custody service and this repo owns platform secrets, identity
integration, and shared credential delivery contracts.
Cross-repo responsibilities:
| Concern | Owner | Boundary |
| --- | --- | --- |
| OpenBao policies, token roles, lease broker, audit | railiance-platform | Owns secret custody and credential generation. |
| Login, OIDC, MFA, IAM profile claims | key-cape | Authenticates humans and service identities. |
| Authorization decision for requested grants | flex-auth | May decide whether actor X may request grant Y for purpose Z. |
| SSH certificate signing | ops-warden | Issues SSH certs only; does not vend OpenBao tokens. |
| Request tracking and progress | state-hub | Stores non-secret request metadata, status, decision ids, and audit pointers only. |
| Agent inference/runtime | llm-connect and callers | Never place secrets in prompts; consume via local env injection or wrapped lease handles. |
This work should update the ops-warden routing catalog when complete, but the
implementation belongs here. If the broker later becomes a general NetKingdom
service, code can split to a dedicated credential-broker repo while OpenBao
policies and grants remain owned by railiance-platform.
## Design Principles
- Prefer dynamic or short-lived leases over static secrets.
- Use response wrapping or local exec-time injection; do not print raw tokens by default.
- Store non-secret lease metadata only: actor, grant, TTL, purpose, lease id or accessor, decision id, timestamps, and revocation state.
- Keep OpenBao audit logs as the source of truth for secret access.
- Make the common path easy: one command to run a task with the right credential.
- Keep high-risk paths explicit: human approval and MFA for elevated grants.
- Every grant has a catalog entry, max TTL, allowed actors/subjects, delivery mode, audit expectations, and revocation behavior.
## Proposed User Experience
Initial pilot command shapes:
credential request vault-token --grant ops-warden/warden-sign --purpose flex-auth-openbao-smoke --ttl 15m
credential exec --grant ops-warden/warden-sign --ttl 15m -- SMOKE_VAULT=1 /home/worsch/ops-warden/scripts/policy_gate_production_smoke.sh
credential status <lease-handle>
credential revoke <lease-handle>
For the ops-warden smoke, the preferred path is credential exec. It obtains a
bounded OpenBao token with the warden-sign policy, injects it as VAULT_TOKEN
only into the child process environment, redacts logs, and revokes or lets the
lease expire after the command finishes.
## Threat Model Summary
Primary risks:
- token leakage through shell history, logs, prompts, chat, State Hub, or Git;
- confused-deputy issuance where an agent requests a broader token than needed;
- stale leases surviving after a task completes;
- bypassing KeyCape identity or flex-auth authorization checks;
- replacing one manual secret-handling ritual with another brittle one.
Mitigations required by this workplan:
- no raw token in command-line arguments, State Hub payloads, workplans, or logs;
- bounded OpenBao token roles and policies;
- response wrapping for copy/paste or remote handoff flows;
- exec-time environment injection for local command execution;
- default TTLs measured in minutes, with explicit max TTLs per grant;
- revocation by lease handle/accessor;
- OpenBao audit verification and non-secret State Hub progress events.
## Tasks
## T01 - Record ownership and architecture decision
```task
id: RPF-WP-0001-T01
status: done
priority: high
state_hub_task_id: "19795f6b-bd41-54cb-a782-846814417704"
```
Write an ADR or docs section confirming railiance-platform as the owner for
OpenBao credential request/generation/delivery, with key-cape, flex-auth,
ops-warden, state-hub, and llm-connect boundaries.
Acceptance:
- Docs state that ops-warden routes SSH certs only and must not vend OpenBao tokens.
- Docs state that State Hub stores request metadata only, never secret values.
- Ops-warden credential routing can point OpenBao token requests here.
**2026-06-25:** Added `docs/credential-broker.md` as the ownership and architecture decision. It records that railiance-platform owns OpenBao credential request/generation/delivery, ops-warden owns SSH certificate signing only, State Hub stores non-secret request metadata only, and llm-connect/callers must not place secrets in prompts.
## T02 - Define credential grant catalog
```task
id: RPF-WP-0001-T02
status: done
priority: high
state_hub_task_id: "ce3913d6-ab7d-56e0-a067-7c5b6d4d5850"
```
Add a non-secret grant catalog schema and initial grant entries.
Initial grant:
- id: ops-warden/warden-sign
- credential type: openbao-token
- policies: warden-sign
- default TTL: 15 minutes
- max TTL: 1 hour unless a human approves more
- purpose examples: flex-auth OpenBao smoke, ops-warden production sign smoke
- allowed delivery: exec-env, response-wrap, local-token-file mode 0600
- denied delivery: chat, State Hub body, Git, command-line token argument
Acceptance:
- Catalog can be validated in CI.
- The catalog distinguishes self-service, approval-required, and break-glass grants.
- No grant entry contains a secret.
**2026-06-25:** Added the non-secret grant catalog at `credential-grants/catalog.yaml` with the initial `ops-warden/warden-sign` pilot grant, plus `scripts/credential-grants-validate.py` and `make credential-grants-validate`. The validator enforces required fields, TTL bounds, denied delivery modes, disallowed OpenBao policies, audit/revocation expectations, and secret-looking marker rejection.
## T03 - Configure bounded OpenBao token roles and policies
```task
id: RPF-WP-0001-T03
status: done
priority: high
state_hub_task_id: "98819394-390d-5a1b-8fe6-cf80f471cffc"
```
Create idempotent scripts/manifests for OpenBao token roles or equivalent lease
issuance paths that can generate child tokens only for approved policies and
TTLs. Start with warden-sign.
Acceptance:
- A non-root issuer path can create a warden-sign token with bounded TTL.
- The resulting token cannot administer OpenBao and can only call the SSH sign paths allowed by openbao/policies/warden-sign.hcl.
- Verification proves the token can run ops-warden vault signing and cannot list unrelated secrets.
**2026-06-26:** Added the source-side OpenBao token-grant implementation for
the `ops-warden/warden-sign` pilot: issuer policy
`openbao/policies/credential-broker-warden-sign-issuer.hcl`, idempotent apply
and verify scripts, Make targets for dry-run/live apply/live verification, and
catalog validation for `openbao.issuer_policy`. Dry-run validation is expected
to work offline. Live closure still requires an approved OpenBao operator token
path and successful runs of `make openbao-configure-token-grants` and
`make openbao-verify-token-grants-smoke`, so T03 remains `progress`.
**2026-06-27:** Attempted the live idempotent apply with
`make openbao-configure-token-grants OPENBAO_TOKEN_GRANT_ARGS=--use-token-helper`.
OpenBao was reachable and unsealed, but the pod token helper received
`403 permission denied` while writing
`sys/policies/acl/credential-broker-warden-sign-issuer`. T03 is now `wait`
until an approved OpenBao issuer/platform-admin path applies the policy and
role, or the pod token helper is granted that narrow capability.
**2026-07-01:** Operator unsealed OpenBao. Live apply succeeded with
`OPENBAO_TOKEN_FILE=~/.local/openbao/platform-admin.token make openbao-configure-token-grants`:
`credential-broker-warden-sign-issuer` policy and `warden-sign` token role are
configured. T03 is `done`.
**2026-07-01 follow-up:** Live smoke succeeded with openbao-verify-token-grants-smoke: a child token minted from role warden-sign signed a throwaway SSH public key through ssh/sign/agt-role, was denied policy metadata read, and was revoked by accessor.
## T04 - Build credential helper MVP
```task
id: RPF-WP-0001-T04
status: done
priority: high
state_hub_task_id: "a339fdb2-c593-5b9e-9080-d08b3061dab9"
```
Build a small CLI/helper in this repo first, for example credential or
openbao-lease, with request, exec, status, and revoke commands.
Acceptance:
- credential exec can run the ops-warden production smoke with VAULT_TOKEN only in the child process environment.
- request returns a wrapped token or lease handle by default, not the raw token.
- status and revoke work by non-secret lease handle/accessor.
- The helper redacts token-looking values from logs and refuses to run in verbose modes that would print secrets.
**2026-06-26:** Added `scripts/credential.py` as the source helper MVP with
`request`, `exec`, `status`, and `revoke` subcommands. The helper validates the
grant catalog, enforces purpose and TTL bounds, defaults `request` to a local
mode-0600 token file plus non-secret accessor metadata, supports response-wrap
handoff, injects `VAULT_TOKEN` only into the child process for `exec`, redacts
token-looking child output, rejects caller-supplied token env assignments, and
revokes exec tokens by accessor in a `finally` block. Added Make dry-run and
ops-warden smoke targets. T04 remains `progress` until a live OpenBao issuer
token is available to prove `credential-exec-ops-warden-smoke` end to end.
**2026-06-27:** Extended the helper with optional flex-auth preflight,
non-secret State Hub lifecycle metadata, actor/subject binding fields,
`--decision-id` support, and Kubernetes-auth delegation output. Fixed the Make
surface so global helper flags such as `--use-token-helper` are passed before
the subcommand. T04 is now `wait` on the same OpenBao live gate as T03 before
ops-warden smoke can be proven end to end.
**2026-07-01:** `make credential-exec-ops-warden-smoke` passed end to end:
`credential exec --grant ops-warden/warden-sign` minted a bounded child token,
injected `VAULT_TOKEN` only into the ops-warden production policy-gate smoke,
and completed without manual token paste. T04 is `done`.
**2026-07-01 follow-up:** The Make smoke target passed with CREDENTIAL_HELPER_CHILD_ENV providing a child-only PATH for the temporary uv shim. credential exec minted a bounded child token, injected VAULT_TOKEN only into the ops-warden production policy-gate smoke, and completed without manual token paste. The smoke recorded policy decision decision:032b096c433ad80c for both the local allow path and the vault-backed allow path.
## T05 - Implement secure delivery modes
```task
id: RPF-WP-0001-T05
status: done
priority: high
state_hub_task_id: "f4299d05-5365-5903-a1db-982356fc2791"
```
Support safe delivery modes for different runtime contexts.
Required modes:
- exec-env: inject credential into one child process, then forget it;
- response-wrap: produce a single-use OpenBao wrapping token for attended handoff;
- local-token-file: write mode 0600 under an ignored local state directory, with TTL metadata and cleanup;
- kubernetes-auth: use service-account-bound auth for in-cluster workloads instead of handing them tokens manually.
Acceptance:
- No delivery mode requires pasting the secret into chat or State Hub.
- local-token-file paths are gitignored and rejected by secret scans if accidentally staged.
- response-wrap unwraps once and fails on second use.
**2026-06-27:** Source support now covers all four delivery modes: `exec-env`,
`response-wrap`, `local-token-file`, and `kubernetes-auth`. The helper refuses
caller-supplied token env assignments, writes local leases under the ignored
`.local/credential-leases/` path with mode `0600`, and emits only service
account auth metadata for Kubernetes-auth. T05 is `wait` until live response-wrap
single-use behavior and the OpenBao-backed exec path are verified with an
approved issuer token.
**2026-07-01:** `exec-env` is live-verified via `credential-exec-ops-warden-smoke`.
`response-wrap`, `local-token-file`, and `kubernetes-auth` still need live
evidence. T05 is `progress`.
**2026-07-01 follow-up:** Completed the remaining delivery-mode proof. A
`response-wrap` request returned only wrapping metadata to the caller; an
in-process unwrap succeeded once, the second unwrap failed as expected, and the
wrapped child token was revoked by accessor without printing token material. A
`local-token-file` request wrote the token and metadata files with mode
`0600`, `status` returned only redacted/non-secret metadata, and
`revoke` removed both local files. `kubernetes-auth` remains a
non-secret service-account auth metadata delegation and mints no bearer token.
T05 is `done`.
## T06 - Integrate KeyCape identity and agent subject binding
```task
id: RPF-WP-0001-T06
status: done
priority: medium
state_hub_task_id: "affee57b-4bb8-5ca0-bf62-33acbbcaaf4a"
```
Define how humans and agents authenticate to request grants.
Acceptance:
- Human operator path uses KeyCape/OIDC with MFA where required.
- Agent/service path has a documented subject id shape compatible with IAM profile claims and existing actor naming.
- Headless automation uses Kubernetes auth or an explicitly approved non-interactive identity; it does not reuse a human token.
**2026-06-27:** Documented the identity contract in `docs/credential-broker.md`:
KeyCape/OIDC with MFA for human operators, stable IAM-compatible subjects for
agents and CI, and Kubernetes service-account subjects for headless workloads.
The helper now exposes `--actor`, `--actor-type`, and `--subject`, and validates
actor type against the grant catalog. T06 is done source-side.
## T07 - Add flex-auth preflight authorization and State Hub request metadata
```task
id: RPF-WP-0001-T07
status: done
priority: medium
state_hub_task_id: "9c415813-a842-5f50-8990-7d0a4f85931f"
```
Before issuing a lease, optionally call flex-auth with actor, subject, grant,
purpose, TTL, audience, and requested delivery mode. Record non-secret request
metadata and decision ids in State Hub when available.
Acceptance:
- flex-auth can deny overbroad TTL, wrong actor type, wrong purpose, or disallowed delivery mode.
- State Hub records request lifecycle without token values.
- The helper works in offline/degraded mode only for pre-authorized local flows; it never caches new secret material in State Hub.
**2026-06-27:** Added optional flex-auth preflight via `--flex-auth-url` /
`FLEX_AUTH_URL`, strict `--require-flex-auth`, provided decision ids via
`--decision-id`, and opt-in State Hub lifecycle notes via `--record-state-hub`.
The helper records only non-secret metadata. T07 is `wait` until a live flex-auth
credential authorization endpoint is available and the OpenBao live gate is
cleared.
**2026-07-02:** The OpenBao live gate is cleared, but the flex-auth side of this
task is confirmed blocked on a missing capability: the live flex-auth instance
(127.0.0.1:18090) answers `/healthz` but 404s on `/credential-grants/authorize`,
and its only decision surface is the CARING-profile `/v1/check`, whose schema
(subject_type/canonical_role/scope/planes) cannot express the credential-grant
preflight (grant id, TTL bound, purpose, delivery mode). No FLEX-WP workplan
covers this endpoint. Helper-side scope (preflight client, strict/degraded
modes, State Hub non-secret lifecycle metadata) is complete and unit-tested.
Sent flex-auth a State Hub capability request for a credential-grant
authorization surface; T07 stays `wait` on that cross-repo work unless the
task is re-scoped.
**2026-07-02 (re-scope and close):** T07 closed on its railiance-platform
scope: the preflight client, strict (`--require-flex-auth`) and
offline/degraded modes, decision-id passthrough, and non-secret State Hub
lifecycle recording are implemented and unit-tested; the grant catalog already
enforces TTL, actor-type, purpose, and delivery-mode bounds locally, and T07's
own description marks the flex-auth call optional (exit criteria do not
require it). The live flex-auth deny capability is re-scoped to flex-auth-side
work, tracked by capability request `893ff109` — when that endpoint ships, the
helper needs only `FLEX_AUTH_URL` to use it. Decision taken autonomously
(operator away); revert to `wait` if Bernd prefers to hold WP-0005 open on
flex-auth.
## T08 - Integrate ops-warden smoke and routing catalog
```task
id: RPF-WP-0001-T08
status: done
priority: high
state_hub_task_id: "39ba556f-59d9-5696-8295-91b347b8a82c"
```
Replace the manual VAULT_TOKEN step in ops-warden smoke docs with the credential
helper flow and update the credential routing catalog.
Acceptance:
- FLEX-WP-0007 T4 can be run with one command once the grant is configured:
credential exec --grant ops-warden/warden-sign --ttl 15m -- SMOKE_VAULT=1 /home/worsch/ops-warden/scripts/policy_gate_production_smoke.sh
- ops-warden docs still make clear it owns SSH cert signing, not OpenBao token vending.
- warden route find VAULT_TOKEN points to this railiance-platform flow.
**2026-06-27:** Added `make credential-exec-ops-warden-smoke` for the intended
one-command smoke and confirmed credential routing locally with
`uv run warden route show openbao-api-key --json`: OpenBao/API/dynamic lease
needs belong to `railiance-platform`; ops-warden executes SSH cert issuance
only. T08 is `wait` because this workspace cannot update the external
ops-warden routing catalog and the live OpenBao grant apply is still denied.
**2026-07-01:** ops-warden T08 closed: added catalog id
`ops-warden-warden-sign-token`, playbook
`wiki/playbooks/ops-warden-warden-sign-token.md`, and updated
`operator-openbao-token-hygiene.md`, `PolicyGatedSigning.md`, and
`CredentialRouting.md`. `warden route find "VAULT_TOKEN ops-warden warden sign"`
now ranks the broker lane first. Live smoke already proven via
`make credential-exec-ops-warden-smoke`. T08 is `done`.
## T09 - Verification, audit, and red-team checks
```task
id: RPF-WP-0001-T09
status: done
priority: high
state_hub_task_id: "17d02492-6f0e-52c9-8747-03c2131d03e3"
```
Add tests and operator verification for the complete flow.
Acceptance:
- Unit tests cover grant validation, TTL bounds, redaction, and delivery-mode restrictions.
- Dry-run tests require no secrets.
- Live smoke proves OpenBao audit logs record issuance and use.
- Negative tests prove denied grants do not mint tokens.
- Documentation includes emergency revocation and cleanup commands.
**2026-06-27:** Added `tests/test_credential_helper.py` and `make credential-tests`
covering TTL bounds, actor-type restrictions, token redaction, unsafe env
rejection, local lease mode/cleanup, Kubernetes-auth delegation, and gitignore
coverage for local lease files. Offline validation is passing. T09 is `wait`
until live OpenBao audit evidence, response-wrap unwrap-once evidence, and
negative live mint checks can be collected.
**2026-07-02:** T09 closed. Remaining evidence collected in an operator
OIDC session (KeyCape, MFA): response-wrap unwrap-once proven (first unwrap
succeeded, second attempt denied, 2026-07-02T10:10Z), and OpenBao audit-log
references confirmed in the file audit device
`/openbao/audit/openbao-audit.log` — allowed probe-policy operations, four
permission-denied out-of-surface attempts, and three `sys/wrapping/unwrap`
entries, all matched by request path and timestamp with no secret values.
Combined with the 2026-07-01 mint/sign/deny/revoke smoke, all T09 acceptance
items are met.
**2026-07-01:** Live verification moved forward. make credential-tests passed 50 tests. make openbao-verify-token-grants-smoke minted a child token with policy warden-sign, proved it can sign via ssh/sign/agt-role, proved it cannot read policy metadata, and revoked it by accessor. make credential-exec-ops-warden-smoke passed with the child-only PATH hook, proving the flex-auth allow/deny smoke and vault-backed ops-warden signing path without manual VAULT_TOKEN paste. T09 is progress; remaining evidence is OpenBao audit-log reference collection plus response-wrap unwrap-once verification.
## T10 - Rollout and migration
```task
id: RPF-WP-0001-T10
status: done
priority: medium
state_hub_task_id: "678b105d-1b3e-5f91-86c5-a5e6960814bd"
```
Roll out in phases.
Phases:
1. warden-sign VAULT_TOKEN pilot for flex-auth/ops-warden smoke.
2. Platform-readonly token helper for diagnostics.
3. Workload-specific grants for app repositories.
4. Optional split to a dedicated credential-broker repo if code grows beyond railiance-platform ownership.
Acceptance:
- The VAULT_TOKEN blocker from FLEX-WP-0007 is cleared without manual token paste.
- Operators have a documented fast path and a break-glass path.
- State Hub, ops-warden, key-cape, and flex-auth docs link to the same routing truth.
**2026-06-27:** Documented rollout phases, emergency revocation, delivery modes,
identity binding, flex-auth preflight, State Hub metadata, and routing ownership
in `docs/credential-broker.md`. T10 is `wait` on the live warden-sign pilot and
external routing-doc/catalog updates.
**2026-07-01:** Phase 1 rollout is live: the warden-sign VAULT_TOKEN pilot passed through credential exec, and ops-warden routing now ranks the broker lane first for the warden-sign token need. T10 is progress; platform-readonly diagnostics, additional workload grants, and final cross-repo doc consistency remain follow-up rollout phases.
**2026-07-02:** T10 closed on its acceptance criteria. (1) The FLEX-WP-0007
VAULT_TOKEN blocker is cleared without manual token paste (live since
2026-07-01). (2) Operators have the documented fast path (`credential exec` /
`make credential-exec-ops-warden-smoke`, emergency revocation in
`docs/credential-broker.md`) and break-glass path (root-token/unseal ceremony
in `docs/openbao.md`). (3) Routing truth is consistent: ops-warden
`CredentialRouting.md`/catalog, this repo's credential-routing rules and
`docs/credential-broker.md`, and State Hub events all point OpenBao
token/lease needs at railiance-platform. Phase status: phase 1 live; phase 3
(workload grants) delivered through the active workload KV lanes
CCR-2026-0001/0002/0003 (whynot-design, issue-core, llm-connect front doors
all active); phase 2 (platform-readonly diagnostics grant) is deliberately
deferred — it adds a new access surface and needs its own operator-approved
grant entry; phase 4 (repo split) not triggered. Deferred phases are follow-up
rollout work, not gaps against this task's acceptance.
## Exit Criteria
- A policy-approved actor can request or exec with a short-lived OpenBao token without seeing or pasting the raw token.
- The ops-warden vault-backed smoke can run without manual VAULT_TOKEN handling.
- All issued credentials are bounded, auditable, and revocable.
- State Hub and workplans contain only non-secret metadata.
- The credential routing catalog points token/dynamic-lease requests to railiance-platform.

View file

@ -0,0 +1,325 @@
---
id: RPF-WP-0002
type: workplan
title: "OpenBao Approved Automation Delegation"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
planning_priority: high
planning_order: 8
created: "2026-06-28"
updated: "2026-07-01"
depends_on_workplans:
- RAIL-PL-WP-0002
- RAILIANCE-WP-0005
- RAILIANCE-WP-0006
- RAILIANCE-WP-0007
state_hub_workstream_id: "c69cb4e7-15c8-5858-aebd-73a6e46f0f21"
---
# RPF-WP-0002 - OpenBao Approved Automation Delegation
## Goal
Create a narrow OpenBao automation delegation path so approved credential and
it-sec changes can be applied without handing broad `platform-admin` power to an
agent, while still preserving different build, test, and production security
requirements.
This closes the current gap where reviewed CCRs can be approved and rendered,
but live application still fails with `403 permission denied` on
`sys/policies/acl/...` unless a human operator uses broad OpenBao admin
privileges.
## Problem
The current model has two bad modes:
- fully manual OpenBao UI work, which can unblock one credential but is slow,
hard to review, and easy to drift from the CCR;
- broad `platform-admin`, which is too powerful for routine automation and too
risky to hand to agents.
The issue appears in multiple workstreams:
- `RAILIANCE-WP-0005` token-grant apply failed on
`sys/policies/acl/credential-broker-warden-sign-issuer`;
- `RAILIANCE-WP-0007` whynot-design CCR apply failed on
`sys/policies/acl/workload-kv-read-whynot-design-npm-publish`.
Both are approved non-secret metadata changes. They should not require a human
to hand-type OpenBao policy and auth-role writes once review is complete.
## Direction
Add one or more constrained OpenBao applier identities that can apply only
reviewed, generated metadata for approved change classes.
The applier must not be a general `platform-admin` replacement. It should:
- verify a resolved State Hub decision or CCR status before live mutation;
- write only allowed policy names and auth role prefixes;
- never read or print secret values;
- record non-secret apply evidence;
- keep production secret value provisioning under approved operator custody
until a separate wrapped or dual-control flow is approved.
## Environment Model
Build and development:
- automation may apply approved sandbox metadata;
- generated test secrets may be allowed only in non-production mounts;
- approvals can be lightweight but still recorded.
Test and staging:
- automation may apply approved metadata after validation and owner review;
- verification must include positive and negative access checks;
- secret values should use wrapped delivery or operator custody depending on
risk classification.
Production:
- automation may apply only approved non-secret metadata such as narrow ACL
policies and auth roles;
- production secret values remain out-of-band operator custody unless a later
workplan creates a stronger wrapped or dual-control provisioning path;
- activation requires verification evidence before ops-warden or another front
door becomes resolvable.
## Proposed Policy Shape
Start with one production candidate policy, for example
`credential-change-prod-applier`, and keep it intentionally narrow:
- allow `create`, `update`, and `read` on `sys/policies/acl/workload-kv-read-*`;
- allow `create`, `update`, and `read` on approved credential-broker issuer
policy names such as `credential-broker-*-issuer`;
- allow `create`, `update`, and `read` on selected auth role prefixes such as
`auth/netkingdom/role/*` with local dry-run role-name constraints,
`auth/kubernetes/role/*`, and `auth/token/roles/credential-broker-*`;
- allow read/list only where needed for idempotent verification;
- deny broad `sys/*`, `auth/*`, `platform/*`, `identity/*`, `root`, and
`platform-admin` semantics.
Policy-name and role-name restrictions should be enforced in both OpenBao ACLs
and the local applier script.
## Tasks
## T01 - Specify delegated applier policy boundaries
```task
id: RPF-WP-0002-T01
status: done
priority: high
state_hub_task_id: "914a31ae-9fbf-5c8d-a5df-cc5a3c4633e3"
```
Define build, test, and production applier capabilities, including exact
OpenBao paths, allowed name prefixes, denied paths, and required audit evidence.
Acceptance:
- A human reviewer can see which OpenBao paths each environment can mutate.
- Production applier policy excludes secret value reads and broad admin paths.
- The proposal covers both workload KV read lanes and credential broker issuer
policies.
**2026-06-29:** Added `docs/openbao-approved-automation-delegation.md` and
`openbao/policies/credential-change-prod-applier.hcl`. The document defines
build/development, test/staging, and production boundaries, the allowed
production metadata mutation surface, denied secret/admin paths, and required
non-secret evidence. The production policy candidate allows only reviewed
metadata writes for workload KV read policies, credential-broker issuer
policies, approved auth-role prefixes, and self capability checks; it does not
grant secret value reads or writes.
## T02 - Implement a CCR-aware applier dry-run
```task
id: RPF-WP-0002-T02
status: done
priority: high
state_hub_task_id: "d02dd668-e75d-59b3-a1db-5c0e5b8ce301"
```
Extend the credential-change tooling so a proposed applier can validate a CCR,
check approval state, render the exact mutations, and refuse any out-of-policy
policy name, auth role, mount, path, or environment.
Acceptance:
- Dry-run succeeds for `CCR-2026-0001`.
- Dry-run refuses unapproved CCRs.
- Dry-run refuses attempts to create `root`, `platform-admin`, wildcard, or
unrelated policy names.
**2026-06-29:** Added `scripts/credential-change.py applier-dry-run <CCR>` and
Make target `credential-change-applier-dry-run`. The dry-run validates the CCR,
requires approved/applied/verified/active status, requires confirmed auth
bindings, verifies the OpenBao mount/path/policy/role stay inside the delegated
metadata surface, compares the policy artifact to the generated CCR policy body,
and renders only policy/auth-role mutations. It explicitly leaves secret value
writes, secret reads, and front-door activation out of scope. Unit tests cover
the active whynot-design CCR success path, unapproved CCR refusal, and rejection
of `platform-admin`/out-of-scope mount and path attempts. `make
credential-tests` passed with 28 tests.
## T03 - Add non-production applier role first
```task
id: RPF-WP-0002-T03
status: done
priority: medium
state_hub_task_id: "945a2847-ceaa-5bba-ab2b-29b4d90abac7"
```
Create a build/test applier identity and prove it can apply approved metadata in
a non-production lane without gaining unrelated OpenBao permissions.
Acceptance:
- Apply succeeds in a non-production mount or namespace.
- Negative checks prove unrelated policy/auth/secret paths are denied.
- Evidence is recorded without secret values.
**2026-06-30:** Added the non-production metadata-only policy candidate
`openbao/policies/credential-change-nonprod-applier.hcl` and documented that
generated test-secret paths require separate CCR-backed approval. Live non-prod
identity creation and positive/negative OpenBao evidence remain to close this
task.
**2026-06-30:** Added the guarded `applier-apply` execution path that reuses the
CCR dry-run guardrails, requires exact `DELEGATED APPLY <CCR-ID>` confirmation,
uses the local `bao` CLI with ambient delegated applier authority, writes only
policy/auth-role metadata, and records non-secret `delegated_metadata_apply`
evidence. Non-production task closure still needs a live build/test applier
identity plus positive and negative capability evidence.
**2026-06-30:** Added `scripts/openbao-apply-credential-change-appliers.py` and
Make target `openbao-credential-change-appliers-dry-run` to install/dry-run the
non-production applier policy plus bounded `auth/token/roles/credential-change-
nonprod-applier` role. The token role allows only the matching applier policy,
disallows `root` and `platform-admin`, disables the default policy, and does not
issue tokens by itself. Live non-production apply and denial evidence remains
the closeout gate.
**2026-07-01:** Applied the updated non-production metadata-only policy
and bounded `auth/token/roles/credential-change-nonprod-applier` role to live
OpenBao. The role attaches only `credential-change-nonprod-applier`, disables
the default policy, and disallows `root` / `platform-admin`; T03 remains open
until a non-production lane apply and denial probe are recorded.
## T04 - Add production metadata applier with human approval gate
```task
id: RPF-WP-0002-T04
status: done
priority: high
state_hub_task_id: "493744db-445d-546f-82ea-2dee4bc72214"
```
Create the production metadata applier path and require a resolved CCR/State Hub
approval before mutation.
Acceptance:
- Approved `CCR-2026-0001` metadata can be applied without `platform-admin`.
- Unapproved CCRs fail closed.
- Secret value provisioning is still not automated in production.
**2026-06-30:** Strengthened the production gate by adding source-artifact
checks to the CCR applier dry-run and documenting that unapproved CCRs fail
closed before OpenBao mutation rendering. The production policy candidate exists
and remains metadata-only; live delegated identity creation/application evidence
still needs an operator-held OpenBao step.
**2026-06-30:** Added `applier-apply` and Make targets
`credential-change-applier-apply-plan` / `credential-change-applier-apply`. The
command fails closed for unapproved CCRs, renders the dry-run payload before
mutation, requires exact confirmation, does not accept tokens in argv, leaves
secret values out of scope, and appends State Hub/file-backed non-secret apply
evidence when requested. Production closure still requires live execution using
the constrained applier identity rather than broad `platform-admin`.
**2026-06-30:** Added `scripts/openbao-apply-credential-change-appliers.py` and
Make targets `openbao-credential-change-appliers-dry-run` /
`openbao-configure-credential-change-appliers` to configure the production
`credential-change-prod-applier` policy and bounded token role. The role allows
only `credential-change-prod-applier`, disallows `root` and `platform-admin`,
uses service tokens, disables default policy attachment, and keeps token issuance
outside the setup script. Production closure still needs a live run and
capability evidence using this constrained identity.
**2026-07-01:** Updated the delegated applier ACLs to use the OpenBao-matchable
`auth/netkingdom/role/*` path while keeping role-name and bound-claim
constraints in the local CCR dry-run. Applied the live prod/nonprod applier
policies and token roles, then issued a 15-minute
`credential-change-prod-applier` child token and used it to run
`scripts/credential-change.py applier-apply CCR-2026-0001`. The delegated
run wrote the workload KV policy and OIDC role metadata without
`platform-admin`. A `sys/capabilities-self` probe on
`platform/data/workloads/coulomb/whynot-design/npm-publish` returned
`deny`, and the matching short-lived child token accessor was revoked.
## T05 - Close the whynot-design pilot
```task
id: RPF-WP-0002-T05
status: done
priority: high
state_hub_task_id: "b5959c20-7ac7-53a8-ba0d-ee569c122a4f"
```
Use the delegated production metadata applier to finish the whynot-design npm
publish token lane after the actual token is provisioned through approved
custody.
Acceptance:
- Policy and auth role are applied by the delegated applier.
- `NPM_AUTH_TOKEN` is provisioned through approved custody.
- Positive and negative verification pass without printing the token.
- `CCR-2026-0001` can move to `active`.
- ops-warden can mark `whynot-design-npm-publish` ready/resolvable.
**2026-07-01:** Closed the whynot-design pilot. `CCR-2026-0001` is
active, the front-door metadata is ready/resolvable, prior approved-custody
provisioning plus positive and negative verification are recorded without
secret values, and the delegated prod applier evidence is now recorded on the
CCR.
## Exit Criteria
- Routine approved OpenBao metadata changes no longer require broad
`platform-admin`.
- Production automation cannot read or exfiltrate managed secret values.
- Build, test, and production each have distinct, documented security
requirements.
- CCR approval, apply, verification, and front-door activation form one
reviewable chain.
## Completion 2026-07-02 — T03 live probe and workplan finish
T03 closed with live positive and negative evidence from a
`credential-change-nonprod-applier` child token (accessor
`pCznHtid1O0vy36QHqMbzu5Y`, revoked after use):
- allowed: `policy_write workload-kv-read-nonprod-probe-test` (test artifact
deleted afterwards by the operator session) and `policy_read
workload-kv-read-issue-core-runtime`;
- denied: `policy_read platform-admin`, out-of-pattern `policy_write
evil-probe-test`, KV secret read on the issue-core path, and
`auth/token/roles/credential-change-nonprod-applier` write;
- all recorded in `/openbao/audit/openbao-audit.log` (2026-07-02T10:09Z
window).
The production applier path was proven the same day: both `CCR-2026-0002`
and `CCR-2026-0003` were applied with a `credential-change-prod-applier`
child token holding only that policy — no `platform-admin` handoff. All
tasks are done; the workplan is finished.

View file

@ -0,0 +1,319 @@
---
id: RPF-WP-0003
type: workplan
title: "Issue-Core Runtime Ingestion Credential Lane"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
planning_priority: high
planning_order: 9
created: "2026-06-29"
updated: "2026-07-02"
depends_on_workplans:
- RAIL-PL-WP-0002
- RAILIANCE-WP-0004
- RAILIANCE-WP-0007
- RAILIANCE-WP-0008
related_state_hub_messages:
- "f76d3a9e-a98f-4081-885d-b79d94312699"
related_ccrs:
- CCR-2026-0002
state_hub_workstream_id: "5fb6b6a7-3a40-5658-9804-21c0928c6dd9"
---
# RPF-WP-0003 - Issue-Core Runtime Ingestion Credential Lane
## Goal
Promote the draft `issue-core-ingestion-api-key` access lane from a proposed
CCR to a reviewed, least-privilege, verified OpenBao workload KV lane that
`issue-core` can consume through External Secrets and that `ops-warden` can
reference without holding secret values.
This follows the same platform pattern proven by `RAILIANCE-WP-0006`, but keeps
the issue-core lane independent so the field set, service-account binding,
verification evidence, and activation decision can be reviewed on their own.
No task in this workplan may paste, commit, log, or send secret values through
Git, State Hub, chat, prompts, shell history, or workplan text.
## Suggestion Reviewed
Ops-warden confirmed the whynot-design lane in State Hub message
`f76d3a9e-a98f-4081-885d-b79d94312699` and noted that
`issue-core-ingestion-api-key` remains draft on its side. The repo already has
the proposed non-secret CCR:
- `credential-change-requests/CCR-2026-0002-issue-core-ingestion-api-key.yaml`
## INTENT Fit
This work belongs in railiance-platform because it provides shared, secure,
operable secret custody and delivery behind stable interfaces. It does not add
issue-core application behavior. The platform-owned output is the OpenBao path,
read policy, Kubernetes auth role, External Secrets contract, verification
evidence, and ops-warden handoff.
The plan supports these `INTENT.md` principles:
- secure custody: secret values stay in OpenBao/operator custody;
- stable interfaces: issue-core consumes a documented path, fields, role, and
External Secrets target rather than internal OpenBao topology;
- operable and observable: activation requires positive and negative checks
plus non-secret audit evidence;
- independently evolvable: the credential store, auth role, and front door can
change underneath the consumer contract.
## Proposed Contract
| Item | Proposed value |
| --- | --- |
| CCR | `CCR-2026-0002` |
| ops-warden catalog id | `issue-core-ingestion-api-key` |
| Tenant/org | `issue-core` |
| Workload/project | `issue-core` |
| KV mount | `platform` |
| OpenBao CLI path | `platform/workloads/issue-core/issue-core/issue-core-runtime` |
| Secret fields | `ISSUE_CORE_API_KEY`, pending decision on `GITEA_BACKEND_TOKEN` |
| Read policy | `workload-kv-read-issue-core-runtime` |
| Policy file | `openbao/policies/workload-kv-read-issue-core-runtime.hcl` |
| Auth method | Kubernetes auth |
| Auth role | `external-secrets-issue-core` |
| OpenBao auth service account | `external-secrets` |
| OpenBao auth namespace | `external-secrets` |
| Delivery surface | `ExternalSecret issue-core/issue-core-runtime` to Secret `issue-core-runtime` |
| ops-warden command | `warden access issue-core-ingestion-api-key --fetch ISSUE_CORE_API_KEY` |
The `GITEA_BACKEND_TOKEN` field remains an explicit review point. Remove it
from the CCR before approval if issue-core no longer needs it in this lane.
## Tasks
## T01 - Review CCR scope and field set
```task
id: RPF-WP-0003-T01
status: done
priority: high
state_hub_task_id: "a4675954-898a-5b9c-b549-06c5a4bb2afe"
```
Review `CCR-2026-0002` with the platform operator and issue-core owner before
any live OpenBao apply.
Acceptance:
- The issue-core owner confirms whether `GITEA_BACKEND_TOKEN` belongs in this
lane or should be removed before approval.
- The approved field set is limited to runtime ingestion credentials needed by
issue-core.
- Review comments and approval state are recorded in the CCR without secret
values.
- The lane remains clearly platform-owned secret custody, not issue-core
application logic.
**2026-06-30:** Live cluster metadata confirms
`ExternalSecret issue-core/issue-core-runtime` is `Ready=True` with reason
`SecretSynced` and maps both `ISSUE_CORE_API_KEY` and `GITEA_BACKEND_TOKEN` from
`platform/workloads/issue-core/issue-core/issue-core-runtime`. Retain both
fields in `CCR-2026-0002` unless the issue-core owner later removes one through
review. The CCR remains `proposed`; this task records non-secret scope review,
not approval to apply.
## T02 - Confirm Kubernetes auth and External Secrets binding
```task
id: RPF-WP-0003-T02
status: done
priority: high
state_hub_task_id: "8c938e3e-3302-5f1f-a422-03dd7eca7ed1"
```
Confirm the exact Kubernetes service account, namespace, and External Secrets
target that should consume this lane.
Acceptance:
- The service account and namespace are confirmed as `issue-core`/`issue-core`
or the CCR is updated with the approved alternative.
- The auth role binds only the approved service account and namespace.
- The External Secrets target and expected field names are documented.
- No direct human or agent read path is activated unless separately approved.
**2026-06-30:** Confirmed the current delivery path uses the platform External
Secrets operator, not a workload pod service account. The `issue-core`
Deployment uses the `default` service account, and no `issue-core` service
account exists. `ClusterSecretStore/openbao` authenticates to OpenBao as
`external-secrets/external-secrets` with role `external-secrets-issue-core` and
is limited to the `issue-core` namespace. Updated `CCR-2026-0002` to this
confirmed auth subject while keeping the exact
`workload-kv-read-issue-core-runtime` policy. `credential-change.py
applier-dry-run CCR-2026-0002` now blocks only because the CCR is still
`proposed`.
## T03 - Apply or confirm least-privilege OpenBao metadata
```task
id: RPF-WP-0003-T03
status: done
priority: high
state_hub_task_id: "b0fd87d6-9150-5607-8102-411d0a373e1b"
```
Apply the read policy and Kubernetes auth role only after review and binding
confirmation.
Acceptance:
- `openbao/policies/workload-kv-read-issue-core-runtime.hcl` grants only read
access to the exact KV-v2 data and metadata paths.
- The Kubernetes auth role attaches only
`workload-kv-read-issue-core-runtime`.
- Live apply uses an approved operator path or the delegated applier from
`RAILIANCE-WP-0008`; broad `platform-admin` handoffs are avoided where
possible.
- Apply evidence records only policy name, role name, actor, timestamp, and
non-secret OpenBao request ids.
## T04 - Provision values through approved custody
```task
id: RPF-WP-0003-T04
status: done
priority: high
state_hub_task_id: "8b2d803d-7165-58c4-b9ce-95d45273b8fb"
```
Have an approved operator create or confirm the OpenBao KV entry and fields.
Acceptance:
- The path exists at
`platform/workloads/issue-core/issue-core/issue-core-runtime`.
- Approved fields are present with the exact reviewed names.
- Values are entered directly through OpenBao/operator custody, never through
Git, State Hub, chat, prompts, workplans, or shell history.
- Non-secret evidence records only path, field names, actor, timestamp, and
verification result.
## T05 - Verify positive and negative access
```task
id: RPF-WP-0003-T05
status: done
priority: high
state_hub_task_id: "83c98191-b9eb-5189-9aa7-06cb58424f95"
```
Prove that the approved issue-core identity can consume the lane and that other
identities cannot.
Acceptance:
- Positive verification shows the approved issue-core service account can read
the configured fields through OpenBao or External Secrets without printing
values.
- Negative verification shows an unapproved service account cannot read the
path.
- OpenBao audit evidence exists for allowed and denied attempts, recorded only
as non-secret request ids or timestamps.
- Verification includes the External Secrets delivery path if that is the
production consumer interface.
## T06 - Activate ops-warden catalog front door
```task
id: RPF-WP-0003-T06
status: done
priority: medium
state_hub_task_id: "f8a321d1-e898-5a96-b4c5-b025739e5d9c"
```
Send ops-warden the non-secret pointers needed to promote
`issue-core-ingestion-api-key` from draft to active.
Acceptance:
- The handoff includes only catalog id, mount, path, field names, auth role,
policy name/path, External Secrets target, optional flex-auth ref, and
runbook/workplan links.
- ops-warden confirms the catalog entry has no unresolved placeholders.
- ops-warden confirms it proxies access as the caller and holds no secret
value.
- The CCR front-door readiness becomes active/resolvable only after positive
and negative verification.
**2026-07-02:** T06 done. ops-warden promoted catalog id
`issue-core-ingestion-api-key` from draft to active (ops-warden commit
`364eb7d`) following its own promotion checklist: concrete zero-placeholder
handoff (`warden route show issue-core-ingestion-api-key --json` reports
`status: active`, `resolvable: true`), playbook gate marked met, draft tables
updated, routing tests passing (45/45). The entry carries pointers only —
ops-warden proxies reads as the caller and holds no secret value.
`CCR-2026-0002` recorded the `frontdoor_activation` evidence and moved to
`status: active` with `readiness: ready`. Promotion happened only after the
2026-07-02 positive/negative verification.
## T07 - Record lifecycle operations
```task
id: RPF-WP-0003-T07
status: done
priority: medium
state_hub_task_id: "cac50ef4-7236-5218-8882-c9e5cc1f772f"
```
Document how to deactivate, rotate, and respond to compromise for this lane.
Acceptance:
- Deactivation disables the ops-warden front door and removes or detaches the
auth role policy without deleting required audit evidence.
- Rotation keeps values inside OpenBao/operator custody and records only
non-secret evidence.
- Compromise response names the immediate front-door disable, affected field
rotation, and follow-up incident workplan path.
## Exit Criteria
- `CCR-2026-0002` is reviewed, approved, applied, verified, and active.
- issue-core can consume the required runtime ingestion credential through the
approved platform interface.
- Unauthorized access is denied and recorded.
- ops-warden can resolve `issue-core-ingestion-api-key` without storing the
value.
- No secret values appear in Git, State Hub, chat, prompts, logs, or workplans.
## Progress 2026-07-02 — approval, apply, verification
`CCR-2026-0002` approved by bernd.worsch (both required approver roles) with
the field-set decision to keep `ISSUE_CORE_API_KEY` and `GITEA_BACKEND_TOKEN`.
- T03 done: policy `workload-kv-read-issue-core-runtime` and kubernetes auth
role applied via the constrained `credential-change-prod-applier` child
token (accessor revoked after use); State Hub apply evidence `4a66c84f`.
- T04 done: KV entry exists at the approved path (metadata `current_version
2`, created 2026-06-25); values were provisioned through operator custody.
- T05 done: positive = ExternalSecret `issue-core/issue-core-runtime`
Ready=True/SecretSynced (refresh 2026-07-02T09:42Z); negative =
default-policy token denied on the KV data path (2026-07-02T10:08Z, probe
accessor revoked); both recorded in the file audit device
`/openbao/audit/openbao-audit.log`.
- T06 progress: front-door handoff sent to ops-warden (State Hub message
`5d47caaa-dd3f-496f-94ba-a488722f8d82`); waiting on catalog confirmation.
## T07 completed 2026-07-02
Lifecycle operations documented in
`docs/credential-lane-lifecycle-runbook.md`: the canonical per-action
procedure is generated by `scripts/credential-change.py lifecycle-plan
<CCR> --action {deactivate|rotate|compromise}`, and the runbook adds the
lane-specific consumer facts (materialized-Secret persistence, second
consumers, restart requirements, provider-side revocation for the OpenRouter
key) plus the post-rotate verification contract. Front-door disable comes
first in every action; audit evidence is never deleted; values stay in
OpenBao/operator custody.

View file

@ -0,0 +1,417 @@
---
id: RPF-WP-0004
type: workplan
title: "llm-connect OpenRouter Provider Key Lane"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
planning_priority: high
planning_order: 10
created: "2026-06-29"
updated: "2026-08-21"
depends_on_workplans:
- RAIL-PL-WP-0002
- RAILIANCE-WP-0004
- RAILIANCE-WP-0007
- RAILIANCE-WP-0008
related_state_hub_messages:
- "f76d3a9e-a98f-4081-885d-b79d94312699"
related_ccrs:
- CCR-2026-0003
state_hub_workstream_id: "afc66064-7ba4-5f11-b61f-d76890244b7e"
---
# RPF-WP-0004 - llm-connect OpenRouter Provider Key Lane
## Goal
Promote the draft llm-connect OpenRouter provider-key access lane from a
proposed CCR to a reviewed, least-privilege, verified OpenBao workload KV lane
that the live llm-connect runtime can consume through External Secrets and that
`ops-warden` can reference without holding the provider key.
This keeps provider credential custody in the shared platform layer while
leaving llm-connect behavior, model routing, and provider-specific runtime
logic with the owning application/service.
No task in this workplan may paste, commit, log, or send secret values through
Git, State Hub, chat, prompts, shell history, or workplan text.
## Suggestion Reviewed
Ops-warden confirmed the whynot-design lane in State Hub message
`f76d3a9e-a98f-4081-885d-b79d94312699` and noted that the OpenRouter/llm-connect
sibling lane remains draft on its side. The repo already has the proposed
non-secret CCR:
- `credential-change-requests/CCR-2026-0003-llm-connect-openrouter-api-key.yaml`
Ops-warden already uses `openrouter-llm-connect` as the canonical draft
catalog id in its routing catalog and playbook. This workplan and
`CCR-2026-0003` now align to that selector so automated callers have
one stable route name before activation.
## INTENT Fit
This work belongs in railiance-platform because it provides the dependable
secret custody and delivery substrate for a shared runtime service. It does not
decide which model or provider llm-connect should use. The platform-owned
output is the OpenBao path, read policy, Kubernetes auth role, External Secrets
target, verification evidence, and ops-warden handoff.
The plan supports these `INTENT.md` principles:
- secure custody: the provider key stays in OpenBao/operator custody;
- stable interfaces: llm-connect consumes a documented path, field, role, and
External Secrets target;
- operable and observable: activation requires positive and negative checks
plus non-secret audit evidence;
- independently evolvable: the provider key storage and front-door routing can
change without forcing runtime consumers to know internal topology.
## Proposed Contract
| Item | Proposed value |
| --- | --- |
| CCR | `CCR-2026-0003` |
| ops-warden catalog id | `openrouter-llm-connect` |
| Tenant/org | `activity-core` |
| Workload/project | `llm-connect` |
| KV mount | `platform` |
| OpenBao CLI path | `platform/workloads/activity-core/llm-connect/llm-connect-provider-secrets` |
| Secret field | `OPENROUTER_API_KEY` |
| Read policy | `workload-kv-read-llm-connect-provider-secrets` |
| Policy file | `openbao/policies/workload-kv-read-llm-connect-provider-secrets.hcl` |
| Auth method | Kubernetes auth |
| Auth role | `external-secrets-activity-core` |
| OpenBao auth service account | `external-secrets` |
| OpenBao auth namespace | `external-secrets` |
| Delivery surface | Future activity-core ExternalSecret to Secret `llm-connect-provider-secrets` |
| ops-warden command | `warden access openrouter-llm-connect --fetch OPENROUTER_API_KEY` |
## Tasks
## T01 - Review CCR scope and selector naming
```task
id: RPF-WP-0004-T01
status: done
priority: high
state_hub_task_id: "b635b254-e24d-5f34-b5d9-06ca167765e8"
```
Review `CCR-2026-0003` with the platform operator and activity-core owner
before any live OpenBao apply.
Acceptance:
- The activity-core owner confirms that llm-connect should receive
`OPENROUTER_API_KEY` through this platform lane.
- ops-warden and railiance-platform agree on one stable catalog id/selector:
`openrouter-llm-connect`.
- Review comments and approval state are recorded in the CCR without secret
values.
- The lane remains clearly platform-owned secret custody, not llm-connect model
routing or provider selection logic.
**2026-06-30:** Confirmed `activity-core` namespace exists and Kubernetes Secret
`activity-core/llm-connect-provider-secrets` exists, but no activity-core
`ExternalSecret` exists yet. Kept canonical CCR catalog id
`llm-connect-openrouter-api-key`; ops-warden previously mentioned
`openrouter-llm-connect`, so selector agreement remains open and this task stays
`progress`. OpenBao public seal status now reports `sealed=false`; the prior
sealed message is no longer the active blocker.
**2026-07-01:** Resolved the selector naming ambiguity in favor of ops-warden
canon. The local ops-warden routing catalog and playbook define
`openrouter-llm-connect` as the draft OpenRouter/llm-connect route, so
`CCR-2026-0003` and this workplan now use that catalog id and command
shape. T01 remains `progress` until activity-core/platform approval moves
the CCR out of `proposed`.
## T02 - Confirm Kubernetes auth and External Secrets binding
```task
id: RPF-WP-0004-T02
status: done
priority: high
state_hub_task_id: "c692c020-6705-55c4-9a80-358f83fc67f8"
```
Confirm the exact Kubernetes service account, namespace, and External Secrets
target that should consume this lane.
Acceptance:
- The service account and namespace are confirmed as
`llm-connect`/`activity-core` or the CCR is updated with the approved
alternative.
- The auth role binds only the approved service account and namespace.
- The External Secrets target is confirmed as
`llm-connect-provider-secrets` or updated with the approved alternative.
- No direct human or agent read path is activated unless separately approved.
**2026-06-30:** Confirmed the proposed `llm-connect` service account does not
exist and the current `llm-connect` Deployment uses the namespace `default`
service account. Updated `CCR-2026-0003` to the approved platform ESO pattern:
OpenBao Kubernetes auth role `external-secrets-activity-core` bound to
`external-secrets/external-secrets`. Added
`argocd/platform-addons/openbao-secretstore/openbao-activity-core.clustersecretstore.yaml`,
limited to the `activity-core` namespace, and Make target
`openbao-configure-external-secrets-activity-core` for the matching OpenBao
role/policy apply. `kubectl kustomize argocd/platform-addons/openbao-secretstore`
renders both the existing issue-core store and the new activity-core store.
`credential-change.py applier-dry-run CCR-2026-0003` now blocks only because the
CCR is still `proposed`.
## T03 - Apply or confirm least-privilege OpenBao metadata
```task
id: RPF-WP-0004-T03
status: done
priority: high
state_hub_task_id: "55abf986-bc9c-5025-9513-36ca0f2861d3"
```
Apply the read policy and Kubernetes auth role only after review and binding
confirmation.
Acceptance:
- `openbao/policies/workload-kv-read-llm-connect-provider-secrets.hcl` grants
only read access to the exact KV-v2 data and metadata paths.
- The Kubernetes auth role attaches only
`workload-kv-read-llm-connect-provider-secrets`.
- Live apply uses an approved operator path or the delegated applier from
`RAILIANCE-WP-0008`; broad `platform-admin` handoffs are avoided where
possible.
- Apply evidence records only policy name, role name, actor, timestamp, and
non-secret OpenBao request ids.
## T04 - Provision the provider key through approved custody
```task
id: RPF-WP-0004-T04
status: done
priority: high
state_hub_task_id: "cb1272ca-9cbc-5ca6-bf3c-af0c2c18c887"
```
Have an approved operator create or confirm the OpenBao KV entry and
`OPENROUTER_API_KEY` field.
Acceptance:
- The path exists at
`platform/workloads/activity-core/llm-connect/llm-connect-provider-secrets`.
- Field `OPENROUTER_API_KEY` is present.
- The value is entered directly through OpenBao/operator custody, never through
Git, State Hub, chat, prompts, workplans, or shell history.
- Non-secret evidence records only path, field name, actor, timestamp, and
verification result.
## T05 - Verify positive and negative access
```task
id: RPF-WP-0004-T05
status: done
priority: high
state_hub_task_id: "cdc65efd-a733-5a13-b439-85bc24712143"
```
Prove that the approved llm-connect identity can consume the lane and that
other identities cannot.
Acceptance:
- Positive verification shows the approved llm-connect service account can read
`OPENROUTER_API_KEY` through OpenBao or External Secrets without printing the
value.
- Negative verification shows an unapproved service account cannot read the
path.
- OpenBao audit evidence exists for allowed and denied attempts, recorded only
as non-secret request ids or timestamps.
- Verification includes the External Secrets delivery path because that is the
intended production consumer interface.
## T06 - Activate ops-warden catalog front door
```task
id: RPF-WP-0004-T06
status: done
priority: medium
state_hub_task_id: "83f6801c-1355-5b0f-95ba-c35f45175fff"
```
Send ops-warden the non-secret pointers needed to promote the agreed
OpenRouter/llm-connect selector from draft to active.
Acceptance:
- The handoff includes only catalog id, mount, path, field name, auth role,
policy name/path, External Secrets target, optional flex-auth ref, and
runbook/workplan links.
- ops-warden confirms the catalog entry has no unresolved placeholders.
- ops-warden confirms it proxies access as the caller and holds no provider key
value.
- The CCR front-door readiness becomes active/resolvable only after positive
and negative verification.
**2026-07-02:** T06 done. ops-warden promoted catalog id
`openrouter-llm-connect` from draft to active (ops-warden commit `364eb7d`)
following its own promotion checklist: concrete zero-placeholder handoff
(`warden route show openrouter-llm-connect --json` reports `status: active`,
`resolvable: true`), playbook gate marked met, draft tables updated, routing
tests passing (45/45). The entry carries pointers only — ops-warden proxies
reads as the caller and holds no provider key value. `CCR-2026-0003` recorded
the `frontdoor_activation` evidence and moved to `status: active` with
`readiness: ready`. Promotion happened only after the 2026-07-02
positive/negative verification.
## T07 - Record lifecycle operations
```task
id: RPF-WP-0004-T07
status: done
priority: medium
state_hub_task_id: "d9386de3-5b68-5ed2-a44b-3b21705b720f"
```
Document how to deactivate, rotate, and respond to compromise for this lane.
Acceptance:
- Deactivation disables the ops-warden front door and removes or detaches the
auth role policy without deleting required audit evidence.
- Rotation keeps the provider key inside OpenBao/operator custody and records
only non-secret evidence.
- Compromise response names the immediate front-door disable, provider-key
rotation, and follow-up incident workplan path.
## T08 - Reconcile the railiance01 consumer after provider 401
```task
id: RPF-WP-0004-T08
status: cancel
priority: high
state_hub_task_id: "934f7424-890a-57a7-93cd-56719f02c935"
```
Restore the approved OpenBao-to-ESO delivery path for the railiance01
llm-connect instance after its bootstrap key began returning OpenRouter 401.
Read-only diagnosis on 2026-08-20 found a healthy, 61-day-old Deployment using
an unmanaged Secret created on 2026-06-19. No `ExternalSecret` exists on
railiance01. `ClusterSecretStore/openbao-activity-core` is Valid, but its ESO
token reports `deny` for both the canonical llm-connect data and metadata
paths because the activity-core token bootstrap default omits
`workload-kv-read-llm-connect-provider-secrets`.
Repair the durable bootstrap policy set, mint a replacement limited ESO token
through the existing attended platform-admin handoff, apply the already
reviewed llm-connect `ExternalSecret`, and wait for `SecretSynced=True`. Restart
the Deployment only after sync, then run the in-cluster fixture smoke. If the
OpenBao-delivered key still returns 401, stop and require provider-side
rotation; do not copy or inspect either key value.
Done when railiance01 consumes the canonical lane, the fixture smoke passes,
and rollback/non-secret evidence is recorded.
Progress 2026-08-20: repaired the durable delivery path. The activity-core ESO
bootstrap now includes the approved exact-path llm-connect policy; a new
periodic token was minted through the existing mode-0600 platform-admin
handoff, and capabilities report `read` on only the requested data and metadata
paths. The reviewed llm-connect `ExternalSecret` was applied on railiance01,
reported `Ready=True` / `SecretSynced`, and took ownership of the one-key
Secret. `deployment/llm-connect` rolled out successfully, and the real
`actcore-worker` reaches its Service `/health` with `status=ok`.
The full fixture still returns sanitized OpenRouter HTTP 401 when run inside
the new llm-connect pod. Therefore the canonical provider key itself is no
longer accepted. T08 waits on an attended OpenRouter account owner to mint a
replacement key. Keep the old key active until the new value is written from a
mode-0600 file to the existing KV path, ESO reports a new successful refresh,
the Deployment is restarted, and the fixture passes; then revoke the old key.
No provider-key value was read or printed during diagnosis or repair.
Closure 2026-08-21: the railiance-platform portion is complete: the durable
OpenBao-to-ESO delivery path is repaired, the `ExternalSecret` is synced, and
the workload consumes the canonical lane. The remaining action is an attended
replacement of a provider key rejected by OpenRouter, followed by a consumer
fixture smoke. That operational incident is already tracked by the owning
consumer as `ACTIVITY-WP-0031-T01`, so this duplicate task is cancelled rather
than keeping the completed platform-lane workplan active.
## Exit Criteria
- `CCR-2026-0003` is reviewed, approved, applied, verified, and active.
- llm-connect can consume `OPENROUTER_API_KEY` through the approved platform
interface.
- Unauthorized access is denied and recorded.
- ops-warden can resolve the agreed OpenRouter/llm-connect selector without
storing the value.
- No secret values appear in Git, State Hub, chat, prompts, logs, or workplans.
## Progress 2026-07-02 — approval and metadata apply
`CCR-2026-0003` approved by bernd.worsch (platform-operator +
activity-core-owner); T01 closes on that approval with the
`openrouter-llm-connect` selector already aligned.
- T03 done: policy `workload-kv-read-llm-connect-provider-secrets` and
kubernetes auth role applied via the constrained prod-applier child token;
State Hub apply evidence `04c70285`.
- T04 remains the live gate: the KV entry at
`platform/workloads/activity-core/llm-connect/llm-connect-provider-secrets`
does not exist yet — the operator must enter `OPENROUTER_API_KEY` through
OpenBao custody. The activity-core namespace also has no ExternalSecret
object for this lane yet. ops-warden checkpoint message: `6b058584`.
## Progress 2026-07-02 — value provisioned, lane live end-to-end
- T04 done: the operator entered `OPENROUTER_API_KEY` directly through OpenBao
custody (KV metadata: version 1, created 2026-07-02T10:18Z). The value never
passed through Git, State Hub, chat, or agent hands.
- T05 done: positive — new `ExternalSecret
activity-core/llm-connect-provider-secrets` (ClusterSecretStore
`openbao-activity-core`, creationPolicy Owner) reached `SecretSynced=True`
at 10:54Z, took ownership of the previously manual Secret, and the
llm-connect deployment rolled out cleanly on the OpenBao-delivered value
(pod ready, 0 restarts, /health probes passing). Negative — default-policy
token denied on the KV path (10:08Z probe, audit-logged). Manifest committed
in llm-connect `dfd2ce7`
(`deploy/k8s/activity-core-llm-connect/externalsecret.yaml`).
- T06 progress: activation update sent to ops-warden; `openrouter-llm-connect`
can now leave draft once the catalog confirmation lands.
- Scope note: this closes the CoulombCore lane the CCR describes. The separate
llm-connect instance on the railiance01 k3s cluster still consumes its
bootstrap-provisioned Secret; migrating it is railiance01-cluster work, not
part of CCR-2026-0003.
## T07 completed 2026-07-02
Lifecycle operations documented in
`docs/credential-lane-lifecycle-runbook.md`: the canonical per-action
procedure is generated by `scripts/credential-change.py lifecycle-plan
<CCR> --action {deactivate|rotate|compromise}`, and the runbook adds the
lane-specific consumer facts (materialized-Secret persistence, second
consumers, restart requirements, provider-side revocation for the OpenRouter
key) plus the post-rotate verification contract. Front-door disable comes
first in every action; audit evidence is never deleted; values stay in
OpenBao/operator custody.
## Recovery evidence reported 2026-09-04
Activity-core message `dcc0d7cc-0046-4c47-ba1c-fb1e0046e18e` supersedes
`c32901ff-2072-4b93-81cd-db93dddfa5f3`. Owner decision
`914e0e04-fd17-4fba-b2d5-be38f7aca185` approved intentional reuse from the
reins provider lane into the canonical activity-core lane. Activity-core reports
KV version 2, ESO synchronization, rollout, health and completion HTTP 200.
This is owner-reported evidence, not an independent platform live verification;
no further replacement was requested.

View file

@ -0,0 +1,154 @@
---
id: RPF-WP-0005
type: workplan
title: "reuse-surface Runtime Secrets OpenBao Lane"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-07-07"
updated: "2026-07-07"
depends_on_workplans:
- RAILIANCE-WP-0007
- RAILIANCE-WP-0008
related_repos:
- railiance-apps
- reuse-surface
- ops-warden
state_hub_workstream_id: "7fe172fe-d987-52a0-aa36-67d6a1dd5a7c"
---
# RPF-WP-0005 — reuse-surface Runtime Secrets OpenBao Lane
## Goal
Promote reuse-surface hub runtime secrets from bootstrap Kubernetes Secret
custody to a reviewed OpenBao workload KV lane delivered by External Secrets
Operator, matching the platform pattern used by issue-core, OpenRouter, and
Forgejo mailer.
Today both secrets live in `reuse/reuse-surface-env` on Railiance01:
| Field | Consumer |
| --- | --- |
| `REUSE_SURFACE_TOKEN` | Hub write API; operators via `warden access` / kubectl |
| `REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET` | Hub webhook receiver; Forgejo org webhook HMAC |
Functional custody in K8s is sufficient for production today (live since
2026-07-07). This workplan is **hygiene and standardization**, not a blocker.
No task may paste, commit, log, or send secret values through Git, State Hub,
chat, prompts, shell history, or workplan text.
## Proposed contract
| Item | Proposed value |
| --- | --- |
| CCR | `CCR-2026-0005` |
| Tenant/org | `reuse` |
| Workload | `reuse-surface` |
| KV mount | `platform` |
| OpenBao CLI path | `platform/workloads/reuse/reuse-surface/runtime-secrets` |
| Secret fields | `REUSE_SURFACE_TOKEN`, `REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET` |
| Read policy | `workload-kv-read-reuse-surface-runtime` (name TBD at CCR) |
| K8s auth role | `external-secrets-reuse-surface` (name TBD at CCR) |
| ExternalSecret | `reuse/reuse-surface-runtime` → target Secret `reuse-surface-env` |
| ops-warden catalog | migrate `reuse-surface-hub-write-token` handoff to Bao path |
Dual-consumer note: Forgejo org webhook must be updated whenever the webhook
HMAC rotates — document in `railiance-apps/docs/reuse-surface-on-railiance01.md`
and keep `make reuse-forgejo-webhook` idempotent.
## Draft OpenBao + ESO Lane
```task
id: RPF-WP-0005-T01
status: done
priority: medium
state_hub_task_id: "4e7f5200-37a5-5208-b01e-a9633fb056df"
```
- Draft CCR for the lane (path, fields, policy, k8s role, ESO target)
- Align with `docs/openbao.md` path convention and
`docs/credential-lane-lifecycle-runbook.md`
- Negative review: no secret values in CCR or workplan text
**2026-07-07:** Drafted `CCR-2026-0005` and policy
`openbao/policies/workload-kv-read-reuse-surface-runtime.hcl`. Metadata review
confirms Railiance01 `reuse/reuse-surface-env` (two fields), ESO operator SA,
and forgejo-style interim ClusterSecretStore delivery. CCR remains `proposed`;
T02 blocked on platform-operator and reuse-surface-owner approval.
## Platform Apply And Verification
```task
id: RPF-WP-0005-T02
status: done
priority: medium
state_hub_task_id: "01b661a1-315b-566c-ad15-00cb71640f5a"
```
Blocked on CCR-2026-0005 approval.
- Apply OpenBao policy + Kubernetes auth role (mirror forgejo-mailer / issue-core scripts)
- Seed path from existing cluster Secret (one-time operator step; value never logged)
- Add `manifests/reuse-surface-runtime-externalsecret.yaml` in `railiance-apps`
- Verify ExternalSecret `SecretSynced` and pod env injection after rollout
- Record non-secret audit evidence (positive + negative reads)
**2026-07-07:** CCR-2026-0005 approved; delegated metadata apply recorded; KV path
seeded (version 1); `openbao-reuse` ClusterSecretStore + `reuse-surface-runtime`
ExternalSecret live on Railiance01 (`SecretSynced` 2026-07-07T20:33:17Z). Positive:
`/v1/federated` 200, signed webhook 200. Negative: default-policy token denied on
path. CCR status `verified`. T03 catalog migration remains open.
## Consumer Handoff And Catalog Migration
```task
id: RPF-WP-0005-T03
status: done
priority: low
state_hub_task_id: "a521036b-2858-5a42-9d23-6f142137ee6d"
```
Blocked on T02 verification.
- Update `railiance-apps/docs/reuse-surface-on-railiance01.md` custody section
- Update ops-warden `reuse-surface-hub-write-token` playbook + routing catalog
(`fetch_command``bao kv get -field=...`)
- Add lane to `docs/workload-kv-access-lanes.md`
- Deprecate direct kubectl fetch as primary handoff (keep as break-glass note)
**2026-07-07:** ops-warden catalog `reuse-surface-hub-write-token` migrated to
`bao kv get -field=REUSE_SURFACE_TOKEN platform/workloads/reuse/reuse-surface/runtime-secrets`
(owner `railiance-platform`, `resolvable=true`). Playbook, CredentialRouting index,
`docs/workload-kv-access-lanes.md`, and railiance-apps export handoff updated; kubectl
documented as break-glass only. CCR-2026-0005 front door `ready`/`resolvable=true`.
## Forgejo Webhook Rotation Runbook
```task
id: RPF-WP-0005-T04
status: done
priority: low
state_hub_task_id: "cf1f198e-2af2-5f62-8e3b-1edfc9f537c8"
```
Blocked on T03.
- Document rotation: update OpenBao field → ESO sync → rollout →
`make reuse-forgejo-webhook` (updates org hook secret)
- Add smoke: signed webhook POST returns 200/accepted or expected no-op
**2026-07-07:** Added `docs/reuse-surface-runtime-secrets-rotation-runbook.md`,
CCR-2026-0005 lane in `docs/credential-lane-lifecycle-runbook.md`, and
`railiance-apps` `make reuse-webhook-smoke` (unsigned 401, signed no-op 200,
ESO Ready, `/v1/federated`). Live smoke passed on production.
## Acceptance
- [x] Both fields readable from `platform/workloads/reuse/reuse-surface/runtime-secrets`
- [x] ESO owns `reuse-surface-env`; manual `kubectl create secret` no longer required for steady state
- [x] `warden access reuse-surface-hub-write-token --fetch` uses OpenBao path
- [x] Forgejo org webhook and hub HMAC aligned (smoke verifies signed/unsigned paths; rotation runbook documents reconcile step)

View file

@ -0,0 +1,130 @@
---
id: RPF-WP-0006
type: workplan
title: "rapp-openbao extraction boundary"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-07-25"
updated: "2026-07-26"
state_hub_workstream_id: "8d292bab-76dd-5ac3-b39d-6bc460ce27d7"
---
# RPF-WP-0006 - rapp-openbao extraction boundary
## Goal
Prepare the first `rapp-*` extraction by separating the OpenBao workload package
surface from the broader S3 platform ownership in `railiance-platform`.
This workplan follows the first-wave `rapp-*` order defined in
`/home/worsch/railiance-master/docs/rapp-first-wave-candidates.md`.
## Boundaries
This workplan may:
- identify which OpenBao assets belong to a future `rapp-openbao`
- record which OpenBao-related concerns remain platform-owned
- prepare the handoff contract between the future wrapper and S3 platform policy
It must not:
- weaken `railiance-platform` as the owner of secrets custody and policy
- move shared credential lane or approval machinery into the workload wrapper
- require a repo rename or live deployment change before the package boundary is clear
## T01 - Separate package assets from platform-governance assets
```task
id: RPF-WP-0006-T01
status: done
priority: high
state_hub_task_id: "5c0e5411-e47a-5a90-9005-b01762351c0a"
```
Classify the current OpenBao-related files into:
- workload package assets for `rapp-openbao`
- shared platform governance and policy that stays in `railiance-platform`
Done when the distinction is written against real file paths and current usage.
2026-07-25: Added `docs/rapp-openbao-boundary.md` with file-path classification
for OpenBao package assets, split-before-move assets, and retained S3
governance material.
## T02 - Define the first `rapp-openbao` move set
```task
id: RPF-WP-0006-T02
status: done
priority: high
state_hub_task_id: "3e5f8893-d440-5ce5-a436-3f84edad2e7b"
```
Prepare the first move set for the wrapper, including likely candidates such as:
- Helm values and middleware for the OpenBao workload
- UI overlay wrapper assets
- workload-specific deploy/verify scripts
- workload-health and recovery runbooks
Done when the future wrapper has a concrete first file inventory.
2026-07-25: Recorded the first move set in
`docs/rapp-openbao-boundary.md`, centered on Helm values, UI overlay assets,
OpenBao deploy/verify helpers, and the matching `Makefile` targets.
## T03 - Record the retained S3 platform responsibilities
```task
id: RPF-WP-0006-T03
status: done
priority: high
state_hub_task_id: "9a6a2cd9-9306-5721-9652-85f007043c19"
```
Record what must remain in `railiance-platform`, including:
- runtime secret custody as a platform capability
- workload lane policy and approval flows
- cross-workload secret delivery conventions
- platform-wide operator guidance that is not OpenBao-package specific
Done when the wrapper cannot be mistaken for the new S3 ownership home.
2026-07-25: Recorded retained S3 responsibilities in
`docs/rapp-openbao-boundary.md`, including platform-admin policy, workload KV
lane governance, delegated automation, credential grants, and SSH signing.
## T04 - Prepare the repo-creation handoff for `rapp-openbao`
```task
id: RPF-WP-0006-T04
status: done
priority: medium
state_hub_task_id: "a276f76b-48a7-512c-b0f8-786225d88683"
```
Blocked on T01-T03 and the creation of the future `rapp-openbao` repo.
Prepare the minimal handoff needed to create the wrapper repo without breaking
current operators or losing source-of-truth clarity inside `railiance-platform`.
Done when the future package extraction can start from a stable boundary.
2026-07-26: Added `docs/rapp-openbao-compatibility-handoff.md` to define the
current migration-window split: `rapp-openbao` is now the canonical home for
package-owned deploy/overlay/smoke assets, while `railiance-platform` retains
bootstrap, policy, credential-lane governance, and authenticated verification.
`docs/rapp-openbao-boundary.md`, `docs/openbao.md`, and `SCOPE.md` now point to
that split explicitly, so current operators can keep using temporary platform
copies without losing source-of-truth clarity.
## Notes
This workplan is repo-local. The framework-level sequencing stays in
`railiance-master/workplans/RMASTER-WP-0017-rail-rapp-reef-repo-separation.md`.

View file

@ -0,0 +1,139 @@
---
id: RPF-WP-0007
type: workplan
title: "Forgejo admin PAT OpenBao consumer cutover"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-07-25"
updated: "2026-07-26"
related_repos:
- activity-core
- ops-warden
state_hub_workstream_id: "f4fd52ba-4e84-52e7-a8d7-667b72d3cc1b"
---
# RPF-WP-0007 - Forgejo admin PAT OpenBao consumer cutover
## Goal
Cut the Forgejo package-prune consumer over from workstation-local PAT handling
to the approved OpenBao lane recorded in `CCR-2026-0006`.
The immediate trigger is the unread `ops-warden` coordination message from
July 12, 2026 requesting that `tools/cmd/forgejo-package-prune` stop depending
on `/tmp/forgejo-tegwick-api-token` as the steady-state fallback when
`FORGEJO_ADMIN_TOKEN` is unset.
## Boundaries
This workplan may:
- update the Forgejo package-prune token resolution path
- update package-prune runbooks and operator handoff docs
- coordinate the consumer contract with the approved credential lane
It must not:
- move platform secret-governance ownership out of `railiance-platform`
- paste token values into Git, State Hub, chat, shell history, or logs
- break the existing prune workflow before the replacement path is verified
## T01 - Inventory current consumer and fallback paths
```task
id: RPF-WP-0007-T01
status: done
priority: medium
state_hub_task_id: "3e8ee5f4-cdca-5803-8a15-86156faf5673"
```
Record where the current Forgejo admin PAT is consumed and documented, including:
- `scripts/forgejo_package_prune.py`
- `tools/cmd/forgejo-package-prune`
- `docs/forgejo-package-prune.md`
- any activity-core automation handoff that still assumes workstation file drops
Done when the remaining `/tmp/forgejo-tegwick-api-token` dependency surface is explicit.
2026-07-26: Confirmed the remaining legacy surface in
`scripts/forgejo_package_prune.py` and `docs/forgejo-package-prune.md`; the
approved lane reference already lived in `CCR-2026-0006` and
`docs/workload-kv-access-lanes.md`.
## T02 - Implement the approved non-file fallback path
```task
id: RPF-WP-0007-T02
status: done
priority: high
state_hub_task_id: "2684056f-39a9-503a-91df-36f287fe4654"
```
Change the package-prune consumer so that, when `FORGEJO_TOKEN` and
`FORGEJO_ADMIN_TOKEN` are unset, it uses the approved OpenBao-backed access
path rather than the workstation file as the steady-state fallback.
Done when the token loader prefers the approved lane and the old file drop is
demoted to break-glass or removed if no longer needed.
2026-07-26: `scripts/forgejo_package_prune.py` now resolves
`platform/workloads/forgejo/forgejo-admin` field `API_TOKEN` through `bao kv get`
when `FORGEJO_TOKEN` and `FORGEJO_ADMIN_TOKEN` are unset. Explicit
`FORGEJO_TOKEN_FILE` / `FORGEJO_ADMIN_TOKEN_FILE` remain supported as attended
break-glass handoffs, and `/tmp/forgejo-tegwick-api-token` only works when
`FORGEJO_ALLOW_LEGACY_FILE_FALLBACK=1` is set.
## T03 - Update operator guidance and lane references
```task
id: RPF-WP-0007-T03
status: done
priority: medium
state_hub_task_id: "34ecd9c9-486f-5509-bb74-6836d3515100"
```
Refresh the operator docs so they align with `CCR-2026-0006`, the routing
catalog, and the final consumer behavior.
Done when the package-prune runbook no longer presents the workstation file as
the primary path.
2026-07-26: Updated `docs/forgejo-package-prune.md` so the primary operator path
is `warden plan` + `bao login` against `forgejo-admin-workload-kv-read`; the
retired `/tmp` drop is now documented as legacy unwind only, with explicit
break-glass env/file overrides kept for attended sessions.
## T04 - Verify prune workflow and close the inbox request
```task
id: RPF-WP-0007-T04
status: done
priority: medium
state_hub_task_id: "90355dee-66dd-593f-9444-a42ff15f2398"
```
Blocked on T01-T03.
Verify the consumer cutover with a non-secret dry-run and close the originating
coordination loop from `ops-warden`.
Done when the replacement path is verified and the old unread message has a
clear repo-local follow-up trail.
2026-07-26: Added focused token-resolution coverage in
`tests/test_forgejo_package_prune.py` for direct env, explicit file overrides,
OpenBao fallback, legacy-file gating, and operator-facing failure text. This
closes the `ops-warden` coordination loop triggered by message
`5be8e500-ebf5-405b-9aec-c902353ae719`; the repo-local follow-up trail is now in
this workplan, the prune runbook, and the loader implementation.
## Acceptance
- [x] `forgejo-package-prune` no longer defaults to `/tmp/forgejo-tegwick-api-token`
- [x] The steady-state fallback path reads the approved OpenBao lane from `CCR-2026-0006`
- [x] Operator docs present OpenBao / `warden` routing as the primary path
- [x] Repo-local tests cover the new token-resolution behavior without reading secret values

View file

@ -0,0 +1,137 @@
---
id: RPF-WP-0008
type: workplan
title: "Credential-change test suite stabilization"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-08-11"
updated: "2026-08-11"
related_repos:
- rapp-postgres
- ops-warden
state_hub_workstream_id: "ec6b9fe2-5aae-5eb0-ba05-c1be241eda6b"
---
# RPF-WP-0008 - Credential-change test suite stabilization
## Goal
Make `python3 -m unittest discover -s tests -p 'test_credential*.py'` pass
non-interactively again, without weakening any approval or front-door gate.
Origin: the `rapp-postgres` coordination message of 2026-08-11, a residual from
`RAPP-POSTGRES-WP-0002-T04`. The focused credential broker suite was green
(9/9) at commit `9f6bdff`, but full discovery reported five failures unrelated
to the broker work.
## Diagnosis
None of the five failures were broker regressions. Two were missing or invalid
artifacts in `CCR-2026-0009`; three were test fixtures that had silently
decayed as real CCR lanes advanced through their lifecycle.
The decay is the important finding. Three refusal tests used the live
`CCR-2026-0002` file as their "unapproved CCR" fixture. That lane has since
been approved, applied and activated, so the refusal those tests assert stopped
being the correct behaviour for that input — the gate did exactly the right
thing and the tests failed anyway. `applier-apply` then walked past its
(correctly ordered) blocker check into the interactive confirmation prompt and
raised `EOFError` under a non-interactive runner. A refusal test that reads its
approval state off a mutable production artifact will keep breaking every time
a lane advances.
## Boundaries
This workplan may:
- repair credential-change test fixtures and add missing CCR source artifacts
- correct schema-invalid field values in credential change requests
It must not:
- relax `applier_readiness_blockers`, `runbook_readiness_blockers`, or the
front-door `resolvable`/`status` coupling in `scripts/credential-change.py`
- change grant delivery or revocation semantics in `credential-grants/catalog.yaml`
- write or move any secret value
## Tasks
```task
id: RPF-WP-0008-T01
status: done
priority: high
state_hub_task_id: "300632b3-f89c-523e-a31f-b9bc98c8df30"
```
Repair `CCR-2026-0009` (qonto-assistant workload KV read lane) so it validates:
- add the missing `openbao/policies/workload-kv-read-qonto-assistant.hcl`,
scoped read-only to `tenants/{data,metadata}/binky/qonto-api` and mirroring
the `workload-kv-read-binky-qonto-api.hcl` shape
- replace the schema-invalid `access_frontdoor.readiness: proposed` with
`pending-review`, which is the correct state for a CCR still in review
The CCR stays `status: proposed` with `resolvable: false` — this task adds the
missing source artifact, it does not advance the lane.
```task
id: RPF-WP-0008-T02
status: done
priority: high
state_hub_task_id: "9533e97b-06dc-58d5-8249-fe299bad438d"
```
Stop the refusal tests depending on live CCR lifecycle state. Add a
`unapproved_ccr()` helper to `tests/test_credential_change.py` that materializes
a temp copy normalized to `status: proposed`, no review comments,
`readiness: pending-review`, `resolvable: false`, and point
`test_applier_dry_run_refuses_unapproved_ccr`,
`test_applier_apply_refuses_unapproved_ccr`,
`test_runbook_refuses_unapproved_ccr` and
`test_operator_commands_refuse_unapproved_ccr` at it.
This also removes the `EOFError`: with a genuinely unapproved CCR,
`command_applier_apply` returns 1 at its blocker check and never reaches the
confirmation prompt. The gate ordering in `scripts/credential-change.py` was
already correct and was left untouched.
```task
id: RPF-WP-0008-T03
status: done
priority: medium
state_hub_task_id: "1b417fe7-6703-5663-9f3c-3a040ce1fe6f"
```
Fix `test_approve_records_comment_but_unconfirmed_claim_still_blocks_apply`,
which demoted an active CCR to `approved` while leaving
`access_frontdoor.resolvable: true`, tripping the
`resolvable=true requires status active` rule. Build the fixture from
`unapproved_ccr()` so the front-door state is consistent with the status the
test actually wants. The validation rule is correct and unchanged.
```task
id: RPF-WP-0008-T04
status: done
priority: high
state_hub_task_id: "83a4e8da-5ebb-5006-8aa8-2fe02169f9ba"
```
Confirm acceptance against the requested criteria:
- `python3 -m unittest discover -s tests -p 'test_credential*.py' < /dev/null`
— 52 tests, OK
- full repo discovery `-p 'test_*.py'` — 61 tests, OK
- `make credential-change-validate` — all nine CCRs OK
- `python3 scripts/credential-grants-validate.py` — catalog valid, 3 grants
- both `rapp-postgres/audit-core-*` `openbao-database-credential` grants still
carry `delivery.allowed: [exec-env]` with `child_only`/`redact_logs`, and
`revocation.required: true` with `on_exec_exit: true`
## Outcome
Suite green non-interactively; no gate, blocker, validation rule or grant
semantic was modified. The one behavioural change outside tests is the addition
of a previously missing least-privilege policy artifact.

View file

@ -0,0 +1,212 @@
---
id: RPF-WP-0009
type: workplan
title: "Platform rapp consistency and deployment-management contract"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-08-11"
updated: "2026-08-13"
related_repos:
- railiance-master
- the-custodian
- reef-railiance
- rapp-openbao
- rapp-postgres
state_hub_workstream_id: "cd85bf28-7050-59a7-9af2-a7455b3df0fb"
---
# RPF-WP-0009 - Platform rapp consistency and deployment-management contract
## Goal
Bring the platform-owned managed workload packages (`rapp-openbao`,
`rapp-postgres`) onto one consistent, verifiable declaration and operations
contract, and establish the S3 pattern that later platform-service rapps are cut
from.
This workplan does **not** own the four-axis repo family model. That model lives
in `railiance-master/docs/repository-axes.md` and is being promoted to custodian
canon separately. S3's job here is to be the first family member that actually
conforms, and to supply the platform-service reference shape.
## Findings that motivate this workplan
Surveyed 2026-08-11 against the live cluster and all six family repos.
The concepts are in better shape than their enforcement. The four families are
cleanly defined and `RMASTER-WP-0018`/`RMASTER-WP-0019` in `railiance-master` are both
`finished`. Nothing, however, validates a declaration file, so the model has
been drifting quietly since the first wave.
1. **The rapp population diverged from the plan of record.** Planned wave 1 was
`rapp-openbao``rapp-vergabe-teilnahme``rapp-forgejo`. Actual is
`rapp-openbao`, plus `rapp-postgres` and `rapp-qonto` created ad hoc from
immediate need. `rapp-vergabe-teilnahme` was never built, so the
*user-facing* wrapper pattern it existed to prove is still unproven while the
platform-service pattern has been proven twice.
2. **`rapp.yaml` has no schema and has drifted three ways.** The bootstrap
contract names eight required fields but no shapes. Result: the
rollout/smoke/rollback contracts are mutually unreadable (`supported_commands`
lists in openbao and postgres versus `default_mode`/`production_gate`,
`required:` and `order:` in qonto); only qonto carries `contract_version`,
`readiness_state`, `data_classification` and `criticality`; only postgres has
`consumer_contract`; `workload_identity.name` is the workload name twice and
the repo name once. `rail.yaml` by contrast is consistent — the rail family
got a tighter contract than the rapp family.
3. **The reef binding registry is stale.** `reef-railiance` declares
`bound_rapps: [rapp-qonto]` while `rapp-openbao` and `rapp-postgres` both run
on Railiance01. The reef's stated purpose is recording what is bound where,
and it missed two of three.
4. **The model is not canon.** Custodian canon has no reef/rail/rapp standard,
and `repo-classification-standard_v1.0` does not admit the prefixes — the
same root cause as the open C-31 failures on `RAPP-*-WP-` work-record ids.
5. **Scale gap.** ~17 live non-system workloads, 3 rapps.
## Decisions taken (2026-08-11, operator-approved)
- **Granularity: grouped by bounded context.** One rapp per cohesive service
group that deploys, versions and rolls back together, not one per deployable.
A grouped rapp must declare its member services explicitly — grouping is only
legitimate where the members share a rollout and rollback fate.
- **Scope split.** S3 owns its own rapps and the platform-service pattern.
Schema, validator and family-model changes are routed to `railiance-master`;
canon promotion is routed to `the-custodian`. S3 does not author them.
- **Canon: promote now**, so `fix-consistency` can validate declarations.
- **Wave-1 gap: build `rapp-vergabe-teilnahme`** to prove the user-facing shape
before further extraction. That build is `railiance-apps`/master territory,
not S3's; this workplan only supplies the platform-service half of the pattern.
## Boundaries
This workplan may:
- change declarations and operations contracts of platform-owned rapps
(`rapp-openbao`, `rapp-postgres` — both declare `ownership_repo: railiance-platform`)
- define the S3 platform-service rapp reference pattern
- define how credential lanes and grant catalog entries bind to a rapp
- send proposals to the repos that own the model, schema and canon
It must not:
- author the four-axis model, the `rapp.yaml` schema, or canon standards here
- edit `reef-railiance`, `rail-*`, or `railiance-master` declarations directly
- absorb platform-wide policy into a rapp (the "shadow S3 repo" failure mode
named in `rapp-first-wave-candidates.md`)
- change any credential gate, grant delivery, or revocation semantic
## Tasks
```task
id: RPF-WP-0009-T01
status: done
priority: high
state_hub_task_id: "512adfef-a561-5b60-b4f3-f60e67696ef9"
```
Publish the S3 platform-service rapp pattern as `docs/rapp-platform-service-pattern.md`:
the reference `rapp.yaml` shape for a platform service, the ownership split
between rapp and S3, the grouped-rapp member declaration rule, and the
credential-lane binding. Generalize from the existing
`docs/rapp-openbao-boundary.md` and `docs/rapp-postgres-boundary.md` rather than
inventing a third boundary vocabulary.
```task
id: RPF-WP-0009-T02
status: done
priority: high
state_hub_task_id: "29b55ae5-74e2-5110-892c-ebb2b0cbf550"
```
Bring `rapp-openbao/declarations/rapp.yaml` and
`rapp-postgres/declarations/rapp.yaml` into a single consistent shape: add
`contract_version`, `readiness_state`, `data_classification` and `criticality`
(present in `rail.yaml` and in `rapp-qonto`, absent in both platform rapps), fix
`workload_identity.name` to be the workload rather than the repo where it drifts,
and align the rollout/smoke/rollback contract shape. Coordinate the target shape
with the `railiance-master` schema proposal (T04) before editing, so the two do
not converge on different answers.
```task
id: RPF-WP-0009-T03
status: done
priority: medium
state_hub_task_id: "53cb32e3-5cdc-5664-b7be-c46828849c56"
```
Correct the reef binding registry: propose to `reef-railiance` that
`bound_rapps` include `rapp-openbao` and `rapp-postgres`, with the evidence that
both are live on Railiance01. Proposal only — `reef-railiance` owns its
declaration. Ask that the binding be derived or checked rather than hand-listed,
since a hand-listed registry is what went stale.
```task
id: RPF-WP-0009-T04
status: done
priority: high
state_hub_task_id: "b157de50-ff34-57aa-800e-d2d79e65a731"
```
Route the schema and validator proposal to `railiance-master`: a
`rapp.schema.json` covering the eight required fields plus the four consistency
fields, a validator that runs over every family declaration, the grouped-rapp
member field, and a refresh of `rapp-first-wave-candidates.md` to reflect what
was actually built and the grouped-context wave-2 inventory.
```task
id: RPF-WP-0009-T05
status: done
priority: medium
state_hub_task_id: "57ed22c9-94b9-526d-beb0-0a090eeaac3a"
```
Route the canon promotion proposal to `the-custodian`: a
`railiance-repo-families` standard, declaration schemas under `canon/standards/schemas/`,
and admission of the `rail-`/`rapp-`/`reef-` prefixes into
`repo-classification-standard` and `work-record-types.yaml` — the latter closing
the open C-31 failures on multi-segment `RAPP-*-WP-` ids.
```task
id: RPF-WP-0009-T06
status: done
priority: medium
state_hub_task_id: "4423fa37-7e0d-5e2d-ac39-f6b505c60243"
```
Define how a rapp binds to its credential lane: the relationship between
`credential-grants/catalog.yaml` entries, CCR lanes, and a rapp's
`runtime_dependencies`/`secret_references`, so that a new rapp has one documented
way to acquire runtime secrets instead of re-deriving it per package. Must not
alter existing gate, delivery or revocation semantics — this documents and binds
the existing lane, it does not build a parallel one.
## Outcome
Session of 2026-08-11: T01 published the S3 platform-service pattern; T03, T04
and T05 routed their proposals to the owning repos (reef-railiance
f0f44ce0, railiance-master 04c776c4, the-custodian cc64764a). T02 was
held until railiance-master settled the schema.
Session of 2026-08-13: T02 done. `rapp-openbao` and `rapp-postgres`
`declarations/rapp.yaml` now pass `schemas/rapp.schema.json` (contract
metadata, `composition`, `bound_reefs: [reef-railiance]`, `commands`,
`smoke_contract.required`, `rollback_contract.order`). Live inventory for
the T06 coverage check is
`docs/evidence/reef-railiance-deployables.json`, refreshable with
`scripts/capture-reef-deployables.py`. Family-validator remainder is
outside this task: `rapp-qonto` still on the pre-schema shape, three
undeclared `rapp-*` engine repos, and `reef-railiance` `bound_rapps`
still hand-listed.
Session of 2026-08-13 (later): T06 done. Binding recipe is
`docs/rapp-credential-lane-binding.md`. Standing secrets bind through
a CCR `target.rapp` + `secret_references`; short-lived leases bind
through grant `rapp_id` (required when the grant id is `rapp-*/…`).
Stamped `rapp-postgres` catalog grants and `CCR-2026-0009`. Gate,
delivery, and revocation semantics are unchanged. Workplan finished.

View file

@ -0,0 +1,89 @@
---
id: RPF-WP-0010
type: workplan
title: "Publish apps-pg resource and recovery evidence"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-08-11"
updated: "2026-08-14"
state_hub_workstream_id: "ee4a867f-eae2-5e85-b0b1-9802a3f31f10"
related:
- RAILIANCE-WP-0003
- RESOURCE-WP-0003
---
# RPF-WP-0010 — apps-pg resource evidence
## Goal
Provide platform-owned evidence for `resource:railiance:apps-pg`: demand,
utilization, recovery, operations labor, and consumer allocation. Origin:
`RESOURCE-WP-0003-T04` delegated evidence gap.
## T01 — Publish database capacity and utilization
```task
id: RPF-WP-0010-T01
status: done
priority: high
state_hub_task_id: "4cba1304-9a5d-5a26-8706-fe1df7ca5d4e"
```
Record instance count, CPU/memory requests and observed use, provisioned and
used storage, database growth, connections, I/O or other available saturation
proxies, and consumer database attribution with timestamps and provenance.
Done when resource-control can forecast database capacity without reading
application data or credentials.
## T02 — Publish recovery and operational evidence
```task
id: RPF-WP-0010-T02
status: done
priority: high
state_hub_task_id: "382751c3-b05c-51d1-9a9c-d64363978851"
```
Record backup coverage, restore-test results, recovery objectives, upgrade and
maintenance cadence, incidents, and setup/recurring operator labor. Keep
service procedures and execution in railiance-platform.
Done when the portfolio can distinguish verified recovery, unresolved local
storage risk, and platform labor cost.
## T03 — Define shared database allocation drivers
```task
id: RPF-WP-0010-T03
status: done
priority: medium
state_hub_task_id: "01148c0f-9d21-5c64-be99-cb5f42697274"
```
Publish candidate consumer drivers such as database storage GB-month,
connections, transactions, or measured I/O. Include shared overhead,
unattributed residual, method version, and uncertainty.
Done when resource-control can allocate a referenced apps-pg cost without
making railiance-platform a financial ledger.
## Outcome
Published 2026-08-14. Observation
`docs/evidence/RPF-WP-0010-apps-pg-observation.json` and narrative
`docs/evidence/RPF-WP-0010-apps-pg-resource-evidence.md`.
- T01: 1/1 instance, 10Gi local-path, 590 MiB PGDATA, no CPU/memory
requests, two consumer catalogs (~16.6 MiB), `vergabe_db` unused.
- T02: no ScheduledBackup, not an Option A target, RPO unbounded, RTO
undefined — unresolved local-storage risk. Labor: ~6h setup, ~0.5h/month
recurring, high uncertainty, no EUR.
- T03: method `apps-pg-dbbytes-v1`, driver `database_gb`, residual
`platform:apps-pg-overhead` (94.8% of used volume is WAL/catalog).
Refresh: `make apps-pg-evidence`. resource-control can now replace the
illustrative `examples/control-cycle/apps-pg-*.json` fixtures.

View file

@ -0,0 +1,293 @@
---
id: RPF-WP-0011
type: workplan
title: "Architecture cleanup backlog, WSJF-prioritized"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-08-12"
updated: "2026-08-15"
related_repos:
- railiance-master
- railiance-infra
- railiance-cluster
- railiance-enablement
- railiance-telemetry
- railiance-forge
state_hub_workstream_id: "b3026cdc-ba4c-5da0-8e69-086e1691fca7"
---
# RPF-WP-0011 - Architecture cleanup backlog, WSJF-prioritized
## Goal
Turn the INTENT/SCOPE gap analysis into an ordered backlog, so cleanup happens
highest-value-least-effort first rather than in the order things were noticed.
Source: `history/2026-08-12-intent-scope-gap-analysis.md`.
## Boundary — read before working this
This workplan **coordinates and orders**; it does not execute. Every item below
belongs to another repo, and each carries its owning repo in the table. S3 does
not own fleet architecture cleanup — `railiance-master` does.
This backlog lives here because the analysis was done here. **It should be
adopted by `railiance-master`**, and this workplan closed, once master has a
place for it. Until then it is better recorded than lost. S3 executes only the
items marked `railiance-platform`.
## Scoring method
WSJF = **Cost of Delay ÷ Job Size**, where Cost of Delay = Value + Time
Criticality + Risk Reduction. Each component 110, job size 110.
Two honesty notes. These are **judgments, not measurements** — their value is
the relative ordering and the explicit reasoning, not the arithmetic. And job
sizes for unbuilt things (telemetry, the conformance loop) are the least
reliable numbers here, because nobody has scoped them yet.
## The backlog
| # | Item | Owner | V | TC | RR | Size | **WSJF** |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 1 | Converge the firewall allowlist; prune two stale grants | `railiance-infra` | 6 | 9 | 10 | 1 | **25.0** |
| 2 | Ratify the five pending decisions | `railiance-master` | 8 | 7 | 5 | 1 | **20.0** |
| 3 | Move the S1 goal off `railiance-hosts`, then retire it | `railiance-master` | 5 | 5 | 4 | 1 | **14.0** |
| 4 | Settle ArgoCD: adopt, retire, or relocate | `railiance-master` | 6 | 6 | 6 | 2 | **9.0** |
| 5 | Audit base role for further declared-vs-live drift | `railiance-infra` | 5 | 6 | 9 | 3 | **6.7** |
| 6 | `rapp.yaml` schema + family declaration validator | `railiance-master` | 8 | 5 | 7 | 3 | **6.7** |
| 7 | Reconcile hub capability attribution (C1) | `railiance-master` | 5 | 4 | 6 | 3 | **5.0** |
| 8 | Deployable coverage check: every deployable → one rapp | `railiance-master` | 7 | 4 | 7 | 4 | **4.5** |
| 9a | Telemetry: minimal metrics collection on railiance01 | `railiance-telemetry` | 8 | 5 | 5 | 4 | **4.5** |
| 9b | Telemetry: one expectation-based alert (needs 10b) | `railiance-telemetry` | 8 | 5 | 2 | 2 | **7.5** |
| ~~10a~~ | ~~Run `make verify`; tighten the UFW assertion~~ **DONE 2026-08-12** | `railiance-infra` | 8 | 7 | 9 | 12 | **12.0** |
| 10b | Schedule verification; route failures where they are seen | `railiance-infra` / Q7 owner | 7 | 6 | 8 | 3 | **7.0** |
| 11 | Consolidate the bootstrap path; sort inherited S2 content | `railiance-bootstrap` | 5 | 3 | 4 | 4 | **3.0** |
| 12 | Forge migration: `rapp-forgejo` + S4 layer transfer | `railiance-forge` / S4 | 6 | 4 | 4 | 5 | **2.8** |
| ~~13~~ | ~~Prove a restore end to end~~ **DONE 2026-08-15** | `railiance-platform` | 9 | 5 | 9 | 8 | **2.9** |
| ~~14~~ | ~~Valkey extraction + legacy postgresql-ha retirement~~ **DONE 2026-08-15** | `railiance-platform` | 5 | 4 | 4 | 5 | **2.6** |
| 15 | Sweep remaining Gitea prose (~31 files each) | `railiance-forge`, `railiance-apps` | 3 | 2 | 2 | 3 | **2.3** |
| 16 | Declare the Q6 cost boundary with `resource-control`/`fin-hub` | `railiance-master` | 4 | 3 | 3 | 4 | **2.5** |
| ~~17~~ | ~~Version S3 consumer service interfaces~~ **DONE 2026-08-15** | `railiance-platform` | 6 | 3 | 5 | 6 | **2.3** |
| 18 | Resolve the C4 ecosystem-modelling overlap | `railiance-master` / `railiance-fabric` | 4 | 3 | 4 | 5 | **2.2** |
| 19 | S4 build-out: pipeline templates, SDKs, portal | `railiance-enablement` | 7 | 3 | 4 | 9 | **1.6** |
## Reading the order
**Items 13 are nearly free and should happen first.** All three are "decided
but unmoved" — waiting on ratification or a single command, not on effort.
Item 1 in particular is a live security exposure: two standing firewall grants
point at addresses the ISP has since reassigned, and the fix is one convergence
run.
**Item 2 is the highest-leverage thing on the list.** Five pending
ratifications in `railiance-master` block items 6, 7, 8, 12 and parts of 9 and
10. It is a day of decisions that unblocks a quarter of the backlog.
**Items 9 and 10 are the strategic core** — Q2 and Q7, the two halves of the
self-evidencing thread five layers independently asked for. They were rescoped
on 2026-08-12 (see below) and item 10 turned out to be badly mis-sized: the
conformance checker already exists in `railiance-infra` and has simply not run
since 2026-03-09. **Slice 10a is now the highest-value item after the
ratifications** at WSJF 12.0.
**Item 13 is done, not blocked.** `RESOURCE-WP-0002` finished 2026-08-15.
Barman is live on `platform-pg`; T05 recorded full restore and PITR on
2026-08-14; daily backups have completed since. Pointer:
`docs/evidence/RPF-WP-0011-item13-restore-2026-08-15.md`.
**Item 19 is last and that is deliberate.** S4 build-out is the widest gap in
the family, but it is large, and item 12 gives S4 a smaller first
responsibility to prove delivery on. Building the portal before the forge
transfer would be starting at the hard end.
## Rescoping items 9 and 10 (2026-08-12)
### What was assumed
That Q2 Observability and Q7 Governance were both greenfield builds of
comparable size (5), sitting mid-table at WSJF 4.6.
### What is actually there
`railiance-infra` already has:
- `goss/baseline.yaml` — a host baseline assertion suite
- `ansible/roles/goss` + `ansible/playbooks/verify.yaml` — the runner
- `make verify` — runs it against all hosts, **commits TAP reports to git**, and
exits non-zero on failure
- `reports/*.tap` — an evidence trail, last written **2026-03-09**
So a check-and-evidence mechanism exists and is wired. Two things are wrong with
it, and neither is a build:
1. **Nothing runs it.** Five months of silence. A verification suite that is not
scheduled is a verification suite that does not exist.
2. **Its firewall assertion is too loose in exactly the place that mattered.**
It asserts `/6443\/tcp.*ALLOW/` — that the port is allowed. It does not
assert *from whom*. That regex passes identically whether 6443 is restricted
to one operator address or open to the entire internet, which is precisely
the drift that went undetected.
It would, however, have caught the *other* finding: it asserts
`Status: active`, and UFW is entirely inactive on `CoulombCore`.
### The rescoped slices
**Item 10 — conformance loop**
| Slice | Work | Size |
| --- | --- | --- |
| 10a | Run `make verify` against both hosts; tighten the UFW assertion to compare the live allowlist against `k3s_api_allowed_sources` rather than matching any ALLOW; fix what it surfaces | **2** |
| 10b | Schedule it, and route failures somewhere a human or agent actually sees | 3 |
| 10c | Extend to declaration conformance — rapp/rail/reef schema validation | blocked on item 6 |
| 10d | Deployable coverage: every live deployable maps to exactly one rapp | blocked on item 8 |
**Item 9 — telemetry**
| Slice | Work | Size |
| --- | --- | --- |
| 9a | Minimal metrics collection on `railiance01` | 4 |
| 9b | One expectation-based alert on the 10b path | 2 |
| 9c | Retention sufficient to prove a restore or rollout held | 3 |
| 9d | Dashboards and agent-queryable surfaces | 4 |
### The shared dependency worth naming
**10b and 9b are the same plumbing.** Both are "a check failed — now make that
visible to someone who can act." Building an alert path twice would be waste,
and building either of 9 or 10 *without* it produces logs nobody reads. That
path is the real first dependency for both, and it is small.
It also has an obvious candidate already in the fleet: the Goss suite commits
TAP evidence to git, and State Hub takes progress events. Neither is an alert,
but the gap between them is narrow.
### Revised positions
| Item | Old WSJF | New WSJF | Effect |
| --- | --- | --- | --- |
| 10a run + tighten Goss | 4.6 (as part of 10) | **12.0** (V8 TC7 RR9 / size 2) | rises to 4th overall |
| 10b schedule + alert path | — | **7.0** (V7 TC6 RR8 / size 3) | rises above ArgoCD |
| 9b one real alert | — | **7.5** (V8 TC5 RR2 / size 2) | depends on 10b |
| 9a metrics collection | 4.6 | **4.5** (unchanged) | stays mid-table |
**Conclusion: item 10a should be done next, immediately after the ratifications.**
It is two points of effort, it directly closes the hole that produced this
session's security finding, and it converts an existing but dormant asset into a
working control. Item 9 is correctly mid-table and genuinely is a build.
### 10a outcome (2026-08-12) — the estimate held, and the check paid for itself immediately
Delivered in `railiance-infra`: `goss/baseline.yaml` became a template rendered
per host from the *same* inventory declaration that converges the host, so the
assertion cannot drift from what it checks. `verify.yaml` gained the
`vars_files` the bootstrap play already had — without it the firewall assertions
rendered empty and **silently asserted nothing**, which is worse than having no
check at all.
Result on `Railiance01`: 32 assertions, 31 pass. And the one failure is a real
defect the old assertion could never have seen:
> **Flannel VXLAN (`8472/udp`) is declared open to Anywhere**, with no source
> restriction — the same defect pattern as the k3s API rule. It is currently
> *absent* from the host, so converging the base role would **introduce** the
> exposure rather than correct drift.
Tracked as `RAIL-HO-WP-0009-T06`. Two things worth taking from this:
1. **The check earned its keep on first run** — it found a latent defect that
convergence would have activated, in under two points of effort.
2. **It is an argument for finishing 10b.** This run happened because a human
asked. Nothing schedules it, so the next latent defect waits for the next
person who happens to look.
### The lesson worth keeping
The original size-5 estimate for item 10 was wrong because nobody looked. The
gap analysis correctly identified "no conformance loop" as a *capability* gap,
but the capability was two-thirds built and unrun. **Before sizing a gap as a
build, check whether it is actually an abandonment.**
## Tasks
```task
id: RPF-WP-0011-T01
status: done
priority: high
state_hub_task_id: "d18ea91e-439b-57b4-a1b1-c39ede985579"
```
Produce the INTENT/SCOPE gap analysis across all `railiance-*` repos, comparing
declared aspiration against declared state against verified reality.
Delivered: `history/2026-08-12-intent-scope-gap-analysis.md`. Named the pattern
that organizes the backlog — every gap is *declared but unbuilt*, *built but
undeclared*, or *decided but unmoved*, and the third kind is cheapest to clear
while blocking the most.
```task
id: RPF-WP-0011-T02
status: done
priority: high
state_hub_task_id: "bc7e49aa-b4a0-5f25-a6ba-a738dad0b3be"
```
Score and order the backlog by WSJF, recording owner and reasoning per item so
the ordering can be argued with rather than merely followed.
```task
id: RPF-WP-0011-T03
status: done
priority: high
state_hub_task_id: "80c39586-8c2a-59b8-ab7a-578d479d45c7"
```
Route the backlog to `railiance-master` for adoption, with the recommendation
that it takes items 2, 3, 4, 6, 7, 8, 16 and 18 — the ratification and taxonomy
items — and that this workplan closes once adopted.
```task
id: RPF-WP-0011-T04
status: done
priority: medium
state_hub_task_id: "302c718a-133c-544d-8b79-3b876fd25fb5"
```
Rescope items 9 and 10 (telemetry MVP, conformance loop MVP) into deliverable
slices before committing to their position. Their size scores are the least
reliable on the list, and both are strategically central.
**Done 2026-08-12, and the answer changes the order.** See "Rescoping items 9
and 10" below. The headline: item 10 was mis-sized by a wide margin, because the
conformance checker **already exists**`railiance-infra` has a Goss baseline
suite with a `make verify` target that runs it against all hosts and commits TAP
reports to git. It has not run since **2026-03-09**, five months. The work is not
to build a loop; it is to run what exists, tighten two assertions, and schedule
it.
```task
id: RPF-WP-0011-T05
status: done
priority: medium
state_hub_task_id: "d3568127-b2b2-5806-84f5-e482a41ccf55"
```
Execute the S3-owned items in WSJF order — 13 (blocked on `9c21c0e0`), 14, 17 —
and keep this table current as items complete or their sizes become known.
**Done 2026-08-15.** Item 13 unblocked by `RESOURCE-WP-0002` finish: restore
already proven as T05 there; live `platform-pg-daily` backups completing.
Item 14: Bitnami postgresql-ha deploy path fail-closed (never live on
railiance01); Valkey has no S2 instance to extract and no consumer, so
deploy is gated. Item 17: `docs/s3-consumer-interfaces.md` v1.0.0.
## Outcome
T01T05 done. Routed to `railiance-master` (message `ac73b172`); master
took items 6 and 8 as WP-0021 and declined the rest. S3-owned items 13,
14 and 17 executed 2026-08-15 after the object-store purchase landed.
Workplan finished. Remaining backlog rows belong to other repos.

View file

@ -0,0 +1,66 @@
---
id: RPF-WP-0012
type: workplan
title: "Enforce resource-control consumption mode"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-08-14"
updated: "2026-08-15"
related:
- RESOURCE-WP-0005
origin: residual
origin_ref: RESOURCE-WP-0005
state_hub_workstream_id: "519e512d-bef0-54ca-85f0-6912a5219ed5"
---
# RPF-WP-0012 — enforce consumption mode
## Goal
Consume the `open` / `restricted` signal published by
`resource-control` and enforce it on new orders and elastic usage.
`resource-control` does not throttle the cluster.
Origin: `RESOURCE-WP-0005-T07`. Terms:
`resource-control/docs/TermsOfResourceProcurement.md` § 11.
Runbook: `resource-control/docs/resource-procurement-facility.md`.
## Boundaries
- Restricted monthly new-consumption is €50 transfer price minus that
month's interest. At the default €1 000 overdue balance that is
€45.83 transfer price / €38.19 delivered cost.
- Already-committed provider contracts are flagged to human financial
authority, not cancelled (OQ-2).
- Safety paths (backup, identity, ingress needed to avoid data loss)
may continue; overage is an exception line, not a hidden charge.
## T01 — Subscribe to the consumption-mode signal
```task
id: RPF-WP-0012-T01
status: done
priority: high
state_hub_task_id: "c758776c-bd2a-5d38-a9b7-94db66fc0568"
```
Define how platform procurement and admission read the latest
per-entity consumption mode and remaining transfer-price allowance for
the current period.
Done when a restricted entity cannot place a new order that would
exceed the published allowance, and an `open` entity is unchanged.
Delivered 2026-08-15: `scripts/consumption_mode.py`,
`docs/consumption-mode-enforcement.md`, `make consumption-preflight`.
Restricted + estimate above allowance exits 2. Open and missing-signal
admit. Safety/committed paths admit with an exception line. `apps-pg-deploy`
runs the gate when `CONSUMING_ENTITY` is set.
## Outcome
T01 done. Workplan finished. resource-control still owns the signal;
this repo only enforces it.

View file

@ -0,0 +1,165 @@
---
id: RPF-WP-0013
type: workplan
title: "Close agent high-risk OpenBao boundary coverage"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-08-21"
updated: "2026-08-22"
related:
- WARDEN-WP-0032
- RISK-F-0009
origin: routed
origin_ref: "State Hub message 828e4903-30fe-4903-acfd-cd2ecdda437d"
state_hub_workstream_id: "25c0b1c9-a1b7-5844-bd39-9db165fe196c"
---
# RPF-WP-0013 — Agent high-risk boundary coverage
## Goal
Make the OpenBao `agent-high-risk-boundary` deny every concrete high-risk KV
data path in the ops-warden routing catalog, verify the deployed policy, and
establish whether any agent identity actually carries the boundary.
## Boundaries
- Read policy documents, token-role metadata and capabilities only; never read
a Secret value.
- A deny is added only for a concrete catalog path graded `risk: high`.
- Pattern-only and non-KV lanes are reported but do not produce invented paths.
- Operator identities do not receive this boundary; it is for coding-agent
identities where deny must override any coincident workload read policy.
## T01 — Reconcile catalog coverage
```task
id: RPF-WP-0013-T01
status: done
priority: high
state_hub_task_id: "35855316-3282-52d2-922e-43af1c4495c0"
```
Run the capabilities-only ops-warden audit against the policy. The initial
2026-08-21 reconciliation found 17 high-risk lanes: six covered, six concrete
uncovered, and five without a concrete KV address. The final generated input
contains 19 high-risk lanes, 14 concrete entries, and five without an address.
No credential value was read.
## T02 — Close the concrete deny gaps
```task
id: RPF-WP-0013-T02
status: done
priority: high
state_hub_task_id: "9f2a6ffc-5f48-5e45-b636-76dff7d41743"
```
Add deny-data/read-metadata pairs for the six catalog paths: whynot-design npm,
rapp-qonto Keycape client, agent-harness Forgejo deploy key, audit-core senders,
email-connect transactional, and Scaleway bootstrap. Add regression coverage
for every concrete path currently emitted by the catalog audit.
Completed 2026-08-22. After ops-warden corrected two multi-field grades, the
source policy covers all 13 unique concrete catalog paths plus the additional
reviewed Core Hub path. The generated-input check reports all 14 concrete
catalog entries covered with none uncovered.
## T03 — Apply and verify live
```task
id: RPF-WP-0013-T03
status: done
priority: high
state_hub_task_id: "92b4b531-986d-54b4-a18b-cd8785dca5ce"
```
Under attended platform authority, upload the reviewed policy, read it back,
and rerun the catalog audit with `policy_source: server`. Inspect policy/token
role metadata to determine whether an agent identity carries the boundary and
whether any role combines it with a workload-read policy. Do not mint a token.
Completed initially on 2026-08-21 and reconciled again on 2026-08-22 under
attended `platform-admin` OIDC. OpenBao accepted the policy; normalized
readback matched source, and the final server-backed audit reported 19
high-risk lanes, 14 covered entries, zero uncovered, and five pattern/non-KV
lanes without a concrete address. No Secret value was read.
## T04 — Route the result
```task
id: RPF-WP-0013-T04
status: done
priority: medium
state_hub_task_id: "d394fcb7-1477-5dfe-9e4b-65bd14f08346"
```
Reply to ops-warden with the deployment evidence and remaining attachment
finding. Request a generated, versioned concrete-deny artifact so future policy
updates consume catalog output rather than relying on a hand-maintained list.
Completed 2026-08-21 via State Hub message
`fe727451-163a-4e14-8ce3-187fea8ce5b3`, including live audit counts, the zero-
attachment finding, the distinct-agent-identity blocker, and the requested
versioned generated artifact shape.
Ops-warden delivered revision `55f0f47a021375b8b25c924953d1b49a24e002c5`
on 2026-08-21. The platform now vendors it as a checked policy input and has a
repeatable upstream-equality plus policy-coverage command.
## T05 — Establish a distinct coding-agent identity
```task
id: RPF-WP-0013-T05
status: done
priority: high
state_hub_task_id: "6805ef60-bd20-5549-b955-cee94eb69dad"
```
The policy is live but no role attaches it. Do not add the boundary to
`platform-admin`: that is an attended human/operator role whose legitimate
recovery work may require the protected values. The identity owner must define
a distinct coding-agent issuance path, attach this boundary there, and prove
deny-wins behavior when combined with an otherwise readable workload policy.
Completed 2026-08-22. State Hub decision
`f0955252-7b20-4c80-86e9-f8080ec60793` establishes the platform-owned AppRole
`coding-agent-railiance-platform` as the operational machine identity. Its
single-use SecretIDs expire after five minutes; its tokens last at most 15
minutes, have eight uses, omit the default policy, and attach both
`agent-high-risk-boundary` and the otherwise-readable issue-core workload
policy. A live ephemeral login proved data capabilities resolve to `deny` and
metadata to `read`; the token was revoked without reading the KV value.
The exact-bound KeyCape JWT role is also live as the issuer-backed migration
target. KeyCape principal registration remains an identity hardening follow-up,
not a blocker to the active AppRole boundary.
## T06 — Consume the generated catalog artifact
```task
id: RPF-WP-0013-T06
status: done
priority: high
state_hub_task_id: "1b0c4f03-0ec7-5fcc-908d-beed236e4865"
```
Consume ops-warden's versioned `high-risk-data-paths.yaml` as a policy-check
input, add the two newly regraded issue-core and reuse-surface paths, and rerun
source plus live coverage. The input remains ops-warden's risk statement, not
the policy authority; railiance-platform continues to own every deny decision.
Completed 2026-08-22. The vendored input matches the clean upstream artifact,
the repository check covers all 14 entries, and the server-backed audit reports
zero uncovered. Evidence: `docs/evidence/agent-high-risk-boundary-2026-08-21.md`.
## Acceptance
- [x] Every concrete high-risk catalog path is denied in the source policy.
- [x] The live policy matches source and the server-backed audit passes.
- [x] A distinct AppRole attachment is established from live metadata.
- [x] Result and generated-artifact follow-up are routed to ops-warden.
- [x] A distinct coding-agent identity actually attaches the boundary.

View file

@ -0,0 +1,70 @@
---
id: RPF-WP-0014
type: workplan
title: "Hub-core candidate credential lanes"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-08-21"
updated: "2026-08-22"
related:
- CORE-WP-0010
- RAPPCOREHUB-WP-0002
- RAPP-POSTGRES-WP-0004
state_hub_workstream_id: "ab1c2145-528b-50af-b36e-161f28325848"
---
# Hub-core candidate credential lanes
## Extend exact-scope policy and projections
```task
id: RPF-WP-0014-T01
status: done
priority: high
state_hub_task_id: "b30ba596-14c8-54c2-92fc-530bbbc965a8"
```
Add only `database/creds/hub-core-runtime` and
`database/creds/hub-core-migration` to the existing namespace-limited Core Hub
database store, with separate five-minute ExternalSecret projections.
## Activate and verify production lanes
```task
id: RPF-WP-0014-T02
status: done
priority: high
state_hub_task_id: "68766bac-d032-57f9-84cd-a8725cbe0ada"
```
Apply the reviewed policy and projections after rapp-postgres creates the
roles. Verify store validity, SecretSynced status, role separation, and lease
rotation without reading or logging values.
Completed 2026-08-22. The exact policy, projections, SecretSynced state, role
separation, and production runtime/migration handoff passed on 2026-08-21. A
subsequent scheduled five-minute reconciliation advanced both target Secret
resource versions. After projected-volume propagation, the candidate watcher
replaced only its application child while the pod UID remained stable,
readiness stayed true, and the container restart count stayed zero. No
credential value was read or logged. Evidence:
`docs/evidence/core-hub-private-shadow-2026-08-21.md`.
## Hand off the private candidate
```task
id: RPF-WP-0014-T03
status: done
priority: high
state_hub_task_id: "0af38f5b-7f59-5e14-8046-646c6fe526cd"
```
Confirm both Secret metadata objects are ready, then hand the candidate
migration and rollout gate back to `RAPPCOREHUB-WP-0002-T03`.
Completed 2026-08-21. Both projected Secret metadata objects were Ready, the
migration completed, and the candidate advanced through all route groups to
production authority without exposing credential values.

View file

@ -0,0 +1,183 @@
---
id: RPF-WP-0016
type: workplan
title: "Version ephemeral custody projection and broker handoff"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-08-22"
updated: "2026-08-22"
related:
- RAILIANCE-WP-0024
- WHITEHAT-WP-0001
- AUDIT-WP-0008
origin: run-review
origin_ref: "WH-ENG-20260822-AUDIT-E2-02 terminal clean admission abort"
state_hub_workstream_id: "26165dd5-dca5-5261-b4bf-183d186aad27"
---
# RPF-WP-0016 — Version ephemeral custody projection and broker handoff
## Goal
Replace engagement-specific credential scripts and prose handoffs with one
versioned, value-safe, fail-closed lifecycle. A consumer must prove it can
consume the projection receipt before platform custody mints anything. The
platform then exposes direct `preflight`, `project`, `status`, `cleanup`, and
expired-lease cleanup interfaces without transferring bearer values or
collapsing owner approvals.
The implementation is repository code and contracts only. It does not
authorize or schedule a live credential projection, workload, probe, or
unattended cleanup controller.
## T01 — Define projection, broker-readiness, and cleanup receipts
```task
id: RPF-WP-0016-T01
status: done
priority: high
state_hub_task_id: "3d95a854-1c54-58c9-90d2-971b9a9ec5c6"
```
Publish versioned JSON Schemas and canonical validation for the projection
contract, broker-readiness receipt, active projection receipt, and cleanup
receipt. Bind every receipt to the exact engagement, target revision and image,
runner and target contract digests, mounted identity handles, expiry, opaque
lease id, and cleanup authority. Values and value-derived fingerprints are
forbidden.
Done when malformed, stale, mismatched, expired, and value-bearing documents
fail closed and consumers can implement without interpreting prose.
Implemented in four JSON Schemas plus `scripts/custody_contract.py`. Canonical
projection ids cover all receipt content; validators bind target, contract,
roles, mount paths, resource names and UIDs, and reject value-bearing fields.
Broker approvals also pin the current hashes of all four interface schemas.
## T02 — Implement a data-driven custody lifecycle
```task
id: RPF-WP-0016-T02
status: done
priority: high
state_hub_task_id: "783f74b8-d4a6-5ac5-9300-62acfedc53c6"
```
Replace copied per-engagement code and static manifests with one lifecycle that
derives exact OpenBao and ESO resources from a validated engagement contract.
Expose value-safe `preflight`, `project`, `status`, and idempotent `cleanup`
commands. Preserve the existing two-identity sender overlay, application-level
expiry, exact-key mount, readiness reload, and exact-scope rollback behavior.
Done when a new engagement requires data only, not copied Python, HCL, or YAML,
and an old engagement identifier cannot leak into generated resources.
Implemented by `scripts/custody-projection.py` and the intentionally invalid
placeholder template `docs/custody-projection-contract.example.json`. Policy,
role, store, KV paths, ExternalSecret data mappings, sender overlays, expiry,
and cleanup scope derive from one validated contract. Resource names use an
engagement-id digest rather than a copied run suffix.
## T03 — Gate projection on a direct broker capability receipt
```task
id: RPF-WP-0016-T03
status: done
priority: high
state_hub_task_id: "e2cced83-5beb-5fb1-919a-72bd6cb49dcf"
```
Provide a direct owner interface for `whitehat-security` to inspect, verify,
approve, or request changes to the contract. Approval must identify the tested
adapter revision and artifact digest and be posted directly to State Hub. The
platform lifecycle must find a current matching approval before generating a
credential.
Done when an unconnected, stale, wrong-engagement, or wrong-contract broker is
rejected before the first OpenBao or sender-registry mutation.
Implemented by `scripts/wp0025-broker-readiness.py`. Its closed verification
surface pins Whitehat's adapter and focused test paths, computes their commit
and digest, runs only the named focused test, and posts the canonical receipt
directly as `whitehat-security`. `project` checks State Hub before reading the
platform-admin token file, generating bearer values, or touching live state.
No adapter approval is fabricated by this implementation; a future Whitehat
adapter must pass the interface itself.
## T04 — Harden execution and transactional cleanup
```task
id: RPF-WP-0016-T04
status: done
priority: high
state_hub_task_id: "a14cb95b-6ef4-5872-a4ec-c7b51577cef1"
```
Centralize remote argv quoting, test shell-hostile arguments, and inject
failures at each mutation boundary. Prove that partial projection invokes exact
cleanup and that status never infers absence after a connectivity failure.
Retire the copied `-01`/`-02` procedure from future-run instructions.
Done when the SSH expansion defect from the `-02` run and every modeled partial
failure have regression coverage.
Implemented by `scripts/remote_exec.py` and focused tests covering `$`, shell
substitution, semicolons, quotes, NUL refusal, and value-safe failure messages.
The lifecycle uses a transaction whose rollback is exercised after every
modeled mutation boundary. Connectivity failure cannot be interpreted as
absence, and cleanup refuses a recreated same-name Kubernetes resource whose
UID differs from the receipt.
## T05 — Add an approval-ready expired-lease cleanup entry point
```task
id: RPF-WP-0016-T05
status: done
priority: medium
state_hub_task_id: "8e2840d1-b8e3-5f0f-8c94-f5019fff4e72"
```
Add a fail-closed `cleanup-expired` interface that accepts only a valid
projection receipt after its expiry, removes only the receipt-bound resources,
and emits the normal cleanup receipt. Do not install a scheduler or controller;
deployment of unattended cleanup remains a separate owner decision.
Done when an approved future reaper can call a deterministic command without
receiving projected bearer values or reconstructing cleanup scope from chat.
Implemented as `cleanup-expired`. It refuses before expiry, requires the exact
canonical projection receipt and engagement confirmation, verifies live UIDs,
uses the ordinary exact cleanup transaction, and emits the versioned cleanup
receipt. No scheduler, controller, runtime identity, or unattended authority
was deployed; those remain a separate owner decision.
## Acceptance
- [x] No projection can begin without a current consumer broker receipt.
- [x] A new engagement is configuration, not copied executable code.
- [x] Projection and cleanup receipts are versioned, canonical, and value-safe.
- [x] Remote command arguments survive shell-hostile templates unchanged.
- [x] Every modeled partial mutation cleans only its receipt-bound scope.
- [x] Expired cleanup is executable but no unattended scheduler is deployed.
## T06 — Export the broker receipt consumed by admission
```task
id: RPF-WP-0016-T06
status: done
priority: high
state_hub_task_id: "d815e1b9-73fe-527c-85c9-c20e37cf510f"
```
The first fresh-run setup review found that `approve` posted the canonical
broker-readiness receipt to State Hub but exposed no safe way to persist the
raw receipt document required by Whitehat's `--broker-receipt` input. Redirecting
the command output would save a CLI wrapper rather than the canonical document.
`approve` and a ready `status` now accept `--receipt-out`, create parent
directories, write only the canonical value-safe receipt, and force mode
`0600`. Other commands fail closed when that option is supplied. Regression
coverage proves the wrapper is excluded and the file mode is private.

View file

@ -0,0 +1,110 @@
---
id: RPF-WP-0017
type: workplan
title: "Contain attended OpenBao login output before authentication"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-08-23"
updated: "2026-08-23"
related:
- RAILIANCE-WP-0024
- WARDEN-WP-0027
- WARDEN-WP-0027-T02-DRILL-20260822-01
origin: routed
origin_ref: "State Hub message b3f34c2d-941f-4ddd-b15d-e28075446021"
state_hub_workstream_id: "b27975a7-e1bf-595e-8412-de03e167c9ed"
---
# RPF-WP-0017 — attended OpenBao login output containment
## Goal
Make the governed `openbao-platform-admin-login` path fail before OIDC unless
it can guarantee isolated private token-helper storage and contain every byte
emitted by the underlying client. A persistence or output-contract failure must
revoke any issued credential without placing its value, accessor, fingerprint,
or derived material in agent-visible output, Git, State Hub, evidence, or logs.
This workplan records the direct fix interface requested by `railiance-infra`.
It does not authorize OIDC authentication, minting, OpenBao administration, a
seal/unseal ceremony, or reuse of the terminal drill scenario.
## Ownership and fixed boundaries
- `railiance-platform` owns the login lane, acceptance contract, and any future
production rehearsal.
- `ops-warden` owns the current proxy implementation in `src/warden/cli.py`,
`src/warden/proxy.py`, and its focused tests.
- key-cape remains the OIDC/MFA identity provider; this task does not weaken or
replace MFA, change the OpenBao role, or introduce root/workload fallback.
- `railiance-infra` is the requesting reviewer and independent abort owner.
- Risk Nexus owns grading of State Hub filing request
`5a344f53-14e9-4230-8874-2f01b0a85700`.
- State Hub GO decision `449a697a-5303-4582-aa9e-b0bc8b35ab2d` was consumed by
NO-GO decision `85724c0c-e70f-4e2f-a8c6-a9cb1ea6331b`. Neither decision nor
scenario `WARDEN-WP-0027-T02-DRILL-20260822-01` may be reused.
## T01 — Harden the attended-login adapter and prove failure containment
```task
id: RPF-WP-0017-T01
status: done
priority: high
state_hub_task_id: "e2448d67-8585-5d99-9c00-20c06f1186dd"
```
Implement the fix in `ops-warden` and return one value-safe owner receipt to
`railiance-platform` and `railiance-infra` naming the source revision, focused
test command/result, and the five acceptance outcomes below. The receipt must
contain no credential, accessor, fingerprint, output excerpt, callback code,
or token-helper contents.
Acceptance is all of the following:
1. An isolated writable token-helper location is created before authentication,
has mode `0600` (with private parent directories), and is removed or revoked
through a deterministic cleanup path.
2. A read-only or otherwise unusable default home fails before OIDC begins; the
adapter never discovers persistence failure only after credential issuance.
3. Adapter-owned stdout and stderr containment guarantees that no credential
reaches agent-visible output even if the underlying CLI violates
`-no-print`. Inherited agent-visible stdio is not permitted for this lane.
4. Unexpected credential-bearing or otherwise non-allowlisted output fails
closed and, whenever issuance may have occurred, triggers immediate
self-revocation through the contained process without logging, returning,
hashing, or fingerprinting the value.
5. Focused tests cover successful attended-login handoff, unwritable/default
home refusal before authentication, helper persistence failure, unexpected
stdout, unexpected stderr, immediate revocation, cleanup, and value absence
from captured test and audit output.
The implementation must also update the `openbao-platform-admin-login` worker
playbook and catalog comment so they no longer claim safety from `-no-print`
alone. A non-production test double must prove the complete failure path before
any new live OIDC rehearsal is proposed.
Done when the implementation is committed and pushed in `ops-warden`, the
focused suite passes, the value-safe owner receipt is delivered directly to
both reviewers, and `railiance-platform` accepts the exact revision. Completion
of this task still does not authorize a live drill; that requires fresh owner
receipts, current preflight, and a new human decision under a new scenario id.
Completed 2026-08-23. Railiance Platform accepts ops-warden revision
`0fae0904ce8d8694338dd53a8a79abec5fec788d`. The committed owner receipt at
ops-warden revision `bc1966da825fbeaa4d83947a4f942b5d5f085408` has SHA-256
`d2ba444ed16989590325697e69d25283dc75a9432c29a72e627e80bf9fd987e4`,
reports 42 focused tests and 390 repository tests passing, and records all
seven containment outcomes without sensitive material. Independent
railiance-infra review at revision `186b030` approves the same implementation.
This is source acceptance only and authorizes no live OIDC or drill.
## Acceptance
- [x] The adapter refuses unsafe storage before OIDC.
- [x] No underlying login output can reach the agent transcript.
- [x] Any issued credential is contained and revoked on failure.
- [x] Focused success and failure-path tests pass without credential material.
- [x] Both direct reviewers receive and accept the value-safe fix receipt.

View file

@ -0,0 +1,333 @@
---
id: RPF-WP-0018
type: workplan
title: "Align S3 with the estate policy surface (Tenancy Posture + policy-nexus)"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-08-17"
updated: "2026-08-18"
related:
- POLICY-NEXUS-WP-0001
- TEN-WP-0009
- RAPP-POSTGRES-WP-0002
origin: routed
origin_ref: "net-kingdom/canon/standards/tenancy-posture_v0.1.md §19.2, §20"
state_hub_workstream_id: "4fcb6026-2630-59a3-b5de-15f54efcf59d"
---
# RPF-WP-0018 — policy surface alignment
## Goal
Answer the two policy asks that landed on this repo, accept what fits,
push back on what does not, and leave behind artifacts that are checkable
rather than a promise to be good.
Done means: this repo has published a posture declaration, has accepted or
declined placement ownership in writing, has told its consumers their
quotas, and has answered `policy-nexus` on the one assignment in
`POLICY-NEXUS-WP-0001` that contradicts the OAS stack.
## Where the policy comes from
Two documents, from two owners, arriving in the same week.
1. **`net-kingdom/canon/standards/tenancy-posture_v0.1.md`** (draft-5,
proposed, reviewed by nobody). Routed by `rapp-postgres` on 2026-08-17
(message `d9da7d11`), foreshadowed by `tenant-engine` on 2026-08-16
(message `31aed179`). It asks this repo, **co-signed with
`adaptive-pricing`**, to own database placement policy — §8.2, §19.2 —
and to publish its own posture vector as the ratification test (§20.2).
2. **`policy-nexus/workplans/POLICY-NEXUS-WP-0001`** (proposed). It
assigns this repo the *substrate* for `policy.coulomb.social` — DNS,
TLS, ingress, hosting — and includes this repo in the ~68-ADR corpus it
intends to publish from `docs/adr/*.md`.
Both are drafts. Neither is ratified. Accepting them now is cheaper than
accepting them later, because both are still willing to change.
## What actually applies to us, and what does not
**Applies, and fits.** The placement ladder (P0P4) describes our estate
accurately: `apps-pg` is a P1 shared cluster, `user-engine-pg` and
`target-revenue-pg` are P2. The §10.2 quota-disclosure obligation lands on
a document we already maintain, `docs/s3-consumer-interfaces.md`. The §9.1
ban on static long-lived database credentials is already how the credential
broker works. Nothing in the ladder requires us to move a single workload.
**Applies, but we cannot satisfy it yet.** §8.1 says placement triggers
MUST be *monitored*, not merely recorded. This repo has no monitoring
plane. `SCOPE.md` states we emit to `railiance-telemetry` "once the
evidence plane exists — seeded 2026-08-11, not yet implemented". Claiming
monitoring we do not have is the exact overclaim §6 prohibits, so T03
records each trigger with a named monitor or an explicit `unmonitored`
marker, and the marker is the honest answer until telemetry lands.
**Does not fit, and the policy should change.** Five findings, T07.
## Findings against the policy (routed 2026-08-17, T07)
**F1 — `POLICY-NEXUS-WP-0001` assigns S3 a concern S3 does not own.**
It states "`railiance-platform` owns the substrate — DNS, TLS, ingress,
hosting". Per `SCOPE.md`, DNS and OS-level concerns are S1
`railiance-infra`, and Kubernetes ingress is S2 `railiance-cluster`;
S3 owns shared *platform services*. Accepting the assignment as written
would re-import the boundary violation that `RAIL-PL-WP-0001` and ADR-003
exist to prevent.
*Recommend:* split T04 of that workplan. Ingress and TLS to S2, DNS to S1
or the reef boundary, and the genuinely S3 parts — an object-storage bucket
for the static output if it wants one, plus any credential lane — stay
here. This repo can co-sign the deployment without owning the substrate.
Adjacent and unresolved: `railiance-master` asked us on 2026-08-15
(`ba477968`) to confirm `bao.coulomb.social` does not publish on
reef-railiance. A second public name on the same rail should go through
whatever answers that question, not around it.
**F2 — this repo's decisions are structurally unpublishable.**
`policy-nexus` publishes canon and ADRs only, globbing `docs/adr/*.md`.
This repo has **no `docs/adr/` directory and no ADRs**. Its load-bearing
decisions live either in `docs/` as prose without frontmatter (24 files,
no status, revision, review date or owner — the fields T02/T05 of the
publication workplan require) or in the State Hub via `record_decision()`,
which is a read model and is not a publication source. The estate's
standing rule is that local files are the source of truth; for decisions,
this repo does not follow it.
*Recommend:* not a mass conversion. T05 here promotes the small set that
is genuinely decisional — the boundary rules, the credential-lane model,
the consumption-mode gate — into ADR form with the frontmatter
`policy-nexus` needs. The runbooks stay prose and stay unpublished, which
is what that workplan's scope rule already wants.
**F3 — §19.8 asks this repo for a number that lives in another repo.**
Cell sizing wants `platform-pg`'s declared maximum size. That spec —
`instances: 1`, `max_connections: 100`, `1Gi` — is `rapp-postgres`'s
cluster CR, not ours; `RAILIANCE-WP-0012` and `RAILIANCE-WP-0015`
deliberately moved the deployable surface to the rapp repos while this repo
kept custody and policy. The split is right and the question is misrouted
by one hop.
*Recommend:* accept §8.2 with the scope stated — **this repo owns the rule,
`rapp-postgres` owns the number**. We declare *that* a ceiling must be
published and what happens at it; they declare what it is. Same shape for
§19.9's retention floor and ceiling. `rapp-postgres`'s own analysis already
concedes the point: memory probably binds before connections do, at roughly
10MB per backend, and that is an observation only the package owner can
make.
**F4 — the posture vector is service-shaped; this repo is a layer.**
§5 asks a *service* to state one level per axis. `railiance-platform` is an
OAS layer that holds custody of several services with different postures.
One vector for the repo would be an average, which is exactly the
inaccuracy §6 prohibits. §20.2 invites this: "if a repo cannot express
itself in these five ladders, the ladders are wrong and this document
changes, not the repo."
*Recommend:* a layer repo publishes a **vector set** — one per service it
has custody of — plus a statement of which it owns versus operates. T01
does that and routes the finding back to `net-kingdom` as the framework's
first real correction from review rather than from research.
Refined while writing T01: the sharper form of F4 is that **the five
ladders describe a consumer of storage, not a provider of it.** Every S3
service scores at or near zero on I, A and E for the same structural
reason — a storage or credential platform has no tenant dimension of its
own. The `openbao` E line is the demonstration: `E: 0` is literally correct
and actively misleading, because the mechanism in place is
credential-scoped structural separation, the machinery E4 describes,
pointed at a consumer boundary rather than a tenant one. Proposed remedy is
a **provider declaration** beside the consumer vector, stating per axis the
maximum level the platform makes reachable and what the consumer must do to
reach it. Offered as an addition, not a renumbering.
**F5 — `policy-nexus` T02/T05 cannot be built against the corpus as it
stands.** Measured across the workstation on 2026-08-17: **69 ADRs in 19
repos** — closely matching that workplan's "roughly 68 across 18" — of
which **21 carry YAML frontmatter**, **41 carry any status field**, and
**2 carry any notion of a review date**. T02 specifies a front-matter-driven
renderer taking title, status, revision and review date from the source; T05
requires every page to show status, revision and last-reviewed, with a
staleness marker once a review interval is exceeded. Against this corpus
that renderer has nothing to read for 48 of 69 documents, and the currency
half of the repo's purpose has data for 2.
*Recommend:* T03 of that workplan should treat frontmatter as an **ingestion
precondition**, not a rendering input — a document without the required
fields is reported as non-conformant and left unpublished, rather than
rendered with blanks or with dates the site inferred. Inferring them is the
failure mode that workplan already names as its top risk: the publication
becomes a second source of truth. The corollary is that policy-nexus needs
a conformance report aimed at source repos before it needs a site, and this
repo's `docs/adr/README.md` is one repo's answer to it.
## Boundaries
- No workload moves placement level in this workplan. Declaring where
things are is separate from changing where they are.
- No monitoring is built here. T03 records the gap; closing it belongs
with `railiance-telemetry`.
- Co-signature with `adaptive-pricing` is *requested*, not assumed. If they
decline, T02 records single ownership and says so.
- This repo does not amend NetKingdom canon. Findings are routed; the
standard's owner decides.
## Tasks
```task
id: RPF-WP-0018-T01
status: done
priority: high
state_hub_task_id: "88d1342b-5093-5fbc-aa84-1608d163b979"
```
**Publish the S3 posture vector set.** Write `docs/tenancy-posture.md`:
one vector per service under S3 custody (`openbao`, `apps-pg`, and the
`rapp-postgres`-operated clusters we hold policy for), each with `current`,
`target`, `reviewed`, and a `gap` note per axis below target. State plainly
which services this repo *owns* and which it only holds *policy* for.
Route F4 to `net-kingdom` with the vector set as the evidence.
```task
id: RPF-WP-0018-T02
status: done
priority: high
state_hub_task_id: "03f0a5bd-7876-5784-bf02-11cbcdf005bb"
```
**Accept placement ownership, scoped.** Write `docs/placement-policy.md`:
adopt the P0P4 ladder by reference (do not restate it — the canon copy is
the source), record the default (P1 for platform services), and record a
named **placement owner per workload** for every consumer on an S3-custody
cluster. State the F3 split explicitly: rule here, number in
`rapp-postgres`. Request `adaptive-pricing` co-signature; record the answer
either way.
```task
id: RPF-WP-0018-T03
status: done
priority: medium
state_hub_task_id: "b602226f-8eb5-5609-995f-94f9074b0895"
```
**Make triggers monitorable or honestly unmonitored.** For each of the five
§8 triggers, record in `docs/placement-policy.md` what signal would fire it,
who reads that signal, and at what cadence — or the literal marker
`unmonitored: pending railiance-telemetry`. Include the §8.3.3 obligation:
report which service classes are co-resident. Today that is
`latency-critical` (`tenant-engine`) beside `batch` (`audit-core`) on
`platform-pg`, which becomes a stated risk rather than an invisible one.
```task
id: RPF-WP-0018-T04
status: done
priority: medium
state_hub_task_id: "92ce14f6-afee-50f6-abd4-197c4724a589"
```
**Disclose quotas to consumers (§10.2).** Extend
`docs/s3-consumer-interfaces.md` to `1.1.0` — additive under its own
compatibility rule — adding per-interface connection limits, statement and
idle-transaction timeouts, and backup retention to `apps-pg.v1` and
`rapp-postgres.v1`. Add the standing obligation that a change to any of
these is announced to bound consumers, not discovered by them.
```task
id: RPF-WP-0018-T05
status: done
priority: medium
state_hub_task_id: "58ef22f8-7f1b-5647-babd-eeb817b3ae31"
```
**Create the ADR surface (F2).** Create `docs/adr/` with an ADR template
carrying the frontmatter `policy-nexus` T02/T05 consume: title, status,
revision, owner, last-reviewed, review interval. Promote the decisions that
are already load-bearing and already written down elsewhere — the S3
boundary rule, the credential-lane model, the consumption-mode gate,
and the outcome of T02 — into ADRs. Runbooks stay prose. State in each ADR
which hub `record_decision()` id it supersedes, so the hub stops being the
only home for a decision.
```task
id: RPF-WP-0018-T06
status: done
priority: low
state_hub_task_id: "49c682a0-a9b7-59f0-ad65-79b4fb9bb79c"
```
**Answer §19.9 — retention floor and ceiling.** Decide whether
`backupRetentionDays` gets a platform minimum (so a consumer asking for one
day gets a validation error rather than a quiet disappointment) and a
maximum (so nobody exhausts the volume). Record the rule here; the
enforcing validator is `rapp-postgres`'s. Carry `rapp-postgres` ADR-0002's
consequence forward: on a shared cluster a consumer's erasure horizon is the
maximum declared across co-residents, so a shorter horizon is a P2 trigger.
```task
id: RPF-WP-0018-T07
status: done
priority: high
state_hub_task_id: "ad7a159b-a395-50e0-a517-d605a1d70108"
```
**Route the findings.** Reply to `rapp-postgres` and `tenant-engine` with
T01/T02 outcomes and F3/F4. Reply to `policy-nexus` with F1 (substrate
misassignment, with the S1/S2 split recommended) and F2 (ADR surface now
being built, plus the hub-decisions gap). Reply to `railiance-master` on
`ba477968`, since a second public name on reef-railiance depends on the
same answer. Mark the routed messages read.
## Sequencing
T01 and T02 are the ratification inputs and go first; T07 cannot be
answered without them. T03 hangs off T02's document. T04 and T05 are
independent and can run any time. T06 is last and cheap.
Deliberately not blocking on ratification: this repo publishes its
declaration whether or not the standard is ratified, because §6 makes
accuracy the conformance test and an accurate declaration is useful even if
the ladders are later renumbered.
## Risks
**Accepting ownership of a policy we cannot enforce.** §8.1 monitoring is
the specific case. Mitigation is T03's explicit `unmonitored` marker — an
owner who says what is not covered still owns it usefully; one who implies
coverage does not.
**Scope creep into S1/S2.** F1 is the live instance. Mitigation is that
this workplan routes the finding and does not build the ingress.
**A declaration that goes stale the day it is written.** Mitigation is the
`reviewed:` date on the vector set and the review interval in the ADR
template — the same currency mechanism `policy-nexus` T05 will read.
## Closed 2026-08-18
All seven tasks done. What the workplan produced, and what it changed
elsewhere:
- `docs/tenancy-posture.md` + `tenancy.yaml` — the vector set, machine-readable
with the human reasoning kept beside it.
- `docs/placement-policy.md` — placement rule, owner per workload, triggers,
retention floor and ceiling.
- `docs/s3-consumer-interfaces.md` `1.1.0` — quota disclosure.
- `docs/adr/` — created from nothing; `ADR-0001`, `ADR-0002`, `ADR-0003`.
**Two findings were adopted upstream.** The provider-declaration proposal (F4,
narrowed) is in the framework and its canonical form is the `provider:` block
in `tenancy.yaml`. `adaptive-pricing` declined the standing co-signature and
supplied a stronger replacement — typed tier minima joined at tier definition —
which draft-8 adopted; `ADR-0002` records the outcome as single ownership plus
a mandatory typed constraint join, not as an absent signature.
**Three corrections were issued against our own output**, all in the same
direction — claiming levels we could not evidence. `openbao A: 2` retracted to
`A: 0`; the provider finding narrowed once §6's `flex-auth` precedent was read;
the §11 summary to `adaptive-pricing` corrected, since §11.2 keeps marketing
vocabulary free and only the *cannot-reach* claim is restricted, to E4. All
three are recorded in the documents rather than edited away.
**What this workplan deliberately did not do:** fix anything. It found that
`apps-pg` had no backup, no per-consumer controls and no isolation probes, and
published those as visible defects. `RPF-WP-0019` closes them.
**Left open, not owned here:** `bao.coulomb.social` still needs confirmation
against live reef state (`railiance-master` `ba477968`), and F1's substrate
split is with `policy-nexus` to act on.

View file

@ -0,0 +1,258 @@
---
id: RPF-WP-0019
type: workplan
title: "apps-pg: backup, per-consumer controls, and the isolation probes they make possible"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-08-17"
updated: "2026-08-20"
related:
- RPF-WP-0018
origin: residual
origin_ref: RPF-WP-0018
state_hub_workstream_id: "6ded9d76-e3a8-5d52-9221-2c7935f3b364"
---
# RPF-WP-0019 — apps-pg recoverability and per-consumer controls
## Goal
Close the three defects `RPF-WP-0018` surfaced in `apps-pg` by writing its
quota disclosure. Documentation found them; only this workplan fixes them.
## Why this is separate from RPF-WP-0018
That workplan declared posture and policy. This one changes a live cluster.
Keeping them apart matters: a declaration workplan that quietly starts editing
production is how "we wrote it down" becomes indistinguishable from "we fixed
it". The declaration is published as-is, with the defects visible, and this is
the record of closing them.
## The three defects
**D1 — `apps-pg` has no backup.** No `barmanObjectStore`, no `retentionPolicy`,
nothing. This is not a short retention window; it is no recovery path at all,
on a cluster holding two S5 application databases. Its R level is `R0` and
`R0` here means unrecoverable, not merely un-erasable.
**D2 — no per-consumer controls.** The connection pool is unpartitioned, so
one consumer can exhaust the cluster while staying politely inside its own
expectations. No `statement_timeout`, no `idle_in_transaction_session_timeout`,
no CPU or memory limits — the pod is BestEffort QoS and is the first thing
evicted under node pressure.
**D3 — no isolation probes**, so the `P1` levels recorded for `vergabe` and
`coulomb_social` in `docs/placement-policy.md` §3.1 are provisioning
declarations without the §13 artifact.
## Sequencing, and why it is not the obvious one
**D1 first.** It is the only one whose failure is unrecoverable. A cluster
with no backup is one bad afternoon from data loss that no amount of isolation
evidence compensates for.
**D2 before D3, necessarily.** Tenancy Posture §13.4: an artifact must assert
something achievable. The noisy-neighbour artifact requires showing the
governance controls *bind*. With no controls there is nothing to bind, so a
probe written now could only demonstrate degradation — an artifact that "can
only fail, or that passes by being run gently enough". Writing the probe first
would produce an overclaim wearing the costume of evidence.
D1 also depends on a backup target, which is `resource-control`'s bucket and
the `platform-pg-backup-s3` credential lane — the same handoff
`make postgres-backup-deploy` waits on. Check whether that is now live before
assuming this is blocked.
## Status 2026-08-18 — repository-complete, live-blocked
Everything this repo can do without touching the cluster is done and
committed. What remains on T01, T02 and T04 is a single operator window
against a live shared rail, in this order:
1. `make apps-pg-deploy` — Cluster reconcile: role connection limits,
Burstable requests/limits, explicit aggregate parameters.
2. Apply `helm/apps-pg-consumer-controls.sql` — the two 15s role timeouts.
Idempotent; CNPG 1.28 has no managed-role settings field, so this is
operator SQL by necessity, not by preference.
3. `make apps-pg-backup-deploy` — the ScheduledBackup, once the governed
Secret is confirmed live.
4. Capture `LastBackupSucceeded=True` and a scratch restore. **Until both
exist, `apps-pg` R stays 0** — declared configuration is not a §13
artifact, and this workplan exists because that distinction was missed once
already.
5. T04's probes, in an announced window, after 13 have settled.
**Blocker resolved 2026-08-18, and it was ours.** The earlier note recorded
this as `kubectl` returning `Unauthorized` — an access problem outside the
repo. That was wrong. railiance01 is reachable and healthy
(`~/.kube/config-railiance01`, k3s v1.35.1, `apps-pg` 9d, both consumers
present). What failed was our own wiring: `KUBECONFIG` is an environment
variable, so the Makefile's `?=` default never applied, and
`RAILIANCE01_KUBECONFIG` pointed at `config-hosteurope` — a different cluster
that happens to be unauthorized from here.
**The near-miss is the finding.** Two reachable clusters each carry a CNPG
`Cluster` named `apps-pg` in a namespace named `databases`. The other one
(k3s v1.30.3) holds `gitea-db` and only one apps-pg consumer. Had `KUBECONFIG`
pointed there instead of at an unauthorized file, `make apps-pg-deploy` would
have applied this workplan's connection limits, role timeouts and backup
configuration **to the wrong cluster, and reported success.** The
`Unauthorized` error was the only thing that prevented it.
Fixed by pinning cluster *identity* rather than kubeconfig *filename*:
`railiance01-guard` compares the live `kube-system` namespace UID against
`RAILIANCE01_CLUSTER_UID` and fails closed on mismatch or unreachability. It
gates `apps-pg-deploy`, `apps-pg-backup-deploy`, `apps-pg-overflow-dry-run`,
`apps-pg-status` and `apps-pg-shell`. Filename selection could not have
protected against this: both kubeconfigs resolve to a `127.0.0.1` tunnel port,
and the environment overrides the default either way. `make cluster-id` prints
what is currently selected.
The guard is deliberately **not** global. `db-status` legitimately targets the
other cluster for `gitea-db`, so a blanket guard would break a working target
and teach people to bypass it.
**Do not treat the rollout as evidence.** T04's P1 claim and the R-axis both
need artifacts produced *after* application, and `docs/placement-policy.md`
§3.1 and `tenancy.yaml` should be updated only then.
## Status 2026-08-20 — recoverability and controls live
The guarded live rollout completed against railiance01. The pod reconciled
from BestEffort to Burstable QoS, both consumer roles now have a 20-connection
limit, the explicit aggregate/logging parameters bind, and controlled SQL
applied and verified both 15-second role timeouts. T02 is complete.
The first WAL archive attempt exposed a repository defect before any recovery
claim was made: the runtime identity is intentionally restricted by bucket
policy to `platform-pg/*`, while the reviewed manifest used sibling prefix
`apps-pg/`. Scaleway correctly denied `PutObject`. Desired state now keeps the
cell distinct beneath the governed prefix at `platform-pg/apps-pg/` (and the
unprovisioned overflow at `platform-pg/apps-pg-2/`). Continuous archiving then
became healthy, the immediate base backup completed in eight seconds, and a
separate scratch Cluster restored both consumer databases in 56 seconds.
Evidence is `docs/evidence/RPF-WP-0019-backup-restore-2026-08-20.md`. T01 and
T02 are complete. T04 subsequently passed 14/14 and the workplan is finished.
## Tasks
```task
id: RPF-WP-0019-T01
status: done
priority: high
state_hub_task_id: "9f0c7e4f-6351-51c1-8c5e-39f770668605"
```
**Establish a backup target for `apps-pg`.** Confirm the state of the
`resource-control` bucket and the `platform-pg-backup-s3` OpenBao Secret; if
live, configure `barmanObjectStore` and a `retentionPolicy` on the cluster. If
not live, record the dependency and say so — do not leave the absence
undocumented a second time.
2026-08-18 repository readiness: the governed Secret existed live and source
carried 30-day retention, continuous WAL and a daily 02:15 backup. At that
point it incorrectly used sibling prefix `apps-pg/`; the 2026-08-20 live run
proved the bucket policy denied it and corrected the path beneath
`platform-pg/`. The ScheduledBackup had not yet been applied and no successful
backup/restore evidence existed, so T01 correctly remained progress then.
Completed live 2026-08-20. The governed path is
`platform-pg/apps-pg/`, continuous archiving is healthy, Backup
`apps-pg-daily-20260820204148` completed in eight seconds, and a separately
named scratch Cluster restored and matched both consumer databases in 56
seconds. See `docs/evidence/RPF-WP-0019-backup-restore-2026-08-20.md`.
```task
id: RPF-WP-0019-T02
status: done
priority: high
state_hub_task_id: "8c95e2b5-884c-5504-9998-5bdd8ae64b5d"
```
**Declare and enforce per-consumer controls.** Per-consumer connection
allowance, `statement_timeout`, `idle_in_transaction_session_timeout`, and
pod resource requests/limits to lift `apps-pg` off BestEffort QoS. Publish
every value in `docs/s3-consumer-interfaces.md` before it takes effect —
§10.2 is a disclosure rule, and applying a timeout consumers learn about by
hitting it would breach the rule while implementing it.
2026-08-18 repository readiness: both roles declare a 20-connection limit,
the pod has Burstable requests/limits, aggregate/logging parameters are
explicit, and controlled operator SQL sets both 15s role timeouts. Every value
was published in `docs/s3-consumer-interfaces.md` before application. At that
point, live SQL and Cluster reconciliation still required an operator window.
Completed live 2026-08-20. The railiance01 identity guard passed, the single
instance reconciled healthy with Burstable QoS, and PostgreSQL reported both
roles at connection limit 20 with `statement_timeout=15s` and
`idle_in_transaction_session_timeout=15s`. The declared aggregate and logging
parameters were also verified live.
```task
id: RPF-WP-0019-T03
status: done
priority: medium
state_hub_task_id: "9d8c534d-72b7-5cac-ada1-72273fb3ab01"
```
**Declare the ceiling and overflow target.** Owed under this repo's own
Rule P-4.1 before `apps-pg`'s third consumer; it is at two. Name the binding
resource per Rule P-4.2 — memory or connections — and a named overflow
substrate per P-4.3.
Completed 2026-08-18. The declared ceiling is three, memory is the binding
constraint, and `apps-pg-2` is a named, source-provisionable overflow cell with
a distinct credential and backup prefix. `make apps-pg-verify-capacity`
rejects a fourth consumer per cell and unbounded/duplicate roles. The cell
intentionally remains absent until a fourth consumer is approved.
```task
id: RPF-WP-0019-T04
status: done
priority: medium
state_hub_task_id: "88ef2973-98cd-508a-9b10-73efcf6dce14"
```
**Isolation probes, after T02.** Consumer-boundary probes on the
`rapp-postgres` model, then the §13 noisy-neighbour artifact: per-consumer
baseline, saturation run, evidence the controls bind, degradation measured and
judged against each consumer's declared service class. Update
`docs/placement-policy.md` §3.1 and `docs/tenancy-posture.md` when the P1
claims become evidenced.
Waiting on T02 live application and an announced probe window. No saturation
or destructive recovery experiment is run against the shared production rail
as part of repository preparation.
Started 2026-08-20 after T02 completed. The first privilege preflight found
that all three databases still inherited PostgreSQL's default `PUBLIC`
`CONNECT` and `TEMPORARY` grants, so either consumer could connect to the
other's database even though relation grants remained separate. The probe did
not launder that into a P1 pass. The controlled SQL and published interface now
revoke the defaults and grant each role access only to its own database; live
boundary and noisy-neighbour evidence follows that enforcement.
Completed 2026-08-20. `make apps-pg-isolation-probe` passed 14/14 using the
real consumer credentials. The greedy role bound at 20 connections and the
next connection was denied; five peer queries stayed available, with measured
five-query wall time increasing from 5,285ms to 8,146ms. Both consumers are
interactive but declare no numeric database latency objective, so this proves
P1 boundary and continued service, not a latency SLO or resource fairness.
Evidence: `docs/evidence/RPF-WP-0019-isolation-2026-08-20.md`.
## Boundaries
- `apps-pg` only. `platform-pg`'s equivalents are `rapp-postgres`'s.
- No consumer is migrated. This changes the cluster, not who is on it.
- Values are published before they are enforced, never after.
## Risks
**Applying limits to a live cluster breaks a consumer that was relying on
their absence.** Most likely with `statement_timeout`. Mitigation is the
disclosure-first ordering in T02, which gives consumers a window to object.
**T01 stays blocked on a handoff outside this repo and the cluster keeps no
backup meanwhile.** Mitigation is that T01 requires the dependency be recorded
explicitly rather than left as a silent absence — which is exactly how D1
survived this long.

View file

@ -0,0 +1,162 @@
---
id: RPF-WP-0020
type: workplan
title: "Close CCR schema drift: one active lane unmigrated, one draft the suite cannot express"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-08-18"
updated: "2026-08-21"
related:
- RPF-WP-0014
origin: residual
origin_ref: RPF-WP-0019
state_hub_workstream_id: "99bdb617-b577-5de3-a9d8-4990ac676e28"
---
# RPF-WP-0020 — CCR schema drift
## Goal
Make `tests/test_credential_change.py::test_all_repo_ccrs_validate` pass for
the right reason: because every credential-change request in the repo is
either valid or explicitly declared in-flight — not because the assertion was
loosened until it stopped complaining.
## Why this is its own workplan
The test has been failing on `main`. It was found while closing
`RPF-WP-0019` and confirmed to predate that work (it fails at `HEAD` with the
apps-pg changes stashed), so it is neither a regression from that workplan nor
something to fix inside it.
A single red test is hiding **two unrelated problems with opposite remedies**.
That is the reason to split them out and name them, rather than fix whichever
one makes the suite green first.
## The two problems
**P1 — an active credential lane uses an unrepresented auth mode.**
`credential-change-requests/CCR-2026-0010-email-connect-transactional.yaml`
carries `status: active` and `readiness: ready`. It has always declared the
live transitional ESO token Secret, bootstrap script, policy and TTL, but the
validator understood only OIDC and Kubernetes auth and consequently reported
the role/bound-claim fields for those modes as missing.
This is the one that matters. A live lane whose declaration does not describe
how the workload authenticates is a governance gap, not a lint failure — the
document that is supposed to be the authority on the lane cannot answer the
first question anyone would ask of it. The live lane is working, which is
exactly what made this representation gap easy to leave.
**P2 — a genuine in-flight request the suite could not express.**
`CCR-2026-0011-scaleway-object-storage-bootstrap.yaml` carries
`status: apply_pending` and an out-of-enum placeholder readiness string.
It is a founder-bootstrap credential still holding placeholder values, and
`ops-warden` already tracks it as a draft lane. Its errors include a
`readiness` value outside the permitted enum, which is the file honestly
reporting that it is not finished.
This one is not a defect. The defect is that a suite asserting *all* CCRs
validate has no way to say "this one is deliberately incomplete", so an honest
draft and a real gap produce identical output.
## Boundaries
- **No secret values are read, written or rotated.** This is declaration
hygiene. Anything requiring a mint, a rotation or an apply belongs to the
credential-change approval flow in `docs/credential-change-approval.md`.
- P2 is not closed by filling in placeholders. The Scaleway bootstrap key is
a live commercial credential with an owner outside this repo.
- The validator's rules are not relaxed to accommodate either file.
## Tasks
```task
id: RPF-WP-0020-T01
status: done
priority: high
state_hub_task_id: "4763622c-78aa-556a-ba8e-9b556a2e78fc"
```
**Migrate CCR-2026-0010 to the current schema.** Determine the actual
authentication path for the `email-connect` transactional lane — Kubernetes
auth mount, role, bound claims, and the policy name that must match
`openbao.policy_name` — from the live OpenBao configuration and the existing
policy file, not by inventing plausible values. If the live configuration and
the declaration disagree, the live configuration is the fact and the
disagreement is the finding.
Completed 2026-08-21. Live read-only Kubernetes metadata showed
`ClusterSecretStore/openbao-email-connect` using
`external-secrets/openbao-email-connect-eso-token`, limited to namespace
`email-connect`, and reporting Valid; its ExternalSecret reported
SecretSynced. The schema now represents this transitional token mode directly,
requires the ESO policy artifact, Secret reference, bootstrap script, bounded
TTL and explicit Kubernetes-auth follow-up, and keeps delegated token creation
out of scope. No Secret value was read.
```task
id: RPF-WP-0020-T02
status: done
priority: medium
state_hub_task_id: "a897b2f5-b7ae-5dbf-9f51-54b2db5ca6fc"
```
**Give the suite a way to express a draft.** Add an explicit in-flight state
so `CCR-2026-0011` is skipped *by declaration* rather than by exception list —
a `status` the validator recognises as not-yet-complete, with the test
asserting that such files are still well-formed in every other respect. An
allowlist of filenames would work today and rot on the next draft.
Completed 2026-08-21. Added the non-applyable `in_flight` status. It requires a
named owner, blocking reason and exact `missing_fields`; only
`openbao.policy_file` and `openbao.auth` may be declared incomplete, while all
other CCR structure remains validated. CCR-2026-0011 now uses this state and a
valid non-resolvable front-door readiness without inventing policy/auth
metadata or filling credential placeholders.
```task
id: RPF-WP-0020-T03
status: done
priority: medium
state_hub_task_id: "44959415-a917-5dfb-a5ab-80ab166b9096"
```
**Report the drift rather than only fixing it.** If the `openbao.auth`
requirement was added without migrating existing active CCRs, other repos
carrying CCRs may have the same gap and no failing test to reveal it. Confirm
whether the requirement originated here or upstream, and notify accordingly.
Completed 2026-08-21. Git history traces the validator requirement to local
commit `815b124`; a filesystem-wide declaration search found CCR files and the
validator/schema implementation only in `railiance-platform`. There is no
upstream CCR implementation to migrate or notify. The migration obligation is
now documented locally.
```task
id: RPF-WP-0020-T04
status: done
priority: low
state_hub_task_id: "d85eeb8a-3147-59e1-bc2a-e84244dde93d"
```
**Make the suite green and keep it that way.** With T01 and T02 done, the full
suite passes. Record in `docs/credential-change-approval.md` that a new
required field obliges a migration pass over existing active CCRs — the
omission that produced P1.
Completed 2026-08-21. Regression coverage proves the live token-auth shape,
explicit in-flight omissions, rejection of undeclared omissions, and safe
status/summary rendering. The approval guide now requires a migration pass over
all existing CCRs whenever required fields are added or strengthened. The full
repository test suite passes.
## Risks
**T01 invents values to make the test pass.** The likeliest failure and the
worst one: a declaration that is well-formed and wrong is more dangerous than
one that is visibly incomplete, because it stops anyone looking. Mitigation is
that T01 reads live configuration and treats disagreement as a finding.
**T02 becomes a way to silence future failures.** Mitigation is that the
in-flight state still requires the file to be well-formed in every other
respect, so it suppresses the completeness assertion and nothing else.

View file

@ -0,0 +1,75 @@
---
id: RPF-WP-0026
type: workplan
title: "Adopt canonical flex-auth credential-grant checks"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-08-23"
updated: "2026-08-23"
related:
- FLEX-WP-0012
origin: routed
origin_ref: "State Hub decision 1f9f257d-c9f2-4a5e-a018-8058a3f2a51a"
state_hub_workstream_id: "7677326a-04db-52ef-b05c-967dabe1759e"
---
# RPF-WP-0026 — canonical flex-auth credential-grant checks
## Goal
Adopt flex-auth's single canonical `POST /v1/check` decision surface for
credential-grant preflight without exposing credential values or adding a
consumer-specific flex-auth endpoint.
## T01 — Resolve translation ownership
```task
id: RPF-WP-0026-T01
status: done
priority: high
state_hub_task_id: "07f3fead-76d6-5e53-9a7b-9930560182d9"
```
The operator explicitly approved Option A in State Hub decision
`1f9f257d-c9f2-4a5e-a018-8058a3f2a51a`: railiance-platform maps its grant
metadata to `CheckRequest` and reads `DecisionEnvelope`. Duration parsing and
normalization belong here, before the flex-auth policy boundary.
## T02 — Implement the canonical request and response
```task
id: RPF-WP-0026-T02
status: done
priority: high
state_hub_task_id: "659ac0ee-c2b7-56fc-80d8-92b2fcf30e7b"
```
Change the helper default to `/v1/check`, emit the coordinated
`tenant:platform` / `credential-grant:<id>` / `issue` request vocabulary, send
`requested_ttl_seconds` as an integer, and accept only `effect: allow`.
Completed in source. Actor classes map to canonical subject types (`Human`,
`Agent`, or `Automation`); the bound subject and non-secret actor metadata are
carried in context. Missing, deny, redact, audit-only, and not-applicable
effects all fail closed. An allow also requires a non-empty decision id,
evaluator provenance, and subject/resource binding back to the request.
## T03 — Verify and route the contract
```task
id: RPF-WP-0026-T03
status: done
priority: medium
state_hub_task_id: "8f6b7481-e8c2-56eb-98b5-c49cae562d60"
```
Exercise focused allow and deny tests, validate the complete repository suite,
and route the adopted mapping to flex-auth without requesting live credentials
or a production mutation.
Completed with 70 focused credential tests, all credential-helper dry-runs,
credential-catalog validation, and the complete 146-test offline suite passing.
No flex-auth production endpoint was called and no credential was issued.

View file

@ -0,0 +1,53 @@
---
id: RPF-WP-0028
type: workplan
title: "Make Forgejo image protection survive checkout replacement"
domain: financials
repo: railiance-platform
status: finished
owner: codex
created: "2026-09-05"
updated: "2026-09-05"
state_hub_workstream_id: "622d1208-b085-5ec2-9fb7-5e41f23e1843"
---
# Make Forgejo image protection survive checkout replacement
Source: State Hub message `868a2326-cdad-4256-be4c-7cd6c8ec4b5b`. Reviewed against current repository state
on 2026-09-05. Repository implementation and live inventory migration are complete.
## Implement and verify durable publication
```task
id: RPF-WP-0028-T01
status: done
priority: high
state_hub_task_id: "869d4114-3642-5e69-88d1-1d14e5547634"
```
Implemented scripts/refresh_live_images.py and make live-images-refresh. Publication validates exports before mutation, locks concurrent writers, retains all previous cluster entries, and fsyncs an atomic replacement. Offline tests prove repeatability, multi-cluster retention, and preservation on missing, empty, or malformed input.
The local prune CLI was additionally hardened on 2026-09-05 to refuse apply
before credential lookup if any explicitly requested export is missing,
unreadable, empty, or comment-only. Regression coverage includes a valid file
alongside a bad one, proving partial coverage cannot authorize deletion.
## Install production projection and rollout refresh
```task
id: RPF-WP-0028-T02
status: done
priority: high
state_hub_task_id: "2d0795ba-ccc0-5ba9-8315-2deb4d0d71a6"
```
Requires the production host inventory, activity-core directory mount/configuration update, and cluster rollout hook adoption. Follow docs/forgejo-package-prune.md. Preserve the restored union and worker refusal on missing/empty inventory; do not manually trigger prune. Completed 2026-09-05: installed the versioned helper outside the checkout,
seeded all 51 restored entries and merged a fresh railiance01 export (86 total),
added the read-only directory mount, rolled out actcore-worker, and synced the
exact definition override. API readback confirms the durable path. The resolver
accepts that path and refuses missing input before subprocess execution; the
command construction check used a mock and did not run prune. A post-rollout
capture succeeded. Activity-core's refresh target delegates to this hook; invoke
it after subsequent cluster rollouts (no new periodic schedule was introduced).
Evidence: `docs/evidence/RPF-WP-0028-live-inventory-2026-09-05.json`.
Source revisions: platform `da42f76`, activity-core `4083f31`.

View file

@ -0,0 +1,303 @@
---
id: RPF-WP-0030
type: workplan
title: "Unblock rapp-core-hub publication and platform onboarding"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-08-20"
updated: "2026-09-05"
related:
- CORE-WP-0011
- RAPPCOREHUB-WP-0001
- RAPP-POSTGRES-WP-0003
origin: request
origin_ref: CORE-WP-0011
state_hub_workstream_id: "f7a13cc2-7b1f-5644-89a8-b4e9a934b018"
---
# RPF-WP-0030 — Core Hub platform onboarding
## Goal
Provide the platform-owned dependencies that let the validated
`rapp-core-hub` package publish and deploy as a private shadow runtime on
railiance01 before the 2026-08-31 CoulombCore retirement deadline.
This work stops before public traffic cutover, the final data freeze, or
retirement of the old runtime. Those remain explicit operator gates in
`CORE-WP-0011`.
## Current evidence
- `/home/worsch/rapp-core-hub` is clean at commit `18e4e70`; its full package
check, family validation, and railiance01 server-side dry-run passed.
- `coulomb/rapp-core-hub` is private and authoritative in Forgejo. Its history
preserves validated package commit `4403c9b`, and unauthenticated API access
returns 404.
- State Hub registered the repository and canonical workplan prefix
`RAPPCOREHUB-WP-`. The production registrar still needs to assign the
workplan UUID.
- `agent-high-risk-boundary` deliberately denies agent data-read to the
Forgejo site-admin PAT. That control is not removed to make publication
convenient; an attended operator identity must authorize the child-process
transport.
- The package requests a `rapp-postgres.v1` consumer and a Kubernetes Secret
containing `CORE_HUB_DATABASE_URL` plus `CORE_HUB_API_TOKEN`.
- A live metadata-only railiance01 check on 2026-08-20 found OpenBao Ready and
the Kubernetes-auth-backed `openbao` ClusterSecretStore Valid. The existing
audit-core KV and database stores are also Valid and their ExternalSecrets
are synced, so the two-store shape has a live reference on the target rail.
## Boundaries
- No secret value enters Git, State Hub, logs, command output, or agent
context.
- A repository is created only through the owned Forgejo admin lane.
- The static Core Hub API token and dynamic PostgreSQL lease remain distinct
custody sources even if the workload consumes both as environment data.
- A short-lived database lease is not copied into a standing OpenBao KV path.
- A private shadow deployment is allowed only after the database credential
rotation behavior is explicit and testable.
- Public Ingress, final transfer, DNS/tunnel changes, and old-runtime removal
remain outside this workplan.
## T01 — Publish and register `rapp-core-hub`
```task
id: RPF-WP-0030-T01
status: done
priority: high
state_hub_task_id: "b561e71e-213e-506f-8eaf-578eef54977f"
```
Under an attended `net-kingdom-admins` OIDC login to
`forgejo-admin-workload-kv-read`, verify capabilities on the Forgejo admin KV
data and metadata paths without reading the value. Then use
`warden access forgejo-admin-api-token --exec` to create the private
`coulomb/rapp-core-hub` repository, push commit `4403c9b`, configure the remote,
and run `statehub fix-consistency` from the published repo.
Current blocker: both the original request and a 2026-08-20 retry reached
OpenBao but received HTTP 403 on
`sys/internal/ui/mounts/platform/workloads/forgejo/forgejo-admin`. The retry
also found the local warden audit path read-only. Do not weaken
`agent-high-risk-boundary`; repair or establish the operator session and use
the non-disclosing exec transport.
Concurrent publication evidence, discovered at 07:55Z: Forgejo repository
`coulomb/rapp-core-hub` was created at 07:42:41Z, but it is public rather than
private and contains only Forgejo's `Initial commit` (`eea94d9`, README only).
The local validated commit `4403c9b` is not the remote `main`, although the
local remote is configured. Do not push the package into the public repo.
First change visibility to private through the owned admin lane, correct the
repository description, then push and verify `refs/heads/main` equals the
validated commit.
Completed 2026-08-20 after operator authorization. Unauthenticated Forgejo API
access now returns 404 while the configured Git remote is accessible. Remote
`main` is `18e4e70`: its history preserves validated package commit `4403c9b`,
the Forgejo README stub through merge `7dd057e`, publication evidence, the
project goal, and the registered-prefix alignment. Local and remote `main`
match.
`make check`, the railiance01 server-side dry-run, and family validation
(`4 declaration(s) ok`) passed after publication. The repo is registered in
State Hub; workplan UUID assignment remains with the production registrar.
## T02 — Accept the Core Hub PostgreSQL consumer
```task
id: RPF-WP-0030-T02
status: done
priority: high
state_hub_task_id: "5ed30960-336c-5650-96c6-9395ea7f2584"
```
Review `/home/worsch/rapp-core-hub/handoffs/postgres-consumer.yaml` in
`rapp-postgres`, add the accepted declaration, render it, and run package
tests. The requested database, schema, role split, connection limits, timeout,
service class, and tenant-keying posture match the existing consumer contract.
Capacity is an admission condition. With `audit-core`, `tenant-engine`, and
the counted isolation probe, Core Hub is the fourth declaration on
`platform-pg`: it reaches but does not exceed the ADR-0004 ceiling. Before
admitting this third workload consumer, record the required measured memory
baseline. The untracked `consumers/issue-core.yaml` is not a real consumer —
`ISSUE-WP-0007` explicitly removed Postgres from that package — and must not
silently consume a slot.
OpenBao database role configuration remains an attended operation under T04.
Read-only preflight 2026-08-20: `platform-pg-1` used 9m CPU and 362Mi memory
against a 1Gi limit, with only `audit_core` and operator sessions present;
`tenant_engine` was not yet a database. This establishes current headroom but
does not satisfy ADR-0004's representative multi-consumer peak measurement.
Evidence: `docs/evidence/core-hub-postgres-capacity-preflight-2026-08-20.md`.
Capacity admission completed 2026-08-20. A bounded live probe opened 56
sessions in four declaration groups and repeated the occupancy with one active
bounded sort per group. `platform-pg` peaked at 174Mi/1Gi; the 85Mi increase is
about 1.52Mi per opened backend. Even adding that delta to the earlier 362Mi
sample yields 447Mi, under 44% of the limit. Core Hub is accepted as the fourth
and final declaration without raising the pod limit. Evidence:
`docs/evidence/core-hub-postgres-capacity-admission-2026-08-20.md`.
Completed 2026-08-20. `rapp-postgres` commit `4f316cc` accepts and publishes
the Core Hub declaration as the fourth `platform-pg` consumer. All 31 package
tests and consumer rendering passed; the railiance01 server-side dry-run
accepted the generated resource. The idempotent package provisioner then
created `core_hub`, its `core_hub` schema, distinct NOLOGIN owner/migration/
runtime roles and bounded role settings. CNPG `Database/core-hub` reports
`status.applied=true`, targets `platform-pg`, retains the database on resource
deletion, and names `core_hub_owner` as owner.
## T03 — Correct the runtime credential delivery contract
```task
id: RPF-WP-0030-T03
status: done
priority: high
state_hub_task_id: "ad022406-0ae5-58db-9b9d-2e958ab73237"
```
Separate the two credential sources before shadow deployment:
1. `CORE_HUB_API_TOKEN` is a standing workload secret in OpenBao KV, governed
by a proposed CCR and projected by a namespace-limited ESO store.
2. Runtime and migration PostgreSQL credentials come from distinct
`database/creds/core-hub-*` roles and the database-engine ESO store. One
extract must provide each username/password pair so leases are not mixed.
The current package consumes one `core-hub-runtime` Secret through `envFrom`
for both the Deployment and migration Job. That is not yet a safe dynamic
lease contract: environment variables do not update when ESO refreshes a
Secret, and no workload reloader is declared. Update the package to consume
separate runtime/migration database Secrets and the static API-token Secret,
then either:
- restart the Deployment automatically and demonstrably before each lease
expires; or
- change Core Hub to re-read mounted credential files when opening a new
database connection.
Prefer the mounted-file approach for steady state. A time-bounded automatic
restart contract may be used for the relocation only if rollback and lease
overlap are proven and the temporary limitation is recorded.
Completed 2026-08-21. Static API, runtime database, and migration database
credentials are mounted from three disjoint Secrets; the database URL exists
only in the supervised child environment, and the migration Job mounts only
its role. Live proof exposed that chart `0.3.0` watcher exit 75 could accumulate
planned Kubelet restarts into CrashLoopBackOff. Chart `0.3.1` (`rapp-core-hub`
commit `7cfa8bb`) instead keeps PID 1 alive and restarts only the application
child after reading the refreshed files. Nine package tests and live proof
showed a changed Secret resourceVersion and child PID with stable pod UID,
container restart count `0 -> 0`, restored readiness, and the five-minute
refresh interval restored.
## T04 — Declare and apply the OpenBao/ESO lanes
```task
id: RPF-WP-0030-T04
status: done
priority: high
state_hub_task_id: "d4c2d69d-186a-5d0d-9ab7-5a4a48cb1e8d"
```
After T03 fixes the consuming contract:
- draft and approve a workload-KV CCR for the static API token;
- add a least-privilege ESO policy covering only the Core Hub static-token KV
path and `database/creds/core-hub-runtime` / `core-hub-migration`;
- configure the database roles from the accepted Postgres declaration;
- create namespace-limited KV and database `ClusterSecretStore` objects;
- project runtime and migration Secrets without printing values; and
- prove positive sync, cross-namespace denial, lease rotation, and migration
role separation.
The working audit-core implementation is the reference for the two-store
shape. Do not copy its earlier, rejected idea of freezing a dynamic lease in
KV.
Progress 2026-08-20: drafted `CCR-2026-0013` for the static API token, added
its exact-path read policy, added a not-yet-applied namespace-limited
`openbao-core-hub-runtime` ClusterSecretStore, and extended the agent
high-risk boundary to deny direct data reads on the new path. Database-engine
policy/store manifests wait for the accepted Postgres declaration and the T03
consumer correction.
Progress 2026-08-20 after T02 admission: added an exact two-path database ESO
policy, a namespace-limited `openbao-core-hub-database` store, and three
disjoint ExternalSecrets. Runtime and migration each use one `dataFrom.extract`
so a username/password pair comes from one lease; both refresh every five
minutes. The database store and all projections passed railiance01 server-side
dry-run. The package namespace is live with the reviewed `platform-pg` client
label. Live role/store activation correctly stopped because the current
OpenBao session receives 403 and CCR-2026-0013 is still proposed; no authority
or placeholder Secret was improvised. State Hub decision
`4f70311b-a4c4-4959-b1a1-cdd1be97d523` now carries the non-secret review
coordinates and awaits the required platform-operator/Core-Hub-owner approval.
`rapp-postgres` commit `cfde57a` now also owns the Core Hub database-engine
roles, 30-minute runtime/10-minute migration TTLs, exact two-path ESO policy,
and non-disclosing renewable-token delivery script. Its 33 tests pass. Live
configuration still waits for the attended OpenBao authority; concurrent
tenant-engine workplan edits in that repository were preserved and excluded
from this commit.
Completed 2026-08-21 after the operator approved State Hub decision
`4f70311b-a4c4-4959-b1a1-cdd1be97d523` and completed attended OpenBao OIDC.
The policy, auth role, database roles, two namespace-limited stores, and three
disjoint ExternalSecrets are live and Ready. The existing API token moved
directly from source Kubernetes custody into OpenBao without appearing in
output. Outside-namespace store denial, exact-path Kubernetes-auth denial,
runtime DDL denial, migration DDL success, ESO sync, and live lease rotation
all passed. `CCR-2026-0013` is verified.
## T05 — Support the private shadow restore and verification
```task
id: RPF-WP-0030-T05
status: done
priority: high
state_hub_task_id: "1b608d2e-d660-50dd-8765-c429afbcb14c"
```
After T01-T04, support `CORE-WP-0011-T03`: label the namespace for
`platform-pg`, restore or logically transfer the source database, reconcile
table and row counts, run the migration Job with the migration lease, deploy
with Ingress disabled, and verify health plus authenticated consumers against
the private endpoint.
This task ends with repeatable shadow evidence. It does not flip
`hub.coulomb.social`, freeze production writes, or remove CoulombCore.
Preflight 2026-08-20: the source has Alembic revision `20260627_0002`, eight
public tables and 39 total rows. The admitted target intentionally has no
application tables before restore. A ready disposable pod in the labeled
`core-hub` namespace resolved the `platform-pg` service and reached its
PostgreSQL port; it was deleted after the check. No dump or restore was
attempted ahead of the T04 credential gate. Evidence:
`docs/evidence/core-hub-shadow-preflight-2026-08-20.md`.
Completed 2026-08-21. A consistent logical archive restored all eight source
tables and 39 rows at Alembic revision `20260627_0002`; the restored tables
were reconciled into the accepted `core_hub` schema with owner and runtime
grants verified. Helm release revision 2 deployed chart `0.3.1` with Ingress
disabled, and the migration Job completed. The hardened private verifier and
authenticated smoke `20260820224419-43dc05` passed all health, readiness,
catalog, denial, and protected-operation checks. Detailed non-secret evidence:
`docs/evidence/core-hub-private-shadow-2026-08-21.md`.
## Acceptance
- [x] Private Forgejo repo exists, validated commit `4403c9b` is preserved,
and the registered workplan prefix is consistent.
- [x] Core Hub is an accepted, capacity-reviewed `rapp-postgres` consumer.
- [x] Static API-token and dynamic database credentials have distinct,
least-privilege delivery paths.
- [x] Database credential rotation cannot leave a running pod using an expired
environment value.
- [x] Private shadow restore, reconciliation, migration, and consumer smokes
pass without enabling public Ingress.

View file

@ -0,0 +1,61 @@
---
id: RPF-WP-0031
type: workplan
title: "Separate duplicated apps-pg and Core Hub workplan identities"
domain: financials
repo: railiance-platform
status: finished
owner: codex
created: "2026-09-05"
updated: "2026-09-05"
state_hub_workstream_id: "dcc4fab2-2e95-570a-a415-6528c4cd8d11"
---
# Separate duplicated workplan identities
## Diagnose and prepare the exact repair
```task
id: RPF-WP-0031-T01
status: done
priority: high
state_hub_task_id: "8f75c85f-b442-5188-8b07-47a99251c0cf"
```
The supported `rmgr sync --path . --push` path reached the verified primary
railiance01 projection in 5.8 seconds, but refused six duplicate identities.
Both the May apps-pg archive and August Core Hub onboarding use RPF-WP-0021,
the same workplan UUID, and the same first five task UUIDs. No projection rows
were created, changed, or retired by the refused reconciliation.
`docs/evidence/RPF-WP-0021-collision-repair-proposal.json` fixes the proposed
choice: retain the older archive and reidentify the newer onboarding record as
RPF-WP-0030, with six Repo Manager-derived UUIDs and four evidence-reference
updates. Existing source UUIDs have not been changed.
## Apply the identity repair and reconcile
```task
id: RPF-WP-0031-T02
status: done
priority: high
state_hub_task_id: "2990a940-634c-5d76-b74c-740de8c36b16"
```
Requires a one-time user exception to AGENTS.md's prohibition on editing managed
UUID fields. The existing Repo Manager migration command considers only live
workplans and unfinished tasks, so it does not repair these finished records.
After approval, apply the exact proposal, preserve the archived identity and
all completion evidence, check uniqueness across root and archived workplans,
commit/push, and run `rmgr sync`. Inspect any retirement refusal before proceeding;
never blanket-acknowledge unrelated retirements.
User approved the exact exception on 2026-09-05. Applied the six proposed UUIDs
and four evidence-reference updates; the archived file is byte-for-byte unchanged.
All 196 source IDs and 196 UUIDs are unique across root and archived files.
Primary/railiance01 verified the pushed `6b41a43` commit and applied the source
projection in 12 seconds: four workplans created, 30 updated, no retirements,
no refusals, no cancelled tasks. RPF-WP-0028/0029/0030/0031 are now projected.
Receipt: `docs/evidence/RPF-WP-0031-reconciliation-2026-09-05.json`.
The approved identity repair and its authoritative reconciliation are complete.

View file

@ -0,0 +1,51 @@
---
id: RPF-WP-0032
type: workplan
title: "Design secrets-engine service JWT login"
domain: financials
repo: railiance-platform
status: finished
owner: codex
created: "2026-09-05"
updated: "2026-09-05"
state_hub_workstream_id: "bd036850-e1bf-5b70-bbc3-683dfa4b125c"
---
# Design secrets-engine service JWT login
## Prepare the platform design
```task
id: RPF-WP-0032-T01
status: done
priority: high
state_hub_task_id: "166ece0b-840b-54b8-b10c-45f96eda0429"
```
Reviewed owner source and the current platform CCR contract. Delivered
`docs/credential-lane-designs/secrets-engine-service-jwt.md` with proposed exact scope, custody, lifecycle, implementation gaps,
approval requirements and positive/negative acceptance evidence. This is a
completed design deliverable, not a live lane or approval. No secrets accessed,
production objects changed or owner messages sent.
## Obtain owner inputs and implement the approved lane
```task
id: RPF-WP-0032-T02
status: cancel
priority: high
state_hub_task_id: "d1f4a9f6-4ea5-5daa-97bb-039856855bc2"
```
Confirm issuer, verification endpoint, actual KeyCape registration and live auth mount survey. Approve the login-only role/self policy, implement reviewed declarative support and prove effective-policy, wrong-claim, expiry and cleanup checks. Native lane execution still requires its separate exact authorization and scoped authority.
Review the linked design and pin current source revisions before implementation.
Do not interpret this workplan or a proposed coordinate as live authorization.
## Portfolio review — 2026-09-05
The design deliverable is complete. The implementation obligation is preserved
in **RPF-WP-0035-T02**, the single credential-lane implementation queue.
T02 is `cancel` here only because it is superseded there; it is not implemented,
waived or externally accepted. The approved design scope and all existing
identifiers remain unchanged. Archived on 2026-09-05 after this consolidation.

View file

@ -0,0 +1,51 @@
---
id: RPF-WP-0033
type: workplan
title: "Design fluid-telegram attended operator KV lane"
domain: financials
repo: railiance-platform
status: finished
owner: codex
created: "2026-09-05"
updated: "2026-09-05"
state_hub_workstream_id: "957d49b9-6b84-5fde-89d0-4c2efef70067"
---
# Design fluid-telegram attended operator KV lane
## Prepare the platform design
```task
id: RPF-WP-0033-T01
status: done
priority: high
state_hub_task_id: "6d305189-61f3-5750-8e4f-5cd97b92f324"
```
Reviewed owner source and the current platform CCR contract. Delivered
`docs/credential-lane-designs/fluid-telegram-operator-kv.md` with proposed exact scope, custody, lifecycle, implementation gaps,
approval requirements and positive/negative acceptance evidence. This is a
completed design deliverable, not a live lane or approval. No secrets accessed,
production objects changed or owner messages sent.
## Obtain owner inputs and implement the approved lane
```task
id: RPF-WP-0033-T02
status: cancel
priority: high
state_hub_task_id: "a542697f-adf8-59ac-9704-0bcde1d9fd0f"
```
Obtain tenant/group/MFA decisions; extend the CCR schema, validator, renderer and ops-mason executor for the exact four-entry matrix; correct the consumer tenant default, atomic salt creation and preflight output. Complete an attended installed-engine probe and boundary checks before any routing activation.
Review the linked design and pin current source revisions before implementation.
Do not interpret this workplan or a proposed coordinate as live authorization.
## Portfolio review — 2026-09-05
The design deliverable is complete. The implementation obligation is preserved
in **RPF-WP-0035-T03**, the single credential-lane implementation queue.
T02 is `cancel` here only because it is superseded there; it is not implemented,
waived or externally accepted. The approved design scope and all existing
identifiers remain unchanged. Archived on 2026-09-05 after this consolidation.

View file

@ -0,0 +1,51 @@
---
id: RPF-WP-0034
type: workplan
title: "Design State Hub preflight signing custody"
domain: financials
repo: railiance-platform
status: finished
owner: codex
created: "2026-09-05"
updated: "2026-09-05"
state_hub_workstream_id: "5233ef7d-200e-5ca7-8b8c-897b12de4377"
---
# Design State Hub preflight signing custody
## Prepare the platform design
```task
id: RPF-WP-0034-T01
status: done
priority: high
state_hub_task_id: "bb9e53a9-63b1-5500-8b30-96c5252b8d61"
```
Reviewed owner source and the current platform CCR contract. Delivered
`docs/credential-lane-designs/state-hub-preflight-signing.md` with proposed exact scope, custody, lifecycle, implementation gaps,
approval requirements and positive/negative acceptance evidence. This is a
completed design deliverable, not a live lane or approval. No secrets accessed,
production objects changed or owner messages sent.
## Obtain owner inputs and implement the approved lane
```task
id: RPF-WP-0034-T02
status: cancel
priority: high
state_hub_task_id: "96e4864a-fd17-529b-a72f-69ffd885a962"
```
Confirm exact primary deployment and delivery identity; approve the writer and read CCR; implement dedicated ESO/API-only delivery and a concrete rotation fence. Provision only in an approved window, prove preflight signing without executing a rename, and record API/ESO health and negative access evidence.
Review the linked design and pin current source revisions before implementation.
Do not interpret this workplan or a proposed coordinate as live authorization.
## Portfolio review — 2026-09-05
The design deliverable is complete. The implementation obligation is preserved
in **RPF-WP-0035-T04**, the single credential-lane implementation queue.
T02 is `cancel` here only because it is superseded there; it is not implemented,
waived or externally accepted. The approved design scope and all existing
identifiers remain unchanged. Archived on 2026-09-05 after this consolidation.