Keeper (Fletcher)
View raw

Operating Fletcher (the keeper)

Fletcher is the provider's off-chain service. It holds the hash-chain seed, watches for callback requests addressed to your provider, computes the matching reveal, and submits revealWithCallback to deliver randomness. This guide covers running it reliably.


What Fletcher does

  1. Preflight — confirms the provider is registered and reports how many random values remain in the chain.
  2. Backfill — scans recent blocks for callback requests missed while offline and queues them.
  3. Watch — subscribes to RandomnessRequested for your provider and fulfills each callback request (serially, to avoid nonce races).
  4. Skips requests already fulfilled on-chain; retries transient failures.

It does not need to serve the pull flow — pull-flow requesters reveal themselves. If you also offer pull-flow randomness, expose an endpoint that returns revelation <seq> for already-requested sequence numbers only (never future ones).

CLI

fletcher genseed                 # generate a 32-byte seed (store as FLETCHER_SEED)
fletcher register                # register the provider (commitment = keccak^N(seed))
fletcher run                     # start the keeper
fletcher info                    # print the on-chain provider record
fletcher revelation <seq>        # print the revealed value for a sequence (debug / pull-flow serving)
fletcher rotate <seedHex> <len>  # rotate onto a fresh chain segment

Run via pnpm --filter @quiver/fletcher exec tsx src/index.ts <cmd>, or build (pnpm --filter @quiver/fletcher build) and use the fletcher bin.

Configuration (environment)

VarRequiredMeaning
FLETCHER_NETWORKyesrh_testnet | rh_mainnet | local
RH_TESTNET_RPC_URL / RH_MAINNET_RPC_URL / LOCAL_RPC_URLrecommendedRPC endpoint (use a dedicated provider in prod)
QUIVER_COORDINATOR_ADDRESSyesdeployed coordinator
PRIVATE_KEYyesthe provider/operator key
FLETCHER_SEEDyes32-byte hash-chain seed — secret
FLETCHER_CHAIN_LENGTHyesRandom values per chain segment
FLETCHER_FEE_WEIno (0)per-request fee at registration
FLETCHER_MAX_NUM_HASHESno (500)reveal hash bound at registration
FLETCHER_ANCHORno (0)sequence anchor of the current segment (set after a rotation)
FLETCHER_BACKFILL_BLOCKSno (5000)startup look-back window
FLETCHER_POLL_MSno (3000)event poll interval

Seed security — this is the crown jewel

Anyone who learns your seed can predict every future value and thus every outcome you'll serve. Treat it exactly like a private key:

  • Generate with fletcher genseed (CSPRNG). Never derive it from something guessable.
  • Store in a secrets manager / KMS, not in a plaintext file on a shared box.
  • Never log it, commit it, or put it in a shell history (export from a secrets tool).
  • Rotate on any suspicion of compromise (below). Future values become safe immediately.

Rotation

Chains are finite. Rotate before exhaustion (Fletcher warns when values run low) or to retire a compromised seed:

NEW=$(fletcher genseed 2>/dev/null)
fletcher rotate "$NEW" 100000
# then update env for the NEW segment and restart:
#   FLETCHER_SEED=$NEW  FLETCHER_CHAIN_LENGTH=100000  FLETCHER_ANCHOR=<printed anchor>

Rotation anchors the new chain at sequenceNumber - 1, so the next request verifies in one hash. Keep the old segment's config if old requests may still be outstanding — Fletcher must reveal those against the old seed. For zero-downtime multi-segment serving, run the keeper with a persisted list of segments (roadmap) or briefly run two instances (old-segment reveal + new-segment run).

Running in production

  • Process manager: systemd or a container with restart: always. Fletcher exits cleanly on SIGINT/SIGTERM.
  • Dedicated RPC: public endpoints are rate-limited; use Alchemy or another dedicated provider for both reads (event polling) and writes.
  • Operator ETH: the provider key pays gas for every reveal. Alert and top up before it runs dry. (A dry keeper = withheld reveals = stalled consumers.)
  • Monitoring / alerts:
    • low operator ETH balance,
    • remaining values below a threshold (endSeq - sequenceNumber),
    • reveal failures / growing gap between requests and reveals,
    • CallbackFailed volume (consumer bugs, not yours, but worth visibility).
  • Idempotency: Fletcher tracks seen sequence numbers and re-checks on-chain state, so a restart won't double-submit.

Example systemd unit

[Unit]
Description=Quiver Fletcher keeper
After=network-online.target

[Service]
WorkingDirectory=/opt/quiver
EnvironmentFile=/etc/quiver/fletcher.env      # holds FLETCHER_SEED etc. (chmod 600)
ExecStart=/usr/bin/node /opt/quiver/fletcher/dist/index.js run
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Failure handling reference

SituationFletcher behavior
Request already fulfilleddetected via getRequest → skipped
Reveal tx fails (RPC/gas)logs error, drops from "seen" so it retries
Consumer callback revertson-chain buffer + CallbackFailed; consumer retries via retryCallback
seq outside current segmenterror asking you to configure the segment's seed/anchor
Random values nearly exhaustedwarns on preflight — rotate