LedgerKeep has four moving parts: a standard, a registry, a vault, and a keeper. The first three are on-chain contracts (or a crate contracts adopt); the fourth runs off-chain.
A crate, not a deployed contract. A protocol adopts it by calling one macro, impl_maintainable!, which lists — at compile time — the persistent keys the contract wants kept alive, plus a threshold and a target time-to-live.
The macro generates a permissionless extend_all(keeper) function. Anyone can call it. It extends the contract's declared keys and its instance storage, then records who called it and at which ledger, in a small piece of state called MaintenanceState. It also exposes lk_state, which returns that record.
Because the keys are fixed at compile time, there is no on-chain list of keys to store, read, or tamper with. The contract knows its own keys because they were written into it.
A permissionless, on-chain directory. A contract publishes an entry naming itself and listing the keys it wants maintained — as opaque, XDR-encoded values that only off-chain tooling decodes. The registry exists so a keeper can discover what to maintain without reading each protocol's source code.
The registry has no administrator, no upgrade authority, and no pause function. It is a public good; a privileged role would undermine the reason to trust it. The only thing that can publish an entry for a contract address is that contract itself — enforced by a single authorization check, explained in Protocol mechanics.
The registry also adopts the maintainable standard and registers itself, so its own state stays alive by the same mechanism it offers everyone else.
An on-chain contract that lets a protocol pre-fund maintenance and pay whoever performs it. A protocol opens a vault pointing at its contract, funds it in a stablecoin or XLM, and sets a tip amount and a minimum interval between payouts. A keeper that maintains the contract then claims the tip.
The vault confirms the work happened by reading the MaintenanceState the contract recorded — it checks that maintenance occurred since the last payout, that the claimant is the keeper who did it, and that the interval has elapsed. What it cannot confirm is that the maintenance was necessary. That limit is deliberate and documented; see Economic model and Security.
The off-chain half. It reads a contract's time-to-live over RPC — the one place the countdown is visible — and reports how many ledgers remain before each entry expires. When an entry runs low, it calls the contract's extend_all. Run as a daemon, it discovers every contract in the registry, scans each on a schedule, and maintains the ones that need it.
The keeper is the only component that observes time-to-live, because it is the only component that can.
- A protocol adopts the standard and publishes its keys to the registry.
- The keeper reads the registry and finds the protocol's contract.
- The keeper scans the contract's time-to-live over RPC.
- An entry is running low. The keeper calls
extend_all. - The contract extends its keys and records the maintenance.
- The keeper claims a tip from the protocol's vault, which verifies the record.
Every step maps to a real capability of the component that performs it. Nothing observes what it cannot observe, and nothing is trusted to attest to what it cannot prove.