ops-mason/plans/reuse-surface-hub-writer.md
tegwick fca5591bc6 MASON-WP-0012: named-role login without session reuse, diagnosis receipt, capabilities-self proof (NK-WP-0051-T06)
verify-reuse-surface-hub-writer.py logs in with skip_browser=true and
shows the address (or opens a fresh temporary browser profile), so the
default Authelia session is never reused. The receipt records uid,
openbao_error and assurance per net-kingdom/docs/sign-in-diagnosis.md,
with tokens, codes and emails redacted. Capabilities come from
sys/capabilities-self on the lane token; no operator or root token is
read. Needs railiance-platform CCR-2026-0045 applied to run live.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1041694@bnt-lap001
Assistant-Session: 7af0bb68-ac4a-4ce3-8639-24ddd84c0b52
2026-10-14 23:48:11 +02:00

188 lines
9.3 KiB
Markdown

---
id: reuse-surface-hub-writer
demand_source: the-custodian/workplans/CUST-WP-0081 and State Hub message 08b254af-59d4-4d1a-9544-fed3dcaaf213
consumer_repo: reuse-surface
credential_type: openbao-oidc-kv-read
status: draft
approved_by: null
approved_at: null
created: "2026-10-14"
updated: "2026-10-14"
filed_as: CCR-2026-0044
---
# Construction plan: dedicated reuse-surface hub-writer caller
## 1. Demand
CUST-WP-0081 needs to register reviewed capabilities at the reuse-surface hub.
The write bearer already exists at
`platform/workloads/reuse/reuse-surface/runtime-secrets`, field
`REUSE_SURFACE_TOKEN`, delivered to the hub by External Secrets under
CCR-2026-0005. `warden access reuse-surface-hub-write-token --exec` is the
attended fetch. On 2026-10-14 that fetch returned OpenBao HTTP 403 after
flex-auth decision `decision:f730e3da14beebdd`. The helper did not run.
The operator then confirmed OpenBao CLI entity
`4a1f663c-0621-0483-edd0-f852274f4b6b` carries `default`, `operator-custody`
and `platform-admin`. `bao token capabilities` on that token reports
create, delete, list, read and update on the exact data path, the exact
metadata path, and an unrelated sibling. That token is not a dedicated
caller. OpenBao adds entity policies to every token for the entity, so
attaching `workload-kv-read-reuse-surface-runtime` to this entity would not
produce a token that carries only that policy. The request forbids that
attachment and forbids sending any secret value.
## 2. Existing-structure survey
Live `plan-survey.py` was not run. There is no `ops-mason-survey` grant in
this session, and the admin token must not be used as the survey credential.
This section is from the repositories on disk at 2026-10-14.
- Policy `workload-kv-read-reuse-surface-runtime`
(`railiance-platform/openbao/policies/workload-kv-read-reuse-surface-runtime.hcl`)
grants read on the exact data path and the exact metadata path. It grants
nothing else. Sibling denial is the absence of any other grant. No auth
role in railiance-platform names this policy.
- ESO uses the separate policy `workload-kv-read-reuse-surface-runtime-eso`
and Kubernetes role `reuse-surface-runtime-eso` (CCR-2026-0005, updated
2026-09-05). That lane is the hub pod's delivery path, not the workstation
caller.
- `agent-high-risk-boundary` denies data read and allows metadata read on
this path for tokens that carry it. `platform-admin` grants `platform/*`.
A token with both would be denied on the data path, because deny wins.
The reported capability set does not include deny, so the 403 and the
capability report are not the same effective token. The verifier must
measure the token that `warden access` will actually use.
- Catalog entry `reuse-surface-hub-write-token` is active and
`resolvable: true`. Its `auth_method` says the caller's own OpenBao token,
either operator OIDC or a token carrying the workload policy. It does not
name a role or an entity. CCR-2026-0005 does not name one either.
- Redirects that use `https://bao.coulomb.social` are stale. The public
ingress is gone. Fluid-telegram's role uses localhost, 127.0.0.1:8250, and
the 127.0.0.1:18200 tunnel.
**Does an existing lane already satisfy this demand?** The KV path and the
read policy do. The caller binding does not. The change is a role bound to
a different principal, plus the two self-lifecycle stanzas the platform
generator emits for `token_self_lifecycle: true`. No new secret, no new KV
path, no change to the ESO role.
## 3. Proposed changes
SEC-DSM §37, in order:
- Q1. The hub still requires its existing bearer. Reading that bearer from
OpenBao can use a person or service identity. Do not create a secret to
stand in for the caller.
- Q2. OpenBao accepts OIDC on `auth/netkingdom`. Use that. A KeyCape group
selects the principal. The principal's OpenBao entity must not be
`4a1f663c-0621-0483-edd0-f852274f4b6b` and must not carry `platform-admin`
or `operator-custody`. The uuid `f94b7c34-e151-30bb-b473-df3ccdbb9bf1` is
not an OpenBao entity.
- Q3. The OpenBao token is a 15-minute service token from the role. The hub
bearer stays the static field already in KV. No new issuance layer.
- Q4. Rotation of the hub bearer stays CCR-2026-0005. This plan does not
rotate it.
- Q5. No new persistent secret. Owner of the binding is railiance-platform.
The consumer is the attended hub-registration helper. Authority is the
OIDC role. Delivery is `warden access --exec` with the lane token.
Revocation is `auth/token/revoke-self` and deletion of the role.
Preference hierarchy: federated identity for the read, existing vaulted
static bearer for the hub. Not a new static secret and not a new AppRole.
1. `modify` policy `workload-kv-read-reuse-surface-runtime`. Add
`auth/token/lookup-self` read and `auth/token/revoke-self` update. Leave
the two KV paths as read. Do not add a glob deny: in OpenBao a deny on a
prefix also denies the more specific path. Sibling denial remains "not
granted", checked on a parent path and on
`platform/data/workloads/issue-core/issue-core/issue-core-runtime`.
2. `create` OIDC role `auth/netkingdom/role/reuse-surface-hub-writer-workload-kv-read`.
`token_policies` is only the workload policy, `token_no_default_policy` is
true, TTL 15m, max 30m, group `reuse-surface-hub-writers`, assurance
`aal2`. Callbacks are the workstation URIs above.
3. `create` KeyCape group `reuse-surface-hub-writers` with member
`bernd.worsch-3`. The OpenBao entity is created on first login. The
member receipt rejects the admin entity and any token that also carries
`platform-admin` or `operator-custody`.
4. No KV write. No attachment of this policy, or of
`workload-kv-read-reuse-surface-runtime-eso`, to entity
`4a1f663c-0621-0483-edd0-f852274f4b6b`.
Artifacts: `builds/reuse-surface-hub-writer/`. The CCR id is a draft.
railiance-platform assigns the real id and applies it. ops-mason does not
install the policy or the role.
## 4. Review notes
The 403 is not evidence that the path is closed to every token. It is
evidence that the token `warden access` used could not read the field.
The later capability check describes a different, broader token. The member
receipt has to show both facts on one token: exact policy set, and deny on
a sibling. A pass on the admin entity is a failure.
`credential-change.py` will not keep a hand-written `sys/capabilities-self`
stanza. The verifier therefore asks `sys/capabilities` with the operator
token and puts the lane token only in a mode-0600 body file that it
deletes. The receipt never contains the token.
Superseded by NK-WP-0051-T06 (MASON-WP-0012): railiance-platform now
generates the grant from `openbao.token_capabilities_self` (CCR-2026-0045),
and the verifier calls `sys/capabilities-self` with the lane token only.
Catalog `reuse-surface-hub-write-token` stays active. This plan does not
ask ops-warden to vend the value. After both verifier modes pass, ops-warden
can name the role in `auth_method`. Until then the entry's caller is unnamed
and the draft front door stays `pending-review`.
## 5. Attended apply and proof
Founder, after platform-operator and reuse-surface-owner approval, and after
key-cape confirms the group:
1. Copy the policy file over
`railiance-platform/openbao/policies/workload-kv-read-reuse-surface-runtime.hcl`
and apply the CCR with the delegated applier. Do not run `bao kv put`.
2. The capability query uses the first local token allowed to call
`sys/capabilities`: `BAO_TOKEN` or `VAULT_TOKEN`, then
`~/.local/openbao/platform-admin.token`, then `~/.vault-token`.
In a private window, as the group member:
```bash
python3 scripts/verify-reuse-surface-hub-writer.py member
python3 scripts/verify-reuse-surface-hub-writer.py non-member
```
3. Receipts are mode 0600 under
`~/.local/state/railiance/receipts/reuse-surface-hub-writer-*.txt`.
Copy the sanitized text into
`docs/evidence/` only after both results are `pass`. That copy is the
verification receipt. It is not written in advance.
4. Reply to message `08b254af` with the receipt text. Then a registration
command may use the same role's unprinted token:
```bash
warden access reuse-surface-hub-write-token --exec --field REUSE_SURFACE_TOKEN -- <silent helper>
```
Set `BAO_TOKEN` to the lane token for that one command. Do not use `--fetch`.
Do not export the field. Revoke the lane token when the command returns.
## 6. Result
Applied 2026-10-14T17:47:43Z. Both attended receipts passed on 2026-10-14.
Member at 20:05:45Z is entity `c204099b-cb94-363f-e207-5ff4d4aa6e94` with
only `workload-kv-read-reuse-surface-runtime`. Non-member at 20:10:49Z was
refused. Copies are in `docs/evidence/2026-10-14-reuse-surface-hub-writer-member.txt`
and `docs/evidence/2026-10-14-reuse-surface-hub-writer-non-member.txt`.
2026-10-14: railiance-platform filed this bundle as proposed CCR-2026-0044
(`88b4aed`, message `14e23602`). The live policy file stays as it was until
apply. The founder created LLDAP group `reuse-surface-hub-writers` with
member `bernd.worsch-3`. On 2026-10-14 that user enrolled a second factor.
`tegwick` is not the member. The founder reported uuid
`f94b7c34-e151-30bb-b473-df3ccdbb9bf1`. OpenBao has no entity with that id
and no entity named `bernd.worsch-3`. CCR-2026-0044 is approved in
railiance-platform `8655857`, with the group and `aal2` binding confirmed.
A passing login still has to present `/assurance/level=aal2`.