railiance-platform/docs/openbao-open-questions-session.md

130 lines
7.5 KiB
Markdown
Raw Normal View History

# Attended OpenBao session — four open custody questions
One founder-attended session that settles the questions currently blocking four
owner threads. Everything in it is read-only against OpenBao. It provisions
nothing, approves nothing, and rotates nothing.
Prepared 2026-09-09 under RPF-WP-0035. Not yet run.
## Why a session rather than a reply
Four owner threads are each blocked on a fact that only an authenticated read can
establish, and each has been answered so far with "I will not guess":
| Question | Thread | What is undecidable from repo source |
| --- | --- | --- |
| Q1 | secrets-engine `546403e4` | Whether the legacy path `secret/coulomb/whynot-design/npm/publish` still exists and holds a real token |
Declare the security layer, and repair the placement admission check Layer declaration (gate-house). INTENT.md now carries the declaration in its own voice with layer.yaml as the machine-readable form, adapted from ops-warden's reference. railiance-platform is Staff: operating OpenBao is not a claim to the Tooling layer, because §4 is explicit that no operator-of-third-party-Tooling shape exists and that someone running it stays a declared gap. Six direct Tooling contacts are mapped by capability rather than by file — one §5.2 conduit, one §5.1 diagnostic, four §5.3 gaps with intended owners and review dates — and the uncatalogued contacts are listed so the check is total. We are PEP-shaped and the unreachable-engine stance map is NOT published; that is recorded as an open obligation to build against v0.8, not left silent. Placement admission. canned-prompts was added as a PostgresConsumer on platform-pg-2 in rapp-postgres 1b68b4c without a placement owner here, which is exactly the cross-repo drift the assurance check exists to catch; the check had been failing on it. Registered with its real boundary evidence, corrected the stale test expectation that pinned the overflow cell at one consumer, and updated the SCOPE occupancy line to 2/4. Also records owner input received today: key-cape's issuer view on CCR-2026-0020's presenting actor, and their confirmation that codex-railiance-platform correctly stays tenant:coulomb, so the flagged T02 discrepancy is closed as not-a-defect. The whynot-design npm field is NOT changed. Two dated live receipts here name NPM_AUTH_TOKEN as the field, including an attended founder fetch; that is recorded against the counterparty claim rather than either side being flipped before the session settles it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WLUjpv3ssxNRAEPPgLFnEB Assistant: claude-code Assistant-Model: opus Assistant-Process: 1275505@bnt-lap001 Assistant-Session: 97265baa-f08f-4032-b290-a1e2965a69c5
2026-09-09 23:27:14 +02:00
| Q2 | secrets-engine `546403e4`, ops-warden `b6212006` | Which field backs the authoritative npm lane. Two dated live receipts here name `NPM_AUTH_TOKEN`; secrets-engine says `npm_token` and ops-warden has already changed their catalog to it. Both can be true if the path carries both fields. Field names are not visible in KV v2 metadata |
| Q3 | internal | The 2026-09-09 activation receipt records `policy_applied: false` for both KeyCape approval lanes while `role_applied: true`. Reads verified, so the policies must exist — but existence and repo agreement were never confirmed |
| Q4 | CCR-2026-0019 | Which netkingdom OIDC roles and bound group claims already exist, as input to the operator group claim that lane is missing |
| Q5 | risk-nexus `ee702ac9` | Which fields the governed backup lane actually carries, as RPF-WP-0029-T02 context |
## Authority and containment
Authority is the governed `openbao-platform-admin-login` lane: `founder_required`,
attended OIDC through `bao login -method=oidc -path=netkingdom role=platform-admin`.
The owner command is never run directly.
```sh
make openbao-open-questions SESSION_RECEIPT=/tmp/<new-unique-name>.json
```
Use the Make target rather than typing the underlying command: it is short enough
not to be broken by terminal wrapping, it refuses a `SESSION_RECEIPT` that already
exists or is missing, and it clears any ambient token before invoking the
envelope.
`scripts/openbao-attended-exec.py` supplies the WSL browser launcher and then
`exec`s into `warden access openbao-platform-admin-login --exec`, so Warden keeps
the envelope: it captures both streams, owns the temporary token helper and
self-revokes when the command returns. Use a receipt path that does not exist —
the writer opens it `O_EXCL` at mode 0600 and refuses to overwrite.
The runner prints nothing. Its entire output is the receipt.
**To settle the npm field alone**, run only the two questions that bear on it:
```sh
make openbao-open-questions SESSION_QUESTIONS=Q1,Q2 SESSION_RECEIPT=/tmp/<new-unique-name>.json
```
`--questions` defaults to all five. Q5 is the only step that reads the backup
lane, so naming a subset is also how to exclude it.
**Ambient-token guard.** The Make target clears these before invoking the
envelope; the runner independently refuses to start if `OPENBAO_TOKEN`,
`BAO_TOKEN` or `VAULT_TOKEN` is set in the environment. A standing token would
let these reads succeed without the attended login, producing a receipt that
looks attended and is not. Unset it first; the envelope supplies the identity.
`--allow-ambient-token` overrides, and then the receipt records
`attended_identity: false` — which is the honest label for that read, not a
formality.
## What the runner reads, and the one honest caveat
| Step | Call | Emitted |
| --- | --- | --- |
| Q1 | `secrets list`, then `kv metadata get` on the legacy path if the mount exists | mount present, path present, current version, created/updated time |
| Q2 | `kv get` on `platform/workloads/coulomb/whynot-design/npm-publish` | **sorted field names only**, plus KV version |
| Q3 | `policy read` for both KeyCape approval policies | present, matches-repo-source, sha256 of each normalized text |
| Q4 | `list auth/netkingdom/role`, then `read` each | role names, bound claims, groups/user claim, token policies, TTL |
| Q5 | `kv get` on `platform/workloads/railiance/backup/offsite-lane` | **sorted field names only**, plus KV version |
**The caveat, stated plainly:** Q2 and Q5 are data reads. Field names cannot be
obtained from KV v2 metadata, so the value transits the runner's memory. It is
never printed, never passed on argv, never written to disk, and never placed in
the receipt — `field_names()` returns key names and the values go out of scope
with the function. If that is not acceptable for the backup lane in particular,
drop Q5: it is context for RISK-F-0010, not a dependency of it.
For Q5 the receipt records `predecessor_material_recorded: false`, and no value,
fingerprint, length or shape of any predecessor credential is captured — which is
the constraint risk-nexus set.
## What each answer unblocks
Declare the security layer, and repair the placement admission check Layer declaration (gate-house). INTENT.md now carries the declaration in its own voice with layer.yaml as the machine-readable form, adapted from ops-warden's reference. railiance-platform is Staff: operating OpenBao is not a claim to the Tooling layer, because §4 is explicit that no operator-of-third-party-Tooling shape exists and that someone running it stays a declared gap. Six direct Tooling contacts are mapped by capability rather than by file — one §5.2 conduit, one §5.1 diagnostic, four §5.3 gaps with intended owners and review dates — and the uncatalogued contacts are listed so the check is total. We are PEP-shaped and the unreachable-engine stance map is NOT published; that is recorded as an open obligation to build against v0.8, not left silent. Placement admission. canned-prompts was added as a PostgresConsumer on platform-pg-2 in rapp-postgres 1b68b4c without a placement owner here, which is exactly the cross-repo drift the assurance check exists to catch; the check had been failing on it. Registered with its real boundary evidence, corrected the stale test expectation that pinned the overflow cell at one consumer, and updated the SCOPE occupancy line to 2/4. Also records owner input received today: key-cape's issuer view on CCR-2026-0020's presenting actor, and their confirmation that codex-railiance-platform correctly stays tenant:coulomb, so the flagged T02 discrepancy is closed as not-a-defect. The whynot-design npm field is NOT changed. Two dated live receipts here name NPM_AUTH_TOKEN as the field, including an attended founder fetch; that is recorded against the counterparty claim rather than either side being flipped before the session settles it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WLUjpv3ssxNRAEPPgLFnEB Assistant: claude-code Assistant-Model: opus Assistant-Process: 1275505@bnt-lap001 Assistant-Session: 97265baa-f08f-4032-b290-a1e2965a69c5
2026-09-09 23:27:14 +02:00
- **Q1 + Q2 → secrets-engine and ops-warden.** Q2 is now the more urgent of the
two. ops-warden changed their catalog and playbook from `NPM_AUTH_TOKEN` to
`npm_token` on secrets-engine's statement, while this repository holds two dated
live receipts — including an attended founder fetch that exited zero — naming
`NPM_AUTH_TOKEN` as the field. If those receipts are right, that change points
the front door at a field that does not exist and the lane breaks on next use.
Q2 enumerates the real field names: `npm_token` only, `NPM_AUTH_TOKEN` only, or
both, and each outcome names exactly one side that has to move, as a reviewed
lane change carrying its own approval. If Q1 finds a populated legacy path, that
is a stale duplicate holding a real token and becomes its own disposition item —
do not delete it inside this session.
- **Q3 → internal.** Either the policies match the repo, and the activation's
`policy_applied: false` meant "no change needed", or they drift and that is a
finding to fix under RPF-WP-0035 before anyone treats the verifier lanes as
clean.
- **Q4 → CCR-2026-0019.** Existing bound claims show what the estate already uses
for operator lanes. This is **input, not confirmation** — the authorized group
claim is NetKingdom's and KeyCape's to state, and the receipt says so in-line so
a later reader cannot mistake the listing for an approval.
- **Q5 → risk-nexus.** Confirms the governed lane is populated with the expected
field names, supporting the fail-closed argument already made on that thread.
## Explicitly out of scope
- **The rapp-qonto KeyCape client rotation** (ops-warden `c1aafba4`). It is a
two-custodian CAS write with a service restart and needs its own reviewed
request first. Do not fold it into a read-only session because the operator
happens to be authenticated.
- **Predecessor share invalidation** for RPF-WP-0029-T02. That is a provider-side
action in the Nextcloud UI, not an OpenBao operation, and it needs the provider
owner. It can share the same sitting, but it is a separate step with a separate
receipt.
- **Any write, seed, rotation or policy apply.** The runner contains no mutating
verb; if a question turns out to need one, it comes back as a request.
- **Client-side reads for the approval clients.** CCR-2026-0019/0020 remain
in_flight; this session does not advance them beyond Q4's input.
## After the session
1. Store the receipt under `docs/evidence/` with the run date, values absent.
2. Reply to secrets-engine (Q1/Q2), risk-nexus (Q5) and, if Q3 drifts, record the
finding against RPF-WP-0035.
3. If Q1 finds a populated legacy path, open a disposition item for it rather
than acting on it in the moment.