WARDEN-WP-0026 finish Strand A (T04/T05/T07)
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Promote railiance-backup-offsite-lane to active/resolvable after
capabilities-safe re-verify. Add catalog risk=high, agent read-boundary
(exit 7 + OpenBao policy companion), EXPOSED taint via warden taint, and
close WP-0026.
This commit is contained in:
tegwick 2026-07-16 23:26:26 +02:00
parent 7d0c7c7684
commit b971403dad
16 changed files with 689 additions and 31 deletions

View file

@ -95,10 +95,14 @@ run the owner's tool as the caller and preserve owner custody.
| `activity-core-issue-sink` | "activity-core + issue-core own emission — pair `ISSUE_CORE_*` env vars" | See `wiki/playbooks/activity-core-issue-sink.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 |
| `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 (`risk: high`) |
| `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 |
| `railiance-backup-offsite-lane` | "railiance-platform OpenBao KV for Nextcloud WebDAV + age recovery" | `warden access railiance-backup-offsite-lane --out FILE` (`risk: high`; agents: no raw stream) |
| `forgejo-admin-api-token` | "railiance-platform OpenBao KV for Forgejo admin PAT" | `warden access forgejo-admin-api-token --out FILE` (`risk: high`) |
Promotion criteria: `wiki/playbooks/catalog-lane-promotion.md`.
Promotion criteria: `wiki/playbooks/catalog-lane-promotion.md`.
High-risk / agent boundary: `wiki/playbooks/agent-read-boundary.md`.
EXPOSED taint: `wiki/playbooks/exposed-taint.md` · `warden taint <id>`.
**Draft** (hidden from default lookup until owner path ships — `warden route list --all`):

View file

@ -0,0 +1,77 @@
# Agent read-boundary on high-risk lanes
Date: 2026-07-16
Workplan: WARDEN-WP-0026 T04
OpenBao policy: `railiance-platform/openbao/policies/agent-high-risk-boundary.hcl`
Coding agents must not hold **raw data-read** on high-risk secrets. They may
inspect **capabilities** and **metadata**, and may receive values only through
sanctioned transports (file / exec env / response-wrapping token) under a human
operator identity.
---
## Risk classification (catalog `risk:`)
| Class | Criteria | Catalog default |
| --- | --- | --- |
| `high` | Recovery escrow (e.g. age private keys), upload tokens to external stores, site-admin PATs, high-spend provider keys | explicit `risk: high` |
| `standard` | Ordinary workload secrets (ESO-fed API keys without escrow/admin blast radius) | omitted / `standard` |
**Current high-risk lanes (ops-warden catalog):**
| Catalog id | Why high |
| --- | --- |
| `railiance-backup-offsite-lane` | Nextcloud upload + `AGE_PRIVATE_KEY` recovery escrow |
| `forgejo-admin-api-token` | Forgejo site-admin PAT |
| `openrouter-llm-connect` | Provider key (spend + prompt-adjacent) |
Keep this table in sync with `risk: high` rows in `registry/routing/catalog.yaml`
and path denials in `agent-high-risk-boundary.hcl`.
---
## OpenBao side
1. **Operator OIDC roles** keep `workload-kv-read-*` for the lane (data `read`).
2. **Agent identities** attach `agent-high-risk-boundary` (or equivalent) and
**must not** also attach the lane's `workload-kv-read-*` policy.
3. Verify with capabilities only (never `kv get` for deny tests):
```bash
# Agent-shaped token
AGENT=$(bao token create -policy=agent-high-risk-boundary -ttl=5m -field=token)
bao token capabilities "$AGENT" platform/data/workloads/railiance/backup/offsite-lane
# → deny
bao token capabilities "$AGENT" platform/metadata/workloads/railiance/backup/offsite-lane
# → read
bao token revoke "$AGENT"
```
Wrapped/proxied access for agents: a human operator (or credential broker with
response-wrap) fetches under an operator identity and delivers via
`warden access … --wrap` / `--out` / `--exec`. Agents do not unwrap into chat.
---
## ops-warden side
When `WARDEN_AGENT_ID` is set and the lane is `risk: high`, `warden access --fetch`
**refuses raw value streaming** (exit 7). Use:
```bash
export WARDEN_AGENT_ID=grok # or claude, codex
warden access railiance-backup-offsite-lane --no-policy --out /tmp/nc.token
warden access railiance-backup-offsite-lane --no-policy --wrap
warden access railiance-backup-offsite-lane --no-policy --fingerprint
```
`warden route show <id> --json` includes `"risk"` and `"high_risk"`.
---
## See also
- `.claude/rules/credential-routing.md` — safe transports
- `wiki/playbooks/exposed-taint.md` — EXPOSED metadata convention
- `history/2026-07-16-credential-disclosure-lessons.md`

View file

@ -87,13 +87,17 @@ out on the OpenBao-delivered value, positive + negative verification audit-logge
---
## Draft lanes (2026-07-02)
## Draft lanes (2026-07-16)
| Catalog `id` | Blocker |
| --- | --- |
| `object-storage-sts` | NK-WP-0007 vending path not production-exercised |
| `database-dynamic-credentials` | OpenBao database engine role paths TBD per workload |
**Promoted 2026-07-16:** `railiance-backup-offsite-lane` — CCR-2026-0004
capabilities-safe re-verify (WP-0026 T07); primary field `NC_WEBDAV_TOKEN`;
`risk: high` + EXPOSED taint on version 2 (operator may rotate optionally).
Re-run promotion when the owning repo closes the blocker; do not promote on
playbook prose alone.

View file

@ -0,0 +1,71 @@
# EXPOSED taint convention (OpenBao KV v2)
Date: 2026-07-16
Workplan: WARDEN-WP-0026 T05
Mark a secret as **EXPOSED** when it may have landed in a logged or shared context
(agent transcript, chat, CI log). Taint is **advisory**: it does not revoke access
or rotate values. Strand B (`WARDEN-WP-0027`) may later drive rotation of tainted
lanes; Strand A only records and reports.
---
## Custom metadata keys
Set on the KV v2 secret **metadata** (never in secret data values):
| Key | Required | Meaning |
| --- | --- | --- |
| `exposed_at` | yes | ISO-8601 UTC when disclosure was recognized |
| `exposed_version` | recommended | KV version that was (or may have been) disclosed |
| `exposed_reason` | optional | short slug, e.g. `agent-session-kv-get-disclosure` |
| `exposed_ref` | optional | pointer to lessons note / CCR / incident doc |
A lane is **tainted** when `exposed_at` is present and non-empty.
---
## Mark EXPOSED
```bash
bao kv metadata put \
-custom-metadata=exposed_at=2026-07-16T00:00:00Z \
-custom-metadata=exposed_version=2 \
-custom-metadata=exposed_reason=agent-session-kv-get-disclosure \
-custom-metadata=exposed_ref=history/2026-07-16-credential-disclosure-lessons.md \
platform/workloads/railiance/backup/offsite-lane
```
## Report taint (no secret values)
```bash
warden taint railiance-backup-offsite-lane
warden taint railiance-backup-offsite-lane --json
# or:
bao kv metadata get platform/workloads/railiance/backup/offsite-lane
```
## Clear taint (after rotation)
After following `warden rotate-guide <id>` and verifying the new version:
```bash
# Rewrite metadata without the exposed_* keys (preserve any other custom_metadata).
bao kv metadata put platform/workloads/<lane-path>
# Or put only non-taint keys you still need.
warden taint <id> # expect tainted: no
```
---
## Semi-automatic candidates
Reads of high-risk paths from agent/shared identities in the OpenBao audit log
are candidates for marking EXPOSED. Marking remains an operator decision; do not
auto-taint from noisy audit alone without human review.
## See also
- `history/2026-07-16-credential-disclosure-lessons.md`
- `wiki/playbooks/catalog-lane-promotion.md` (capabilities-safe verify)
- OpenBao policy `agent-high-risk-boundary` (WP-0026 T04)

View file

@ -1,7 +1,7 @@
# Railiance Offsite Backup Lane
Date: 2026-07-07
Catalog: `railiance-backup-offsite-lane` (status `draft`, `resolvable: false` until verified)
Date: 2026-07-16
Catalog: `railiance-backup-offsite-lane` (status `active`, `resolvable: true`, `risk: high`)
Owner: `railiance-platform` (CCR-2026-0004)
Nextcloud WebDAV upload token and URL for age-encrypted offsite backups (Option A).
@ -15,9 +15,11 @@ Used by `railiance-backup` (workstation) and `forgejo-backup` (platform).
| --- | --- |
| Mount | `platform` |
| Path | `platform/workloads/railiance/backup/offsite-lane` |
| Fields | `NC_WEBDAV_TOKEN`, `NC_WEBDAV_URL`, `AGE_PRIVATE_KEY` |
| Policy | `workload-kv-read-railiance-backup-offsite-lane` |
| Fields | `NC_WEBDAV_TOKEN` (primary fetch), `NC_WEBDAV_URL`, `AGE_PRIVATE_KEY` (recovery escrow) |
| Policy | `workload-kv-read-railiance-backup-offsite-lane` (operator OIDC) |
| Agent policy | `agent-high-risk-boundary`**deny** data-read; metadata only |
| OIDC role | `railiance-backup-workload-kv-read` (`groups=net-kingdom-admins`) |
| Risk | `high` (upload token + age recovery escrow) |
---
@ -29,7 +31,16 @@ Used by `railiance-backup` (workstation) and `forgejo-backup` (platform).
bao login -method=oidc -path=netkingdom role=railiance-backup-workload-kv-read
```
2. **Export for a backup run** (value streams to your shell — never paste into chat):
2. **Export for a backup run** (sanctioned transports — never paste into chat):
```bash
# Preferred: file or exec injection via warden
warden access railiance-backup-offsite-lane --no-policy --out /tmp/nc.token
# or:
warden access railiance-backup-offsite-lane --no-policy --exec -- env | grep -v .
```
If you must use raw bao in an interactive human shell (not an agent session):
```bash
export RAILIANCE_BACKUP_NC_TOKEN=$(
@ -40,10 +51,11 @@ Used by `railiance-backup` (workstation) and `forgejo-backup` (platform).
)
```
3. **Or proxy via warden access** (after catalog promotion):
3. **Proxy via warden access** (catalog active + resolvable):
```bash
warden access railiance-backup-offsite-lane --no-policy --fetch --field NC_WEBDAV_TOKEN
warden access railiance-backup-offsite-lane --no-policy --fetch --out /tmp/nc.token
# Primary field is NC_WEBDAV_TOKEN. AGE_PRIVATE_KEY is recovery escrow only.
```
4. **Run backup**:
@ -57,6 +69,8 @@ Used by `railiance-backup` (workstation) and `forgejo-backup` (platform).
```
`AGE_PRIVATE_KEY` in the same path is recovery escrow — fetch only for restore drills.
**Agents** (`WARDEN_AGENT_ID` set) cannot stream raw high-risk values; use `--out` /
`--exec` / `--wrap`. See `wiki/playbooks/agent-read-boundary.md`.
---
@ -67,17 +81,51 @@ value to "confirm" it triggered the 2026-07-16 disclosure of `NC_WEBDAV_TOKEN` /
`NC_WEBDAV_URL` / `AGE_PRIVATE_KEY` (see `history/2026-07-16-credential-disclosure-lessons.md`).
```bash
# Positive: lane OIDC identity can read the data path
bao login -method=oidc -path=netkingdom role=railiance-backup-workload-kv-read
bao token capabilities "$(bao print token)" platform/data/workloads/railiance/backup/offsite-lane
# Positive: lane policy identity can read the data path
LANE=$(bao token create -policy=workload-kv-read-railiance-backup-offsite-lane -ttl=2m -field=token)
bao token capabilities "$LANE" platform/data/workloads/railiance/backup/offsite-lane
# → expect: read
bao token revoke "$LANE"
# Negative: default-only identity is denied (no value is read)
DEFAULT_TOKEN=$(bao token create -policy=default -field=token) # if this is denied, that IS the pass — do NOT fall back
bao token capabilities "$DEFAULT_TOKEN" platform/data/workloads/railiance/backup/offsite-lane
# → expect: deny
bao token revoke "$DEFAULT_TOKEN"
# Agent boundary: data deny, metadata allow
AGENT=$(bao token create -policy=agent-high-risk-boundary -ttl=2m -field=token)
bao token capabilities "$AGENT" platform/data/workloads/railiance/backup/offsite-lane # deny
bao token capabilities "$AGENT" platform/metadata/workloads/railiance/backup/offsite-lane # read
bao token revoke "$AGENT"
```
The capability lists contain no secret material — safe to record on
`CCR-2026-0004` as promotion evidence. Full pattern:
`wiki/playbooks/catalog-lane-promotion.md#capabilities-safe-lane-verification`.
`wiki/playbooks/catalog-lane-promotion.md#capabilities-safe-lane-verification`.
### Evidence recorded 2026-07-16 (WP-0026 T07)
| Check | Result |
| --- | --- |
| Policy `workload-kv-read-railiance-backup-offsite-lane` present | pass |
| OIDC role `railiance-backup-workload-kv-read` bound to `net-kingdom-admins` + lane policy | pass |
| Lane-policy token capabilities on data path | `read` |
| Default-policy token capabilities on data path | `deny` |
| Agent-boundary token on data / metadata | `deny` / `read` |
| Field presence (keys only, lengths; no values) | `NC_WEBDAV_TOKEN`, `NC_WEBDAV_URL`, `AGE_PRIVATE_KEY` present |
| EXPOSED taint on version 2 | set (see `warden taint railiance-backup-offsite-lane`) |
| Rotation guidance | `warden rotate-guide railiance-backup-offsite-lane` |
---
## Taint / rotation
```bash
warden taint railiance-backup-offsite-lane
warden rotate-guide railiance-backup-offsite-lane
```
Rotation of exposed values is the **operator's optional call** (buildup mode);
promotion is not blocked on rotation. After rotation, clear `exposed_*`
custom_metadata keys (`wiki/playbooks/exposed-taint.md`).