Compare commits
2 commits
ff74a60af4
...
36b707f0c3
| Author | SHA1 | Date | |
|---|---|---|---|
| 36b707f0c3 | |||
| 97fcc56a4d |
12 changed files with 481 additions and 10 deletions
11
Makefile
11
Makefile
|
|
@ -1,11 +1,12 @@
|
|||
PY := python3
|
||||
TOOLS := tools
|
||||
|
||||
.PHONY: help register check checked
|
||||
.PHONY: help register check checked due
|
||||
|
||||
help:
|
||||
@echo "make register - rebuild REGISTER.md from findings/"
|
||||
@echo "make check - verify the index is current, then report what is going quiet"
|
||||
@echo "make check - verify the index, report what is going quiet, and read the inbox"
|
||||
@echo "make due - just the work list: what needs a check right now"
|
||||
@echo "make checked - record a check outcome: make checked ARGS=\"RISK-F-0002 clean\""
|
||||
|
||||
register:
|
||||
|
|
@ -15,7 +16,13 @@ check:
|
|||
@$(PY) $(TOOLS)/register_index.py --check
|
||||
@echo
|
||||
@$(PY) $(TOOLS)/register_check.py
|
||||
@echo
|
||||
@$(PY) $(TOOLS)/inbox_check.py
|
||||
|
||||
checked:
|
||||
@$(PY) $(TOOLS)/record_check.py $(ARGS)
|
||||
@$(PY) $(TOOLS)/register_index.py
|
||||
|
||||
due:
|
||||
@$(PY) $(TOOLS)/register_check.py | sed -n '/Checks due/,/^$$/p'
|
||||
@$(PY) $(TOOLS)/inbox_check.py
|
||||
|
|
|
|||
87
docs/method/check-procedure.md
Normal file
87
docs/method/check-procedure.md
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
---
|
||||
id: RISK-METHOD-CHECK
|
||||
type: method
|
||||
title: "How a check is actually performed"
|
||||
status: adopted
|
||||
owner: risk-nexus
|
||||
adopted: "2026-08-20"
|
||||
workplan: RISK-WP-0004-T01
|
||||
review_interval: 180d
|
||||
---
|
||||
|
||||
# Performing a check
|
||||
|
||||
The ladder in `docs/method/review.md` says *when*. This says *what*, in order,
|
||||
so that a check is repeatable by whoever or whatever is doing it.
|
||||
|
||||
A check takes minutes. It is not an investigation, and it is not a re-grading
|
||||
unless something moved.
|
||||
|
||||
## The order
|
||||
|
||||
```
|
||||
make check # what is due, what is malformed, what the inbox has said
|
||||
```
|
||||
|
||||
Then, per due finding:
|
||||
|
||||
0. **Read the inbox first.** `make check` ends with the inbox report; anything
|
||||
it lists is read before that finding is touched. This is step zero because
|
||||
skipping it on 2026-08-19 produced a wrong grade and a nearly-sent
|
||||
escalation.
|
||||
1. **Is the grade still right?** Re-read impact and likelihood against what has
|
||||
changed. New facts move grades in both directions.
|
||||
2. **Is every stated blocker still true?** A blocker is a claim about the world
|
||||
at a date. Re-check it; do not carry it forward.
|
||||
3. **Has the fix moved?** Read the owner's tracking record, not our memory of
|
||||
it. Confirm the record still exists and still refers to this defect.
|
||||
4. **Is the disclosure state still right?** A met embargo condition publishes;
|
||||
a condition that has not moved in two checks is a stall — and a condition can
|
||||
be met while the embargo still holds, if a sibling finding would be exposed
|
||||
by publishing (`RISK-F-0003`).
|
||||
|
||||
Then record it, in the same sitting:
|
||||
|
||||
```
|
||||
make checked ARGS="RISK-F-0002 clean"
|
||||
make checked ARGS="RISK-F-0002 moved 'ops-warden enabled the gate'"
|
||||
make checked ARGS="RISK-F-0002 defer 2026-09-01 'operator: after the migration'"
|
||||
make register
|
||||
```
|
||||
|
||||
`record_check.py` writes the front-matter and the dated line together. **A
|
||||
check that is not written down did not happen** — the same rule this register
|
||||
applies to every repo it grades.
|
||||
|
||||
## What must not happen
|
||||
|
||||
**Stamping `clean` without doing the four questions.** It costs nothing, takes
|
||||
no time, and produces a `1q` rung that is a lie about stability. That is
|
||||
precisely the `RISK-F-0002` failure — a control that produces a false record is
|
||||
worse than no control — applied to this register's own instruments.
|
||||
|
||||
`clean_streak` exists partly to make that visible: a register where everything
|
||||
climbs smoothly and nothing ever resets is either extraordinarily stable or not
|
||||
being checked, and those two look identical from the outside.
|
||||
|
||||
## Who performs it
|
||||
|
||||
Unresolved, and the honest state of `RISK-WP-0004-T01`.
|
||||
|
||||
The procedure is executable and the tooling is built. What does not yet exist
|
||||
is the thing that runs it on schedule. The options, worst to best:
|
||||
|
||||
- **A human habit.** Fails exactly when the operator is busy, which is the
|
||||
condition the ladder was designed for.
|
||||
- **A cron that only reports.** Better: the due list arrives whether or not
|
||||
anyone remembers. Still needs someone to act on it.
|
||||
- **A scheduled agent session** that runs `make check`, works the due list,
|
||||
performs the four questions, and records outcomes — escalating to the
|
||||
operator only what the escalation rule says to escalate.
|
||||
|
||||
The third is the only one that survives the operator being busy. It is also a
|
||||
standing commitment of compute, and this register does not get to arm one on
|
||||
the operator's behalf. **Awaiting that decision.**
|
||||
|
||||
Until it is made, checks happen when someone runs `make check`, and every
|
||||
finding sitting at `instant` is telling the truth about how often that is.
|
||||
84
docs/method/verification.md
Normal file
84
docs/method/verification.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
---
|
||||
id: RISK-METHOD-VERIFICATION
|
||||
type: method
|
||||
title: "What this register may verify for itself, and what it must take from owners"
|
||||
status: adopted
|
||||
owner: risk-nexus
|
||||
adopted: "2026-08-20"
|
||||
workplan: RISK-WP-0004-T05
|
||||
review_interval: 180d
|
||||
---
|
||||
|
||||
# Verification
|
||||
|
||||
`RISK-WP-0004-T05` asked what this repo can legitimately establish itself,
|
||||
because two gradings were resting on file comparison. The answer was found by
|
||||
trying, on 2026-08-20, rather than by reasoning about it.
|
||||
|
||||
## The rule
|
||||
|
||||
**A register may look. It may not touch, and it may not conclude on a system's
|
||||
behalf.**
|
||||
|
||||
Verification here means: running a read-only command a reporter has already
|
||||
named, or that answers a question the register itself wrote down as open, and
|
||||
recording exactly what came back.
|
||||
|
||||
Permitted:
|
||||
|
||||
- read-only reads of live state (`kubectl get`, `bao policy read`, an HTTP
|
||||
probe of a documented endpoint);
|
||||
- comparing what is deployed against what a repo said is deployed;
|
||||
- recording the discrepancy and routing it as a **question**.
|
||||
|
||||
Not permitted:
|
||||
|
||||
- any write, apply, patch, restart or rotation — including one that would
|
||||
obviously improve things;
|
||||
- concluding what a defect means for a system this repo does not own. The
|
||||
system stays authoritative about itself (`INTENT.md`);
|
||||
- verifying instead of asking. The owner is asked first; verification settles
|
||||
what an owner cannot see or has invited someone to check.
|
||||
|
||||
`rapp-postgres` declined to check the NetworkPolicy on `flex-auth`'s behalf,
|
||||
saying that would be reporting on a system they do not own. That was right
|
||||
**for a reporter**. This register is the downstream party whose grade depended
|
||||
on the answer, and `flex-auth` explicitly named the command and asked for
|
||||
someone with credentials to run it. Those two positions are compatible.
|
||||
|
||||
## What this host can actually reach — established 2026-08-20
|
||||
|
||||
| Target | Result | Consequence |
|
||||
| --- | --- | --- |
|
||||
| Kubernetes (`railiance01`) | **reads work** — `kubectl get ns`, `get networkpolicy -o json` | Live cluster state is verifiable by this register |
|
||||
| OpenBao (`bao.coulomb.social`) | **403 permission denied** on `token lookup` and `policy read` | Policy claims are not verifiable here; `RISK-F-0009` rests on file comparison |
|
||||
|
||||
The asymmetry is worth stating plainly: **the register can check what the
|
||||
cluster admits, and cannot check what the secret store permits.** Every grade
|
||||
touching an OpenBao policy is therefore a grade on a document, and says so.
|
||||
|
||||
That is not a gap to close by acquiring credentials. A risk register holding
|
||||
production secret-store access has traded a verification problem for a much
|
||||
worse one. If OpenBao claims need verifying, the right answer is for the owner
|
||||
to run the read and report it, which is what `ops-warden` did — the obstacle
|
||||
there was an expired token, not the arrangement.
|
||||
|
||||
## Recording a verification
|
||||
|
||||
One file per verification in `docs/verifications/`, `RISK-V-NNNN`, stating the
|
||||
command, the raw result, what it confirms, what it contradicts, and what it
|
||||
does not establish. It names the findings it bears on, and those findings link
|
||||
back.
|
||||
|
||||
A verification is evidence, not a ruling. It can move a grade; it does not
|
||||
close a finding on its own, and it never speaks for the owning repo.
|
||||
|
||||
## Verification and the cadence ladder
|
||||
|
||||
A verification that contradicts something is a check that is **not clean**: the
|
||||
affected findings reset to `instant`. A verification that confirms what was
|
||||
already recorded is clean, and may be exactly the evidence a rung is built on.
|
||||
|
||||
The first one (`RISK-V-0001`) did both — it confirmed the ingress claim
|
||||
`RISK-F-0001` was graded on, contradicted the egress claim in the same message,
|
||||
and surfaced a third policy nobody had mentioned that bears on `RISK-F-0002`.
|
||||
|
|
@ -8,7 +8,10 @@ determined: "2026-08-20"
|
|||
finding: RISK-F-0008
|
||||
sources_read: "GDPR Arts 5, 6, 17, 21, 32; Recitals 49, 65; HGB §257; AO §147"
|
||||
external_review: none
|
||||
review_by: "2026-11-17"
|
||||
last_checked: "2026-08-20T05:25:00Z"
|
||||
next_check: "2026-08-20T05:25:00Z"
|
||||
cadence: instant
|
||||
clean_streak: 0
|
||||
---
|
||||
|
||||
# RISK-REG-0001 — the retention basis, written down
|
||||
|
|
|
|||
100
docs/verifications/2026-08-20-flex-auth-networkpolicy.md
Normal file
100
docs/verifications/2026-08-20-flex-auth-networkpolicy.md
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
---
|
||||
id: RISK-V-0001
|
||||
type: verification
|
||||
title: "flex-auth NetworkPolicy, verified against the live cluster"
|
||||
date: "2026-08-20"
|
||||
verified_by: risk-nexus
|
||||
method: "read-only kubectl against railiance01"
|
||||
findings: [RISK-F-0001, RISK-F-0002]
|
||||
result: "ingress claim confirmed; egress claim contradicted; one policy not previously reported"
|
||||
---
|
||||
|
||||
# RISK-V-0001 — the NetworkPolicy, actually looked at
|
||||
|
||||
`RISK-F-0001` was graded twice on a fact nobody had checked. `flex-auth` said
|
||||
so honestly — "I can tell you what is specified, not what is admitted" — and
|
||||
named the command that would settle it. This register ran that command.
|
||||
|
||||
```
|
||||
kubectl -n flex-auth get networkpolicy -o json
|
||||
```
|
||||
|
||||
## What is confirmed
|
||||
|
||||
**The ingress claim is exactly right**, and now rests on the live cluster
|
||||
rather than a manifest:
|
||||
|
||||
| Policy | Ingress from | Port |
|
||||
| --- | --- | --- |
|
||||
| `flex-auth-user-engine` | `namespaceSelector: user-engine` **and** `podSelector: user-engine` | 8080/TCP |
|
||||
| `flex-auth-tenant-engine` | `namespaceSelector: tenant-engine` **and** `podSelector: tenant-engine` | 8080/TCP |
|
||||
|
||||
Created 2026-08-09 and 2026-08-08 respectively — eleven days old, predating the
|
||||
caller-authentication work, as `flex-auth` said. The reachable set during the
|
||||
`A0` period was one workload per Deployment, not the cluster.
|
||||
|
||||
`RISK-F-0001`'s `L2` grade is therefore **verified**, not inferred. That is the
|
||||
first grade in this register standing on evidence this repo gathered itself.
|
||||
|
||||
## What is contradicted
|
||||
|
||||
`flex-auth` stated `egress: []` — "empty, i.e. none at all". **Live, all three
|
||||
policies permit egress to any destination on TCP 443 and 6443**, with no `to`
|
||||
selector.
|
||||
|
||||
```json
|
||||
"egress": [{"ports": [{"port": 443, "protocol": "TCP"},
|
||||
{"port": 6443, "protocol": "TCP"}]}]
|
||||
```
|
||||
|
||||
6443 is the Kubernetes API server, which ADR-0004's TokenReview needs, so its
|
||||
presence is unsurprising. 443 to anywhere is a different statement from "none
|
||||
at all".
|
||||
|
||||
This does **not** change `RISK-F-0001`'s grade — the finding is about who can
|
||||
reach the decision surface, and ingress is what governs that. It does change
|
||||
the evidence base, and it matters because the same sentence has been repeated
|
||||
into two records.
|
||||
|
||||
Routed to `flex-auth` as a question rather than a correction: does the
|
||||
committed manifest say `egress: []`, in which case the deployed policy differs
|
||||
from the file, or did the message misdescribe the manifest? Only they can say
|
||||
which, and the two answers mean very different things.
|
||||
|
||||
## What was not previously reported
|
||||
|
||||
A **third** policy exists: `flex-auth-ops-warden`, created 2026-08-19T12:47Z —
|
||||
the same day `flex-auth` reported the fix.
|
||||
|
||||
```
|
||||
podSelector: app.kubernetes.io/name=flex-auth-ops-warden
|
||||
policyTypes: [Ingress, Egress]
|
||||
ingress: (no rules)
|
||||
egress: 443, 6443
|
||||
```
|
||||
|
||||
`policyTypes` includes `Ingress` with no ingress rules, which in Kubernetes
|
||||
means **deny all ingress**. On its face, nothing can reach that pin.
|
||||
|
||||
**This bears directly on `RISK-F-0002`.** The remaining question there is
|
||||
whether enabling `policy.enabled` breaks signing on availability grounds now
|
||||
that the attestation hazard has lifted. If the flex-auth pin that `ops-warden`
|
||||
is meant to call admits no ingress, then enabling a `fail_closed` gate against
|
||||
it would fail closed — every `warden sign` would stop.
|
||||
|
||||
The register is **not** concluding that. A policy may be mid-rollout, the pin
|
||||
may not be the one `ops-warden` targets, and a second policy elsewhere may
|
||||
admit the traffic. What the register is doing is putting the observation in
|
||||
front of both owners before either of them flips a switch, which is precisely
|
||||
what `RISK-F-0002` exists to prevent happening blind.
|
||||
|
||||
## Limits of this verification
|
||||
|
||||
- Read-only, and a point-in-time snapshot of 2026-08-20.
|
||||
- It confirms what the API server admits **as specified**. Whether the CNI
|
||||
enforces NetworkPolicy at all is a cluster property this check does not
|
||||
establish, and `flex-auth` was right to flag it.
|
||||
- OpenBao could not be checked: `bao token lookup` and
|
||||
`bao policy read agent-high-risk-boundary` both return **403 permission
|
||||
denied** from this host, the same wall `ops-warden` hit. `RISK-F-0009`
|
||||
therefore still rests on file comparison.
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
---
|
||||
id: RISK-V-0002
|
||||
type: verification
|
||||
title: "Tenant boundary of user-engine — verification requested"
|
||||
date: "2026-08-20"
|
||||
requested_by: risk-nexus
|
||||
requested_of: user-engine
|
||||
status: requested
|
||||
findings: [RISK-F-0007]
|
||||
workplan: RISK-WP-0004-T04
|
||||
---
|
||||
|
||||
# RISK-V-0002 — user-engine tenant boundary (requested)
|
||||
|
||||
The first exercise of the on-request path the operator ruled on 2026-08-19.
|
||||
`RISK-F-0007` is `accepted` until production on the strength of that path, and
|
||||
a route nobody has walked is a plan rather than a route
|
||||
(`RISK-WP-0004-T04`).
|
||||
|
||||
## The request
|
||||
|
||||
One named boundary: **can a caller acting for tenant A obtain, modify or infer
|
||||
data belonging to tenant B through `user-engine`?**
|
||||
|
||||
Asked of `user-engine` because they are a consumer with a boundary and are not
|
||||
already carrying a finding about one — `tenant-engine` (`RISK-F-0004`) and
|
||||
`audit-core` (`RISK-F-0005`) both are, and using them would have tested the
|
||||
path against systems already known to fail it.
|
||||
|
||||
## What was asked for, and what deliberately was not
|
||||
|
||||
Asked: whether anything **verifies** the boundary — a test, an assertion, an
|
||||
authorization check on the path — and what would have to be true for the answer
|
||||
to be yes.
|
||||
|
||||
Not asked: a general security review, a posture ladder self-assessment, or a
|
||||
promise. `user-engine` owns the verification; this register scopes and records
|
||||
it and verifies nothing itself here.
|
||||
|
||||
## What this exercise is really measuring
|
||||
|
||||
Whether the path works, and where it rubs. Specifically: does the request reach
|
||||
someone who can act on it, is "one named boundary" a question a repo can
|
||||
actually answer, and does what comes back constitute evidence or reassurance.
|
||||
|
||||
The value is in the friction it exposes, and that gets recorded here whatever
|
||||
the answer turns out to be — including if the answer never comes.
|
||||
|
||||
## Outcome
|
||||
|
||||
Pending. Requested 2026-08-20.
|
||||
|
||||
- If the boundary holds with evidence: `RISK-F-0007`'s likelihood falls **for
|
||||
this consumer**, and the record says how it was shown.
|
||||
- If it does not: a finding of its own, with `user-engine` as fix owner.
|
||||
- If nothing comes back: that is the most useful result of the three, because
|
||||
the estate is currently carrying `RISK-F-0007` on the assumption that asking
|
||||
works.
|
||||
BIN
tools/__pycache__/inbox_check.cpython-312.pyc
Normal file
BIN
tools/__pycache__/inbox_check.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
72
tools/inbox_check.py
Normal file
72
tools/inbox_check.py
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Has the inbox spoken more recently than the register has looked?
|
||||
|
||||
RISK-WP-0004-T02. On 2026-08-19 this register graded RISK-F-0001 `critical`
|
||||
while its fix notice sat unread in the inbox. The residual said: if it slips
|
||||
again, make it a check. It slipped once, so here is the check.
|
||||
|
||||
Mechanical only. It compares timestamps and matches on the system name; it does
|
||||
not read the message and does not decide anything. It reports findings whose
|
||||
inbox has spoken since the register last looked.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
import register_lib as lib
|
||||
|
||||
HUB = "http://127.0.0.1:8000/messages/?to_agent=risk-nexus&limit=100"
|
||||
|
||||
|
||||
def fetch() -> list[dict] | None:
|
||||
try:
|
||||
with urllib.request.urlopen(HUB, timeout=6) as r:
|
||||
return json.load(r)
|
||||
except (urllib.error.URLError, TimeoutError, json.JSONDecodeError, OSError):
|
||||
return None
|
||||
|
||||
|
||||
def main() -> int:
|
||||
msgs = fetch()
|
||||
if msgs is None:
|
||||
# Fail loud: an unreachable inbox means no check can be certified current.
|
||||
print("Inbox: UNREACHABLE — the hub did not answer.")
|
||||
print(" No check can be certified current while this is true.")
|
||||
print(" Findings may have moved without the register hearing about it.")
|
||||
return 0
|
||||
|
||||
fs = [f for f in lib.findings() if lib.watched(f.get("status"))]
|
||||
rows = []
|
||||
for f in fs:
|
||||
looked = lib.moment(f.get("last_checked"))
|
||||
system = str(f.get("system", "")).lower()
|
||||
owner = str(f.get("fix_owner", "")).lower()
|
||||
if not looked or not system:
|
||||
continue
|
||||
for m in msgs:
|
||||
said = lib.moment(m.get("created_at"))
|
||||
if not said or said <= looked:
|
||||
continue
|
||||
blob = f"{m.get('subject','')} {m.get('body','')} {m.get('from_agent','')}".lower()
|
||||
if system in blob or (owner and owner in blob) or f["id"].lower() in blob:
|
||||
rows.append(
|
||||
f"{f['id']} — last looked {looked:%Y-%m-%d %H:%MZ}; "
|
||||
f"{m.get('from_agent')} wrote {said:%Y-%m-%d %H:%MZ}: {m.get('subject','')[:70]}"
|
||||
)
|
||||
break
|
||||
|
||||
print(f"Inbox: {len(msgs)} message(s) addressed to risk-nexus\n")
|
||||
if rows:
|
||||
print("Findings whose inbox has spoken since the register last looked:")
|
||||
for r in rows:
|
||||
print(f" {r}")
|
||||
print("\n Read those before grading or checking. This is question zero.")
|
||||
else:
|
||||
print("No finding has an inbox message newer than its last check.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
|
@ -44,6 +44,36 @@ def main() -> int:
|
|||
if unknown:
|
||||
section("Unrecognised status", unknown, "none")
|
||||
|
||||
# T03 — the `mitigated` defect was one instance of a class: the tooling
|
||||
# quietly tolerating something it did not expect. Everything below is
|
||||
# reported by name, and never silently ignored.
|
||||
ids = {f["id"] for f in lib.findings()}
|
||||
malformed: list[str] = []
|
||||
for f in fs:
|
||||
fid = f["id"]
|
||||
for field in ("last_checked", "next_check", "embargo_since", "deferred_to"):
|
||||
if f.get(field) and lib.moment(f[field]) is None:
|
||||
malformed.append(f"{fid} — {field} is not a date: {f[field]!r}")
|
||||
if (c := f.get("cadence")) and c not in lib.CADENCE_NAMES:
|
||||
malformed.append(f"{fid} — cadence '{c}' is not a rung on the ladder")
|
||||
if (d := f.get("disclosure")) and d not in ("public", "embargoed", "restricted", "unset"):
|
||||
malformed.append(f"{fid} — disclosure '{d}' is not a defined state")
|
||||
if (s := f.get("severity")) and s not in lib.SEVERITIES + ["unset"]:
|
||||
malformed.append(f"{fid} — severity '{s}' is not on the scale")
|
||||
if (on := f.get("constraint_on")) and on not in ids:
|
||||
malformed.append(f"{fid} — constraint_on points at {on}, which does not exist")
|
||||
for ref in (f.get("related") or []):
|
||||
if ref not in ids:
|
||||
malformed.append(f"{fid} — related names {ref}, which does not exist")
|
||||
if f.get("embargo_condition") and f.get("disclosure") != "embargoed":
|
||||
malformed.append(f"{fid} — carries an embargo_condition but disclosure is '{f.get('disclosure')}'")
|
||||
if f.get("disclosure") == "embargoed" and not f.get("embargo_condition"):
|
||||
malformed.append(f"{fid} — embargoed with no condition; a hold with no lift is a silence")
|
||||
if f.get("escalation") == "required" and not f.get("escalation_trigger"):
|
||||
malformed.append(f"{fid} — escalated with no trigger named")
|
||||
if malformed:
|
||||
section("Malformed", malformed, "none")
|
||||
|
||||
ungraded = [
|
||||
f"{f['id']} — {', '.join(k for k in lib.GRADED_FIELDS if str(f.get(k, 'unset')) == 'unset')}"
|
||||
for f in fs
|
||||
|
|
@ -112,6 +142,15 @@ def main() -> int:
|
|||
]
|
||||
section("Owed at the production transition", rescore, "none — no finding is graded lower for build mode")
|
||||
|
||||
reg = []
|
||||
for r in lib.regulatory():
|
||||
when = lib.moment(r.get("next_check")) or lib.moment(r.get("review_by"))
|
||||
if when is None:
|
||||
reg.append(f"{r.get('id')} — no next_check set")
|
||||
elif NOW >= when:
|
||||
reg.append(f"{r.get('id')} — due {when:%Y-%m-%d}: {str(r.get('title','')).strip()}")
|
||||
section("Regulatory records due", reg, "none")
|
||||
|
||||
print(f"Register check — {NOW:%Y-%m-%d %H:%MZ}\n{len(fs)} live finding(s)\n")
|
||||
print("\n".join(lines).rstrip())
|
||||
return 0
|
||||
|
|
|
|||
|
|
@ -77,6 +77,15 @@ def findings() -> list[dict]:
|
|||
return sorted(items, key=lambda f: f["id"], reverse=True)
|
||||
|
||||
|
||||
def regulatory() -> list[dict]:
|
||||
"""Regulatory records expire, so they ride the same ladder as findings."""
|
||||
d = REPO / "docs" / "regulatory"
|
||||
if not d.exists():
|
||||
return []
|
||||
return sorted((load(p) for p in d.glob("*.md") if p.name != "README.md"),
|
||||
key=lambda r: r.get("id", ""))
|
||||
|
||||
|
||||
def notes() -> list[dict]:
|
||||
if not NOTES.exists():
|
||||
return []
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ type: workplan
|
|||
title: "Run the register: make the cadence actually happen, and close the gaps the first week found"
|
||||
domain: infotech
|
||||
repo: risk-nexus
|
||||
status: proposed
|
||||
status: active
|
||||
owner: the-custodian
|
||||
topic_slug: risk-nexus
|
||||
created: "2026-08-20"
|
||||
|
|
@ -48,7 +48,7 @@ Every task below traces to something that happened, not something imagined.
|
|||
|
||||
```task
|
||||
id: RISK-WP-0004-T01
|
||||
status: todo
|
||||
status: progress
|
||||
priority: high
|
||||
```
|
||||
|
||||
|
|
@ -67,11 +67,13 @@ stamps `clean` without doing that is worse than no job, because it manufactures
|
|||
a stability signal that is false. That is the `RISK-F-0002` failure mode
|
||||
applied to this repo's own instruments.
|
||||
|
||||
In progress 2026-08-20. The procedure exists (`docs/method/check-procedure.md`), `make due` prints the work list, and `make checked` records an outcome. **What does not exist is the thing that runs it on schedule**, and this register does not get to arm a standing compute commitment on the operator's behalf. The doc names the three options and says which survives the operator being busy. Stays open until that decision is made; until then, every finding sitting at `instant` is telling the truth about how often `make check` is run.
|
||||
|
||||
### T02 — Inbox before grading, as a check rather than a habit
|
||||
|
||||
```task
|
||||
id: RISK-WP-0004-T02
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
|
|
@ -84,11 +86,13 @@ message this repo has received about that system, and report any finding whose
|
|||
inbox has spoken more recently than its register has. That is mechanical and
|
||||
does not require the tooling to understand the message.
|
||||
|
||||
Completed 2026-08-20. `tools/inbox_check.py`, wired into `make check`. Compares each finding's `last_checked` against messages this repo has received about that system, and reports any finding whose inbox has spoken since the register last looked. Mechanical: it matches timestamps and names, reads nothing, decides nothing. Verified against the actual failure — replayed at the moment of the 2026-08-19 grading it surfaces all three flex-auth/rapp-postgres messages that were already waiting. An unreachable hub reports UNREACHABLE rather than silently passing, because a check that cannot see the inbox cannot be certified current.
|
||||
|
||||
### T03 — Fail loud everywhere else too
|
||||
|
||||
```task
|
||||
id: RISK-WP-0004-T03
|
||||
status: todo
|
||||
status: done
|
||||
priority: medium
|
||||
```
|
||||
|
||||
|
|
@ -99,11 +103,13 @@ not exist, an `embargo_condition` on a finding that is not embargoed.
|
|||
|
||||
Every one of them should be reported by name and keep the finding watched.
|
||||
|
||||
Completed 2026-08-20. `make check` now reports, by name and without ever silently ignoring: unparseable dates, a cadence that is not a rung, an undefined disclosure state, a severity off the scale, a `constraint_on` or `related` pointing at a finding that does not exist, an embargo condition on a finding that is not embargoed, an embargo with no condition, and an escalation with no trigger. Nothing is malformed today, which is the answer that only means something because the check exists.
|
||||
|
||||
### T04 — Exercise the on-request verification path once
|
||||
|
||||
```task
|
||||
id: RISK-WP-0004-T04
|
||||
status: todo
|
||||
status: done
|
||||
priority: medium
|
||||
```
|
||||
|
||||
|
|
@ -115,11 +121,13 @@ path, and see what actually happens: who answers, what evidence comes back,
|
|||
whether the finding's likelihood moves for that consumer, and what the record
|
||||
looks like. The value is in the friction it exposes.
|
||||
|
||||
Completed 2026-08-20 as far as this repo can complete it. `RISK-V-0002`: verification of `user-engine`'s tenant boundary requested through the documented path. `user-engine` was chosen because they are a consumer with a boundary and *not* already carrying a finding about one — using `tenant-engine` or `audit-core` would have tested the path against systems already known to fail it. The outcome is pending and all three outcomes are useful, including silence, which would tell the estate that `RISK-F-0007` is being carried on an assumption that asking works.
|
||||
|
||||
### T05 — The verification credential
|
||||
|
||||
```task
|
||||
id: RISK-WP-0004-T05
|
||||
status: todo
|
||||
status: done
|
||||
priority: medium
|
||||
```
|
||||
|
||||
|
|
@ -133,11 +141,13 @@ source. Establish what this repo can legitimately verify itself, and what it
|
|||
must always take from owners. **If the answer is "nothing", that is worth
|
||||
knowing and writing down**, because it bounds every grade in the register.
|
||||
|
||||
Completed 2026-08-20, by trying rather than reasoning. `docs/method/verification.md`. **Cluster reads work from this host; OpenBao returns 403**, the same wall `ops-warden` hit. So the register can check what the cluster admits and cannot check what the secret store permits, and every grade touching an OpenBao policy is a grade on a document. That asymmetry is recorded rather than closed — a risk register holding production secret-store credentials would have traded a verification problem for a worse one. First use produced `RISK-V-0001`, which confirmed `RISK-F-0001`'s ingress claim against the live cluster, contradicted the `egress: []` claim in the same message, and surfaced a third NetworkPolicy with no ingress rules that bears on whether `ops-warden` enabling its gate would fail closed.
|
||||
|
||||
### T06 — Regulatory records on the same ladder
|
||||
|
||||
```task
|
||||
id: RISK-WP-0004-T06
|
||||
status: todo
|
||||
status: done
|
||||
priority: low
|
||||
```
|
||||
|
||||
|
|
@ -145,6 +155,8 @@ Duplicate of `RISK-WP-0003-T04`, kept here as a pointer rather than a second
|
|||
copy. Whichever workplan reaches it first does it; the other closes with a
|
||||
reference.
|
||||
|
||||
Completed 2026-08-20. Regulatory records ride the findings ladder rather than getting a second review mechanism: `RISK-REG-0001` carries `cadence`, `clean_streak`, `last_checked` and `next_check`, and `make check` reports regulatory records due exactly as it reports findings. `RISK-WP-0003-T04` closes by reference.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- **No dashboard.** `REGISTER.md` and `make check` are the surface, and the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue