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.
- package/README.md +51 -22
- package/index.js +6 -4
- 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.
|
|
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.
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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. **
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1651
|
-
at or after it is consumed — and exit 1 is a refusal. A
|
|
1652
|
-
those three outcomes wrongly either replays rows forever
|
|
1653
|
-
row without ever seeing it. Until now every Node consumer
|
|
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.
|
|
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
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
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
|
|
12
|
-
// (tail), or the scan proves nothing (verify/repair/
|
|
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`
|
|
203
|
-
* that acted on a partial
|
|
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.
|
|
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.
|
|
34
|
+
"reeflog-client": "github:auroraxo/reeflog#v0.7.22"
|
|
35
35
|
}
|
|
36
36
|
}
|