Off-chain tooling that keeps Soroban contract state alive on Stellar.
Soroban ledger entries expire: every entry has a liveUntilLedgerSeq, and once
that passes, persistent entries are archived and must be restored (at a cost)
before they can be used again. This workspace contains two cooperating tools:
rent-keeper— a long-running daemon that watches the TTLs of the entries you care about and submits batchedExtendFootprintTtltransactions before anything expires.restore-planner— a CLI for inspecting entries (live / expired / archived) and generating restore and extend plans as JSON.
Both binaries share rentkeeper-core, which holds the TTL model, the
plan data structures, RPC observation, and the transaction builder/signer.
| Crate | Purpose |
|---|---|
crates/rentkeeper-core |
Ledger keys, TTL math, risk windows, plans, RPC provider, tx building/signing |
crates/rent-keeper |
The daemon: config, poll loop, extend submission, Prometheus metrics |
crates/restore-planner |
The operator CLI: inspect, plan-restore, plan-extend, show-plan |
cargo build --release
# binaries in target/release/{rent-keeper,restore-planner}Create rent-keeper.toml (kept out of git by .gitignore; never commit the
secret key):
rpc_url = "https://soroban-testnet.stellar.org:443"
network_passphrase = "Test SDF Network ; September 2015"
secret_key = "SB..." # fee payer; fund this account
poll_interval_secs = 30 # optional, default 30
risk_window_days = 7.0 # optional, default 7
metrics_port = 9100 # optional; omit to disable
# Watch rules: explicit entries to keep alive.
[[watch]]
type = "instance"
contract_id = "C..." # contract instance entry
[[watch]]
type = "data_key"
contract_id = "C..."
key_xdr = "AAAAAwAAAAc=" # base64 ScVal XDR
durability = "persistent" # or "temporary"
[[watch]]
type = "contract_code"
wasm_hash = "aabb..." # 32-byte hex Wasm hashValidate without running:
rent-keeper --config rent-keeper.toml --checkRun:
rent-keeper --config rent-keeper.tomlEach cycle the daemon fetches the watched entries plus their TTL companions
from getLedgerEntries, builds an ExtendFootprintTtl transaction for every
entry at or below the risk window, signs it with the fee payer key, and
submits it. Entries that have already expired are logged (with a warning) but
not touched — restoring is an operator decision because of the fee; use
restore-planner for that. Metrics are served on metrics_port at
/metrics when configured.
TTL constants (
max_entry_ttl, minimum TTLs) are read from the network'sStateArchivalconfig entry at startup, falling back to public-network defaults if unavailable.
Inspect entries on chain:
restore-planner inspect --contract C... --key-xdr "AAAAAwAAAAc=" --durability persistent
restore-planner inspect --contract C... --wasm-hash aabb...Generate a restore plan for archived entries (validates that the entries really are archived before writing the plan):
restore-planner plan-restore --contract C... \
--key-xdr "AAAAAwAAAAc=" --durability persistent \
--ttl-days 30 --out restore-plan.jsonGenerate an extend plan for live entries:
restore-planner plan-extend --contract C... \
--key-xdr "AAAAAwAAAAc=" --durability persistent \
--ttl-days 30 --out extend-plan.jsonReview a plan file (or - for stdin):
restore-planner show-plan --plan restore-plan.jsonPlans are JSON documents containing base64 ledger-key XDRs, the operation kind, the observed TTL, and the target TTL, so they can be checked into an ops workflow, diffed, and later fed to a signing/submission step.
The daemon uses the protocol semantics for ExtendFootprintTtl:
extend_to is an absolute remaining-TTL target measured from the current
ledger (not an increment), persistent entries are clamped by the network's
max_entry_ttl, and the footprint travels in the transaction's
SorobanTransactionData extension. Plans therefore store the clamped
extend_to value rather than a raw day count.
cargo test --workspace # unit tests (73)
cargo test -p rentkeeper-core --test integration \
--features integration-tests -- --ignored # optional live testnet checks
cargo clippy --workspace --all-targets # zero warnings expected
cargo fmt --all -- --checkThe core crate is fully offline-testable: planning, TTL math, key hashing, plan (de)serialization, and signing are all covered without network access.
- The fee payer secret key must never be committed;
.gitignoreexcludesrent-keeper.toml,.env*, and local overrides. - Restores cost real XLM proportional to entry size; review plans before signing them.
- Extension target is clamped to the network
max_entry_ttl; a plan'sextend_tois always a valid target for persistent entries.