This page collects the security properties and the known limitations of LedgerKeep in one place. The limitations are stated plainly. None of the contracts are audited.
The registry requires contract.require_auth() to register, update, or deregister a manifest. A contract address has no private key, so authorization for it can only be produced from inside the contract's own code. Manifest spoofing — publishing a false key list for a contract you do not control — is therefore not possible. This is the registry's whole security model, and it rests on a property of Soroban addresses rather than on a check that could be misconfigured.
No administrator, no upgrade authority, no pause. There is no key that can alter or freeze the registry. This is deliberate: a public directory that a privileged party could tamper with would not be worth trusting.
Before paying, the vault confirms that maintenance was recorded since the last payout, that the claimant is the keeper who performed it, and that the interval has elapsed. A bystander cannot claim a tip for someone else's maintenance, and a keeper cannot drain a vault with rapid repeat claims.
The keeper signs only at the moment of submission, reads its key from a file path rather than a command-line argument, and never logs key material. Read-only commands never touch the key at all.
The vault confirms that maintenance happened, not that it was needed. No contract can read time-to-live at runtime, so the vault has no way to check whether the extended keys were actually close to expiry. A keeper can, at most, collect one tip per interval for maintenance that may not have been required. The exposure is bounded by the tip size, the interval, and the vault balance — all set by the owner, who can withdraw at any time. It is a rate limit, not a proof of necessity.
This is the most important limitation to understand before running the keeper unattended. Registration is permissionless, and the daemon maintains every registered contract without capping what it will pay per extension. A contract can be written to be expensive to maintain and registered specifically to drain a keeper that picks it up. Drift detection does not catch this, because the expensive extension is genuine work. Until a per-extension fee ceiling exists, do not run lkeep keep against a funded key you are not prepared to expose to this risk. Scripted single extend calls against contracts you have chosen are not affected. This is the top open issue on the CLI.
The vault's initialize sets the tip asset with no authorization — it is guarded only against being set twice. On a fresh deployment, anyone could call it first and set an unexpected token. The mitigation is operational: the deployment must initialize the vault in the same session, before anyone else can. The provided deployment script does this.
Nothing on-chain forces a contract's published manifest to match the keys its compiled code extends. A stale or incomplete manifest is possible. The keeper detects drift by comparing the simulation footprint against the manifest and reports it, but it does not enforce a match. Keeping the two in sync is the contract owner's responsibility.
LedgerKeep has not been through a security audit. Do not deploy it on mainnet with real funds without your own review.
Responsible disclosure is welcome. Contact dillonhopes5@gmail.com with details. Please do not open a public issue for a security vulnerability before it has been addressed.