risk-nexus/findings/RISK-F-0009-openbao-deny-set-covers-a-third-of-high-risk-lanes.md
tegwick e482423523 Five owner replies worked through: one fix closed, two grades corrected, one control retired
RISK-F-0006 fixed and public — railiance-platform's restore evidence was
read, not taken: 56s restore, all 13 coulomb_social row counts matching,
plus the BestEffort QoS this register had graded on, plus a failed first
WAL attempt recorded alongside the successful one.

RISK-F-0004 high -> medium. tenant-engine corrected in both directions:
payloads are returned (worse than graded) but there is no HTTP event-read
route, so the live network-reachable read this register wrote down does
not exist. L3 was a reachability claim inherited from a summary and never
tested.

RISK-F-0002: reading (c) confirmed — nothing blocks policy.enabled, it is
off by decision. ADR-0006 retires it in favour of zone-scoped
enforcement. Ruled: the framing is superseded, the risk is not. A control
retired before its replacement exists is still an absent control. The
successor's blocker is 26 of 27 lanes having no identifiable workload,
which is RISK-N-0004 with a number on it.

RISK-F-0009: uncovered count 8 -> 6, corrected by the reporter against
themselves; the token was never expired; and the deployed policy differs
from the file, which moves 'a file is not a safe proxy for the server'
from suspicion to evidence and amends verification.md — including the
admission that fix_tracker.py reads records, and a record can be stale.

RISK-V-0001 reconciled: ops-warden reaches the pin from the node through
a tunnel, so a podSelector ingress rule does not constrain it. The
observation was right and the inference was not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 09:32:32 +02:00

14 KiB
Raw Blame History

id type title status reported_by reported_via routed_by date_reported system environment fix_owner fix_tracking filed_as answers related severity severity_at_production impact likelihood fidelity_modifier production_rescore disclosure embargo_condition embargo_since escalation last_checked next_check cadence clean_streak waiting_on graded_by ruling checked_by
RISK-F-0009 finding agent-high-risk-boundary denies 6 of 17 high-risk lanes; the direct bao path is unprotected for the rest open ops-warden ops-warden ops-warden 2026-08-20 railiance-platform production railiance-platform unset (railiance-platform) RISK-F-0004 by ops-warden; renumbered by risk-nexus 2026-08-20 (id collision) RISK-F-0003
RISK-F-0003
high high I4 L2 false false embargoed railiance-platform reports the deny set covers every high-risk lane with a KV path (live verification refines the grade, it is not the condition) 2026-08-20 none 2026-08-21T07:32:09Z 2026-08-21T07:32:09Z instant 0
who what since would_change default default_at
railiance-platform report whether the deny set covers every high-risk lane with a KV path 2026-08-20 embargo lifts on coverage; live verification would refine the grade but is not required for it the eight uncovered paths stand as recorded and the finding is re-raised 2026-09-03
risk-nexus RISK-RULING-2026-08-20 risk-nexus

RISK-F-0009 — the OpenBao half of the agent read-boundary covers a third of the lanes

What is true

ADR-0004 (ops-warden) is enforced in two independent layers:

  1. ops-warden's CLI — warden access <lane> --fetch exits 7 when WARDEN_AGENT_ID is set and the lane is high-risk. Verified working for all 17 high-risk lanes as of 2026-08-20 (WARDEN-WP-0032-T05/T06).
  2. OpenBao policy agent-high-risk-boundary (railiance-platform/openbao/policies/agent-high-risk-boundary.hcl) — denies data read and permits metadata read for agent tokens.

Layer 1 protects the ops-warden path. Layer 2 is the one that protects the path an agent actually takes when it bypasses ops-warden — a direct bao kv get, which is the 2026-07-16 disclosure vector.

Comparing the policy's deny list against ops-warden's catalog:

  • 17 lanes graded risk: high
  • 5 data paths denied by the policy
  • 6 high-risk lanes covered (one policy path serves two lanes)
  • 8 high-risk lanes with a concrete KV path that the policy does not deny
  • 3 high-risk lanes with no KV path (policy not applicable)

Not covered:

Lane KV path Newly graded 2026-08-19?
rapp-qonto-keycape-client platform/workloads/rapp-qonto/keycape-client no — already high
agent-harness-forgejo-deploy platform/workloads/agent-harness/forgejo-deploy-key no — already high
email-connect-transactional platform/workloads/email-connect/transactional no — already high
scaleway-bootstrap platform/workloads/railiance/scaleway/bootstrap no — already high
whynot-design-npm-publish platform/workloads/coulomb/whynot-design/npm-publish yes
audit-core-senders platform/workloads/audit-core/senders yes
ops-warden-warden-sign-token credential-broker grant, not a KV path yes
openbao-api-key platform/workloads/<domain>/<workload>/<bundle> — a pattern, not a path yes

Four of the eight were already graded high before this analysis. The divergence is therefore pre-existing and not a consequence of the 2026-08-19 regrade; the regrade only made it visible by making the two lists comparable.

Why it matters

ops-warden's layer can be bypassed by not using ops-warden. An agent session holding a workload-kv-read-* policy can bao kv get these paths directly, and for the eight above nothing denies it. The playbook (ops-warden/wiki/playbooks/agent-read-boundary.md) already states that an agent token carrying agent-high-risk-boundary must not also carry the lane's workload-kv-read-* policy — but that is an instruction to whoever mints the token, not a mechanism.

Exposure — stated only as far as ops-warden can support it

  • The comparison above is static, from the policy file in the railiance-platform checkout and ops-warden's catalog. It has not been confirmed against the live OpenBao. The operator token available to ops-warden is expired (bao token lookup → 403), so bao policy read and bao token capabilities could not be run. The deployed policy may differ from the file.
  • Whether any agent token currently carries agent-high-risk-boundary at all, and whether any carries it together with a workload-kv-read-* policy, is not established. If no agent tokens exist, exposure is theoretical.
  • openbao-api-key is a routing lane whose path_template is a pattern rather than an address; it is listed for completeness but probably cannot be expressed as a policy deny and should be handled by grading the concrete lanes it resolves to.
  • No evidence of an actual disclosure through these lanes has been sought or found.

Suggested direction, not a prescription

The two lists should be generated from one source rather than maintained in two places — that is what let them drift for four lanes without anyone noticing. ops-warden's catalog already carries risk and path_template per lane and could emit the deny set; railiance-platform owns whether to consume it. A check that fails when a high-risk lane has no corresponding deny would turn this from an audit into an invariant.

Live verification — 2026-08-21 (ops-warden)

The exposure section above says the comparison is static and that ops-warden's token was expired. The token was not expired — bao token lookup returned a valid platform-admin token from an OIDC login, and bao policy read agent-high-risk-boundary succeeded against https://bao.coulomb.social. The deployed policy has now been read. Reported by the same repo that filed the static version; correcting our own record.

Three changes, one of them against ourselves:

1. Six lanes are uncovered, not eight. The table above lists ops-warden-warden-sign-token (a credential-broker grant, not a KV path) and openbao-api-key (a <domain>/<workload>/<bundle> pattern) among the "concrete KV path" uncovered set — the prose already said the second was a pattern, and the table counted it anyway. Neither can be expressed as a policy deny. Corrected split: 6 covered, 6 uncovered with a concrete path, 5 with no address. The six that stand are rapp-qonto-keycape-client, agent-harness-forgejo-deploy, email-connect-transactional, scaleway-bootstrap, whynot-design-npm-publish, audit-core-senders.

2. Coverage confirmed at 6 of 17 against the live policy. The headline was right and is no longer an inference from a checkout.

3. New — the deployed policy has drifted from the file. The file denies platform/workloads/core-hub/runtime; the server does not. No ops-warden catalog lane maps to that path, so nothing above changes. It matters as evidence rather than as exposure: this finding flagged "the deployed policy may differ from the file" as an unconfirmed risk, and it does differ. Any conclusion drawn from that file alone — by anyone, about any lane — is unsafe. Whether the drift is an undeployed change or a reverted one is railiance-platform's to say, and this is not an assertion that core-hub/runtime should be denied.

The suggested direction is now half-built. ops-warden/scripts/check_agent_read_boundary.py is the check that "fails when a high-risk lane has no corresponding deny" — run against the deployed policy, falling back to the file only with a loud warning, exit 1 on any uncovered lane. It is capabilities-only by construction: it reads the policy document and lane metadata, never a secret value, and never mints a token. What is still missing is the other half — railiance-platform consuming a generated deny set rather than hand-maintaining one. That half is theirs, and ops-warden does not amend another repo's control.

Not established, still. Whether any agent token carries agent-high-risk-boundary, and whether any carries it together with a workload-kv-read-* policy, remains unverified — confirming it means minting or inspecting tokens, which is a write against railiance-platform's OpenBao and not ops-warden's to perform. Exposure remains theoretical to exactly the degree it was before.

Severity, disclosure and the embargo are risk-nexus's to revise, not ours. Note that the embargo condition is a report from railiance-platform on coverage, which this does not satisfy — coverage is unchanged, it is now measured rather than inferred.

  • RISK-F-0003 — the ops-warden-side blind spot, mitigated 2026-08-19
  • ops-warden ADR-0004, ADR-0007
  • ops-warden/wiki/playbooks/agent-read-boundary.md
  • railiance-platform/openbao/policies/agent-high-risk-boundary.hcl

Register ruling — 2026-08-20

Renumbered from RISK-F-0004. ops-warden filed it as RISK-F-0004, which this register had already assigned to tenant-engine's unfiltered event read hours earlier. Earlier id keeps it; the newcomer moves. Nothing about the finding changes, and the filing contract now says how ids are allocated so the next reporter does not hit this.

high (I4 × L2), embargoed, no escalation.

I4 on the worst uncovered lane. Eight high-risk lanes have a concrete KV path the policy does not deny. Most are I3-shaped inside their own system; scaleway-bootstrap and agent-harness-forgejo-deploy are not — cloud account bootstrap credentials and a forge deploy key propagate past the system that holds them.

L2. It needs an agent token carrying a workload-kv-read-* policy, which the estate does grant. The register holds ops-warden's two stated limits rather than resolving them in either direction: the comparison is static against the policy file because their operator token is expired (bao token lookup → 403), so the deployed policy may differ; and whether any agent token currently carries agent-high-risk-boundary together with a workload-kv-read-* policy is not established.

No fidelity modifier, same call as RISK-F-0003. ADR-0004 describes two independent layers and one of them covers a third of the lanes, so the documentation overstates the control — but the modifier is scoped to attestations produced at runtime, not to documentation drift. Noted twice now in the same system; if it recurs a third time the modifier's scope is what should be re-examined, not the finding.

No escalation. railiance-platform owns the policy, ops-warden owns the catalog, and they have not disagreed about anything — trigger 4 needs a failed routing exchange, not merely two repos. ops-warden's suggestion that the deny set be generated from the catalog rather than maintained twice is routed as a suggestion, which is what it was offered as.

The pre-existing point is the important one. Four of the eight uncovered lanes were already graded high before the 2026-08-19 regrade. The divergence is not a consequence of that work; the regrade only made the two lists comparable enough to see it. A register that took credit for finding a fresh defect here would be misreading its own evidence.

Disclosure: embargoed, and it changes RISK-F-0003's embargo too. This names eight live KV paths that nothing denies.

One expired credential is blocking verification of both this finding and RISK-F-0003's mitigation. That is not a finding — nothing is wrong with the estate because a token expired — but it is why two gradings here rest on file comparison rather than a probe, and the register says so rather than implying otherwise.

Reviews

  • 2026-08-20 — renumbered, graded. Cadence starts at instant: nothing about this has been checked twice yet. Open at the next check: does the deployed policy match the file; do any agent tokens carry both policies; has railiance-platform taken the catalog-generated deny set.
  • 2026-08-20 — clean check: checked against the inbox and the owner's record; nothing moved. Cadence instant → 1h (1 clean in a row); next check 2026-08-20 11:02Z.

Check — 2026-08-21: measured rather than inferred, and one number corrected down

ops-warden ran the live verification. Three changes, one of which matters beyond this finding.

The headline stands and is now measured: coverage confirmed at 6 of 17 high-risk lanes.

The uncovered count was wrong in the direction that overstated the finding. Eight became six: the original eight included openbao-api-key, which is a path pattern rather than an address, and ops-warden-warden-sign-token, which is a broker grant — neither is deniable by a policy. The finding's own prose had already flagged the first as "probably cannot be expressed as a policy deny" while the table counted it anyway, and ops-warden corrected against themselves rather than leaving it.

The I4 grade is unaffected: it was scored on the worst uncovered lane, and scaleway-bootstrap and agent-harness-forgejo-deploy are both still in the six.

The token was never expired. The finding recorded bao policy read as returning 403 and the comparison as static. ops-warden reports the token was valid the whole time and the read succeeded on the first attempt. A stale claim about the world sat in the record for a day — the fourth such instance they self-reported in twelve hours.

The part that reaches past this finding

The deployed policy differs from the file. RISK-F-0009 named that as an unconfirmed risk; it is now established. No ops-warden lane maps to the drifted path, so exposure is unchanged — but "the file is not a safe proxy for the server" has moved from suspicion to evidence, and that bears on every grade this register makes off a checkout, including its own method.

docs/method/verification.md is amended accordingly.

The embargo does not lift. Its condition is a coverage report from railiance-platform, and coverage did not improve — it stopped being an inference, which is a different thing. ops-warden put the correction to them directly and said so.

  • 2026-08-21 — not clean: owner replied; see the dated check section Cadence 1h → instant; checked again immediately.