npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@xyo-network/xl1-cli

v4.4.0

Published

XYO Layer One CLI

Readme

logo

@xyo-network/xl1-cli

npm license

XYO Layer One CLI

Description

The XL1 Command-Line Interface (CLI) tool is a program you run in your terminal to create a local XL1 Node. Once installed, you can invoke it from any directory using its name, the command, and any desired options like:

xl1 <command> [options]

To see a list of all available commands and options run

xl1 --help

Installation

Prerequisites

This CLI tool is built with Node.js, which is required to run the application. Node.js provides the runtime environment for executing JavaScript outside the browser allowing this CLI to function as a standalone executable on your system.

Install Node.js

To get started, install the latest Long-Term Support (LTS) version of Node.js, which includes Node Package Manager (npm) from the official site. After installation, you can verify Node.js is working by running the following in your terminal:

node -v
npm -v

Once Node.js is installed, you can install the XL1 CLI tool globally using npm or yarn.

Install the XL1 CLI

Once Node.js is installed, you can install the XL1 CLI tool globally using npm:

npm install -g @xyo-network/xl1-cli

This makes the command xl1 available anywhere in your terminal.

You can run the following command to verify the XL1 CLI was installed correctly

xl1 --help

Usage

After installing, you can run xl1 from any terminal:

xl1

Runs a full XL1 Node, including both the API and Producer components. This is the default behavior when no subcommand is specified.

xl1 api

Starts only the API Node, which handles external requests and exposes HTTP endpoints.

xl1 producer

Starts only the Producer Node, which is responsible for block production.

Use xl1 --help for available options and configuration flags.

Configuration

You can configure xl1 using CLI flags, environment variables, or a configuration file. CLI flags override environment variables, which override config file values.

CLI flags

xl1 --log.logLevel=info start api

Environment variables

XL1_* variables map to the config shape with double-underscore nesting (XL1_CONNECTIONS__LOCAL_STORE__ROOT, etc.; see .env.example).

export XL1_LOG__LOG_LEVEL=info
export XL1_ACTORS__0__NAME=api
export XL1_ACTORS__0__PORT=8080
xl1 start api

Config file

Create a xyo.config.json (cosmiconfig also accepts xyo.yaml, xyo.toml, xyo.js, etc.) with an xl1 section. Omit -c to search the current directory, or pass -c <path> explicitly.

{
  "xl1": {
    "connections": {
      "local-store": { "type": "lmdb", "root": ".store" }
    },
    "actors": [
      { "name": "api", "host": "localhost", "port": 8080 },
      { "name": "producer" },
      { "name": "finalizer" }
    ]
  }
}

Runnable xyo.json-style configs for XL1 3.0 declare every backing store, RPC endpoint, EVM RPC, and S3 bucket under connections. The pre-3.0 storage, remote, transports, and evm.jsonRpc / evm.infura fields are no longer supported. A remote API is reached with a default-rpc connection of type: "rpc", not the old remote.rpc.

Deployment guide

xl1 actors can be assembled into many physical layouts: one process running everything, one process per actor, or grouped processes sharing one config.

Actors

| Actor | Role | |---|---| | api | JSON-RPC gateway. Serves block / balance / transaction queries and accepts transfers. | | producer | Block producer. Drives heartbeat blocks and packages mempool transactions. | | finalizer | Selects a chain head from competing producer candidates and writes finalizations. Optionally publishes finalized blocks + the chain-state head to S3 (publishOnFinalize). | | indexer | Publishes the chain index (step-summary families) to the S3 index bucket for HTTP/CDN reads. | | mempool | Holds pending transactions until the producer pulls them. | | bridge | EVM bridge — relays deposits / withdrawals between XL1 and an EVM chain. | | rewardRedemption | Claims block rewards for the producer's account. |

xl1 start <actor> [actor ...] runs any subset in one process. With no positional args it defaults to api producer finalizer.

Dimensions

  1. Process layout — mono (everything in one process), split (one actor per process), or grouped (custom).
  2. Storage backingmemory (single-process only, ephemeral), lmdb (single-host, persistent, multi-process safe via shared root), or mongodb (multi-host, persistent, multi-process safe).
  3. Provider tiersimple (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.
  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.

Launch patterns

Copy any example from Development examples or Per-actor production deployment into xyo.json, then:

# Single process
xl1 -c ./xyo.json start api producer finalizer mempool

# Multi-process (one OS process per actor, same config file)
xl1 -c ./xyo.json start api
xl1 -c ./xyo.json start producer
xl1 -c ./xyo.json start finalizer
xl1 -c ./xyo.json start mempool

# One config file per process
xl1 -c ./xyo.api.json start api
xl1 -c ./xyo.producer.json start producer
# Inspect resolved config (secrets redacted)
xl1 --dump-config -c ./xyo.json start api producer finalizer

# Inspect provider wiring without starting actors
xl1 --dump-providers -c ./xyo.json start api producer finalizer

# Non-interactive dev boot (skip insecure-mnemonic prompt)
xl1 --skip-insecure-confirm -c ./xyo.json start

Connections model

Named connections describe where providers read/write state:

| Connection | Type | Purpose | |------------|------|---------| | local-store | lmdb | On-disk LMDB chain authority store | | chain-mongo | mongo | MongoDB chain authority store | | dlq-store | lmdb / mongo | Dead-letter queue backing (dev: aliases to chain store when omitted) | | mempool-store | lmdb / mongo | Mempool backing (dev: aliases to chain store when omitted) | | default-rpc | rpc | HTTP JSON-RPC to another XL1 API | | default-evm-rpc | evm-rpc | EVM JSON-RPC for staking/contracts | | s3-finalized / s3-chain-state / s3-index | s3 | Object storage publish targets | | rest-finalized / rest-chain-state / rest-index | rest | CDN / HTTP read URLs for published static files | | memory | memory | In-process stubs (injected at boot) |

providerBindings map each provider moniker to a connection. When omitted, bindings are synthesized per connection name (chain monikers → local-store / chain-mongo, DLQ → dlq-store or dev-aliased chain store, mempool → mempool-store or dev-aliased chain store, viewers → default-rpc when present, EVM monikers → default-evm-rpc when declared, otherwise memory fallback at plan time).

Set the root option strictDependencies: true to disable implicit in-memory provider creation. In strict mode, every memory-backed dependency must have an explicit providerBindings.<Moniker>.connection pointing to a declared memory connection. An unbound required dependency fails configuration, while an unbound optional dependency is omitted. The option defaults to false; config-less runs retain the in-memory development preset and implicit memory fallback.

Index summaries use two monikers (not one provider per family):

| Moniker | Typical connection | Role | |---|---|---| | IndexViewer | rest-index (CDN) or memory (local dev) | Read step-summary families: balances, blocks, schemas, transfers | | IndexPublishRunner | s3-index | Publish those families to the index bucket |

Finalized EVM contract events reuse the index bucket under an address-scoped evm/events/ layout:

| Moniker | Typical connection | Role | |---|---|---| | EvmEventIndexPublishRunner | s3-index | Read all logs for chain.id through Ethereum's finalized head and publish resumable range frames | | StakeEventsViewer | rest-index | Decode the published raw logs with the staking ABI; no historical eth_getLogs calls |

The EVM index is optional. Bind EvmChainViewer → default-evm-rpc and EvmEventIndexPublishRunner → s3-index on the indexer to enable it. Bind StakeEventsViewer → rest-index on reward/staking consumers. Without the REST binding, EvmStakeEventsViewer remains the direct-RPC fallback.

Local dev omits IndexViewerAccountBalanceViewer computes summaries on demand from BlockViewer. Bind IndexViewermemory (SimpleIndexViewer) when you want in-process summary memoization without S3.

S3 write + REST read: The finalizer writes finalized blocks and the chain-state head to S3 (BlockPublishRunner, ChainStatePublishRunner). The indexer reads the published head and blocks over REST (ChainStateViewer, BlockViewer) and writes the index bucket via IndexPublishRunners3-index. Because BlockViewer must stay on the authority store for finalization, use split per-actor configs (Finalizer + Indexer) or override providerBindings explicitly as in S3 write + REST read.

Development examples

In-memory dev

Single process, ephemeral in-memory chain (fastest local dev). Omits connections — the boot preset injects an in-memory store.

{
  "xl1": {
    "actors": [
      {
        "name": "api",
        "host": "localhost",
        "port": 8080
      },
      {
        "name": "producer",
        "blockProductionCheckInterval": 100,
        "heartbeatInterval": 100
      },
      {
        "name": "finalizer",
        "finalizationCheckInterval": 50,
        "minCandidates": 1
      }
    ]
  }
}

LMDB local

Single process with on-disk LMDB at .store.

{
  "xl1": {
    "connections": {
      "local-store": {
        "type": "lmdb",
        "root": ".store"
      }
    },
    "actors": [
      {
        "name": "api",
        "host": "localhost",
        "port": 8080
      },
      {
        "name": "producer",
        "heartbeatInterval": 1000
      },
      {
        "name": "finalizer"
      },
      {
        "name": "mempool"
      }
    ]
  }
}

LMDB + EVM JSON-RPC

LMDB plus contract-backed chain via default-evm-rpc (local Hardhat/Ganache).

{
  "xl1": {
    "connections": {
      "local-store": {
        "type": "lmdb",
        "root": ".store"
      },
      "default-evm-rpc": {
        "type": "evm-rpc",
        "url": "http://127.0.0.1:8545",
        "chainId": "0xaa36a7"
      }
    },
    "chain": {
      "id": "dd381fbb392c85160d8b0453e446757b12384046"
    },
    "actors": [
      {
        "name": "api",
        "host": "localhost",
        "port": 8080
      },
      {
        "name": "producer",
        "heartbeatInterval": 1000
      },
      {
        "name": "finalizer"
      },
      {
        "name": "mempool"
      }
    ]
  }
}

LMDB + EVM Infura

LMDB plus Sepolia EVM RPC URL.

{
  "xl1": {
    "connections": {
      "local-store": {
        "type": "lmdb",
        "root": ".store"
      },
      "default-evm-rpc": {
        "type": "evm-rpc",
        "url": "https://sepolia.infura.io/v3/YOUR_INFURA_PROJECT_ID",
        "chainId": "0xaa36a7"
      }
    },
    "chain": {
      "id": "dd381fbb392c85160d8b0453e446757b12384046"
    },
    "actors": [
      {
        "name": "api",
        "host": "localhost",
        "port": 8080
      },
      {
        "name": "producer",
        "heartbeatInterval": 1000
      },
      {
        "name": "finalizer"
      }
    ]
  }
}

Explicit connections

Explicit connections + providerBindings overrides.

{
  "xl1": {
    "connections": {
      "local-store": {
        "type": "lmdb",
        "root": ".store"
      },
      "default-evm-rpc": {
        "type": "evm-rpc",
        "url": "http://127.0.0.1:8545",
        "chainId": "0xaa36a7"
      }
    },
    "providerBindings": {
      "BlockViewer": {
        "connection": "local-store"
      },
      "MempoolRunner": {
        "connection": "local-store"
      },
      "ChainContractViewer": {
        "connection": "default-evm-rpc"
      },
      "StakeViewer": {
        "connection": "default-evm-rpc"
      }
    },
    "chain": {
      "id": "dd381fbb392c85160d8b0453e446757b12384046"
    },
    "actors": [
      {
        "name": "api",
        "host": "localhost",
        "port": 8080
      },
      {
        "name": "producer",
        "heartbeatInterval": 1000
      },
      {
        "name": "finalizer"
      }
    ]
  }
}

Federated API

Read-only API proxying a remote chain RPC via explicit providerBindings.

{
  "xl1": {
    "connections": {
      "default-rpc": {
        "type": "rpc",
        "protocol": "https",
        "url": "https://chain.example.com/rpc"
      }
    },
    "providerBindings": {
      "AccountBalanceViewer": { "connection": "default-rpc" },
      "BlockViewer": { "connection": "default-rpc" },
      "FinalizationViewer": { "connection": "default-rpc" },
      "MempoolRunner": { "connection": "default-rpc" },
      "MempoolViewer": { "connection": "default-rpc" },
      "TransactionViewer": { "connection": "default-rpc" },
      "XyoRunner": { "connection": "default-rpc" },
      "XyoViewer": { "connection": "default-rpc" }
    },
    "actors": [
      {
        "name": "api",
        "host": "0.0.0.0",
        "port": 8080
      }
    ]
  }
}

MongoDB

MongoDB-backed archivist via chain-mongo.

{
  "xl1": {
    "connections": {
      "chain-mongo": {
        "type": "mongo",
        "connectionString": "mongodb://root:example@localhost:27017/?retryWrites=true&w=majority",
        "database": "chain",
        "domain": "localhost",
        "username": "root",
        "password": "example"
      }
    },
    "actors": [
      {
        "name": "api",
        "host": "localhost",
        "port": 8080
      },
      {
        "name": "producer",
        "heartbeatInterval": 1000
      },
      {
        "name": "finalizer"
      },
      {
        "name": "mempool"
      }
    ]
  }
}

Multi-process LMDB

Shared LMDB; run one process per xl1 start <actor>.

{
  "xl1": {
    "connections": {
      "local-store": {
        "type": "lmdb",
        "root": ".store"
      }
    },
    "actors": [
      {
        "name": "api",
        "host": "localhost",
        "port": 8080
      },
      {
        "name": "producer",
        "heartbeatInterval": 1000
      },
      {
        "name": "finalizer"
      },
      {
        "name": "mempool",
        "host": "localhost",
        "port": 8081
      }
    ]
  }
}

Mempool remote API

Standalone mempool over JsonRpc to a remote API.

{
  "xl1": {
    "connections": {
      "default-rpc": {
        "type": "rpc",
        "protocol": "http",
        "url": "http://localhost:8080/rpc"
      }
    },
    "providerBindings": {
      "MempoolRunner": {
        "connection": "default-rpc"
      },
      "MempoolViewer": {
        "connection": "default-rpc"
      },
      "BlockViewer": {
        "connection": "default-rpc"
      },
      "WindowedBlockViewer": {
        "connection": "default-rpc"
      },
      "TransactionValidationViewer": {
        "connection": "default-rpc"
      }
    },
    "actors": [
      {
        "name": "mempool",
        "host": "localhost",
        "port": 8081
      }
    ]
  }
}

Mixed local + remote

Local LMDB runners + JsonRpc viewers.

{
  "xl1": {
    "connections": {
      "local-store": {
        "type": "lmdb",
        "root": ".store"
      },
      "default-rpc": {
        "type": "rpc",
        "protocol": "http",
        "url": "http://localhost:8080/rpc"
      }
    },
    "providerBindings": {
      "MempoolRunner": {
        "connection": "local-store"
      },
      "BlockViewer": {
        "connection": "default-rpc"
      },
      "MempoolViewer": {
        "connection": "default-rpc"
      },
      "FinalizationViewer": {
        "connection": "default-rpc"
      },
      "TransactionViewer": {
        "connection": "default-rpc"
      },
      "AccountBalanceViewer": {
        "connection": "default-rpc"
      }
    },
    "actors": [
      {
        "name": "api",
        "host": "localhost",
        "port": 8080
      },
      {
        "name": "mempool"
      }
    ]
  }
}

Finalizer + indexer S3

Mongo store plus S3 publish connections for finalizer and indexer in one process.

{
  "xl1": {
    "connections": {
      "chain-mongo": {
        "type": "mongo",
        "connectionString": "mongodb://user:[email protected]:27017",
        "database": "xl1",
        "domain": "internal"
      },
      "default-evm-rpc": {
        "type": "evm-rpc",
        "url": "https://sepolia.infura.io/v3/YOUR_INFURA_PROJECT_ID",
        "chainId": "0xaa36a7"
      },
      "s3-finalized": {
        "type": "s3",
        "accountId": "YOUR_R2_ACCOUNT_ID",
        "accessKeyId": "YOUR_ACCESS_KEY_ID",
        "secretAccessKey": "YOUR_SECRET_ACCESS_KEY",
        "prefix": "xl1/",
        "bucket": "xl1-chain",
        "readUrl": "https://chain.xl1.example"
      },
      "s3-chain-state": {
        "type": "s3",
        "accountId": "YOUR_R2_ACCOUNT_ID",
        "accessKeyId": "YOUR_ACCESS_KEY_ID",
        "secretAccessKey": "YOUR_SECRET_ACCESS_KEY",
        "prefix": "xl1/",
        "bucket": "xl1-chain-state",
        "readUrl": "https://chain-state.xl1.example"
      },
      "s3-index": {
        "type": "s3",
        "accountId": "YOUR_R2_ACCOUNT_ID",
        "accessKeyId": "YOUR_ACCESS_KEY_ID",
        "secretAccessKey": "YOUR_SECRET_ACCESS_KEY",
        "prefix": "xl1/",
        "bucket": "xl1-index",
        "readUrl": "https://index.xl1.example"
      }
    },
    "providerBindings": {
      "BlockViewer": { "connection": "chain-mongo" },
      "BlockPublishRunner": { "connection": "s3-finalized" },
      "ChainContractViewer": { "connection": "default-evm-rpc" },
      "ChainStatePublishRunner": { "connection": "s3-chain-state" },
      "ChainStateViewer": { "connection": "s3-chain-state" },
      "EvmChainViewer": { "connection": "default-evm-rpc" },
      "EvmEventIndexPublishRunner": { "connection": "s3-index" },
      "FinalizationRunner": { "connection": "chain-mongo" },
      "FinalizationViewer": { "connection": "chain-mongo" },
      "IndexPublishRunner": { "connection": "s3-index" },
      "MempoolViewer": { "connection": "chain-mongo" }
    },
    "actors": [
      {
        "name": "producer",
        "heartbeatInterval": 1000
      },
      {
        "name": "finalizer",
        "publishOnFinalize": true,
        "publishContentEncoding": "br",
        "publishProgressInterval": 100
      },
      {
        "name": "indexer",
        "publishSyncInterval": 5000,
        "publishContentEncoding": "br",
        "publishProgressInterval": 100
      }
    ]
  }
}

S3 write + REST read

Indexer: s3-index publish + REST CDN viewers.

{
  "xl1": {
    "connections": {
      "s3-index": {
        "type": "s3",
        "accountId": "YOUR_R2_ACCOUNT_ID",
        "accessKeyId": "YOUR_ACCESS_KEY_ID",
        "secretAccessKey": "YOUR_SECRET_ACCESS_KEY",
        "prefix": "xl1/",
        "bucket": "xl1-index",
        "readUrl": "https://index.xl1.example"
      },
      "default-evm-rpc": {
        "type": "evm-rpc",
        "url": "https://sepolia.infura.io/v3/YOUR_INFURA_PROJECT_ID",
        "chainId": "0xaa36a7"
      },
      "rest-finalized": {
        "type": "rest",
        "baseUrl": "https://chain.xl1.example"
      },
      "rest-chain-state": {
        "type": "rest",
        "baseUrl": "https://chain-state.xl1.example"
      },
      "rest-index": {
        "type": "rest",
        "baseUrl": "https://index.xl1.example"
      }
    },
    "providerBindings": {
      "BlockViewer": {
        "connection": "rest-finalized"
      },
      "ChainStateViewer": {
        "connection": "rest-chain-state"
      },
      "EvmChainViewer": {
        "connection": "default-evm-rpc"
      },
      "EvmEventIndexPublishRunner": {
        "connection": "s3-index"
      },
      "IndexViewer": {
        "connection": "rest-index"
      },
      "IndexPublishRunner": {
        "connection": "s3-index"
      }
    },
    "actors": [
      {
        "name": "indexer",
        "publishSyncInterval": 5000,
        "publishContentEncoding": "br",
        "publishProgressInterval": 100
      }
    ]
  }
}

Per-actor production deployment

One config per actor process — the shape a real multi-host deployment runs, where each actor is its own container/pod (see .k8s/ and the deploy-prod-* workflows). The read layer is served from S3-compatible object storage (e.g. Cloudflare R2) behind a CDN, so only the finalizer and indexer write to S3; everyone else reads the published blocks / chain-state / index over REST and keeps MongoDB only for the authority/write path.

| Actor | Backing connections | Writes to S3 | Reads | |-------|---------------------|--------------|-------| | api | chain-mongo (mempool + tx submission) | — | blocks / state / index over REST | | producer | chain-mongo authority + default-evm-rpc + producer-wallet | — | authority store + EVM staking contract | | finalizer | chain-mongo authority | s3-finalized (blocks), s3-chain-state (head) | authority store | | indexer | default-evm-rpc (when event indexing is enabled) | s3-index | finalized blocks + chain-state over REST; finalized contract logs from EVM | | mempool | chain-mongo | — | authority store | | bridge | chain-mongo + default-evm-rpc + bridge-wallet + Redis | — | authority store + remote EVM chain | | rewardRedemption | chain-mongo + default-evm-rpc + rewardRedemption-wallet | — | authority store + EVM staking contract |

  • One writer per bucket. The finalizer owns s3-finalized + s3-chain-state; the indexer owns s3-index. No other actor writes object storage.
  • BlockViewer is not auto-bound to REST. When a process declares a local store, BlockViewer defaults to it, so the API and indexer configs bind BlockViewer → rest-finalized explicitly to read finalized blocks from the CDN. ChainStateViewer / IndexViewer bind to their REST connections automatically when those connections are declared (the explicit bindings here are for clarity).
  • Staking. chain.id is the staking contract address on the backing EVM. The indexer needs default-evm-rpc only when EvmEventIndexPublishRunner is enabled. Staking/reward consumers can bind StakeEventsViewer → rest-index for indexed finalized history while retaining EVM bindings for mutating calls and point-in-time contract state.
  • Wallets. Wallet connections (<actor>-wallet) and the XyoSigner binding are auto-synthesized for signing actors (producer, bridge, rewardRedemption); they are shown explicitly here for clarity. bridge and rewardRedemption cannot be co-located in one process (they share XyoSigner).
  • Secrets. YOUR_* placeholders (S3 credentials, Mongo credentials, Infura project id) and wallet mnemonics should be supplied via XL1_* env vars / your secret store in production — do not commit real credentials.

API (per-actor)

{
  "xl1": {
    "connections": {
      "chain-mongo": {
        "type": "mongo",
        "connectionString": "mongodb://YOUR_MONGO_USER:[email protected]:27017/?retryWrites=true&w=majority",
        "database": "xl1",
        "domain": "internal"
      },
      "rest-finalized": {
        "type": "rest",
        "baseUrl": "https://chain.xl1.example"
      },
      "rest-chain-state": {
        "type": "rest",
        "baseUrl": "https://chain-state.xl1.example"
      },
      "rest-index": {
        "type": "rest",
        "baseUrl": "https://index.xl1.example"
      }
    },
    "providerBindings": {
      "AccountBalanceViewer": { "connection": "chain-mongo" },
      "BlockRunner": { "connection": "chain-mongo" },
      "BlockValidationViewer": { "connection": "chain-mongo" },
      "BlockViewer": { "connection": "rest-finalized" },
      "ChainStateViewer": { "connection": "rest-chain-state" },
      "DeadLetterQueueRunner": { "connection": "chain-mongo" },
      "DeadLetterQueueViewer": { "connection": "chain-mongo" },
      "FinalizationRunner": { "connection": "chain-mongo" },
      "FinalizationViewer": { "connection": "chain-mongo" },
      "IndexViewer": { "connection": "rest-index" },
      "MempoolRunner": { "connection": "chain-mongo" },
      "MempoolViewer": { "connection": "chain-mongo" },
      "TransactionValidationViewer": { "connection": "chain-mongo" },
      "TransactionViewer": { "connection": "chain-mongo" },
      "WindowedBlockViewer": { "connection": "chain-mongo" },
      "XyoConnection": { "connection": "chain-mongo" },
      "XyoGateway": { "connection": "chain-mongo" },
      "XyoGatewayRunner": { "connection": "chain-mongo" },
      "XyoRunner": { "connection": "chain-mongo" },
      "XyoViewer": { "connection": "chain-mongo" }
    },
    "actors": [
      {
        "name": "api",
        "host": "0.0.0.0",
        "port": 8080
      }
    ]
  }
}

Producer (per-actor)

{
  "xl1": {
    "connections": {
      "chain-mongo": {
        "type": "mongo",
        "connectionString": "mongodb://YOUR_MONGO_USER:[email protected]:27017/?retryWrites=true&w=majority",
        "database": "xl1",
        "domain": "internal"
      },
      "default-evm-rpc": {
        "type": "evm-rpc",
        "url": "https://sepolia.infura.io/v3/YOUR_INFURA_PROJECT_ID",
        "chainId": "0xaa36a7"
      },
      "producer-wallet": {
        "type": "wallet",
        "accountPath": "0"
      }
    },
    "chain": {
      "id": "dd381fbb392c85160d8b0453e446757b12384046"
    },
    "providerBindings": {
      "AccountBalanceViewer": { "connection": "chain-mongo" },
      "BlockRunner": { "connection": "chain-mongo" },
      "BlockValidationViewer": { "connection": "chain-mongo" },
      "BlockViewer": { "connection": "chain-mongo" },
      "ChainContractViewer": { "connection": "default-evm-rpc" },
      "DeadLetterQueueRunner": { "connection": "chain-mongo" },
      "DeadLetterQueueViewer": { "connection": "chain-mongo" },
      "FinalizationRunner": { "connection": "chain-mongo" },
      "FinalizationViewer": { "connection": "chain-mongo" },
      "MempoolRunner": { "connection": "chain-mongo" },
      "MempoolViewer": { "connection": "chain-mongo" },
      "StakeEventsViewer": { "connection": "default-evm-rpc" },
      "StakeTotalsViewer": { "connection": "default-evm-rpc" },
      "StakeViewer": { "connection": "default-evm-rpc" },
      "TimeSyncViewer": { "connection": "default-evm-rpc" },
      "TransactionValidationViewer": { "connection": "chain-mongo" },
      "TransactionViewer": { "connection": "chain-mongo" },
      "WindowedBlockViewer": { "connection": "chain-mongo" },
      "XyoConnection": { "connection": "chain-mongo" },
      "XyoGateway": { "connection": "chain-mongo" },
      "XyoGatewayRunner": { "connection": "chain-mongo" },
      "XyoRunner": { "connection": "chain-mongo" },
      "XyoViewer": { "connection": "chain-mongo" }
    },
    "actors": [
      {
        "name": "producer",
        "heartbeatInterval": 1000
      }
    ]
  }
}

Finalizer (per-actor)

{
  "xl1": {
    "connections": {
      "chain-mongo": {
        "type": "mongo",
        "connectionString": "mongodb://YOUR_MONGO_USER:[email protected]:27017/?retryWrites=true&w=majority",
        "database": "xl1",
        "domain": "internal"
      },
      "default-evm-rpc": {
        "type": "evm-rpc",
        "url": "https://sepolia.infura.io/v3/YOUR_INFURA_PROJECT_ID",
        "chainId": "0xaa36a7"
      },
      "s3-finalized": {
        "type": "s3",
        "accountId": "YOUR_R2_ACCOUNT_ID",
        "accessKeyId": "YOUR_ACCESS_KEY_ID",
        "secretAccessKey": "YOUR_SECRET_ACCESS_KEY",
        "prefix": "xl1/",
        "bucket": "xl1-chain",
        "readUrl": "https://chain.xl1.example"
      },
      "s3-chain-state": {
        "type": "s3",
        "accountId": "YOUR_R2_ACCOUNT_ID",
        "accessKeyId": "YOUR_ACCESS_KEY_ID",
        "secretAccessKey": "YOUR_SECRET_ACCESS_KEY",
        "prefix": "xl1/",
        "bucket": "xl1-chain-state",
        "readUrl": "https://chain-state.xl1.example"
      }
    },
    "chain": {
      "id": "dd381fbb392c85160d8b0453e446757b12384046"
    },
    "providerBindings": {
      "AccountBalanceViewer": { "connection": "chain-mongo" },
      "BlockPublishRunner": { "connection": "s3-finalized" },
      "BlockRunner": { "connection": "chain-mongo" },
      "BlockValidationViewer": { "connection": "chain-mongo" },
      "BlockViewer": { "connection": "chain-mongo" },
      "ChainContractViewer": { "connection": "default-evm-rpc" },
      "ChainStatePublishRunner": { "connection": "s3-chain-state" },
      "ChainStateViewer": { "connection": "s3-chain-state" },
      "DeadLetterQueueRunner": { "connection": "chain-mongo" },
      "DeadLetterQueueViewer": { "connection": "chain-mongo" },
      "FinalizationRunner": { "connection": "chain-mongo" },
      "FinalizationViewer": { "connection": "chain-mongo" },
      "MempoolRunner": { "connection": "chain-mongo" },
      "MempoolViewer": { "connection": "chain-mongo" },
      "TransactionValidationViewer": { "connection": "chain-mongo" },
      "TransactionViewer": { "connection": "chain-mongo" },
      "WindowedBlockViewer": { "connection": "chain-mongo" },
      "XyoConnection": { "connection": "chain-mongo" },
      "XyoGateway": { "connection": "chain-mongo" },
      "XyoGatewayRunner": { "connection": "chain-mongo" },
      "XyoRunner": { "connection": "chain-mongo" },
      "XyoViewer": { "connection": "chain-mongo" }
    },
    "actors": [
      {
        "name": "finalizer",
        "finalizationCheckInterval": 500,
        "publishOnFinalize": true,
        "publishContentEncoding": "br",
        "publishProgressInterval": 100
      }
    ]
  }
}

Indexer (per-actor)

{
  "xl1": {
    "connections": {
      "rest-finalized": {
        "type": "rest",
        "baseUrl": "https://chain.xl1.example"
      },
      "rest-chain-state": {
        "type": "rest",
        "baseUrl": "https://chain-state.xl1.example"
      },
      "rest-index": {
        "type": "rest",
        "baseUrl": "https://index.xl1.example"
      },
      "default-evm-rpc": {
        "type": "evm-rpc",
        "url": "https://sepolia.infura.io/v3/YOUR_INFURA_PROJECT_ID",
        "chainId": "0xaa36a7"
      },
      "s3-index": {
        "type": "s3",
        "accountId": "YOUR_R2_ACCOUNT_ID",
        "accessKeyId": "YOUR_ACCESS_KEY_ID",
        "secretAccessKey": "YOUR_SECRET_ACCESS_KEY",
        "prefix": "xl1/",
        "bucket": "xl1-index",
        "readUrl": "https://index.xl1.example"
      }
    },
    "providerBindings": {
      "BlockViewer": {
        "connection": "rest-finalized"
      },
      "ChainStateViewer": {
        "connection": "rest-chain-state"
      },
      "EvmChainViewer": {
        "connection": "default-evm-rpc"
      },
      "EvmEventIndexPublishRunner": {
        "connection": "s3-index"
      },
      "IndexViewer": {
        "connection": "rest-index"
      },
      "IndexPublishRunner": {
        "connection": "s3-index"
      }
    },
    "actors": [
      {
        "name": "indexer",
        "publishSyncInterval": 5000,
        "publishContentEncoding": "br",
        "publishProgressInterval": 100
      }
    ]
  }
}

Mempool (per-actor)

{
  "xl1": {
    "connections": {
      "chain-mongo": {
        "type": "mongo",
        "connectionString": "mongodb://YOUR_MONGO_USER:[email protected]:27017/?retryWrites=true&w=majority",
        "database": "xl1",
        "domain": "internal"
      }
    },
    "providerBindings": {
      "AccountBalanceViewer": { "connection": "chain-mongo" },
      "BlockRunner": { "connection": "chain-mongo" },
      "BlockValidationViewer": { "connection": "chain-mongo" },
      "BlockViewer": { "connection": "chain-mongo" },
      "DeadLetterQueueRunner": { "connection": "chain-mongo" },
      "DeadLetterQueueViewer": { "connection": "chain-mongo" },
      "FinalizationRunner": { "connection": "chain-mongo" },
      "FinalizationViewer": { "connection": "chain-mongo" },
      "MempoolRunner": { "connection": "chain-mongo" },
      "MempoolViewer": { "connection": "chain-mongo" },
      "TransactionValidationViewer": { "connection": "chain-mongo" },
      "TransactionViewer": { "connection": "chain-mongo" },
      "WindowedBlockViewer": { "connection": "chain-mongo" },
      "XyoConnection": { "connection": "chain-mongo" },
      "XyoGateway": { "connection": "chain-mongo" },
      "XyoGatewayRunner": { "connection": "chain-mongo" },
      "XyoRunner": { "connection": "chain-mongo" },
      "XyoViewer": { "connection": "chain-mongo" }
    },
    "actors": [
      {
        "name": "mempool",
        "host": "0.0.0.0",
        "port": 8081
      }
    ]
  }
}

Bridge (per-actor)

{
  "xl1": {
    "connections": {
      "chain-mongo": {
        "type": "mongo",
        "connectionString": "mongodb://YOUR_MONGO_USER:[email protected]:27017/?retryWrites=true&w=majority",
        "database": "xl1",
        "domain": "internal"
      },
      "default-evm-rpc": {
        "type": "evm-rpc",
        "url": "https://sepolia.infura.io/v3/YOUR_INFURA_PROJECT_ID",
        "chainId": "0xaa36a7"
      },
      "rest-datalake": {
        "type": "rest",
        "baseUrl": "https://api.archivist.xyo.network/dataLake"
      }
    },
    "chain": {
      "id": "dd381fbb392c85160d8b0453e446757b12384046"
    },
    "providerBindings": {
      "AccountBalanceViewer": { "connection": "chain-mongo" },
      "BlockRunner": { "connection": "chain-mongo" },
      "BlockValidationViewer": { "connection": "chain-mongo" },
      "BlockViewer": { "connection": "chain-mongo" },
      "ChainContractViewer": { "connection": "default-evm-rpc" },
      "DataLakeRunner": { "connection": "rest-datalake" },
      "DataLakeViewer": { "connection": "rest-datalake" },
      "DeadLetterQueueRunner": { "connection": "chain-mongo" },
      "DeadLetterQueueViewer": { "connection": "chain-mongo" },
      "FinalizationRunner": { "connection": "chain-mongo" },
      "FinalizationViewer": { "connection": "chain-mongo" },
      "MempoolRunner": { "connection": "chain-mongo" },
      "MempoolViewer": { "connection": "chain-mongo" },
      "StakeEventsViewer": { "connection": "default-evm-rpc" },
      "StakeTotalsViewer": { "connection": "default-evm-rpc" },
      "StakeViewer": { "connection": "default-evm-rpc" },
      "TimeSyncViewer": { "connection": "default-evm-rpc" },
      "TransactionValidationViewer": { "connection": "chain-mongo" },
      "TransactionViewer": { "connection": "chain-mongo" },
      "WindowedBlockViewer": { "connection": "chain-mongo" },
      "XyoConnection": { "connection": "chain-mongo" },
      "XyoGateway": { "connection": "chain-mongo" },
      "XyoGatewayRunner": { "connection": "chain-mongo" },
      "XyoRunner": { "connection": "chain-mongo" },
      "XyoViewer": { "connection": "chain-mongo" }
    },
    "actors": [
      {
        "name": "bridge",
        "accountPath": "0",
        "redisHost": "redis.internal",
        "redisPort": 6379,
        "scannerIntervalMs": 30000
      }
    ]
  }
}

Reward redemption (per-actor)

{
  "xl1": {
    "connections": {
      "chain-mongo": {
        "type": "mongo",
        "connectionString": "mongodb://YOUR_MONGO_USER:[email protected]:27017/?retryWrites=true&w=majority",
        "database": "xl1",
        "domain": "internal"
      },
      "default-evm-rpc": {
        "type": "evm-rpc",
        "url": "https://sepolia.infura.io/v3/YOUR_INFURA_PROJECT_ID",
        "chainId": "0xaa36a7"
      }
    },
    "chain": {
      "id": "dd381fbb392c85160d8b0453e446757b12384046"
    },
    "providerBindings": {
      "AccountBalanceViewer": { "connection": "chain-mongo" },
      "BlockRunner": { "connection": "chain-mongo" },
      "BlockValidationViewer": { "connection": "chain-mongo" },
      "BlockViewer": { "connection": "chain-mongo" },
      "ChainContractViewer": { "connection": "default-evm-rpc" },
      "DeadLetterQueueRunner": { "connection": "chain-mongo" },
      "DeadLetterQueueViewer": { "connection": "chain-mongo" },
      "FinalizationRunner": { "connection": "chain-mongo" },
      "FinalizationViewer": { "connection": "chain-mongo" },
      "MempoolRunner": { "connection": "chain-mongo" },
      "MempoolViewer": { "connection": "chain-mongo" },
      "StakeEventsViewer": { "connection": "default-evm-rpc" },
      "StakeTotalsViewer": { "connection": "default-evm-rpc" },
      "StakeViewer": { "connection": "default-evm-rpc" },
      "TimeSyncViewer": { "connection": "default-evm-rpc" },
      "TransactionValidationViewer": { "connection": "chain-mongo" },
      "TransactionViewer": { "connection": "chain-mongo" },
      "WindowedBlockViewer": { "connection": "chain-mongo" },
      "XyoConnection": { "connection": "chain-mongo" },
      "XyoGateway": { "connection": "chain-mongo" },
      "XyoGatewayRunner": { "connection": "chain-mongo" },
      "XyoRunner": { "connection": "chain-mongo" },
      "XyoViewer": { "connection": "chain-mongo" }
    },
    "actors": [
      {
        "name": "rewardRedemption",
        "accountPath": "0",
        "host": "0.0.0.0",
        "port": 8082
      }
    ]
  }
}

S3 object storage (finalized / chain-state / index publishing)

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 and the wallet / explorer clients — no RPC server in the read path. It layers on top of any backing; it does not replace it.

One writer per bucket:

| Bucket | Written by | Holds | |---|---|---| | finalized | finalizer (when publishOnFinalize is set) | finalized blocks, payloads, manifest.json | | chainState | finalizer (when publishOnFinalize is set) | the mutable head pointer (head.json) | | index | indexer | step-summary families plus optional address-scoped finalized EVM event frames and their manifests/head pointers |

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.

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.

The chainState bucket also carries the chain-contract manifest (minWithdrawalBlocks, rewardsContract, stakingTokenAddress), read from ChainContractViewer when the publisher starts — so a chain-state-publishing finalizer must bind ChainContractViewer to an evm-rpc connection (config compilation rejects the plan otherwise; an explicit memory binding opts into dev placeholder values instead).

xyo.json:

{
  "xl1": {
    "connections": {
      "chain-mongo": { "type": "mongo", "connectionString": "mongodb://user:[email protected]:27017", "database": "xl1", "domain": "internal" },
      "default-evm-rpc": { "type": "evm-rpc", "url": "https://sepolia.infura.io/v3/YOUR_INFURA_PROJECT_ID", "chainId": "0xaa36a7" },
      "s3-finalized": { "type": "s3", "accountId": "…", "accessKeyId": "…", "secretAccessKey": "…", "prefix": "", "bucket": "xl1-chain", "readUrl": "https://chain.xl1.example" },
      "s3-chain-state": { "type": "s3", "bucket": "xl1-chain-state", "readUrl": "https://chain-state.xl1.example" },
      "s3-index": { "type": "s3", "bucket": "xl1-index", "readUrl": "https://index.xl1.example" }
    },
    "providerBindings": {
      "BlockViewer": { "connection": "chain-mongo" },
      "BlockPublishRunner": { "connection": "s3-finalized" },
      "ChainContractViewer": { "connection": "default-evm-rpc" },
      "ChainStatePublishRunner": { "connection": "s3-chain-state" },
      "ChainStateViewer": { "connection": "s3-chain-state" },
      "EvmChainViewer": { "connection": "default-evm-rpc" },
      "EvmEventIndexPublishRunner": { "connection": "s3-index" },
      "FinalizationRunner": { "connection": "chain-mongo" },
      "FinalizationViewer": { "connection": "chain-mongo" },
      "IndexPublishRunner": { "connection": "s3-index" },
      "MempoolViewer": { "connection": "chain-mongo" }
    },
    "actors": [
      { "name": "producer", "heartbeatInterval": 1000 },
      { "name": "finalizer", "publishOnFinalize": true, "publishContentEncoding": "br" },
      { "name": "indexer", "publishSyncInterval": 5000, "publishContentEncoding": "br" }
    ]
  }
}

Per-bucket credentials override the shared defaults — e.g. give s3-chain-state its own accessKeyId / secretAccessKey on that connection entry.

Actor settings that drive publishing:

| Setting | Actor(s) | Default | Meaning | |---|---|---|---| | publishOnFinalize | finalizer | false | Push newly finalized blocks (head pointer last) to the finalized + chainState buckets immediately after each finalization pass. | | 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. | | publishContentEncoding | finalizer, indexer | unset | Wire/storage encoding for published files: br, gzip, or none. | | publishProgressInterval | finalizer, indexer | 100 | Publishes between progress log lines during a sync pass (runner default when unset). |

Environment-variable form__ nests, and each screaming-snake segment maps to camelCase (CHAIN_STATEchainState, READ_URLreadUrl):

export XL1_CONNECTIONS__S3_FINALIZED__TYPE=s3
export XL1_CONNECTIONS__S3_FINALIZED__ACCOUNT_ID=…
export XL1_CONNECTIONS__S3_FINALIZED__ACCESS_KEY_ID=…
export XL1_CONNECTIONS__S3_FINALIZED__SECRET_ACCESS_KEY=…
export XL1_CONNECTIONS__S3_FINALIZED__BUCKET=xl1-chain
export XL1_CONNECTIONS__S3_FINALIZED__READ_URL=https://chain.xl1.example
export XL1_CONNECTIONS__S3_CHAIN_STATE__TYPE=s3
export XL1_CONNECTIONS__S3_CHAIN_STATE__BUCKET=xl1-chain-state
export XL1_CONNECTIONS__S3_CHAIN_STATE__READ_URL=https://chain-state.xl1.example
export XL1_CONNECTIONS__S3_INDEX__TYPE=s3
export XL1_CONNECTIONS__S3_INDEX__BUCKET=xl1-index
export XL1_CONNECTIONS__S3_INDEX__READ_URL=https://index.xl1.example
# actor publish settings are array entries, e.g. if finalizer/indexer are actors 1 and 2:
export XL1_ACTORS__1__PUBLISH_ON_FINALIZE=true
export XL1_ACTORS__2__PUBLISH_SYNC_INTERVAL=5000

Naming reference (bucket roles vs frozen legacy names)

The naming convention across XL1 code and config: "S3" means the S3-compatible object-storage protocol (Cloudflare R2 is one implementation — "R2" only appears in Cloudflare-specific helpers and docs), and each bucket is identified by its role: finalized, chain-state, index (plus datalake where used). Casing follows context — camelCase in TypeScript identifiers and config record keys (chainState), kebab-case in connection names, env-var segments, and physical names (s3-chain-state).

| Bucket role | Connection name | Env prefix | Typical bucket (frozen) | Endpoint subdomain (frozen) | Wire schemas served (frozen) | |---|---|---|---|---|---| | finalized | s3-finalized | XL1_CONNECTIONS__S3_FINALIZED__* | xl1-chain | blocks.<domain> | network.xyo.s3.chain.manifest | | chain-state | s3-chain-state | XL1_CONNECTIONS__S3_CHAIN_STATE__* | xl1-chain-state | state.<domain> | network.xyo.s3.chain.contract | | index | s3-index | XL1_CONNECTIONS__S3_INDEX__* | xl1-index | indexes.<domain> | network.xyo.s3.index.manifest, network.xyo.s3.index.head |

The columns marked frozen are deployed infrastructure or wire format that predates the role naming — they intentionally do not match the role names and must not be "fixed":

  • The physical bucket xl1-chain serves the finalized role (it predates the role name; live buckets are a migration, not a rename).
  • The blocks. / state. / indexes. endpoint subdomains (derived by the SDK's restGatewayConfigFromEndpoint) are deployed DNS; the finalized role maps to the blocks. subdomain.
  • The static-layout document schemas are namespaced network.xyo.s3.* even though the documents are transport-neutral; renaming them would be a breaking wire change. New static-layout schemas should use a transport-neutral namespace instead.

Mnemonics & wallets

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.

{
  "xl1": {
    "mnemonic": "edit hill neutral usage share kite knee abandon slim open lottery abandon",
    "actors": [
      { "name": "producer", "accountPath": "0" },
      { "name": "finalizer", "accountPath": "1" }
    ]
  }
}

If you omit mnemonic entirely, the CLI falls back to a built-in dev mnemonic and pauses for an interactive RETURN before starting. Pass --skip-insecure-confirm to bypass the prompt in non-interactive deployments — but never deploy production traffic against the dev mnemonic.

Inspecting a configuration

Two CLI flags help you verify what the chain would do without actually starting it:

  • xl1 --dump-config start <actors> — prints the fully resolved config (file + env + CLI flags merged, defaults applied) to stdout, then exits. Secret-bearing fields are redacted by default; pass --with-secrets on a developer box to see raw values.
  • xl1 --dump-providers start <actors> — runs the locator construction up to the point where actor runners would start, then prints a per-actor tree of every registered provider (implementation class, scope, dependencies). Cross-actor duplicates are flagged with ⚠ also in: .... Use this to confirm that, e.g., your federated API actually picked the JsonRpc tier-3 viewers.
xl1 -c ./xyo.json --dump-providers start api producer

License

See the LICENSE file for license rights and limitations (LGPL-3.0-only).

Credits

Made with 🔥 and ❄️ by XYO Foundation