What to know

  • Canonical action hashing binds review to exact content.
  • Reviewer identity must come from an independent authenticated layer.
  • This fixture checks an approval and performs no publication or other action.
limits
Local bounded fixtures passed; no external model or production benchmark tested. Hash binding is not authentication, a digital signature, durable replay protection or an executed side effect.
duration
20–30 minutes
difficulty
Intermediate
environment
Python 3.12.10; standard library; local synthetic fixtures
verified at
2026-10-16
prerequisites
Python 3.12; a synthetic action; separately authenticated reviewer identity in any real integration.

The problem to isolate

An editor approves a draft, then an agent changes the destination or wording before acting. A remembered yes is now ambiguous. This workflow demonstrates one narrow control: bind an approval to a canonical representation of the exact action. It returns only whether the supplied decision matches. It never publishes, sends a message or connects to an account. The practical output is evidence that a particular payload is the one a reviewer considered, not a complete authorization service.

Define the action before asking for approval

The action has exactly three string fields: kind, target and body. The fixture accepts only the simulated publish-draft kind and bounds each field. Canonical JSON uses sorted keys and fixed separators before SHA-256 hashing. Reordering dictionary keys therefore leaves the digest unchanged; changing a body or target changes it. A real application should include every consequential parameter in its own contract, including the destination and version. An omitted field cannot be protected by this hash.

Keep identity outside the submitted payload

The approval object names a reviewer, decision and digest. The function also requires an independently supplied authenticated_reviewer, which must be in its explicit fixture approver set. That argument is a trust boundary: a production server must derive it from a verified session and role policy, never from a caller’s request field. Anyone can compute an ordinary hash. A matching digest does not prove who approved it, and this local example does not implement sign-in or secure approval storage.

Run the bounded example

Save approval_gate.py and run it with Python 3.12. The main block creates a synthetic action, constructs an approval and reports matches with executed set to false. The test suite verifies that key order is stable, a changed body fails, the wrong reviewer fails and a rejection fails. It also checks malformed digests and extra action fields. The expected success is a Boolean describing this fixture. There is intentionally no function that turns a passing result into an external action.

Handle edits and refusal predictably

If review fails, preserve the proposed action and explain which review step needs repeating without exposing its sensitive body in a general log. An edited payload needs a new approval. A rejected action should remain rejected until an authorized person explicitly changes the decision. Do not treat a retry, a clearer explanation or a more capable model as permission to bypass the gate. The interface should show the exact destination and meaningful changes so the reviewer can understand what is being authorized.

What a real application must add

Use durable authenticated approval records, expiry, replay protection, role changes and revocation rules appropriate to the action. Bind approval to the relevant resource revision so another edit cannot make a reviewed operation stale. Enforce the check on the server at execution time, then record the actual outcome separately. These are extension requirements, not features implemented here. The fixture is useful precisely because it separates payload binding from the larger identity, persistence and execution problems that must be solved before real consequences are allowed.

Runnable Python fixture

"""Bind a simulated human decision to an exact action; performs no action."""
import hashlib
import hmac
import json

APPROVERS = frozenset({"fixture-editor"})

def action_digest(action):
    if type(action) is not dict or set(action) != {"kind", "target", "body"}:
        raise ValueError("unexpected action shape")
    if any(type(value) is not str or len(value) > 4000 for value in action.values()):
        raise ValueError("action fields must be bounded text")
    if action["kind"] != "publish-draft" or not action["target"]:
        raise ValueError("unsupported simulated action")
    canonical = json.dumps(action, sort_keys=True, ensure_ascii=True, separators=(",", ":"))
    return hashlib.sha256(canonical.encode("utf-8")).hexdigest()

def approval_matches(action, approval, authenticated_reviewer):
    # authenticated_reviewer MUST come from a separately verified identity layer.
    if type(authenticated_reviewer) is not str or authenticated_reviewer not in APPROVERS:
        return False
    if type(approval) is not dict or set(approval) != {"reviewer", "decision", "digest"}:
        return False
    if approval["reviewer"] != authenticated_reviewer or approval["decision"] != "approve":
        return False
    if (type(approval["digest"]) is not str or len(approval["digest"]) != 64
            or any(char not in "0123456789abcdef" for char in approval["digest"])):
        return False
    expected = action_digest(action)
    return hmac.compare_digest(expected, approval["digest"])

if __name__ == "__main__":
    action = {"kind": "publish-draft", "target": "fixture-article", "body": "Reviewed fixture"}
    approval = {"reviewer": "fixture-editor", "decision": "approve", "digest": action_digest(action)}
    print({"matches": approval_matches(action, approval, "fixture-editor"), "executed": False})

Verification and references

The approval gate passed five fixture tests on 16 October 2026 using Python 3.12.10, including changed payloads and refused decisions. Nothing was published. Run approval_gate.py for the demonstration and test_workflows.py for the complete suite.

Editorial note: AI-assisted original BijliCoin educational workflow with locally tested synthetic fixtures. No external model, publisher feed, production service or private account was tested. AI-generated conceptual illustration.

Sources & context

  1. Python 3.12 hashlib documentation ↗
  2. Python 3.12 JSON encoding documentation ↗

See our editorial policy or report a correction.