OXYGENHUB

Sealed approvals: cryptographic proof of who approved a gated job issue #235 closed: completed andymccall opened this · 0 comments

andymccall

Andy (2026-10-16): an approval gate must carry cryptographic proof of who approved, as landing and review seals do. Today (oxygenflow#57) an approval is a database row (approved_by, approved_ms), a log line and an audit entry, authenticated only by a session cookie or a token; the hub, or anyone who controls its database, could forge one, and an agent token approves as its person (issue #229).

Design, in the shape of review seals:

  1. What is signed. An approval statement, domain-separated (a new signing domain, flow-approval): repository id, run number, the run's state and pipeline hash, the job's name and its job hash (spec 6.2: job and sandbox), its environment, the blobs of its inputs (for an apply job: the reviewed tfplan) and approve or reject. Approving one plan never approves another.
  2. Who signs. The approver's own Oxygen key: oxy seal --flow <hub>/<owner>/<name>/flow/<n> --job apply (and --reject), or the hub's web page asking the client to sign, as the trust page does. The hub accepts it only if the key is linked to an account with the gate's role (and, with others-only, not the pusher), and refuses delegated keys unless the repository allows agents to approve (settles issue #229: default refuse).
  3. Where it lives. Kept as an object in the repository (like review seals and receipts), so it can be verified without the hub; the run page shows "approved and sealed by (key )" or "approved, seal pending".
  4. What it gates. A job whose approval asks for it (approval { by "maintain"; sealed #true }, the default for an environment) does not start until the seal verifies.
  5. Where it travels. The job's identity token carries approved_by and the seal's hash, so the cloud's audit log names the person and points to the proof; the job's receipt lists the seal among its inputs, so the deploy's receipt proves which plan was approved by whom.

Work: oxygen (the statement, oxy seal --flow), oxygenflow (sealed in the approval block, spec), the hub (verification, storage, gating, run page, token claims). Tests: a forged or replayed seal (other run, other plan blob) refused; a delegated key refused by default; the token and receipt carry it.

andymccall set labels security ·

closed by change 47e374020b59 ·

andymccall referenced by change 05f1a85a2842 on main ·