2026-08-20 07:10:17 +02:00
---
Adaptive check cadence: the interval is earned, not assigned
Operator ruling 2026-08-20. Severity no longer sets the review interval.
A check that comes back clean climbs one rung — instant, 1h, 8h, 24h,
48h, 96h, 7d, 14d, 1mo, 1q — and anything wrong drops straight back to
instant. A quarter is the ceiling. The operator may defer an instant
finding to a stated date; that is the only other way off the bottom rung.
The rung is the point: it says how stable the estate has been on that
matter, which is information severity does not carry. Volatile things get
attention automatically; quiet things stop consuming it; neither
judgement has to be made by a person who might be busy.
Escalation trigger 5 rebased onto the ladder — fourteen days at the
bottom rung, whether that is failing checks or no checks.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 07:43:51 +02:00
id: RISK-F-0009
2026-08-20 07:10:17 +02:00
type: finding
title: "agent-high-risk-boundary denies 6 of 17 high-risk lanes; the direct bao path is unprotected for the rest"
2026-09-01 02:41:32 +02:00
status: fixed
owner: risk-nexus
2026-08-20 07:10:17 +02:00
reported_by: ops-warden
reported_via: ops-warden
routed_by: ops-warden
date_reported: "2026-08-20"
system: railiance-platform
environment: production
fix_owner: railiance-platform
2026-09-01 02:41:32 +02:00
fix_tracking: RPF-WP-0013 / RAILIANCE-WP-0022 (finished 2026-08-22)
Adaptive check cadence: the interval is earned, not assigned
Operator ruling 2026-08-20. Severity no longer sets the review interval.
A check that comes back clean climbs one rung — instant, 1h, 8h, 24h,
48h, 96h, 7d, 14d, 1mo, 1q — and anything wrong drops straight back to
instant. A quarter is the ceiling. The operator may defer an instant
finding to a stated date; that is the only other way off the bottom rung.
The rung is the point: it says how stable the estate has been on that
matter, which is information severity does not carry. Volatile things get
attention automatically; quiet things stop consuming it; neither
judgement has to be made by a person who might be busy.
Escalation trigger 5 rebased onto the ladder — fourteen days at the
bottom rung, whether that is failing checks or no checks.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 07:43:51 +02:00
filed_as: "RISK-F-0004 by ops-warden; renumbered by risk-nexus 2026-08-20 (id collision)"
answers: RISK-F-0003
related: [RISK-F-0003]
# Graded by risk-nexus 2026-08-20 — docs/rulings/2026-08-20-openbao-layer.md
severity: high
severity_at_production: high
impact: I4
likelihood: L2
fidelity_modifier: false
production_rescore: false
2026-09-01 02:41:32 +02:00
disclosure: public
publication: pending-handover
publication_id: risk-f-0009-openbao-high-risk-deny-coverage
publication_path: "findings/openbao-high-risk-deny-coverage/v1/index.html"
publication_subtitle: "OpenBao's agent boundary covered only a fraction of high-risk credential lanes; generated source-to-live coverage and deny-wins proof now close the gap."
revision: "fixed-1"
last_reviewed: "2026-09-01"
review_interval: 6m
embargo_lifted: "2026-08-22 — every concrete high-risk KV path is generated into the live deny policy and a dedicated agent identity proved deny-wins"
embargo_was_since: "2026-08-20"
Adaptive check cadence: the interval is earned, not assigned
Operator ruling 2026-08-20. Severity no longer sets the review interval.
A check that comes back clean climbs one rung — instant, 1h, 8h, 24h,
48h, 96h, 7d, 14d, 1mo, 1q — and anything wrong drops straight back to
instant. A quarter is the ceiling. The operator may defer an instant
finding to a stated date; that is the only other way off the bottom rung.
The rung is the point: it says how stable the estate has been on that
matter, which is information severity does not carry. Volatile things get
attention automatically; quiet things stop consuming it; neither
judgement has to be made by a person who might be busy.
Escalation trigger 5 rebased onto the ladder — fourteen days at the
bottom rung, whether that is failing checks or no checks.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 07:43:51 +02:00
escalation: none
2026-09-01 02:41:32 +02:00
date_fixed: "2026-08-22"
last_checked: "2026-09-01T00:32:44Z"
next_check: "2026-09-01T00:32:44Z"
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
cadence: instant
clean_streak: 0
Adaptive check cadence: the interval is earned, not assigned
Operator ruling 2026-08-20. Severity no longer sets the review interval.
A check that comes back clean climbs one rung — instant, 1h, 8h, 24h,
48h, 96h, 7d, 14d, 1mo, 1q — and anything wrong drops straight back to
instant. A quarter is the ceiling. The operator may defer an instant
finding to a stated date; that is the only other way off the bottom rung.
The rung is the point: it says how stable the estate has been on that
matter, which is information severity does not carry. Volatile things get
attention automatically; quiet things stop consuming it; neither
judgement has to be made by a person who might be busy.
Escalation trigger 5 rebased onto the ladder — fourteen days at the
bottom rung, whether that is failing checks or no checks.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 07:43:51 +02:00
graded_by: risk-nexus
ruling: RISK-RULING-2026-08-20
2026-09-01 02:41:32 +02:00
checked_by: "codex/risk-nexus"
2026-08-20 07:10:17 +02:00
---
Adaptive check cadence: the interval is earned, not assigned
Operator ruling 2026-08-20. Severity no longer sets the review interval.
A check that comes back clean climbs one rung — instant, 1h, 8h, 24h,
48h, 96h, 7d, 14d, 1mo, 1q — and anything wrong drops straight back to
instant. A quarter is the ceiling. The operator may defer an instant
finding to a stated date; that is the only other way off the bottom rung.
The rung is the point: it says how stable the estate has been on that
matter, which is information severity does not carry. Volatile things get
attention automatically; quiet things stop consuming it; neither
judgement has to be made by a person who might be busy.
Escalation trigger 5 rebased onto the ladder — fourteen days at the
bottom rung, whether that is failing checks or no checks.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 07:43:51 +02:00
# RISK-F-0009 — the OpenBao half of the agent read-boundary covers a third of the lanes
2026-08-20 07:10:17 +02:00
## 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.
RISK-F-0009: live verification, and a correction to my own count
ops-warden filed this static, saying its OpenBao token was expired. It was not --
bao policy read succeeded, so the deployed policy has now been compared directly.
Coverage confirmed at 6 of 17. But the uncovered count was wrong: eight included
a path pattern and a broker grant, neither of which a policy can deny, and the
finding's own prose already said so about the first. Six stand.
New: the deployed policy differs from the file in railiance-platform -- the file
denies core-hub/runtime, the server does not. No ops-warden lane maps there, so
the numbers are unchanged. It matters because this finding named "the deployed
policy may differ from the file" as unconfirmed, and it does.
Severity, disclosure and embargo left untouched -- risk-nexus's to set. The
embargo condition is a coverage report from railiance-platform, which this does
not satisfy.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 00:50:25 +02:00
## 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.
2026-08-20 07:10:17 +02:00
## Related
- `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`
Adaptive check cadence: the interval is earned, not assigned
Operator ruling 2026-08-20. Severity no longer sets the review interval.
A check that comes back clean climbs one rung — instant, 1h, 8h, 24h,
48h, 96h, 7d, 14d, 1mo, 1q — and anything wrong drops straight back to
instant. A quarter is the ceiling. The operator may defer an instant
finding to a stated date; that is the only other way off the bottom rung.
The rung is the point: it says how stable the estate has been on that
matter, which is information severity does not carry. Volatile things get
attention automatically; quiet things stop consuming it; neither
judgement has to be made by a person who might be busy.
Escalation trigger 5 rebased onto the ladder — fourteen days at the
bottom rung, whether that is failing checks or no checks.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 07:43:51 +02:00
## 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 12:03:12 +02:00
- **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.
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
## 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.
2026-09-01 02:41:32 +02:00
## Closure — 2026-09-01: generated coverage and live deny-wins proof
`RPF-WP-0013` and `RAILIANCE-WP-0022` consumed the Warden-generated high-risk
path artifact and deployed the result. The source and live readback cover all
**14 concrete entries across 19 high-risk lanes**, with 5 non-concrete or
non-KV lanes correctly classified and **0 uncovered** . A dedicated
`coding-agent-railiance-platform` AppRole carrying the boundary plus one
workload read policy proved deny-wins: data read was denied while metadata
remained readable; the single-use identity was revoked and no secret value was
read.
Risk Nexus re-ran the current platform invariant on 2026-09-01; it reports
`ok: true` , 19 lanes, 14 concrete entries, and zero errors or uncovered paths.
The condition is met with both generated-source and live-state evidence. The
finding is `fixed` , its wait is removed, and its embargo lifts.
- **2026-09-01** — not clean: generated and live coverage now span every concrete high-risk KV path with deny-wins evidence; status fixed and embargo lifted. Cadence instant → instant; checked again immediately.