Sealed approvals: cryptographic proof of who approved a gated job issue #235 closed: completed andymccall opened this · 0 comments
andymccall set labels security ·
closed by change 47e374020b59 ·
andymccall referenced by change 05f1a85a2842 on main ·
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:
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.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).approval { by "maintain"; sealed #true }, the default for an environment) does not start until the seal verifies.approved_byand 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 (sealedin 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.