Migrate reuse-surface hub token lane to OpenBao handoff

RAILIANCE-WP-0011-T03: point reuse-surface-hub-write-token catalog,
playbook, and tests at bao kv get on platform/workloads/reuse/reuse-surface/runtime-secrets;
kubectl documented as break-glass only.
This commit is contained in:
2026-07-07 22:38:45 +02:00
parent 3ddccaf701
commit 1e6c4eedf1
5 changed files with 61 additions and 53 deletions

View File

@@ -216,21 +216,25 @@ entries:
- id: reuse-surface-hub-write-token - id: reuse-surface-hub-write-token
title: reuse-surface federation hub write bearer token title: reuse-surface federation hub write bearer token
need_keywords: [reuse-surface, reuse_surface, hub, register, federation, write, token, bearer, REUSE_SURFACE_TOKEN, reuse.coulomb.social] need_keywords: [reuse-surface, reuse_surface, hub, register, federation, write, token, bearer, REUSE_SURFACE_TOKEN, REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET, reuse.coulomb.social]
owner_repo: reuse-surface owner_repo: railiance-platform
subsystem: reuse-surface federation hub subsystem: OpenBao + reuse-surface
warden_executes: false warden_executes: false
wiki_ref: wiki/playbooks/reuse-surface-hub-write-token.md#worker-checklist wiki_ref: wiki/playbooks/reuse-surface-hub-write-token.md#worker-checklist
canon_ref: reuse-surface/specs/FederationHubAPI.md canon_ref: reuse-surface/specs/FederationHubAPI.md
reviewed: "2026-07-07" reviewed: "2026-07-07"
status: active status: active
# Concrete, owner-confirmed lane — REUSE-WP-0011 / RAILIANCE-WP-0007 (hub live # Concrete, owner-confirmed lane — railiance-platform CCR-2026-0005 / RAILIANCE-WP-0011
# 2026-06-15): token is the cluster Secret reuse-surface-env on Railiance01, # (promoted 2026-07-07): policy workload-kv-read-reuse-surface-runtime; ExternalSecret
# not OpenBao. warden access proxies kubectl as the caller and never holds the value. # reuse/reuse-surface-runtime SecretSynced to reuse-surface-env on Railiance01;
auth_method: "kubectl with Railiance01 kubeconfig (~/.kube/config-hosteurope)" # positive + negative access verified. Production consumer is ESO; warden access
path_template: "reuse/reuse-surface-env" # proxies reads as the caller and never holds the value.
fetch_command: "kubectl --kubeconfig ~/.kube/config-hosteurope get secret reuse-surface-env -n reuse -o jsonpath='{.data.REUSE_SURFACE_TOKEN}' | base64 -d" auth_method: "caller's own OpenBao token (operator OIDC via key-cape, or a token carrying workload-kv-read-reuse-surface-runtime)"
path_template: "platform/workloads/reuse/reuse-surface/runtime-secrets"
fetch_command: "bao kv get -field=REUSE_SURFACE_TOKEN platform/workloads/reuse/reuse-surface/runtime-secrets"
policy_ref: "flex-auth check secret.read:reuse"
exec_capable: true exec_capable: true
resolvable: true
lane: secret lane: secret
- id: openrouter-llm-connect - id: openrouter-llm-connect

View File

@@ -64,16 +64,16 @@ def test_resolve_refuses_non_exec_capable():
resolve_fetch_command(_entry(exec_capable=False, fetch_command=None)) resolve_fetch_command(_entry(exec_capable=False, fetch_command=None))
def test_resolve_piped_fetch_uses_shell_cmd(): def test_resolve_bao_fetch_uses_argv():
from warden.routing import load_catalog from warden.routing import load_catalog
catalog = load_catalog(Path(__file__).resolve().parents[1] / "registry" / "routing" / "catalog.yaml") catalog = load_catalog(Path(__file__).resolve().parents[1] / "registry" / "routing" / "catalog.yaml")
entry = catalog.get("reuse-surface-hub-write-token") entry = catalog.get("reuse-surface-hub-write-token")
resolved = resolve_fetch_command(entry) resolved = resolve_fetch_command(entry)
assert resolved.argv is None assert resolved.argv is not None
assert resolved.shell_cmd is not None assert resolved.shell_cmd is None
assert "| base64 -d" in resolved.shell_cmd assert resolved.argv[0] == "bao"
assert "reuse-surface-env" in resolved.shell_cmd assert "platform/workloads/reuse/reuse-surface/runtime-secrets" in resolved.argv
# --- G2: transit-only fetch (inherited stdout) ----------------------------- # --- G2: transit-only fetch (inherited stdout) -----------------------------

View File

@@ -288,8 +288,8 @@ def test_reuse_surface_hub_write_token_lane_is_resolvable():
e = catalog.get("reuse-surface-hub-write-token") e = catalog.get("reuse-surface-hub-write-token")
assert e is not None and e.is_active and e.exec_capable assert e is not None and e.is_active and e.exec_capable
assert e.resolvable is True assert e.resolvable is True
assert e.owner_repo == "reuse-surface" assert e.owner_repo == "railiance-platform"
assert "reuse-surface-env" in e.fetch_command assert "platform/workloads/reuse/reuse-surface/runtime-secrets" in e.fetch_command
def test_find_object_storage_sts(): def test_find_object_storage_sts():

View File

@@ -96,7 +96,7 @@ run the owner's tool as the caller and preserve owner custody.
| `inter-hub-bootstrap-ssh` | "Inter-Hub bootstrap SSH envelope — attended vs unattended branches" | See `wiki/InterHubBootstrapAccessLane.md` | | `inter-hub-bootstrap-ssh` | "Inter-Hub bootstrap SSH envelope — attended vs unattended branches" | See `wiki/InterHubBootstrapAccessLane.md` |
| `issue-core-ingestion-api-key` | "railiance-platform OpenBao KV + ESO deliver `ISSUE_CORE_API_KEY` — here is the path" | ESO consumes in-cluster; `warden access issue-core-ingestion-api-key --fetch ISSUE_CORE_API_KEY` as yourself | | `issue-core-ingestion-api-key` | "railiance-platform OpenBao KV + ESO deliver `ISSUE_CORE_API_KEY` — here is the path" | ESO consumes in-cluster; `warden access issue-core-ingestion-api-key --fetch ISSUE_CORE_API_KEY` as yourself |
| `openrouter-llm-connect` | "railiance-platform OpenBao KV + ESO deliver `OPENROUTER_API_KEY` to activity-core" | ESO consumes in-cluster; `warden access openrouter-llm-connect --fetch OPENROUTER_API_KEY` as yourself | | `openrouter-llm-connect` | "railiance-platform OpenBao KV + ESO deliver `OPENROUTER_API_KEY` to activity-core" | ESO consumes in-cluster; `warden access openrouter-llm-connect --fetch OPENROUTER_API_KEY` as yourself |
| `reuse-surface-hub-write-token` | "reuse-surface hub write bearer — K8s secret `reuse-surface-env` on Railiance01" | `kubectl` from `~/.kube/config-hosteurope`, or `warden access reuse-surface-hub-write-token --fetch` as yourself | | `reuse-surface-hub-write-token` | "railiance-platform OpenBao KV + ESO deliver `REUSE_SURFACE_TOKEN` to reuse-surface" | ESO consumes in-cluster; `warden access reuse-surface-hub-write-token --fetch` as yourself |
Promotion criteria: `wiki/playbooks/catalog-lane-promotion.md`. Promotion criteria: `wiki/playbooks/catalog-lane-promotion.md`.

View File

@@ -1,13 +1,13 @@
# reuse-surface Hub Write Token # reuse-surface Hub Write Token — OpenBao Custody
Date: 2026-07-07 Date: 2026-07-07 (promoted active 2026-07-07)
Catalog: `reuse-surface-hub-write-token` (status `active`, `resolvable: true`) Workplan: RAILIANCE-WP-0011-T03 · CCR-2026-0005
Owner: `reuse-surface` (service) · deploy custody `railiance-apps` (K8s secret) Catalog: `reuse-surface-hub-write-token` (**active** — path live, ESO delivering)
Bearer token for authenticated writes to the production federation hub at Bearer token for authenticated writes to the production federation hub at
`https://reuse.coulomb.social` (`POST /v1/repos`, `reuse-surface hub register`). `https://reuse.coulomb.social` (`POST /v1/repos`, `reuse-surface hub register`).
ops-warden **does not hold this token** — it is a pointer lane to the cluster ops-warden does not vend this token — custody belongs to `railiance-platform`
secret that backs the hub Deployment. (OpenBao) and the `reuse-surface` workload via External Secrets.
--- ---
@@ -15,72 +15,76 @@ secret that backs the hub Deployment.
| Field | Value | | Field | Value |
| --- | --- | | --- | --- |
| Cluster | Railiance01 (`92.205.62.239`) | | OpenBao server | `https://bao.coulomb.social` (coulombcore) |
| Namespace | `reuse` | | KV path | `platform/workloads/reuse/reuse-surface/runtime-secrets` |
| Secret | `reuse-surface-env` | | Hub write field | `REUSE_SURFACE_TOKEN` |
| Field | `REUSE_SURFACE_TOKEN` | | Webhook HMAC field | `REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET` |
| Kubeconfig | `~/.kube/config-hosteurope` | | Read policy | `workload-kv-read-reuse-surface-runtime` |
| ESO delivery | `ExternalSecret reuse/reuse-surface-runtime``reuse-surface-env` (Railiance01) |
| Deploy runbook | `railiance-apps/docs/reuse-surface-on-railiance01.md` | | Deploy runbook | `railiance-apps/docs/reuse-surface-on-railiance01.md` |
| Hub API spec | `reuse-surface/specs/FederationHubAPI.md` | | Hub API spec | `reuse-surface/specs/FederationHubAPI.md` |
This is **not** an OpenBao KV path. The token is generated at deploy time and **Promotion gate (met 2026-07-07):** path seeded, `ExternalSecret reuse/reuse-surface-runtime`
stored only in the Kubernetes Secret consumed by the `reuse-surface` workload. `SecretSynced`, positive hub + webhook verification and negative default-policy denial
recorded in CCR-2026-0005.
### Related key in the same Secret ### Break-glass (cluster read)
| Field | Purpose | If OpenBao is unreachable, operators with Railiance01 kubeconfig may read the
| --- | --- | materialized Secret (ESO cache):
| `REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET` | HMAC for `POST /v1/webhooks/forgejo`; must match the org webhook on Forgejo |
Fetch (same kubectl lane — never paste into chat):
```bash ```bash
kubectl --kubeconfig ~/.kube/config-hosteurope get secret reuse-surface-env -n reuse \ kubectl --kubeconfig ~/.kube/config-hosteurope get secret reuse-surface-env -n reuse \
-o jsonpath='{.data.REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET}' | base64 -d -o jsonpath='{.data.REUSE_SURFACE_TOKEN}' | base64 -d
``` ```
Forgejo org webhook rollout: `railiance-apps` `make reuse-forgejo-webhook` Never paste values into chat, State Hub, workplans, or Git.
(runbook `docs/reuse-surface-on-railiance01.md#forgejo-org-webhook`).
Planned OpenBao migration: **RAILIANCE-WP-0011** (`railiance-platform`).
--- ---
## Worker checklist ## Worker checklist
1. **Confirm kubeconfig reachability** (you act as yourself; ops-warden adds no credential): 1. **Confirm OpenBao reachability** (you act as yourself; ops-warden adds no credential):
```bash ```bash
kubectl --kubeconfig ~/.kube/config-hosteurope get secret reuse-surface-env -n reuse bao kv metadata get platform/workloads/reuse/reuse-surface/runtime-secrets
``` ```
2. **Export for a shell session** (value streams to your terminal — never paste into chat): 2. **Export hub write token for a shell session** (streams to your terminal):
```bash ```bash
export REUSE_SURFACE_URL=https://reuse.coulomb.social export REUSE_SURFACE_URL=https://reuse.coulomb.social
export REUSE_SURFACE_TOKEN=$( export REUSE_SURFACE_TOKEN=$(
kubectl --kubeconfig ~/.kube/config-hosteurope get secret reuse-surface-env -n reuse \ bao kv get -field=REUSE_SURFACE_TOKEN \
-o jsonpath='{.data.REUSE_SURFACE_TOKEN}' | base64 -d platform/workloads/reuse/reuse-surface/runtime-secrets
) )
``` ```
3. **Or proxy via warden access** (same kubectl command, audited metadata only): 3. **Or proxy via warden access** (same `bao kv get`, audited metadata only):
```bash ```bash
warden route show reuse-surface-hub-write-token --json warden route show reuse-surface-hub-write-token --json
warden access reuse-surface-hub-write-token --no-policy --fetch warden access reuse-surface-hub-write-token --no-policy --fetch
``` ```
4. **Register a repo** after publish-check passes: 4. **Webhook HMAC** (same path, second field — must match Forgejo org webhook):
```bash
bao kv get -field=REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET \
platform/workloads/reuse/reuse-surface/runtime-secrets
```
After rotation: `railiance-apps` `make reuse-forgejo-webhook`.
5. **Register a repo** after publish-check passes:
```bash ```bash
reuse-surface hub status reuse-surface hub status
reuse-surface hub register --repo <slug> \ reuse-surface hub register --repo <slug> \
--url <gitea-raw-capabilities-yaml-url> \ --url <forgejo-raw-capabilities-yaml-url> \
--domain <domain> --domain <domain>
``` ```
5. **Verify federated index** picked up the new source: 6. **Verify federated index** picked up the new source:
```bash ```bash
curl -fsS "$REUSE_SURFACE_URL/v1/federated" | jq '.sources | map(.repo) | index("<slug>")' curl -fsS "$REUSE_SURFACE_URL/v1/federated" | jq '.sources | map(.repo) | index("<slug>")'
``` ```
Never commit the token, paste it into State Hub or agent chat, or store it in a Rotation: update fields in OpenBao, wait for ESO refresh (`make reuse-runtime-es-status`),
workplan. Rotation: regenerate the secret on-cluster and roll the Deployment roll `deployment/reuse-surface`, and re-run `make reuse-forgejo-webhook` when the
(`reuse-surface/docs/deploy/reuse-kubernetes.md`). webhook HMAC changes. Lifecycle:
`railiance-platform/docs/credential-lane-lifecycle-runbook.md` (lane entry pending T04).