Skip to content

Security: ledgerkeep/ledgerkeep-docs

Security

security.md

Security

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.

What is designed to be safe

Only a contract can publish its own manifest

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.

The registry has no privileged role

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.

The vault pays only for recorded, attributed work

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.

Keys are never trusted from off-chain

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.

Known limitations

The vault cannot verify necessity

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.

The daemon has no fee ceiling

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 tip token is set first-caller-wins

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.

Published manifests can drift from extended keys

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.

The contracts are unaudited

LedgerKeep has not been through a security audit. Do not deploy it on mainnet with real funds without your own review.

Reporting a vulnerability

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.

There aren't any published security advisories