# 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)

| Var | Required | Meaning |
| --- | --- | --- |
| `FLETCHER_NETWORK` | yes | `rh_testnet` \| `rh_mainnet` \| `local` |
| `RH_TESTNET_RPC_URL` / `RH_MAINNET_RPC_URL` / `LOCAL_RPC_URL` | recommended | RPC endpoint (use a dedicated provider in prod) |
| `QUIVER_COORDINATOR_ADDRESS` | yes | deployed coordinator |
| `PRIVATE_KEY` | yes | the provider/operator key |
| `FLETCHER_SEED` | yes | 32-byte hash-chain seed — **secret** |
| `FLETCHER_CHAIN_LENGTH` | yes | Random values per chain segment |
| `FLETCHER_FEE_WEI` | no (0) | per-request fee at registration |
| `FLETCHER_MAX_NUM_HASHES` | no (500) | reveal hash bound at registration |
| `FLETCHER_ANCHOR` | no (0) | sequence anchor of the current segment (set after a rotation) |
| `FLETCHER_BACKFILL_BLOCKS` | no (5000) | startup look-back window |
| `FLETCHER_POLL_MS` | no (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:

```bash
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

```ini
[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

| Situation | Fletcher behavior |
| --- | --- |
| Request already fulfilled | detected via `getRequest` → skipped |
| Reveal tx fails (RPC/gas) | logs error, drops from "seen" so it retries |
| Consumer callback reverts | on-chain buffer + `CallbackFailed`; consumer retries via `retryCallback` |
| seq outside current segment | error asking you to configure the segment's seed/anchor |
| Random values nearly exhausted | warns on preflight — rotate |
