
Security News
Re-Enabled GitHub Actions Expose Thousands of Repositories to Mini Shai-Hulud
Two compromised GitHub Actions were re-enabled with malicious tags intact, exposing thousands of downstream repositories to Mini Shai-Hulud.
@xyo-network/xl1-cli
Advanced tools
XYO Layer One CLI
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
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.
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.
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
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.
You can configure xl1 using CLI flags, environment variables, or a configuration file. CLI flags override environment variables, which override config file values.
xl1 --log.logLevel=info start api
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
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.
xl1 actors can be assembled into many physical layouts: one process running everything, one process per actor, or grouped processes sharing one config.
| 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 step-summary and transaction-inclusion indexes 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.
memory (single-process only, ephemeral), lmdb (single-host, persistent, multi-process safe via shared root), or mongodb (multi-host, persistent, multi-process safe).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.xl1 start <subset> filters which actors it activates), one file per process, or pure environment variables.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
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.
Chain indexes use two monikers (not one provider per projection):
| Moniker | Typical connection | Role |
|---|---|---|
IndexViewer | rest-index (CDN) or memory (local dev) | Read step-summary families and transaction-inclusion records |
IndexPublishRunner | s3-index | Publish summary families and transaction inclusions 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.
Network stake rewards are a third durable index family:
| Moniker | Typical connection | Role |
|---|---|---|
NetworkStakeRewardsIndexPublishRunner | s3-index | Publish resumable reward-step frames and the content-addressed current snapshot after both source indexes advance |
NetworkStakeStepRewardsByPositionViewer | rest-index | Read reward steps and the current snapshot over REST without priming an in-process rewards cache |
The rewards publisher is optional and requires the finalized XL1 index, finalized EVM event index, StakeViewer → default-evm-rpc, and StakeEventsViewer → rest-index. The indexer advances the XL1 and EVM source indexes concurrently, then publishes rewards against their returned durable heads. If either durable source head is unavailable, rewards wait for the next pass. API processes bind NetworkStakeStepRewardsByPositionViewer → rest-index to serve the generated index directly.
Local dev omits IndexViewer — AccountBalanceViewer computes summaries on demand from BlockViewer. Bind IndexViewer → memory (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 IndexPublishRunner → s3-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.
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
}
]
}
}
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 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 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 + 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"
}
]
}
}
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-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"
}
]
}
}
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
}
]
}
}
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
}
]
}
}
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"
}
]
}
}
Mongo store plus S3 publish connections for finalizer and indexer in one process.
{
"xl1": {
"connections": {
"chain-mongo": {
"type": "mongo",
"connectionString": "mongodb://user:pass@db.internal: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
}
]
}
}
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"
},
"NetworkStakeRewardsIndexPublishRunner": {
"connection": "s3-index"
},
"StakeEventsViewer": {
"connection": "rest-index"
},
"StakeViewer": {
"connection": "default-evm-rpc"
}
},
"actors": [
{
"name": "indexer",
"publishSyncInterval": 5000,
"publishContentEncoding": "br",
"publishProgressInterval": 100
}
]
}
}
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 EVM event or rewards indexing is enabled) | s3-index | finalized blocks + chain-state over REST; finalized contract logs and reward inputs |
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 |
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).chain.id is the staking contract address on the backing EVM. Event publishing binds EvmChainViewer → default-evm-rpc and EvmEventIndexPublishRunner → s3-index. Durable reward publishing additionally binds NetworkStakeRewardsIndexPublishRunner → s3-index, StakeEventsViewer → rest-index, and StakeViewer → default-evm-rpc. API processes bind NetworkStakeStepRewardsByPositionViewer → rest-index to read the resulting index without local cache priming.<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).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.{
"xl1": {
"connections": {
"chain-mongo": {
"type": "mongo",
"connectionString": "mongodb://YOUR_MONGO_USER:YOUR_MONGO_PASSWORD@db.internal: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
}
]
}
}
{
"xl1": {
"connections": {
"chain-mongo": {
"type": "mongo",
"connectionString": "mongodb://YOUR_MONGO_USER:YOUR_MONGO_PASSWORD@db.internal: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
}
]
}
}
{
"xl1": {
"connections": {
"chain-mongo": {
"type": "mongo",
"connectionString": "mongodb://YOUR_MONGO_USER:YOUR_MONGO_PASSWORD@db.internal: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
}
]
}
}
{
"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"
},
"NetworkStakeRewardsIndexPublishRunner": {
"connection": "s3-index"
},
"StakeEventsViewer": {
"connection": "rest-index"
},
"StakeViewer": {
"connection": "default-evm-rpc"
}
},
"actors": [
{
"name": "indexer",
"publishSyncInterval": 5000,
"publishContentEncoding": "br",
"publishProgressInterval": 100
}
]
}
}
The mempool actor is headless by default: it owns the pending-pool prune
timers and nothing else. Set serveRpc: true to also host an HTTP server on
host/port exposing exactly the mempool JSON-RPC surface at POST /rpc —
mempoolViewer_pendingBlocks, mempoolViewer_pendingTransactions,
mempoolRunner_submitBlocks, mempoolRunner_submitTransactions — plus
/livez, /readyz, and /startupz probe routes. Every other method returns
a JSON-RPC MethodNotFound error; the served surface never grows with the
locator.
This is the standalone (internal) mempool-service shape: point the public
api process at it by binding its mempool monikers to an rpc connection,
and the api's /rpc keeps serving the same mempoolViewer_* /
mempoolRunner_* methods to external clients while proxying them here:
{
"xl1": {
"connections": {
"mempool-rpc": { "type": "rpc", "protocol": "http", "url": "http://mempool.internal/rpc" }
},
"providerBindings": {
"MempoolRunner": { "connection": "mempool-rpc" },
"MempoolViewer": { "connection": "mempool-rpc" }
}
}
}
Because the store-backed MempoolRunner performs transaction admission
(validation.signerAuthorizations, maxPendingTransactions), that
enforcement runs in the mempool process — move those config blocks with it.
{
"xl1": {
"connections": {
"chain-mongo": {
"type": "mongo",
"connectionString": "mongodb://YOUR_MONGO_USER:YOUR_MONGO_PASSWORD@db.internal: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",
"serveRpc": true,
"host": "0.0.0.0",
"port": 8081
}
]
}
}
{
"xl1": {
"connections": {
"chain-mongo": {
"type": "mongo",
"connectionString": "mongodb://YOUR_MONGO_USER:YOUR_MONGO_PASSWORD@db.internal: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
}
]
}
}
{
"xl1": {
"connections": {
"chain-mongo": {
"type": "mongo",
"connectionString": "mongodb://YOUR_MONGO_USER:YOUR_MONGO_PASSWORD@db.internal: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
}
]
}
}
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, transaction inclusions, optional finalized EVM event frames, and optional durable network-stake rewards |
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:pass@db.internal: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 advances the XL1 and EVM source indexes, then publishes rewards when both durable heads are available. |
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_STATE → chainState, READ_URL → readUrl):
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
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":
xl1-chain serves the finalized role (it predates the role name; live buckets are a migration, not a rename).blocks. / state. / indexes. endpoint subdomains (derived by the SDK's restGatewayConfigFromEndpoint) are deployed DNS; the finalized role maps to the blocks. subdomain.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.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.
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
See the LICENSE file for license rights and limitations (LGPL-3.0-only).
FAQs
XYO Layer One CLI
The npm package @xyo-network/xl1-cli receives a total of 220 weekly downloads. As such, @xyo-network/xl1-cli popularity was classified as not popular.
We found that @xyo-network/xl1-cli demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 5 open source maintainers collaborating on the project.

Security News
Two compromised GitHub Actions were re-enabled with malicious tags intact, exposing thousands of downstream repositories to Mini Shai-Hulud.

Research
/Security News
A malicious Firefox extension fetches its payload after installation to evade detection, steal Google session cookies, and automate account takeover.

Research
/Security News
The compromise affects MemTensor's MemOS, an open source memory framework for large language models (LLMs) and AI agents. Both npm package @memtensor/memos-cloud-openclaw-plugin and the PyPI package MemoryOS are compromised. They drop cross-platform Go binaries that exfiltrate developer secrets.