Lease Lifecycle
The states a compute lease moves through
A compute lease passes through creation, acceptance, and settlement. Each phase produces an on-chain block that the ledger validates. Two escape hatches complete the state machine: the consumer can cancel a lease no provider has accepted, and can force-settle an accepted lease the provider abandoned.
Lease state is tracked on a Lease record with a State field:
created ──── lease_accept ────► accepted ──── lease_settle ───────► settled
│ │
│ ├──── lease_force_settle ──────► unfulfilled
│ │
│ └──── local archive sweep ─────► expired
│
└──── lease_cancel ────────► cancelledsettled, cancelled, unfulfilled and expired are terminal.
Phase 1: Lease creation (consumer)
The consumer creates a lease block on their account chain. This debits XUSD from their balance into escrow to pay for the requested resources.
{
"type": "lease",
"account": "<consumer pubkey>",
"previous": "<frontier hash>",
"balance": "<previous XUSD balance - cost>",
"asset": "XUSD",
"destination": "<provider pubkey>",
"amount": "<cost in micro-XUSD>",
"vcpus": 2,
"memory_mb": 4096,
"disk_gb": 50,
"duration": 3600,
"access_pub_key": "<ed25519 pubkey hex, 64 chars>",
"certificate_hash": "<provider's performance certificate hash>"
}The block is signed, PoW-solved, and broadcast to the network via gossip.
[!INFO] Cost must match the formula The ledger validates that
amountexactly equals the deterministic cost computed from the resource dimensions, the duration, and the price multiplier carried by the referenced certificate. See Cost Model.
[!NOTE] The certificate is mandatory
certificate_hashmust name an unexpired performance certificate belonging todestination. This locks the price multiplier at offer time, and the provider must accept using the same certificate hash.
What happens on broadcast
The lease block creates an escrowed pending send to the provider. The provider's node sees this pending send and -- if configured with --provide -- automatically begins the acceptance flow. The escrow is not drained on acceptance: it stays alive until it is burned at settlement, refunded on cancel, or refunded on force-settle.
Phase 2: Lease acceptance (provider)
The provider creates a lease_accept block on their account chain. This stakes XUSD collateral and records timekeeper-attested start time.
{
"type": "lease_accept",
"account": "<provider pubkey>",
"previous": "<frontier hash>",
"balance": "<previous XUSD balance - stake>",
"asset": "XUSD",
"source": "<lease block hash>",
"amount": "<stake in micro-XUSD>",
"certificate_hash": "<same certificate hash as the lease block>",
"attestations": [...],
"locked_r": 2000,
"locked_payout_cap": 1550,
"locked_twap_milli": 751
}[!NOTE] Stake calculation Stake =
ceil(cost / 5), with a minimum of 1 µXUSD. The provider must have sufficient XUSD balance to cover the stake.
[!IMPORTANT] Emission parameters are locked at acceptance The accept block must carry
locked_r,locked_payout_capandlocked_twap_milli— the emission rate parameters of the epoch covering the attested accept time. Settlement computes the XE emission from these locked values, so the provider's payout is fixed the moment it commits stake, independent of any later epoch transition. An accept withlocked_runset is rejected, and the values are checked against the epoch the state chain reports for the accept time.
Acceptance steps
- Accept policy — apply the provider's local accept policy (min/max duration, min/max cost, max concurrent leases). A zero-value policy is permissive.
- Balance check — verify sufficient XUSD for the stake.
- Gather attestations — request signed timestamps from trusted timekeepers (see Attestations). Attestations are mandatory.
- Resource check — verify available vCPUs, memory, and disk (including resources reserved by in-flight provisions).
- Provision VM — start the virtual machine with the requested resources. This happens before the accept block is committed — if provisioning fails, the lease is not accepted.
- Create block — build the
lease_acceptblock withcertificate_hashreferencing the provider's performance certificate, and the locked emission parameters. - Validate certificate — the ledger verifies the certificate exists in the gossip cache, matches the hash the consumer referenced, belongs to this provider, and is not expired at the attested start time.
- Broadcast — publish to the network via gossip.
- Send credentials — deliver VM access info to the consumer via the
vm_credentialsdirect message.
Lease record
When the ledger processes a valid lease_accept, it creates a Lease record:
Field
Source
LeaseHash
Hash of the original lease block
State
accepted
Consumer
Lease block's account (creator)
Provider
Lease block's destination
VCPUs
From the lease block
MemoryMB
From the lease block
DiskGB
From the lease block
Duration
From the lease block (seconds)
Cost
Lease block's amount (XUSD)
Stake
lease_accept block's amount (XUSD)
StartTime
Median attested timestamp (unix nanos)
AccessPubKey
From the lease block (ed25519 hex, for SSH access)
CertificateHash
Provider's performance certificate hash
LockedR / LockedPayoutCap / LockedTWAP
Emission parameters from the lease_accept block
Settled
false
Phase 3: Settlement (provider)
After the lease duration expires, and before the settle grace window closes, the provider creates a lease_settle block. This mints the XE emission, returns the XUSD stake, and burns the consumer's escrow.
{
"type": "lease_settle",
"account": "<provider pubkey>",
"previous": "<frontier hash>",
"balance": "<previous XE balance + emission>",
"asset": "XE",
"source": "<lease block hash>",
"amount": "<XE emission>",
"attestations": [
{
"public_key": "<timekeeper pubkey hex>",
"timestamp": 1709658000000000000,
"signature": "<attestation signature hex>"
}
]
}Settlement steps
- Check expiry — the
settleLoop()goroutine checks every 10 seconds whethernow >= startTime + duration, and stops retrying oncenow > expiry + LeaseSettleGrace. - Gather attestations — request signed timestamps for the settle time. Attestations are mandatory.
- Validate window — the ledger verifies that the attested median is at or after
startTime + duration(in nanoseconds) and no later thanexpiry + LeaseSettleGrace. Self-reported timestamps are not accepted. - Create block — build the
lease_settleblock with the XE emission. - Broadcast — publish to the network.
- Burn escrow, return stake — the consumer's escrowed XUSD is burned and the provider's XUSD stake is credited back, both atomically with the block commit.
- Teardown VM — synchronously stop and clean up the virtual machine (not fire-and-forget).
- Orphan cleanup — the settle loop also tears down any VM whose lease has reached a terminal state but whose teardown failed.
[!IMPORTANT] XE emission The XE reward is not numerically equal to the XUSD cost. It is
ceil(cost x R_capped / 1000)in micro-XE, whereR_cappedis derived from the emission parameters locked onto the lease at acceptance (locked_r, capped bylocked_payout_capandlocked_twap_milli). The emission rate curve itself is part of the token economics model, not the lease code — see Economics.
[!WARNING] Settle window closes A settle whose attested median is past
expiry + LeaseSettleGraceis rejected. The provider cannot settle after that; once a furtherLeaseForceSettleGaphas elapsed the lease becomes the consumer's to force-settle (see below). The gap between the two windows keeps them disjoint.
Duration limits
Limit
Value
Human
Minimum
LeaseMinDuration (production default 60)
1 minute
Maximum
31,536,000 seconds
365 days
The ledger rejects lease blocks with durations outside this range.
[!IMPORTANT] Lease timings are network parameters The minimum duration, the settle grace window, the force-settle gap, the escrow expiry and the attestation skew are pinned by the genesis block (#524). The values quoted on this page are the production/mainnet defaults used when a genesis does not pin them. A network — for instance a compressed test network — may pin different values, so read the live figures from
GET /nodeunderlease_timingrather than treating them as protocol constants. The maximum duration is a true protocol constant.
Phase 4: Cancellation (consumer)
A lease that no provider has accepted can be cancelled by its consumer with a lease_cancel block. This deletes the escrowed pending send and refunds the full cost.
{
"type": "lease_cancel",
"account": "<consumer pubkey>",
"previous": "<frontier hash>",
"balance": "<previous XUSD balance + cost>",
"asset": "XUSD",
"source": "<lease block hash>"
}Rules the ledger enforces:
- Only the consumer (the lease block's creator) may cancel.
- The lease must be in
createdstate; a settled, cancelled or unfulfilled lease is already resolved. - The escrowed pending send must still exist.
- The block's balance must equal the current XUSD balance plus the refunded cost.
- No attestations are required — cancellation is not time-gated.
A cancel that races a provider's accept resolves deterministically cancel-wins: a node that already applied the accept unwinds it (refunding the provider's stake and restoring the lease to created) so the cancel can proceed, converging with nodes that applied the cancel first.
Phase 5: Force-settle (consumer)
If a provider accepts a lease and then never settles it, the consumer recovers their money with a lease_force_settle block on their own chain. It refunds the full escrowed cost and leaves the provider's stake burned as the penalty. The lease ends in unfulfilled state.
{
"type": "lease_force_settle",
"account": "<consumer pubkey>",
"previous": "<frontier hash>",
"balance": "<previous XUSD balance + cost>",
"asset": "XUSD",
"source": "<lease block hash>",
"attestations": [...]
}Rules the ledger enforces:
- Only the consumer may force-settle, and only a lease in
acceptedstate. - Timekeeper attestations are mandatory; the attested median must be at or after the eligibility point.
- Eligibility:
expiry + LeaseSettleGrace + LeaseForceSettleGap. On production timing that is 1 h 25 min after expiry (1 h grace + 25 min gap). - Upper bound: the attested median must be before
expiry + LeaseEscrowExpiry(production default 365 days). Past that the refund window has closed. - The escrowed pending send must still exist.
The gap between the provider's settle window and the consumer's force-settle window is deliberately larger than twice the maximum attestation skew, so no single timestamp can satisfy both windows — lease_settle and lease_force_settle can never both be valid.
Abandoned leases
If both parties go offline and neither settles nor force-settles, the escrow is eventually garbage-collected. Once now >= expiry + LeaseEscrowExpiry + LeaseArchiveGap, each node independently deletes the escrowed pending send and marks the lease expired. This is a local, blockless derived-state operation: it emits no block and touches no account balance — total XUSD supply simply drops by the burnt escrow.
Automatic settlement
Every node runs a background settleLoop() goroutine that ticks every 10 seconds and performs four sweeps:
- Settle — for each
acceptedlease where this node is the provider andexpiry <= now <= expiry + LeaseSettleGrace, callsettleLease(), which gathers attestations, creates thelease_settleblock, and tears down the VM. - Force-settle — for each
acceptedlease where this node is the consumer and the force-settle window has opened, submit alease_force_settle. - Archive — garbage-collect the escrow of leases abandoned by both parties (see Abandoned leases).
- Orphan VM cleanup — tear down VMs whose leases have reached a terminal state but whose teardown failed.
[!TIP] No manual intervention needed Neither party needs to settle by hand. The
settleLoop()handles settlement on the provider side and force-settlement on the consumer side, as long as the node is running.
Lease struct
The Lease struct tracks the full state of an active or settled lease:
type Lease struct {
LeaseHash string // hash of the lease block
State LeaseState // created, accepted, settled, cancelled, unfulfilled, expired
Consumer string // consumer account (lease block creator)
Provider string // provider account (lease block destination)
VCPUs uint64
MemoryMB uint64
DiskGB uint64
Duration uint64 // seconds
AccessPubKey string // ed25519 pubkey hex for SSH access
Cost uint64 // micro-XUSD paid by consumer
Stake uint64 // micro-XUSD staked by provider
StartTime int64 // unix nanos (median attested timestamp)
CertificateHash string // provider's performance certificate
Settled bool
// Emission parameters locked at lease_accept time.
LockedR uint64 // R_effective at accept, x1000
LockedPayoutCap uint64 // payout cap at accept, x1000
LockedTWAP uint64 // TWAP at accept, milli-USD
}GET /leases?state=... filters on the State field. The legacy Settled bool stays false for cancelled leases, so state — not Settled — is the field to branch on.
Sequence diagram
Consumer Node Network Provider Node
│ │ │
│ create lease block │ │
│ (debit XUSD) │ │
│ ─────────────────────────►│ │
│ │ gossip lease block │
│ │──────────────────────────►│
│ │ │ detect pending send
│ │ │ gather start attestations
│ │ │ check resources available
│ │ │ provision VM (before accept!)
│ │ │ create lease_accept
│ │ gossip lease_accept │ (stake + cert hash + locked R)
│ │◄──────────────────────────│
│ receive VM credentials │ │ send credentials
│◄──────────────────────────│───────────────────────────│
│ │ │
│ ... lease duration elapses ... │
│ │ │
│ │ │ settleLoop detects expiry
│ │ │ gather settle attestations
│ │ │ create lease_settle
│ │ gossip lease_settle │ (mint XE, burn escrow,
│ │◄──────────────────────────│ recover stake)
│ │ │ teardown VM (synchronous)