RPF-WP-0064: propose isolated original-OpenBao restore for SECRETS-WP-0015-T04
All checks were successful
CCR intake gate / gate (push) Successful in 10s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Accepts hub request 41e9af3e as source preparation. T01 source survey
recorded and done; restore and consumer continuity stay unclaimed.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4384@bnt-lap001
Assistant-Session: 80d1f3da-7a91-4519-9737-9ba128b4da64
This commit is contained in:
codex 2026-10-23 20:15:24 +02:00
parent 1f8fbc7584
commit 3894dc7c54
2 changed files with 271 additions and 0 deletions

View file

@ -43,6 +43,7 @@
| workplan | RPF-WP-0061 | active | — | workplans/RPF-WP-0061-self-service-ccr-intake.md |
| workplan | RPF-WP-0062 | finished | — | workplans/RPF-WP-0062-custodian-agent-observer-identity.md |
| workplan | RPF-WP-0063 | finished | — | workplans/RPF-WP-0063-wave2-resource-right-sizing.md |
| workplan | RPF-WP-0064 | proposed | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-ADHOC-2026-10-10-T01 | done | — | workplans/ADHOC-2026-10-10.md |
| task | RPF-WP-ADHOC-2026-10-11-T01 | done | — | workplans/ADHOC-2026-10-11.md |
| task | RPF-WP-ADHOC-2026-10-11-T02 | done | — | workplans/ADHOC-2026-10-11.md |
@ -192,6 +193,14 @@
| task | RPF-WP-0062-T02 | done | — | workplans/RPF-WP-0062-custodian-agent-observer-identity.md |
| task | RPF-WP-0062-T03 | done | — | workplans/RPF-WP-0062-custodian-agent-observer-identity.md |
| task | RPF-WP-0063-T01 | done | — | workplans/RPF-WP-0063-wave2-resource-right-sizing.md |
| task | RPF-WP-0064-T01 | done | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-0064-T02 | todo | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-0064-T03 | todo | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-0064-T04 | todo | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-0064-T05 | wait | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-0064-T06 | wait | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-0064-T07 | wait | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| task | RPF-WP-0064-T08 | wait | — | workplans/RPF-WP-0064-isolated-original-openbao-restore.md |
| intake | RPF-IN-0001 | closed | — | intakes/intakes.md |
| intake | RPF-IN-0002 | open | — | intakes/intakes.md |
| intake | RPF-IN-0003 | open | — | intakes/intakes.md |

View file

@ -0,0 +1,262 @@
---
id: RPF-WP-0064
type: workplan
title: "Isolated original-OpenBao restore for secrets-engine consumer continuity"
domain: financials
repo: railiance-platform
status: proposed
flavor: implementation
owner: claude
created: "2026-10-23"
updated: "2026-10-23"
related:
- SECRETS-WP-0015
- NK-WP-0049
- RPF-WP-0015
- RPF-WP-0036
- RPF-WP-0035
- RAPP-OPENBAO-WP-0001
---
# RPF-WP-0064 — Isolated original-OpenBao restore for secrets-engine consumer continuity
Receiving workplan for hub message `41e9af3e` from net-kingdom (case
`secrets15-nk49-readiness`). The founder accepted on 2026-10-23 that
railiance-platform owns this as source preparation.
NK-WP-0049-T18 (net-kingdom `a09bf20`, verified receipt sha256 `16d72f7c…1651`,
secrets-engine `bfa1b3b`) proved in isolation that the original KeyCape issuer, its
JWKS key and the archived `secrets-engine-openbao` registration are intact.
SECRETS-WP-0015-T04 also needs the original OpenBao, recovered on its own into an
isolated, network-contained target. That recovery uses the original custody and
unseal path. It must show the KV registration
`platform/workloads/keycape/secrets-engine-openbao`, the narrow secrets-engine
JWT role and the operator auth path. There is no production redirect, no fresh
backend standing in for the original and no injected secret.
Scope of this plan: the restore and its evidence only. The secrets-engine
consumer run (attended in-memory secret source, KeyCape exchange, OpenBao JWT
login, `openbao:admin` refusal, session revocation) is a separate
founder-admitted attempt owned by secrets-engine. This plan prepares its target.
Why not RPF-WP-0036-T03: that task owns *recurring* backup/restore assurance and
waits on the activity-core scheduler (ACTIVITY-WP-0042). This is a one-off
attended restore with no scheduler, so a separate plan avoids taking on that
dependency. Its evidence can later feed T03 as one isolated restore receipt.
RPF-WP-0015 keeps the live snapshot experiments. Its snapshot receipts authorize
no restore.
Nothing in this plan claims restore capability or consumer continuity. Every
live step stays `wait` until attended evidence exists.
## T01 — Source survey and recorded findings
```task
id: RPF-WP-0064-T01
status: done
priority: high
```
### Findings (2026-10-23, read-only source survey; no cluster, OpenBao or snapshot access)
1. **Snapshot capture and location.** The procedure is `docs/openbao.md` §Backup:
`bao operator raft snapshot save` on `openbao-0`, then age encryption before the
snapshot leaves the operator machine. The latest recorded capture is the
attended RPF-WP-0015-T03 hold point on 2026-10-14. That snapshot was encrypted
to the offsite recipient derived from the founder's offline
`railiance-backup.key`, decrypt-verified in memory and uploaded to
`railiance-backups/openbao/` (HTTP 201), and the plaintext was shredded from
tmpfs. An earlier 2026-08-23 drill receipt is at
`reviews/WARDEN-WP-0027-T02-DRILL-20260822-01-openbao-snapshot-receipt.json`.
Receipts are validated against `docs/openbao-reboot-snapshot-receipt.example.json`
(24 h freshness, cluster id, applied index). Those receipts authorize no
restore.
2. **Freshness against the needed objects.** The KV path was corrected to
`platform/workloads/keycape/secrets-engine-openbao` on 2026-10-06, and the JWT
role was proven live on 2026-10-10 (CCR-2026-0038). The 2026-10-14 snapshot
should therefore contain both. This is an inference until the restored
metadata readback confirms it. If they are missing, T07 stops; it does not
repair anything.
3. **Version pin.** Chart `openbao/openbao` `0.28.2` (`Makefile`
`OPENBAO_CHART_VERSION`); image `quay.io/openbao/openbao:2.5.4`
(`helm/openbao-values.yaml`; `docs/openbao.md` still says app `v2.5.3`, which
is stale doc text). The restore target must use the same image, pinned by
digest at rehearsal time. Storage is integrated Raft.
4. **Unseal and custody model in use.** Shamir 2-of-3, unsealed by the founder in
attendance (the 2026-10-14 post-reboot unseal was done outside the transcript).
The transit auto-unseal stanza is still commented out. A Raft snapshot restore
brings back the *original* barrier keyring, so the restored target unseals only
with the original shares. That is the "original unseal path" the request asks
for. Restoring needs a throwaway initialization of the empty target first. Its
throwaway keys and token stay in process memory, are never recorded, and stop
working once `snapshot restore -force` completes. This is not a "fresh backend"
in the request's sense, because no state from it survives.
5. **Role, KV and auth definitions are owned here, not in rapp-openbao.** The
sources are `openbao/auth/secrets-engine-jwt-role.json`,
`openbao/auth/keycape-services-config.json` (mount `keycape-services`),
`docs/contracts/secrets-engine-openbao-jwt-login.yaml`, the operator path
`openbao/auth/netkingdom-oidc-config.json` plus
`netkingdom-platform-admin-role.json` (mount `netkingdom`, role
`platform-admin`), and the workload KV lane (`docs/workload-kv-access-lanes.md`,
KeyCape provisioning window 2026-10-04). rapp-openbao's SCOPE excludes policy,
custody and workload KV lanes. It owns only the package (chart, values, UI
overlay), and neither of its workplans has a restore task. rapp-openbao only
needs to confirm the image/chart pin used by the target.
6. **Isolation constraint found.** `keycape-services` validates JWTs through
`jwks_url: https://kc.coulomb.social/jwks`, and `netkingdom` uses OIDC
discovery at `https://kc.coulomb.social`. A networkless OpenBao can restore,
unseal and read back configuration and metadata. However, a live JWT login
(the consumer run) needs that hostname to resolve inside the contained network
to the *isolated* NK49 KeyCape, with a trusted TLS chain. It must never reach
the production KeyCape. An interactive OIDC operator login is out of scope for
the isolated target. The operator path is proven by restored config and role
readback, plus root-less login refusal where applicable.
7. **Reusable patterns.** These are reused as patterns, not copied:
- Existing in-repo template `docs/openbao-restore-drill-evidence.example.json`
and its validator `make openbao-validate-restore-evidence`.
- net-kingdom `docs/bootstrap-recovery-ceremony.md` and
`tools/bootstrap-recovery/isolated-*.py`: attended, hidden-key, fail-closed
setup with a Docker `--network none` target, a safe receipt and an
independent verifier.
- secrets-engine `docs/founder-admission.md` for the founder SSH-signed
admission with a time window.
## T02 — Isolated target and adapter design
```task
id: RPF-WP-0064-T02
status: todo
priority: high
depends_on: [RPF-WP-0064-T01]
```
Design `scripts/openbao-isolated-restore/` (adapter plus doc). Fixed properties:
- **Target:** a disposable Docker container on the founder workstation, running
the T01 image digest with an in-container Raft data directory on tmpfs. It is
never on railiance01 or the cluster, and it has no kubeconfig, no host
networking and no published ports.
- **Network containment:** phase A (restore + readback) uses `--network none`.
Phase B (handed to the consumer run) uses a single `--internal` Docker network
that has only the isolated KeyCape from NK49 and this OpenBao. In phase B,
`kc.coulomb.social` resolves to the isolated KeyCape, and egress is checked
to be refused before admission. Phase B is offered, not run, by this plan.
- **Custody inputs**, all founder-attended and never through argv, files in the
repo, env dumps or logs:
- (a) the encrypted snapshot object from `railiance-backups/openbao/`, named by
its ciphertext sha256;
- (b) the offline `railiance-backup.key` age identity, used for decryption
straight into the tmpfs stream;
- (c) 2 of 3 original Shamir shares, entered through a hidden prompt.
The restore needs no root token and no secret value is read. Readback uses a
token created in the restored target by an attended `generate-root`, used in
memory only and revoked before teardown, or uses the restored operator path.
Which of the two is decided in T02 review.
- **Readback (metadata only):** `sys/seal-status`, Raft applied index and cluster
id; the mounts `keycape-services` and `netkingdom` and their configs (issuer
and JWKS URL); role `secrets-engine-openbao` (bound claims and policies compared
to the source JSON hash); `platform/workloads/keycape/secrets-engine-openbao`
KV **metadata** (versions and timestamps, never `data`); and policy names. No
KV `data` read.
- **Timeout/abort/cleanup:** a hard wall-clock limit of 45 minutes from the first
custody input. Any fail-closed check, a missing object, an unexpected network
interface, or the founder pressing abort leads to `docker rm -f`, tmpfs
unmount, shredding of the decrypted stream, and a recorded `aborted` receipt.
Teardown also runs on success, unless phase B was admitted in the same window.
The plaintext snapshot never touches persistent disk.
## T03 — Safe receipt schema and independent verifier
```task
id: RPF-WP-0064-T03
status: todo
priority: high
depends_on: [RPF-WP-0064-T02]
```
Extend `docs/openbao-restore-drill-evidence.example.json` into a v2 receipt:
- attempt id and admission hash;
- image digest;
- ciphertext sha256 of the snapshot and the source cluster id and applied index
(from the RPF-WP-0015 receipt);
- restored cluster id and index;
- a network-namespace proof (interfaces and the egress-refusal result);
- presence and source-hash match for each required object;
- result `restored | refused | aborted`;
- the teardown proof;
- `no_secret_material_recorded`.
Add a verifier that runs separately from the adapter. It rejects placeholders,
unknown fields, missing teardown, `data` fields, and anything shaped like a
token or share. Validate it with adversarial fixtures.
## T04 — Staged rehearsal with synthetic material
```task
id: RPF-WP-0064-T04
status: todo
priority: high
depends_on: [RPF-WP-0064-T03]
```
Run the full adapter against a synthetic OpenBao, initialized on purpose with
generated Shamir shares and seeded with stand-in mounts, role and KV metadata
that mirror the T01 source files. Snapshot it, age-encrypt it to a generated
identity, restore it into the T02 target, unseal it with the synthetic shares,
read it back, and tear it down. Rehearse each abort path (timeout, wrong
ciphertext hash, network interface present, missing object). This step uses no
real material.
## T05 — Founder-signed attempt admission
```task
id: RPF-WP-0064-T05
status: wait
priority: high
needs_human: true
depends_on: [RPF-WP-0064-T04]
blocking_reason: "Founder must sign (SSH, secrets-engine founder-admission pattern) an attempt admission naming the snapshot ciphertext sha256, image digest, adapter commit, window start/end, abort operator and phase A only (phase B separately). Needs T04 rehearsal receipt first."
```
## T06 — Owner confirmation of package pin
```task
id: RPF-WP-0064-T06
status: wait
priority: low
depends_on: [RPF-WP-0064-T02]
blocking_reason: "rapp-openbao confirms the chart 0.28.2 / image 2.5.4 pin (and digest) used by production at the 2026-10-14 snapshot time is the one the isolated target runs. Role/KV/auth definitions stay railiance-platform-owned; no rapp-openbao change requested."
```
## T07 — Attended original restore (phase A)
```task
id: RPF-WP-0064-T07
status: wait
priority: high
needs_human: true
depends_on: [RPF-WP-0064-T05, RPF-WP-0064-T06]
blocking_reason: "Requires the founder in attendance holding the offline railiance-backup.key, 2 of 3 original Shamir shares and the encrypted snapshot from railiance-backups/openbao/, inside the T05-signed window. No agent handles custody material."
```
Run the admitted adapter once. The outcome is a T03 receipt verified
independently. The receipt is kept outside Git and referenced by its hash. A
missing object or failed check is a valid `refused` outcome; it is not repaired
in this attempt.
## T08 — Bounded evidence handoff
```task
id: RPF-WP-0064-T08
status: wait
priority: medium
depends_on: [RPF-WP-0064-T07]
blocking_reason: "Needs the verified T07 receipt."
```
Hand to secrets-engine (SECRETS-WP-0015-T04) and net-kingdom (NK-WP-0049-T18)
the receipt hash, the verifier result, the image digest and the recovered-object
list. Also hand over the phase B offer terms (contained network with the
isolated KeyCape, same custody, its own admission). The handoff makes no
continuity claim. A pointer to the receipt goes to RPF-WP-0036-T03 as one
isolated restore receipt; it does not count as recurring assurance.