@xyo-network/xl1-cli 2.0.18 → 3.0.0

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 CHANGED
@@ -127,7 +127,8 @@ Create a `xyo.config.json` (or .js, .yaml) file in the working directory to defi
127
127
  |---|---|
128
128
  | `api` | JSON-RPC gateway. Serves block / balance / transaction queries and accepts transfers. |
129
129
  | `producer` | Block producer. Drives heartbeat blocks and packages mempool transactions. |
130
- | `finalizer` | Selects a chain head from competing producer candidates and writes finalizations. |
130
+ | `finalizer` | Selects a chain head from competing producer candidates and writes finalizations. Optionally publishes finalized blocks + the chain-state head to S3 (`publishOnFinalize`). |
131
+ | `indexer` | Publishes the chain index (step-summary families) to the S3 `index` bucket for HTTP/CDN reads. |
131
132
  | `mempool` | Holds pending transactions until the producer pulls them. |
132
133
  | `bridge` | EVM bridge — relays deposits / withdrawals between XL1 and an EVM chain. |
133
134
  | `rewardRedemption` | Claims block rewards for the producer's account. |
@@ -140,7 +141,7 @@ You make four independent choices when deploying:
140
141
 
141
142
  1. **Process layout** — mono (everything in one process), split (one actor per process), or grouped (custom).
142
143
  2. **Storage backing** — `memory` (single-process only, ephemeral), `lmdb` (single-host, persistent, multi-process safe via shared root), or `mongodb` (multi-host, persistent, multi-process safe).
143
- 3. **Provider tier** — `simple` (in-process tier 1, requires the corresponding backing), or `jsonrpc` (tier 3 proxy to another `xl1` process). The CLI picks `jsonrpc` automatically when the global `remote.rpc` is set; otherwise it picks `simple` when the backing is available.
144
+ 3. **Provider tier** — `simple` (in-process tier 1, requires the corresponding backing), or `jsonrpc` (tier 3 proxy to another `xl1` process). The CLI picks `jsonrpc` automatically when a global `default-rpc` connection is configured; otherwise it picks `simple` when the backing is available.
144
145
  4. **Config layout** — single file shared across all processes (each `xl1 start <subset>` filters which actors it activates), one file per process, or pure environment variables.
145
146
 
146
147
  ### Recipes
@@ -172,13 +173,13 @@ The `api + mempool + finalizer` subset is the canary combo: the CLI builds a sin
172
173
 
173
174
  #### Single process, LMDB (single-host persistent)
174
175
 
175
- Persistent state on one machine. LMDB writes are multi-process safe via the LMDB lockfile, so you can later split actors across processes against the same `storage.root`.
176
+ Persistent state on one machine. LMDB writes are multi-process safe via the LMDB lockfile, so you can later split actors across processes against the same `local-store` connection.
176
177
 
177
178
  `xyo.json`:
178
179
  ```json
179
180
  {
180
181
  "xl1": {
181
- "storage": { "root": "/var/xl1/data" },
182
+ "connections": { "local-store": { "type": "lmdb", "root": "/var/xl1/data" } },
182
183
  "actors": [
183
184
  { "name": "api", "host": "localhost", "port": 8080 },
184
185
  { "name": "producer", "heartbeatInterval": 1000 },
@@ -201,8 +202,9 @@ Same as the LMDB variant but with a MongoDB-backed archivist. Use this when stat
201
202
  ```json
202
203
  {
203
204
  "xl1": {
204
- "storage": {
205
- "mongo": {
205
+ "connections": {
206
+ "chain-mongo": {
207
+ "type": "mongo",
206
208
  "connectionString": "mongodb://user:pass@db.internal:27017",
207
209
  "database": "xl1",
208
210
  "domain": "internal"
@@ -230,7 +232,7 @@ Each actor runs as its own OS process. Useful when you want to restart one role
230
232
  ```json
231
233
  {
232
234
  "xl1": {
233
- "storage": { "root": "/var/xl1/data" },
235
+ "connections": { "local-store": { "type": "lmdb", "root": "/var/xl1/data" } },
234
236
  "actors": [
235
237
  { "name": "api", "host": "localhost", "port": 8080 },
236
238
  { "name": "producer", "heartbeatInterval": 1000 },
@@ -257,8 +259,9 @@ Same as above but with MongoDB as the shared store. Now each process can run on
257
259
  ```json
258
260
  {
259
261
  "xl1": {
260
- "storage": {
261
- "mongo": {
262
+ "connections": {
263
+ "chain-mongo": {
264
+ "type": "mongo",
262
265
  "connectionString": "mongodb://user:pass@db.internal:27017",
263
266
  "database": "xl1",
264
267
  "domain": "internal"
@@ -287,13 +290,15 @@ xl1 start finalizer mempool
287
290
 
288
291
  #### Stateless API client (read-only proxy to a remote chain)
289
292
 
290
- The API actor in `stateless: true` mode owns no local store. All viewer providers resolve to JsonRpc proxies pointed at `remote.rpc`. Run several stateless API instances behind a load balancer to scale read traffic without duplicating state.
293
+ The API actor in `stateless: true` mode owns no local store. All viewer providers resolve to JsonRpc proxies pointed at the `default-rpc` connection. Run several stateless API instances behind a load balancer to scale read traffic without duplicating state.
291
294
 
292
295
  `xyo.json`:
293
296
  ```json
294
297
  {
295
298
  "xl1": {
296
- "remote": { "rpc": { "protocol": "https", "url": "https://chain.example.com/rpc" } },
299
+ "connections": {
300
+ "default-rpc": { "type": "rpc", "protocol": "https", "url": "https://chain.example.com/rpc" }
301
+ },
297
302
  "actors": [
298
303
  { "name": "api", "host": "0.0.0.0", "port": 8080, "stateless": true }
299
304
  ]
@@ -310,7 +315,8 @@ xl1 start api
310
315
  Every config key has a `XL1_*` env var equivalent. Use double-underscore for nested keys and numeric indices for array entries — useful in container orchestration where you'd rather not mount a file.
311
316
 
312
317
  ```sh
313
- export XL1_STORAGE__ROOT=/var/xl1/data
318
+ export XL1_CONNECTIONS__LOCAL_STORE__TYPE=lmdb
319
+ export XL1_CONNECTIONS__LOCAL_STORE__ROOT=/var/xl1/data
314
320
  export XL1_ACTORS__0__NAME=api
315
321
  export XL1_ACTORS__0__HOST=localhost
316
322
  export XL1_ACTORS__0__PORT=8080
@@ -326,12 +332,12 @@ When per-process config needs to live in different config-management systems (Ku
326
332
 
327
333
  `xyo.api.json`:
328
334
  ```json
329
- { "xl1": { "storage": { "root": "/var/xl1/data" }, "actors": [{ "name": "api", "host": "localhost", "port": 8080 }] } }
335
+ { "xl1": { "connections": { "local-store": { "type": "lmdb", "root": "/var/xl1/data" } }, "actors": [{ "name": "api", "host": "localhost", "port": 8080 }] } }
330
336
  ```
331
337
 
332
338
  `xyo.producer.json`:
333
339
  ```json
334
- { "xl1": { "storage": { "root": "/var/xl1/data" }, "actors": [{ "name": "producer", "heartbeatInterval": 1000 }] } }
340
+ { "xl1": { "connections": { "local-store": { "type": "lmdb", "root": "/var/xl1/data" } }, "actors": [{ "name": "producer", "heartbeatInterval": 1000 }] } }
335
341
  ```
336
342
 
337
343
  ```sh
@@ -339,6 +345,72 @@ xl1 -c ./xyo.api.json start api
339
345
  xl1 -c ./xyo.producer.json start producer
340
346
  ```
341
347
 
348
+ ### S3 object storage (finalized / chain-state / index publishing)
349
+
350
+ Beyond the authority store (`lmdb` / `mongodb`), the chain can publish a static, HTTP-servable copy of itself to S3-compatible object storage (e.g. Cloudflare R2). This is what serves cheap, CDN-cached reads to [`@xyo-network/xl1-rest-block-viewer`](https://www.npmjs.com/package/@xyo-network/xl1-rest-block-viewer) and the wallet / explorer clients — no RPC server in the read path. It layers on top of any backing; it does not replace it.
351
+
352
+ **One writer per bucket:**
353
+
354
+ | Bucket | Written by | Holds |
355
+ |---|---|---|
356
+ | `finalized` | `finalizer` (when `publishOnFinalize` is set) | finalized blocks, payloads, `manifest.json` |
357
+ | `chainState` | `finalizer` (when `publishOnFinalize` is set) | the mutable head pointer (`head.json`) |
358
+ | `index` | `indexer` | the step-summary families (`blocks`, `balances`, `schemas`, `transfers`), `manifest.json`, `head.json` watermark |
359
+
360
+ The `indexer` is gated at the finalized head the `finalizer` has published, so `publishOnFinalize` must be enabled on the finalizer for the index to advance.
361
+
362
+ Configure the buckets as named `connections` (`s3-finalized`, `s3-chain-state`, `s3-index`). Credentials and `prefix` can be set per connection; each bucket sets its own `bucket` name and public `readUrl` — the CDN base URL anonymous reads go through, so they hit the CDN rather than the (per-operation billed) S3 API.
363
+
364
+ `xyo.json`:
365
+ ```json
366
+ {
367
+ "xl1": {
368
+ "connections": {
369
+ "chain-mongo": { "type": "mongo", "connectionString": "mongodb://user:pass@db.internal:27017", "database": "xl1", "domain": "internal" },
370
+ "s3-finalized": { "type": "s3", "accountId": "…", "accessKeyId": "…", "secretAccessKey": "…", "prefix": "", "bucket": "xl1-chain", "readUrl": "https://chain.xl1.example" },
371
+ "s3-chain-state": { "type": "s3", "bucket": "xl1-chain-state", "readUrl": "https://chain-state.xl1.example" },
372
+ "s3-index": { "type": "s3", "bucket": "xl1-index", "readUrl": "https://index.xl1.example" }
373
+ },
374
+ "actors": [
375
+ { "name": "producer", "heartbeatInterval": 1000 },
376
+ { "name": "finalizer", "publishOnFinalize": true, "publishContentEncoding": "br" },
377
+ { "name": "indexer", "publishSyncInterval": 5000, "publishContentEncoding": "br" }
378
+ ]
379
+ }
380
+ }
381
+ ```
382
+
383
+ Per-bucket credentials override the shared defaults — e.g. give `s3-chain-state` its own `accessKeyId` / `secretAccessKey` on that connection entry.
384
+
385
+ **Actor settings that drive publishing:**
386
+
387
+ | Setting | Actor(s) | Default | Meaning |
388
+ |---|---|---|---|
389
+ | `publishOnFinalize` | `finalizer` | `false` | Push newly finalized blocks (head pointer last) to the `finalized` + `chainState` buckets immediately after each finalization pass. |
390
+ | `publishSyncInterval` | `indexer` | `5000` | Milliseconds between index sync passes; each pass builds every family's frames up to the published finalized head, then writes the index manifest + watermark. |
391
+ | `publishContentEncoding` | `finalizer`, `indexer` | unset | Wire/storage encoding for published files: `br`, `gzip`, or `none`. |
392
+ | `publishProgressInterval` | `finalizer`, `indexer` | `100` | Publishes between progress log lines during a sync pass (runner default when unset). |
393
+
394
+ **Environment-variable form** — `__` nests, and each screaming-snake segment maps to camelCase (`CHAIN_STATE` → `chainState`, `READ_URL` → `readUrl`):
395
+
396
+ ```sh
397
+ export XL1_CONNECTIONS__S3_FINALIZED__TYPE=s3
398
+ export XL1_CONNECTIONS__S3_FINALIZED__ACCOUNT_ID=…
399
+ export XL1_CONNECTIONS__S3_FINALIZED__ACCESS_KEY_ID=…
400
+ export XL1_CONNECTIONS__S3_FINALIZED__SECRET_ACCESS_KEY=…
401
+ export XL1_CONNECTIONS__S3_FINALIZED__BUCKET=xl1-chain
402
+ export XL1_CONNECTIONS__S3_FINALIZED__READ_URL=https://chain.xl1.example
403
+ export XL1_CONNECTIONS__S3_CHAIN_STATE__TYPE=s3
404
+ export XL1_CONNECTIONS__S3_CHAIN_STATE__BUCKET=xl1-chain-state
405
+ export XL1_CONNECTIONS__S3_CHAIN_STATE__READ_URL=https://chain-state.xl1.example
406
+ export XL1_CONNECTIONS__S3_INDEX__TYPE=s3
407
+ export XL1_CONNECTIONS__S3_INDEX__BUCKET=xl1-index
408
+ export XL1_CONNECTIONS__S3_INDEX__READ_URL=https://index.xl1.example
409
+ # actor publish settings are array entries, e.g. if finalizer/indexer are actors 1 and 2:
410
+ export XL1_ACTORS__1__PUBLISH_ON_FINALIZE=true
411
+ export XL1_ACTORS__2__PUBLISH_SYNC_INTERVAL=5000
412
+ ```
413
+
342
414
  ### Mnemonics & wallets
343
415
 
344
416
  The root `mnemonic` field (or `XL1_MNEMONIC`) defines the wallet from which every actor derives an account at `accountPath`. Per-actor `mnemonic` fields are rejected — use `accountPath` to give each actor a distinct derivation.