Synthe docs
How the commit barrier works, and everything you need to check your first handoff with the public checker. The full spec and threat model live in the repo.
Checker v0.5 · packet schema 0.1 · docs October 2026
The commit barrier
Synthe sits between your agents and the systems they change. Agents never hold the credentials for real-world actions; Synthe does. An agent proposes an action, and Synthe decides at the moment of commit, not when the plan was written.
- ClaimThe receiving agent claims a signed handoff. The checker validates it and records the claim under its idempotency key, so the same work can't be claimed twice.
- Work and proposeThe agent does the work, then proposes the effect, for example a push of its commits to an approved branch. It never touches the GitHub token.
- Re-check at commitSynthe checks the signed handoff and the human approval again, inspects every path the change touches against the agent's allowed paths, and waits for any upstream handoffs to complete.
- Compare and swapSynthe pushes only if the branch still points where the approval saw it. If anything moved, it pushes nothing and refuses with
non_fast_forward. - Confirm and receiptSynthe re-reads the remote to confirm the effect, then signs a hash-chained receipt. Every refusal gets a receipt too, with its reason.
Public today Step 1, the claim ledger, signatures, approvals and evidence checks, in the public checker.
Public beta Steps 2 to 5, in the commit broker. git_push ships first, with an OpenClaw plugin and a guided setup on macOS that takes a few steps (quickstart). Ask about a pilot.
What it can't do: an action a human approved will do exactly what was approved, and receipts prove which key and connector acted, not which model wrote the work. Synthe is only a barrier if agents can't go around it, so take their own credentials away. The full list is on the status page. To watch the rules play out, race two agents for one commit.
Getting started
Clone the public checker and run it against the example packets. Two minutes, nothing to install beyond Python 3.
$ git clone https://github.com/rohansiddam/Synthe.git && cd Synthe
$ python3 src/handoff_check.py examples/v03/packets/valid-signed.json --registry examples/v03/registry.json --workspace examples/v03/workspace --ledger ledger.json
{ "decision": "ACCEPT", "handoff_id": "h-v03-0001", "signature": "verified", "claim": { … } }
Output shortened. Exit code 0 means ACCEPT, 2 means REJECT. Run the same command again and the second claim is refused with idempotency_key_reserved. Leave out --ledger to check a packet without claiming it.
The check order
The checker always runs these seven checks in the same order. If the structure is broken it stops right there. Otherwise it keeps going and reports every problem it finds, and the verdict takes its state from the first one, so the earliest broken link comes first.
- StructureRequired fields, types, full-length hashes, and declared actions when the receiver requires them.
missing_field:id,bad_schema_version,planned_actions_missing - Identity and signatureSender and receiver resolve in the registry by their canonical ids, and the packet's Ed25519 signature verifies.
unknown_agent:from,signature_invalid - Duplicates and conflictsThe idempotency key is looked up in the ledger. Wait-for cycles and overlapping live claims are refused.
idempotency_key_reserved,duplicate_idempotency_key,claim_conflict - FreshnessThe handoff hasn't expired.
handoff_expired - AuthorityPlanned actions against allowed tools, forbidden actions, signed approvals and budget, with the receiver's policy as a ceiling.
approval_missing,authority_exceeds_receiver_policy - ArtifactsEach input file exists inside the workspace and still matches its pinned SHA-256.
artifact_hash_mismatch - EvidenceRequired evidence is present, verbatim quotes are marked verbatim, and pinned evidence matches its file.
evidence_not_verbatim,evidence_hash_mismatch
Core concepts
The packet
A handoff is a typed JSON packet: who sends, who receives, what to do, what evidence backs it, and what the receiver may do next. Nothing free-form.
The registry
Each receiver holds a registry of agent identities, public keys and its own policy. A packet's signature is verified against the registry, and the policy is a ceiling: authority can only narrow as work passes along.
The ledger
An ACCEPT is a claim, recorded under the handoff's idempotency key. The receiver marks it completed only after the effect is real. The same work can never be claimed twice, even across restarts.
Approvals
Actions that need approval must carry a human's signed approval. An approval can pin the exact target, like a branch, and it can arrive after work has started; nothing commits without it.
Receipts
At the commit barrier, every push and every refusal is written as a receipt that carries the hash of the one before it and a signature. Change or delete one and verification shows where the chain breaks. Receipts are part of the commit broker beta.
Reason codes
Every REJECT names its reasons. Each one has a state that says who should act. This is the complete list for the public checker v0.5, from section 10 of the spec.
| invalid · the sender fixes the packet | |
|---|---|
| missing_handoff | The packet has no handoff object. |
| missing_field:name | A required field is absent or empty, for example missing_field:purpose. |
| bad_schema_version | Only packet schema 0.1 is accepted. |
| bad_on_failure | on_failure is not one of reject, retry_with_budget, escalate or request_human. |
| malformed:field | A field has the wrong type or shape, for example a depends_on that isn't a list of keys. |
| bad_sha256:field | A hash is not a full 64-character SHA-256. Shortened or placeholder hashes are refused. |
| planned_actions_missing | The receiver's policy requires declared planned actions, and there are none. |
| unknown_agent:from / :to | The sender or receiver is not in the registry. |
| alias_not_canonical:from / :to | An alias was used where the canonical agent id is required. |
| signature_missing | The receiver requires a signed packet, and there is no signature. |
| signature_malformed | The signature block is not an object with a sig string. |
| signature_alg_unsupported | Only Ed25519 signatures are accepted. |
| signature_signer_mismatch | The signer named in the signature is not the packet's sender. |
| signature_key_unknown | The registry has no Ed25519 key for that signer and key id. |
| signature_invalid | The signature doesn't verify: the packet was changed after signing, or the wrong key was used. |
| signature_unsupported | The checker can't verify signatures because synthe_crypto.py is missing next to it. |
| bad_expires_at | acceptance.expires_at is not a valid timestamp with a time zone. |
| artifact_path_escapes_workspace | An input path points outside the workspace. |
| evidence_path_escapes_workspace | An evidence reference points outside the workspace. |
| evidence_not_verbatim | Evidence of a verbatim kind, like a quote, is marked as a paraphrase or summary. |
| verbatim_evidence_unpinned | Evidence claims to be verbatim but pins no SHA-256, so it can't be checked. |
| workspace_required | A receiver policy is in force but no workspace was given, so pinned hashes can't be checked. Run with --workspace. |
| packet_unreadable | The packet file can't be read or isn't valid JSON. |
| registry_unreadable | The registry file can't be read. |
| registry_malformed | The registry isn't in the expected shape. |
| ledger_corrupt | The ledger file is not valid JSON. |
| ledger_required | Completing a claim needs --ledger, because the claim lives there. |
| missing_idempotency_key | The handoff has no idempotency key to claim. |
| completion_unknown_key | Completion was asked for a key with no live claim. |
| claim_token_required | The entry point requires the claim token to complete, and none was given. |
| release_not_reserved | A release was asked for a key that has no reserved claim. |
| dependency_cycle | depends_on would close a wait-for cycle through live claims. |
| blocked · an owner or approver acts, or the work waits | |
| tool_not_allowed | A planned action uses a tool outside the handoff's allowed_tools. |
| tool_not_allowed_by_receiver | A planned action uses a tool the receiver's policy doesn't allow. |
| forbidden_action | A planned action is forbidden by the handoff's scope. |
| approval_missing | An action that needs approval has none. |
| approval_expired | The covering approval has expired. |
| approval_expires_invalid | An approval's expiry is not a valid timestamp. |
| approver_not_trusted | The approval comes from someone the receiver doesn't trust to approve. |
| approval_unsigned | The receiver requires signed approvals, and this one is unsigned. |
| approval_signature_invalid | An approval's signature doesn't verify for its approver on this handoff. |
| approval_params_mismatch | The approval pins a target, like a branch, that the plan doesn't match. |
| authority_exceeds_receiver_policy | The handoff asks for more than the receiver's policy allows. |
| budget_exceeded | Estimated tokens, dollars or minutes exceed the budget. |
| dependency_incomplete | An upstream handoff in depends_on hasn't completed yet. The commit waits; the claim stays. |
| dependency_unknown | An upstream handoff in depends_on was never claimed in this ledger. |
| claim_conflict | Under exclusive paths, the files this handoff owns may overlap another live claim. |
| incomplete · the sender supplies what's missing | |
| artifact_missing | An input file named in the handoff isn't in the workspace. |
| evidence_missing | A required kind of evidence has no entry. |
| evidence_unreferenced | An evidence item has no reference, so there's nothing to verify. |
| evidence_ref_missing | An evidence reference points to a file that isn't in the workspace. |
| stale · the sender refreshes | |
| handoff_expired | The handoff expired before it was checked. |
| artifact_hash_mismatch | An input file changed after the handoff was made. |
| evidence_hash_mismatch | An evidence file changed, or was swapped, after it was pinned. |
| duplicate, conflicting or unknown · don't run it again | |
| idempotency_key_reserved | Another claim on this key is still running. |
| duplicate_idempotency_key | This work already completed. With a different handoff behind it, the state is conflicting and a human resolves it. |
| completion_handoff_mismatch | The key was claimed by a different handoff than the one being completed. |
| completion_packet_mismatch | Completion must present the exact packet that was claimed. |
| claim_token_invalid | The claim token doesn't match the claim. |
| release_claim_fresh | The claim is younger than its time limit and its holder may still act. Wait, or force it if you know the holder is gone. |
| unknown_outcome | The claim was never completed within its time limit. Reconcile before sending the work again. |
| effect_already_executed | This effect already ran for this handoff. |
| effect_outcome_unknown | This effect started but its outcome was never confirmed. Reconcile before retrying. |
Try it interactively
Every check above runs in your browser, no install needed.