Skip to content

feat(keeper): persist futility backoff across daemon restarts #5

Description

@0dillon

Summary

FutilityTracker in src/keeper/futility.ts holds its state in a Map that dies with the
process. A restarted daemon retries every contract once before it can begin backing off again.

That was a deliberate choice — a restart is often itself the fix, and forgetting is the right
default — but it has a failure mode worth closing: a supervisor restarting a crash-looping daemon
defeats the protection entirely, and the contract that keeps costing money is paid for on every
restart.

Acceptance Criteria

  • Backoff state survives a restart, in a location the operator controls and can inspect or
    delete.
  • A stale state file cannot wedge the daemon: state older than some documented age is discarded
    rather than trusted, so a contract fixed while the daemon was down is retried.
  • The file holds no key material and no secret. It is contract IDs and counters.
  • Losing, corrupting or deleting the file degrades to today's behaviour — retry once, then back
    off — rather than crashing.
  • Tests cover a round trip, a corrupt file, an absent file, and an expired entry.
  • SECURITY.md's "Futility backoff does not survive a restart" limit is updated or removed.

Tech Stack

TypeScript 5.7 on Node 22+ (CI runs 22 and 24), ESM with moduleResolution: "NodeNext" — relative
imports need the .js extension. @stellar/stellar-sdk is pinned to an exact version; verify RPC
API shape against the installed copy, not against documentation. Tests are Vitest under test/ and
run without a network. npm run format:check, npm run lint, npm run build and npm test must
all be clean.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: keepersrc/keeper, the daemon and its decisionscomplexity: mediumHalf a day, some design judgement neededtype: featureAdds behaviour that does not exist yet

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions