Specification
Upoch protocol v0.5
This page summarises the specification. The authoritative text is docs/PROTOCOL.md in the project repository; where they differ, that file wins.
Changes in v0.5
v0.5 adds three things to v0.4 and changes no existing behaviour, check order, error or event:
- Runtime recovery outcomes are recorded on-chain with
recordRecoveryOutcome, which emitsRecoveryFinalizedorRecoveryFailed. - A checkpoint can be looked up by its checkpoint hash with
getCheckpointById. - A relayer can submit a checkpoint that an executor signed (EIP-712, per-executor nonce, deadline) with
submitCheckpointBySig.
Scope
Upoch provides checkpointing, integrity checking and recovery for autonomous on-chain workflows. It does not:
- prove that an agent executed correctly;
- revert or restore any on-chain state (balances, other contracts, past transactions);
- store off-chain data or guarantee its availability;
- run, stop or restart agents.
Roles & permissions
Each workflow has one owner (root of trust, multisig recommended), executors who submit checkpoints, and guardians who can pause and act on recovery. The owner cannot be a guardian or executor, and no address can be both guardian and executor; this is enforced on-chain.
| Action | Owner | Guardian | Executor |
|---|---|---|---|
| Submit checkpoint | ✓ | ||
| Pause | ✓ | ✓ | |
| Unpause directly (no active proposal) | ✓ | ||
| Propose / approve recovery | ✓ | ✓ | |
| Reject a guardian's pending recovery (v0.4) | ✓ if not approved | ||
| Cancel recovery | any | own only | |
| Execute recovery (when ready) | ✓ | ✓ | |
| Remove guardians through a recovery (v0.4) | propose / approve | propose / approve | |
| Add guardians, change policy, transfer ownership (no active proposal) | ✓ |
Checkpoint hash
The checkpoint hash is the EIP-712 struct hash of:
EpochCheckpoint(uint256 chainId,address ledger,uint256 workflowId,uint64 sequence,bytes32 parentHash,bytes32 stateHash,bytes32 traceHash,bytes32 dataURIHash)
Typehash 0xbbf7fb0599dba67cbeafa1d7877623fbbf5d7cf8efad1861a72eb5b403848bf5. The pre-image is exactly 288 bytes: nine 32-byte words with no dynamic data.
| Word | Field | Encoding |
|---|---|---|
| 0 | typehash (bytes32) | as-is |
| 1 | chainId (uint256) | big-endian |
| 2 | ledger (address) | left-padded with 12 zero bytes |
| 3 | workflowId (uint256) | big-endian |
| 4 | sequence (uint64) | big-endian, left-padded |
| 5 | parentHash (bytes32) | all zeros for genesis |
| 6 | stateHash (bytes32) | keccak256(stateBytes) |
| 7 | traceHash (bytes32) | keccak256(traceBytes) |
| 8 | dataURIHash (bytes32) | keccak256(UTF-8 bytes of dataURI) |
Test vector 0 (verified in JavaScript and Solidity): chain 31337, ledger 0x5FbDB2315678afecb367f032d93F642f64180aa3, workflow 1, sequence 0, genesis parent, URI ipfs://example/checkpoint-0.json → 0x3cb346710ba548d06822f8175c38497fbb05308ec1acf454e9ca5b6334b47205.
Submission rules (L1)
| # | Rule | Error |
|---|---|---|
| I1 | Workflow exists | UnknownWorkflow |
| I2 | Workflow is Active | WorkflowPaused |
| I3 | Sender is a current executor | NotExecutor |
| I4 | parentHash equals the current head hash | ParentMismatch |
| I5 | stateHash and traceHash are non-zero | EmptyCommitment |
| I6 | dataURI is 1–512 bytes | InvalidDataURI |
Recovery
A proposal can roll the canonical head back to an ancestor, remove and add executors, remove guardians (v0.4), and resume the workflow. Nothing else: it cannot add guardians, change the policy or the owner, move funds, or call other contracts. A proposal that would resume with no executor is refused. Its lifecycle is Pending → AwaitingOwner (if owner approval is required) → Queued → Executed, with Cancelled and Expired as terminal outcomes. Proposing pauses the workflow; cancelling or expiry never unpauses it.
The two unpause paths are U1 (the owner, with no active proposal) and U2 (a recovery with resumeAfter, including resume-only proposals). A guardian whose proposal was cancelled, rejected or expired must wait one approval window before proposing again.
Rejection (v0.4). While a guardian's proposal is Pending, other guardians who have not approved it may reject it. Once so many have rejected that the rest can no longer reach the guardian threshold, it is cancelled at once. This gives no guardian a new veto; it only stops a minority from holding the single proposal slot. The owner's proposals cannot be rejected.
When the owner is unavailable
| Policy | Guardians can recover / resume? |
|---|---|
| Owner approval not required | Yes: quorum + timelock |
| Owner required, fallback enabled | Yes: quorum + fallback delay + timelock; the owner can still cancel |
| Owner required, no fallback | No: the workflow stays paused until the owner returns |
Adding guardians, changing the policy and transferring ownership always require the owner. Since v0.4 a recovery can remove guardians (for example one that keeps pausing), under the same approvals and with the owner's veto. Policy rules P-8 and P-9 require at least two guardians whenever a recovery can complete without the owner's approval, and at least a one-hour timelock when owner approval is not required.
Rollback & lineage
Rollback walks parent links from the head (at most 256 steps) to the target, moves the head, and appends a rollback record. No checkpoint is modified or deleted. Checkpoints that are not ancestors of the head are superseded; the full history, including which recovery superseded what, can be rebuilt from contract storage alone.
Integrity levels
| Level | Question | v0 |
|---|---|---|
| L1 Record | Recorded on-chain, linked, by an authorised executor? | Guaranteed by contract |
| L2 Data | Off-chain data available and matching its hashes? | Checked off-chain by verifiers |
| L3 Execution | Did the agent compute correctly? | Not attested |
The three levels are always shown separately and never merged into a single badge. L3 is never shown as passing in v0.
Security limitations
- No proof of correct execution.
- Owner key compromise is total for that workflow's configuration.
- A compromised executor can submit well-formed but malicious checkpoints until paused; rollback fixes the record, not on-chain actions.
- Any single guardian can pause at any time, on purpose. A guardian who keeps re-pausing can be removed by a guardian quorum, at the cost of one full recovery cycle.
- A group of guardians at or above the threshold can act without the owner (including removing guardians) unless the owner cancels during the timelock or fallback delay.
- Without an owner fallback, owner unavailability can leave a workflow paused indefinitely.
- Data availability is the operator's responsibility.
- All hashes and URIs are public; encrypt or salt sensitive state.
- Block timestamps can drift; use timelocks of hours, not seconds.
- Checkpoints are only as final as their block.
- Rollback depth is bounded to 256 ancestors.
- Clients trust their RPC node; integrity checks are the observation of whoever ran them.
- Not independently audited yet: the contract has had an internal security review by the development team, and an independent audit is planned. Start with low-value workflows. Deployed on Robinhood Chain mainnet (chain 4663) at 0x6179d9123c3c1f9e82f948101a4350cb55aa8c27.