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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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