Authorization pipeline
Everything else on this site governs the agents that build Steelmoth. This governs the assistant inside Steelmoth, when it acts on a user’s behalf — sending an email, driving a browser.
The product’s assistant can take real-world actions for its users. That needs its own rule system —
one that is multi-tenant (every user’s actions judged separately), fail-closed, and auditable in a way
a user or operator can trust after the fact. It lives in the
steelmoth_runtime/authz/ package and is deliberately separate from the
operator-authority code: different threat, different machinery.
The pipeline: four checks per action
Section titled “The pipeline: four checks per action”Every governed action passes through govern.py, which runs four stages in a fixed
order:
the assistant wants to act (send email, run browser step) │ ▼ the SERVER builds the request envelope ◀── not the model. The description of │ what's being attempted comes from ▼ trusted code, so the model can't 1. RISK score the action sweet-talk its own paperwork. │ (what band is this? reversible?) ▼ 2. POLICY decide: ALLOW / DENY / ESCALATE │ (fail-closed — see below) ▼ 3. DLP scan the content itself │ (may only TIGHTEN: an ALLOW can │ become ESCALATE, never the reverse) ▼ 4. AUDIT append to the hash-chained ledger │ ▼ the action runs, waits for a human, or is refusedThe hash-chained audit ledger
Section titled “The hash-chained audit ledger”Stage 4 writes to a ledger (audit.py) where every record contains the hash of the one
before it — the same idea that makes a blockchain tamper-evident, without any of the rest of the
machinery:
record 1 record 2 record 3 ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ prev: GENESIS │ ◀───── │ prev: hash(1)│ ◀───── │ prev: hash(2)│ │ decision … │ │ decision … │ │ decision … │ └──────────────┘ └──────────────┘ └──────────────┘
verify_chain() recomputes every hash in order. Change a record, delete one, or reorder them — and the chain stops adding up.Hashes are computed over a canonical form of the record (keys sorted, stable formatting), so the same record always produces the same hash. An ordinary log says what happened; this ledger can also prove nobody edited the story afterwards — including someone with write access to the log itself.
Shadow mode: governing before enforcing
Section titled “Shadow mode: governing before enforcing”A blocking pipeline that is wrong blocks real users. So the pipeline shipped in shadow mode first: it runs on every governed action, makes its decision, writes its audit record — and then does nothing. The action proceeds as it always did. The decisions accumulate where the operator can review them, so the policy can be tuned against reality before it is given teeth.
- The mode switch (
governance_settings.py) defaults to shadow, and an unrecognised value also means shadow — the unsafe direction (silently enforcing, or silently off) is not reachable by typo. - The wiring (
governance_wiring.py) sits at the top of the runtime’s action-execution path, wrapped so that no failure in the governance code can break a user’s action while shadowing. A kill switch (STEELMOTH_GOVERNANCE_SHADOW_ENABLED) turns the seam off entirely. - The enforcement half (
enforce_decision()) is already written and tested as a pure function — flipping to enforce mode is a settings change, not a code change.
What gets recorded, and who can see it
Section titled “What gets recorded, and who can see it”Decisions land in a governance_decisions database table protected by row-level
security: each user can only ever see rows about their own actions. The table stores the
decision and a hash — not the raw content of the email or page involved, so the audit trail
doesn’t itself become a copy of everyone’s private data. A second table,
governance_settings_audit, records every change to the governance settings themselves:
who flipped what, old value to new value, whether they had passed two-factor auth, and when. The
switches that control the watchdog are watched too.

