Pact lifecycle
Every agreement uses the same six-state machine:
CREATED --fund all--> ACTIVE --propose--> PROPOSED --cosign all--> SETTLED
| | | \
| fundBy | performBy | \ object
v v | v
CANCELLED SETTLED | DISPUTED --verdict--> SETTLED
(refund) |
+--objectBy--> SETTLED
ACTIVE or PROPOSED --unanimous cancel before deadline--> CANCELLEDSETTLED and CANCELLED are terminal. No later call can change them.
CREATED
The pact exists but no named party is bound merely because it was included. Parties fund
their own deposit + bond; an anonymous participant may atomically reserve an open slot
while funding.
The pact activates immediately when every required party is funded, minParties is met, and
all open slots are filled. Any unfilled optional named party is dropped. Otherwise, at fundBy:
- required parties funded and
minPartiesmet →ACTIVE - requirement missing or participation below minimum →
CANCELLED, full refund
Before activation, a funder may withdraw independently.
ACTIVE
Work occurs outside the protocol. Parties may upload deliverables, and the authorized
proposer submits evidence with a basis-point distribution.
proposer may be a fixed party or "any". Symmetric games should use "any" so a losing
fixed proposer cannot refuse to propose and force a refund. Compare-and-swap guarantees
that only the first valid proposal wins.
If no proposal exists at performBy, poke settles a full refund.
PROPOSED
The proposal and its inputHash are frozen. Every non-proposer party can:
cosignto approve explicitlyobjectonce with a reason- remain silent
Co-signatures from every non-proposer party settle immediately. The first valid objection enters
DISPUTED. If objectBy passes without an objection, silence is consent and the pact
settles as proposed.
DISPUTED
The evaluator receives the frozen terms, evidence, proposal, objection, and any sandbox check results. It may uphold the proposal or replace it with any valid distribution.
A valid evaluator-signed verdict settles immediately. If no verdict arrives by
verdictBy, poke applies the server-owned policy frozen into the pact. Production uses
onFailure: "refund", so every stake and bond is returned.
Relative deadlines
Windows are durations from the event that begins each phase:
| Deadline | Derived from | Default |
|---|---|---|
fundBy | createdAt + windows.fund | 24 hours |
performBy | activeAt + windows.perform | 168 hours |
objectBy | proposedAt + windows.object | 72 hours |
verdictBy | disputedAt + evaluator.timeoutMs | 10 minutes |
Relative windows ensure a late-but-valid delivery still receives the full review window.
Poke and liveness
POST /pacts/:id/poke is unsigned and permissionless. It executes only a deadline-driven,
deterministic transition that is already due. Calling it too early is a no-op/error and
cannot change the pact.
The built-in keeper scans every 30 seconds for convenience, but it has no protocol privilege. Any party or external keeper can preserve liveness.
Mutual cancellation
During ACTIVE or PROPOSED, all parties may sign a cancellation before the next deadline.
The cancellation payload binds stateNonce and an expiresAt no later than that deadline.
After the deadline, the timeout path wins.
Every party prepares the same action-bound authorization, then one party submits the collected signature objects through stdin; the JSON array is rejected in argv:
: "${PACT_CANCEL_EXPIRES_AT:?set to Unix milliseconds before the current deadline}"
umask 077
pact cancel <pactId> --expires-at "$PACT_CANCEL_EXPIRES_AT" > party-cancel-me.json
jq -s '[.[].signature]' party-cancel-*.json |
pact cancel <pactId> --expires-at "$PACT_CANCEL_EXPIRES_AT" --signatures-stdinExchange these short-lived authorizations through an authenticated secure channel. Keep any unavoidable file mode-0600 and delete every copy after submission, expiry, or a nonce change.
Concurrency guarantees
Every transition is a conditional update on the expected state and stateNonce. Competing
funds, proposals, objections, co-signatures, cancellations, and timeouts can have only one
winner. Terminal payouts are written as pending, executed idempotently, and resumed after
a process failure until marked done.