reeflog-client 0.7.21 → 0.7.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +51 -22
  2. package/index.js +6 -4
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -5,7 +5,7 @@ they did to account balances for selected programs into a cursor-addressed
5
5
  append-only log, so a consumer resumes without gaps after a crash,
6
6
  disconnect, or reorg.
7
7
 
8
- **Status: v0.7.21 shipped (2026-11-13), v0 working.** Live against mainnet-beta: see *Measured results* below. Install the binary without a Rust toolchain: `npm install reeflog-client` then `import { bootstrap } from 'reeflog-client'; await bootstrap()` (downloads the prebuilt release archive and verifies its sha256; the archive digests are `6bfec083…` linux / `303d0836…` macos, both equal what the release API publishes). The same install from the tagged release: `npm install github:auroraxo/reeflog#v0.7.21`. Or from source: `cargo install --git https://github.com/auroraxo/reeflog --tag v0.7.21`. The full event envelope (logs, balances, instructions, account list with owners, the bytes each account held, and the authority each token account had at the moment of the event), the Node client (see *The Node client*), webhook fan-out, a dead-letter queue, and archive backfill ship in this tree; Yellowstone as a second transport does not.
8
+ **Status: v0.7.23 shipped (2026-11-13), v0 working.** Live against mainnet-beta: see *Measured results* below. Install the binary without a Rust toolchain: `npm install reeflog-client` then `import { bootstrap } from 'reeflog-client'; await bootstrap()` (downloads the prebuilt release archive and verifies its sha256; the archive digests are `747e2db8…` linux / `fc2bcecc…` macos, both equal what the release API publishes). The same install from the tagged release: `npm install github:auroraxo/reeflog#v0.7.23`. Or from source: `cargo install --git https://github.com/auroraxo/reeflog --tag v0.7.23`. The full event envelope (logs, balances, instructions, account list with owners, the bytes each account held, and the authority each token account had at the moment of the event), the Node client (see *The Node client*), webhook fan-out, a dead-letter queue, and archive backfill ship in this tree; Yellowstone as a second transport does not.
9
9
 
10
10
  ## The idea
11
11
 
@@ -271,13 +271,24 @@ distinguishes four states:
271
271
  incomplete passes exit 3 (same honesty rule as `verify` and `repair`).
272
272
 
273
273
  `reeflog tail` delivers the log array by default; `--no-logs` keeps the
274
- compact identity-only shape. A background worker hydrates recovered rows
275
- continuously, beside the ingest loop — not once at startup, because the
276
- periodic repair pass keeps recovering rows while the daemon runs, and a
277
- killed run never reaches a shutdown pass. A final pass runs at shutdown.
278
- Verified live: `tools/e2e-payload.mjs` checks all 13 conditions against
279
- mainnet, including independent byte-for-byte comparison of stored payloads
280
- against freshly fetched transactions.
274
+ compact identity-only shape. Since v0.7.22 the default (payload-bearing)
275
+ tail shares the delivery rule the accounts filter already had: a row the
276
+ payload queue still owes (`logs IS NULL AND payload_state IS NULL`) HOLDS
277
+ the delivery — rows before it are handed over, the cursor stays behind the
278
+ owed row, tail exits 3 naming `hydrate`, and the same cursor reads the row
279
+ complete once the debt is paid. Measured on the pre-fix tree (v0.7.21):
280
+ tail consumed a pending row, hydrate filled it, and the next pass reported
281
+ `0 events after cursor 2` — the payload existed in the journal and never
282
+ reached the consumer that had already been walked past it. Recorded
283
+ outcomes (`unavailable` / `invalid`) are still delivered in-line with their
284
+ marker — they are answers, not debts — and `--no-logs` callers are never
285
+ held (they asked for identities only). A background worker hydrates
286
+ recovered rows continuously, beside the ingest loop — not once at startup,
287
+ because the periodic repair pass keeps recovering rows while the daemon
288
+ runs, and a killed run never reaches a shutdown pass. A final pass runs at
289
+ shutdown. Verified live: `tools/e2e-payload.mjs` checks all 13 conditions
290
+ against mainnet, including independent byte-for-byte comparison of stored
291
+ payloads against freshly fetched transactions.
281
292
 
282
293
  ## What the event did to accounts
283
294
 
@@ -872,7 +883,24 @@ reeflog start --db reeflog.db --webhook-config webhooks.json --webhook-every 5
872
883
  in chain order and never with a hole in front of it. It may see one
873
884
  twice; the stable id (`X-Reeflog-Event-Id: <target>:<kind>:<seq>`) is
874
885
  what a consumer dedups on for exactly-once.
875
- 3. **The payload is authenticated.** Every request carries
886
+ 3. **A payload the journal still owes is not delivered.** A row whose
887
+ content the journal owes (`logs IS NULL AND payload_state IS NULL`) is a
888
+ signature, not an event — since v0.7.23 the event queue HOLDS at it:
889
+ rows before it are delivered, the target's cursor stays behind the owed
890
+ row, no receipt is written, `push` exits 3 naming `hydrate`, and once the
891
+ debt is paid the SAME target receives the row complete. Measured on the
892
+ published v0.7.22 asset (`research/webhook-payload-hold-baseline-v0722-
893
+ 2026-11-13.json`, verdict `webhook-payload-hold-defect-present`): the
894
+ receiver got the owed row as a signed body with `logs: null`, `next_seq`
895
+ moved 0 → 1, a receipt was written, and the second pass after `hydrate`
896
+ delivered nothing — the finished event was permanently unreachable for
897
+ that consumer while a fresh target read it. This is the same rule
898
+ `tail` holds since v0.7.22, one channel over: the webhook body is
899
+ SIGNED, so a receiver cannot tell a signature without content from a
900
+ real event. A recorded outcome (`unavailable` / `invalid` / `no_metadata`)
901
+ is never held — it is an answer, not a debt — and retractions are never
902
+ held by this rule either.
903
+ 4. **The payload is authenticated.** Every request carries
876
904
  `X-Reeflog-Signature: sha256=<hex>` = HMAC-SHA256(secret, body) over the
877
905
  exact bytes sent (canonical JSON — `serde_json`'s sorted keys, so the same
878
906
  event always produces the same signature). The receiver checks it with
@@ -882,14 +910,14 @@ reeflog start --db reeflog.db --webhook-config webhooks.json --webhook-every 5
882
910
  import hmac, hashlib
883
911
  want = "sha256=" + hmac.new(secret, body, hashlib.sha256).hexdigest()
884
912
  ```
885
- 4. **A retraction is delivered too**, on its own cursor, and it is delivered
913
+ 5. **A retraction is delivered too**, on its own cursor, and it is delivered
886
914
  *even when the event queue is blocked behind a refusal* — a retraction is
887
915
  the one message whose lateness costs money.
888
- 5. **Receipts make the crash window safe.** The state (per-target cursor)
916
+ 6. **Receipts make the crash window safe.** The state (per-target cursor)
889
917
  and the receipts (which exact body was accepted, by sha256) live in the
890
918
  journal. A daemon that dies between "POST succeeded" and "cursor advanced"
891
919
  does not deliver the event twice on the next pass.
892
- 6. **A refusal stops being a verdict only when it stops repeating.** One
920
+ 7. **A refusal stops being a verdict only when it stops repeating.** One
893
921
  4xx can be an accident (mid-deploy, a config reload), so the refusal is
894
922
  counted. After `dead_letter_after` refused attempts on the same event
895
923
  (default 5, `0` disables it) the event moves to the **dead-letter queue**:
@@ -1647,11 +1675,11 @@ is the harness's own).
1647
1675
 
1648
1676
  The CLI is a contract before it is a convenience: exit 0 delivers and
1649
1677
  advances the cursor, exit 3 (on `tail`) means the delivery **held** at a row
1650
- whose account envelope is still owed — rows before it are delivered, nothing
1651
- at or after it is consumed — and exit 1 is a refusal. A consumer that maps
1652
- those three outcomes wrongly either replays rows forever or drops the owed
1653
- row without ever seeing it. Until now every Node consumer had to derive that
1654
- by hand.
1678
+ whose payload or account envelope is still owed — rows before it are
1679
+ delivered, nothing at or after it is consumed — and exit 1 is a refusal. A
1680
+ consumer that maps those three outcomes wrongly either replays rows forever
1681
+ or drops the owed row without ever seeing it. Until now every Node consumer
1682
+ had to derive that by hand.
1655
1683
 
1656
1684
  `index.js` (npm package `reeflog-client`, MIT, zero dependencies, Node ≥ 18)
1657
1685
  ships that mapping as one file. It never opens the journal — every call runs
@@ -1660,7 +1688,7 @@ contract, and a library's idea of the schema cannot drift from the daemon's.
1660
1688
 
1661
1689
  ```bash
1662
1690
  npm install reeflog-client # the registry; this version, no Rust toolchain
1663
- npm install github:auroraxo/reeflog#v0.7.21 # the same client from the tagged release
1691
+ npm install github:auroraxo/reeflog#v0.7.23 # the same client from the tagged release
1664
1692
  ```
1665
1693
 
1666
1694
  The client needs the `reeflog` binary. `bootstrap()` gets it **without a
@@ -1809,10 +1837,11 @@ Checked on 2026-10-23: Solana client errors already name the failure modes, incl
1809
1837
  reeflog does not recover account bytes from before an event, and the account
1810
1838
  envelope is balance MOVEMENTS an event caused — not the state of the accounts
1811
1839
  involved. `tail --with-accounts` does not skip past a row whose envelope has
1812
- not been fetched yet: it holds the cursor there and exits 3 until
1813
- `hydrate --accounts` pays the debt, so an owed row can never be lost to the
1814
- consumer. Rows the chain itself made unfillable are the only ones skipped,
1815
- and each skip is reported. That is a different problem.
1840
+ not been fetched yet, and since v0.7.22 the default tail holds the same way
1841
+ for a row whose payload is still owed: the cursor stays, tail exits 3 until
1842
+ `hydrate` (or `hydrate --accounts`) pays the debt, so an owed row can never
1843
+ be lost to the consumer. Rows the chain itself made unfillable are the only
1844
+ ones skipped, and each skip is reported. That is a different problem.
1816
1845
  Public WebSocket endpoints are also rate-limited, so provider independence
1817
1846
  has to be real rather than assumed.
1818
1847
 
package/index.js CHANGED
@@ -8,8 +8,9 @@
8
8
  // product:
9
9
  //
10
10
  // exit 0 — the batch is fully delivered, the cursor advanced;
11
- // exit 3 — the delivery HELD at a row whose account envelope is still owed
12
- // (tail), or the scan proves nothing (verify/repair/hydrate).
11
+ // exit 3 — the delivery HELD at a row whose payload or account envelope is
12
+ // still owed (tail), or the scan proves nothing (verify/repair/
13
+ // hydrate).
13
14
  // Rows before the hold WERE delivered; nothing at or after it was
14
15
  // consumed. Treating exit 3 as "failure, retry the batch" replays
15
16
  // rows; treating it as success silently drops the owed row;
@@ -199,8 +200,9 @@ export class Reeflog {
199
200
  * CONSUMED (delivered, or skipped because a recorded outcome made it
200
201
  * permanently undeliverable). On exit 3 the hold row and everything after
201
202
  * it are NOT consumed — the next call returns them once the debt is paid
202
- * (`reeflog hydrate --accounts`), so nothing is ever lost to a consumer
203
- * that acted on a partial batch.
203
+ * (`reeflog hydrate`, or `reeflog hydrate --accounts` for the account
204
+ * envelope), so nothing is ever lost to a consumer that acted on a partial
205
+ * batch.
204
206
  *
205
207
  * @param {object} [opts]
206
208
  * @param {number} [opts.limit=1000]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reeflog-client",
3
- "version": "0.7.21",
3
+ "version": "0.7.23",
4
4
  "description": "Node client for reeflog, the durable local mailbox for Solana events: cursor-based event delivery, retractions, health and repair.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,6 +31,6 @@
31
31
  "test": "node --test"
32
32
  },
33
33
  "dependencies": {
34
- "reeflog-client": "github:auroraxo/reeflog#v0.7.20"
34
+ "reeflog-client": "github:auroraxo/reeflog#v0.7.22"
35
35
  }
36
36
  }