> ## Documentation Index
> Fetch the complete documentation index at: https://seilabs-docs-bridge-release-v6-7-0.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Sei Technical Reference

> Access detailed command syntax, configuration parameters, and troubleshooting procedures for node operators and validators running Sei network infrastructure.

This guide is a reference for Sei node operators and validators. It gives
detailed command syntax, configuration parameters, and troubleshooting
procedures. For API documentation, see the API Documentation section.

## Command line interface reference

The `seid` binary has extensive functionality to manage your Sei node.
Understanding these commands is essential for effective node operation and
troubleshooting.

### Node management commands

These commands help you control and monitor your node:

<Danger>
  If you see an error such as `panic: recovered: runtime error: integer divide by zero`, it means that you cannot start nodes directly from the genesis file. Instead, sync to the block tip with [state sync](/node/statesync) or a [snapshot](/node/snapshot).
</Danger>

```bash theme={"dark"}
# Start the node
seid start [flags]

# Show node status
seid status

# Show validator consensus key
seid tendermint show-validator

# Query node information
seid query node info
```

#### Experimental config manager selection (`SEI_CONFIG_MANAGER`)

`seid` reads the experimental `SEI_CONFIG_MANAGER` environment variable to select which configuration manager resolves a node's configuration at startup. The value is matched exactly — it is not trimmed or case-folded.

* Unset or `legacy`: uses the existing legacy config loader. This is the default and leaves the configuration path unchanged.
* `v2`: selects the sei-config-backed manager. This manager boots the node identically to the legacy path — it re-enters the legacy reader on your original `config.toml` and `app.toml`, and never rewrites those files or refuses boot. In addition, it runs an advisory config-validation pass that logs diagnostics at warn level (for example, a missing `chain.min_gas_prices`) without changing the boot outcome. Validation is non-fatal: a diagnostic is informational only. Because the pass reads the on-disk config before the node generates its own files, a brand-new node is not validated on its first boot; diagnostics first appear from the second start onward.
* Any other value: `seid` refuses to start and reports an `invalid SEI_CONFIG_MANAGER` error naming the legal tokens (unset, `legacy`, or `v2`). There is no silent fallback.

```bash theme={"dark"}
# Default behavior (legacy loader) — no variable set
seid start

# Equivalent to the default
SEI_CONFIG_MANAGER=legacy seid start

# Selects the v2 manager, which boots identically to legacy while emitting
# advisory config-validation warnings to the logs
SEI_CONFIG_MANAGER=v2 seid start
```

#### Freeze mode (`--freeze-height`)

As of v6.6.3, you can put a full node into read-only freeze mode at a specified block height. Use the `--freeze-height` start flag or the corresponding `freeze-height` field in `app.toml`. Freeze mode exists for historical RPC nodes. A node frozen at an upgrade height keeps running the pre-upgrade binary and keeps serving the state that binary produced. It does not shut down at the boundary or execute the upgrade block with code that no longer matches that state.

Query RPC remains available, so the node can continue to serve reads, but write and network paths are disabled from startup:

* Transaction and evidence submission is rejected. The `BroadcastTx`, `BroadcastTxAsync`, `BroadcastTxSync`, `BroadcastTxCommit`, and `BroadcastEvidence` RPC calls all return `ErrReadOnly` (`RPC writes are disabled in freeze mode`). The EVM endpoint submits transactions through the same path, so `eth_sendRawTransaction` fails with the same error.
* Mempool gossip is disabled. The mempool reactor does not start, and the node does not advertise the mempool p2p channel to peers.
* State sync is disabled. If it is enabled in `config.toml`, the node logs a notice and falls back to block sync.

Block sync and consensus stop before they execute the configured height and do not advance beyond it.

The node must not have reached the freeze height yet. If the application, block store, or state store height is already at or above `freeze-height`, startup fails with `<source> height <n> has already reached freeze height <h>`. To build a frozen node, start from a data directory that is below the freeze height. This can be a fresh sync from genesis or a [snapshot](/node/snapshot) taken below that height. Then let block sync stop at the boundary.

```bash theme={"dark"}
# Start a full node in read-only freeze mode at a given block height
seid start --freeze-height <height>
```

You can configure the same behavior persistently with the `freeze-height` field. Its value is the first block height that a full node must not execute. A value of `0` disables freeze mode. The key is a top-level entry in `app.toml`, in the base configuration next to `halt-height`, not under any `[section]` header. The generated [default `app.toml`](/node/node-operators#default-configurations) shows the field in context.

```toml theme={"dark"}
# app.toml — top-level key in the base configuration (next to halt-height), not under any [section]
freeze-height = 0
```

<Warning>
  Freeze mode is supported only for full nodes. In validator or seed mode, the node rejects a non-zero `freeze-height` at startup with an error (`freeze height is not supported in <mode> mode`). You also cannot combine `freeze-height` with `halt-height`, `halt-time`, or the `--grpc-only` start flag. The node rejects each combination at startup.
</Warning>

### Frozen RPC router

The `frozen-rpc-router` binary, available as of v6.6.3, is a companion to freeze mode. It exposes a single HTTP EVM JSON-RPC endpoint that transparently proxies requests to a live node and one or more freeze-height-frozen nodes. It routes each request to the correct backend based on the block height that the request references. With the router, a set of archival nodes can collectively serve historical state through one endpoint. Each node is frozen at a different upgrade height and runs the binary that was live for its interval.

Because a freeze height is an exclusive boundary, a node started with `--freeze-height 100` serves blocks through height 99. The router therefore sends height 99 to that node. It sends height 100 to the next configured interval, or to the live node when no frozen interval covers it.

The router is a standalone binary in the `sei-chain` repository. It is not part of `seid`, and `make install` does not install it. Build it from a source checkout with the `build-frozen-rpc-router` target, which writes the binary to `./build/frozen-rpc-router`:

```bash theme={"dark"}
git clone https://github.com/sei-protocol/sei-chain.git
cd sei-chain
git checkout <version-tag>  # v6.6.3 or later
make build-frozen-rpc-router
```

```bash theme={"dark"}
# Route between a live node and two frozen nodes. The router takes the default EVM
# HTTP RPC port (8545), so the live node and the frozen node that share this host
# have been moved to 9545 and 9546. The frozen node on 10.0.0.12 runs on its own
# host and keeps the default port.
./build/frozen-rpc-router \
  --listen-address 127.0.0.1:8545 \
  --live-node localhost:9545 \
  --frozen-node 1000000=localhost:9546 \
  --frozen-node 2000000=10.0.0.12:8545
```

<Warning>
  If you bind the router to a public interface (for example, `--listen-address 0.0.0.0:8545`), put it behind a firewall or reverse proxy. Protect it as you would a node's own EVM RPC port. The router has no authentication of its own, so anyone who can reach its listen address can query every backend behind it. The example binds to `127.0.0.1` so that only local clients can connect.
</Warning>

The binary accepts these flags:

* `--listen-address`: The address on which the router listens (default `127.0.0.1:8545`).
* `--live-node`: The HTTP RPC address of the live node (required).
* `--frozen-node`: A `freeze-height=ip:port` pair. Repeat it once for each frozen node. The router accepts bare `host:port` addresses and `http://` or `https://` URLs. You may list frozen nodes in any order, but each freeze height must be positive and unique.
* `--max-request-body-bytes`: The maximum JSON-RPC request body size in bytes (default `5242880`, which is 5 MiB). It must be positive. The router rejects larger requests with HTTP `413`.
* `--max-block-reference-depth`: The maximum nested block reference depth (default `16`). It limits how deeply nested `blockNumber` object references are parsed when the router resolves a request's block parameter. It must be positive.
* `--batch-request-limit`: The maximum number of calls in a JSON-RPC batch (default `1000`). It must be positive. The router rejects a batch over this limit with JSON-RPC error `-32600` (`batch too large`).
* `--write-timeout`: The maximum duration to write an HTTP response (default `30s`). It must be positive.
* `--shutdown-timeout`: The graceful shutdown timeout (default `10s`). It must be positive.

#### Routing rules

The router inspects and routes only JSON-RPC `POST` requests. It passes every other request, including WebSocket upgrade requests, straight through to the `--live-node` address without inspection. That address is the live node's HTTP RPC endpoint, which does not accept WebSocket upgrades. Clients that need subscriptions should therefore connect directly to the live node's WebSocket port (`8546` by default), not through the router.

* `eth_*` and `debug_*` methods that take an explicit block number or the `earliest` tag are routed to the interval that contains that height. Examples include `eth_getBlockByNumber`, `eth_getBalance`, `eth_call`, `eth_getStorageAt`, and `debug_traceBlockByNumber`. `earliest` resolves to height 0.
* `eth_getLogs` and `eth_feeHistory` are routed only when their entire block range falls within a single interval. The range is explicit for `eth_getLogs`. For `eth_feeHistory`, the router derives it from `blockCount` and `newestBlock` (`newestBlock - blockCount + 1` through `newestBlock`, clamped at `0`). The router rejects a range that crosses an interval boundary with JSON-RPC error `-32000` (`block ranges spanning multiple frozen-node intervals are not supported`).
* An `eth_getLogs` filter that sets `fromBlock` but omits `toBlock` is treated as ending at `latest`. As a result, the router rejects it with the same `-32000` error whenever `fromBlock` is inside a frozen interval. To stay within one interval, set `toBlock` explicitly. The reverse works: a filter that sets only `toBlock` is routed to the interval that contains `toBlock`.
* Requests with latest-style block tags (`latest`, `pending`, `safe`, `finalized`) are forwarded to the live node. So are requests that reference a block by hash, methods without a block parameter, and stateful filter methods.
* Batch requests are split so that each call reaches its correct backend, then reassembled into a single response.
* Calls whose backend cannot be reached return JSON-RPC error `-32001` (`upstream request failed`).

#### Route header

Responses proxied to a single backend carry a `Sei-RPC-Route` header. The header identifies which backend served the response: `frozen:<height>` for the frozen node at that freeze height, or `live` for the live node. A batch split across multiple backends returns `mixed`. Router-generated errors (oversized or malformed requests, batches over the limit, block ranges that span intervals, and unreachable backends) do not carry the header. Neither does non-`POST` traffic that passes through to the live node.

### seidb tooling commands

The `seidb` binary has low-level tools to inspect and maintain a node's on-disk state.

#### Reporting FlatKV EVM migration status

The `migrate-evm-status` subcommand reads the on-disk FlatKV EVM migration state from a FlatKV data directory and prints a JSON summary. It is intended mainly for integration and operator tooling that polls each validator to check whether the FlatKV EVM migration has completed. The tooling then needs no custom RPC handler and does not have to grep through node logs.

```bash theme={"dark"}
# Report FlatKV EVM migration status at the latest available version
seidb migrate-evm-status --db-dir <flatkv-dir>

# The --db-dir flag may be abbreviated as -d
seidb migrate-evm-status -d $HOME/.sei/data/state_commit/flatkv

# Report status at a specific FlatKV version (0 selects the latest)
seidb migrate-evm-status --db-dir <flatkv-dir> --height <n>
```

The command opens FlatKV read-only. It first hardlink-clones the latest snapshot and copies the WAL into a temporary directory. You can therefore run it safely against a directory that a live node is still writing to.

The emitted JSON contains these fields:

* `version_at`: The FlatKV version that was read.
* `migration_version`: The on-disk migration version (`0` means that the FlatKV EVM migration has not completed yet).
* `migrate_evm_complete`: `true` after the migration version reaches the FlatKV EVM (v1) target.
* `boundary_present`: `true` while the migration is in flight (the in-progress resume cursor is still present).
* `boundary_hex`: The hex-encoded migration boundary cursor. It appears only when a boundary is present.
* `version_raw_hex`: The hex-encoded raw migration-version bytes. It appears only when a migration version is present.

#### Comparing EVM state across backends

The `evm-logical-digest` subcommand computes a backend-independent digest of the EVM logical state (the account, code, and storage buckets). With this digest, you can compare a memIAVL node and a FlatKV node at the same chain height.

A freshly migrated FlatKV node stamps a per-key `blockHeight` into each value, and this stamp differs from the memIAVL leaf versions. As a result, a raw byte-for-byte digest would diverge even when the underlying EVM state is identical. This command strips the serialization-version and `blockHeight` header on both sides. It then digests only the height-independent logical payload (storage word, bytecode, or balance+nonce+codehash) and produces a comparable `FINAL_DIGEST` for each backend.

```bash theme={"dark"}
# FlatKV digest at a height (WAL-replays to it). Prints per-bucket
# bucket_digest values and one FINAL_DIGEST line for backend comparison.
seidb evm-logical-digest --backend flatkv \
    --db-dir $HOME/.sei/data/state_commit/flatkv --height 213200000

# memIAVL digest at the same height (0 = current symlink), using the default
# semantic normalization. memiavl resolves snapshot-<height>/evm or current/evm
# and does not replay WAL in this tool.
seidb evm-logical-digest --backend memiavl \
    --db-dir $HOME/.sei/data/state_commit/memiavl --height 213200000

# Translator-based memIAVL digest, which feeds each leaf through the current
# migration mapping (flatkv.ImportTranslator).
seidb evm-logical-digest --backend memiavl \
    --db-dir $HOME/.sei/data/state_commit/memiavl --height 213200000 \
    --memiavl-normalization translator
```

Two backends match when the FlatKV `FINAL_DIGEST` equals the memIAVL `FINAL_DIGEST`. FlatKV also writes an internal migration-version marker row, which a memiavl-only node never owns. The command automatically omits that row from the final comparison.

#### Dumping FlatKV state and verifying its lattice hash

The `dump-flatkv` subcommand dumps every physical `(key, value)` pair of a FlatKV store into per-bucket files, formatted to match `dump-iavl` so the same diff tooling works on both. It can optionally also compute the per-bucket and total LtHash (lattice hash) over the scanned state and verify that total against the committed root recorded in snapshot metadata.

The command opens an independent read-only clone of the store (the snapshot is hard-linked and the changelog WAL is replayed into a temporary directory under the data dir), so it is safe to run against a live, block-producing node. The scan is throttled by `--read-limit-mb` so a dump against a running node does not starve the chain of disk bandwidth.

```bash theme={"dark"}
# Latest version, all buckets, default 64 MiB/s throttle, LtHash + verify.
seidb dump-flatkv -d /.sei/data/state_commit/flatkv -o /tmp/flatkv-dump

# Pin a specific height.
seidb dump-flatkv -d /.sei/data/state_commit/flatkv -o /tmp/flatkv-dump \
    --height 216890000

# Offline / idle disk: go full speed, skip LtHash.
seidb dump-flatkv -d /.sei/data/state_commit/flatkv -o /tmp/flatkv-dump \
    --read-limit-mb 0 --lthash=false

# Verify the FlatKV lattice hash only, without writing key/value dump files.
seidb dump-flatkv -d /.sei/data/state_commit/flatkv --lthash-only
```

The command accepts these flags:

* `--db-dir` (`-d`): The FlatKV data directory (the directory containing `current/`, `snapshot-*`, and `changelog/`). Required.
* `--output-dir` (`-o`): Where to write the per-bucket dump files (one file per bucket). Required unless `--lthash-only` is set.
* `--height`: The target version. `0` (the default) selects the latest available version by replaying the WAL to the tip.
* `--bucket` (`-b`): Restrict the on-disk dump to a single bucket (`account`, `code`, `storage`, or `legacy`). The default is all buckets. This only filters which hex files are written; the full keyspace is always scanned, and `--lthash` always covers all four buckets, so the LtHash total stays valid.
* `--lthash`: Compute the per-bucket and total LtHash over the scanned state and verify the total against committed snapshot metadata. Default `true`. The LtHash is always computed over all buckets regardless of `--bucket`, so the total matches the node's committed LtHash.
* `--lthash-only`: Compute and verify the LtHash without writing any bucket dump files. Default `false`. It requires `--lthash=true`, cannot be combined with `--bucket`, and does not require `--output-dir`.
* `--read-limit-mb`: Throttle the scan to at most this many MiB/s of `(key+value)` bytes read. Default `64`. A value of `0` disables throttling. Keep it low (default or less) on a shared or live node; raise it only for offline runs on idle disks. Negative values are rejected.

With `--lthash`, the command prints an `LtHash (lattice hash)` block listing each bucket's count and checksum and the `TOTAL`, followed by a verification line comparing the re-scanned total against the committed snapshot metadata. A match prints `PASS`; a mismatch prints `FAIL` and the command exits non-zero. Verification is skipped when the selected snapshot predates LtHash metadata (its committed hash then covers only replayed WAL deltas, not full state) or when no committed LtHash is recorded at that version.

#### Inspecting hash log archives

The `hashlog` command group provides read-only tools for inspecting the on-disk hash log archives produced by the hashlogger. Use it to pull the hashes recorded for a single block or to diff two archives without writing any Go code.

##### Printing a single block's hashes

The `get-block` subcommand prints every hash recorded for a single block in a hash log archive.

```bash theme={"dark"}
# Print the hashes recorded for a block in an archive
seidb hashlog get-block <archive> <block>

# Emit JSON instead of human-readable text
seidb hashlog get-block <archive> <block> --json
```

If the block was executed more than once (for example, after a rollback that replayed it), the archive holds several records for that block and each execution's hashes are reported separately. A hash type that was registered but not recorded for the block prints as `<none>` (or serializes to JSON `null`, which is distinguishable from an absent type).

##### Comparing two archives

The `compare` subcommand compares two hash log archives and reports the blocks whose hashes differ between them.

```bash theme={"dark"}
# Compare two archives over their full range
seidb hashlog compare <archive-a> <archive-b>

# Restrict the comparison to a block range (both flags required together)
seidb hashlog compare <archive-a> <archive-b> --low <N> --high <M>

# Cap the number of differing blocks reported
seidb hashlog compare <archive-a> <archive-b> --max-diffs <N>

# Show every column for each differing block, and emit JSON
seidb hashlog compare <archive-a> <archive-b> --full --json
```

The command accepts these flags:

* `--low`: The lowest block to compare (inclusive). Requires `--high`.
* `--high`: The highest block to compare (inclusive). Requires `--low`. The `--low` and `--high` flags are optional, but must be supplied together; a one-sided range fails with an error.
* `--max-diffs`: The maximum number of differing blocks to report. The default is `-1`, which reports all of them. When the output is truncated at the cap, the command warns that there may be more differing blocks.
* `--full`: Show every column of every record for each differing block. The default is a compact view that shows only the columns that differ. This is also the only sensible rendering when the record counts differ between the two sides (a rollback re-executed the block a different number of times), since there is no single pair of records to diff column by column.
* `--json`: Emit JSON instead of human-readable text. The compact-by-default and `--full` column filtering apply to JSON output as well.

When the archives are identical over the compared range, the command reports that and exits.

The command accepts these flags:

* `--backend`: The backend to read (`flatkv`, `memiavl`, or `composite`). Use `composite` to digest the union of FlatKV and memIAVL rows for a node that is mid-migration, so it can be compared against a memiavl-only node at the same height.
* `--db-dir` (`-d`): For FlatKV, the FlatKV data directory. For memIAVL, the memIAVL root directory that contains `current/` and `snapshot-*`. Required unless `--backend composite` is used (composite mode uses `--flatkv-dir` and `--memiavl-dir` instead).
* `--flatkv-dir`: The FlatKV data directory in `composite` mode. Required with `--backend composite`.
* `--memiavl-dir`: The memIAVL root directory (containing `current/` and `snapshot-*`) in `composite` mode. Required with `--backend composite`.
* `--height`: The target version. FlatKV WAL-replays to it, and memIAVL resolves `snapshot-<height>/evm` (`0` selects the `current` symlink).
* `--memiavl-open-mode`: How memIAVL is read. `snapshot` (the default) is the fast path: it sequentially scans the completed snapshot kvs file and requires an on-disk snapshot at `--height` (or `--height 0` for the `current` symlink). `replay` is the slow path (roughly an order of magnitude slower): it opens a read-only DB, replays the changelog up to `--height`, then walks the mmap tree. Use `replay` only when no snapshot exists at the target height. Prefer `snapshot` whenever `--height` matches an existing snapshot boundary.
* `--memiavl-normalization`: The memIAVL normalization mode. Use `semantic` or `independent` for the raw EVM key-value decoder, or `translator` for the current migration mapping. The default is `semantic`.
* `--inspect-bucket`: Inspect one normalized bucket (`account`, `code`, `storage`, or `legacy`) instead of printing the global digest. It supports only `--memiavl-open-mode=snapshot`; combining it with `replay` returns an error.
* `--key-offset` (inspect mode): The byte offset into the physical key, applied before `--key-prefix` or sharding.
* `--key-prefix` (inspect mode): A hex prefix, relative to `--key-offset`, that filters physical keys.
* `--shard-next-bytes` (inspect mode): Group matching keys by this many bytes after `--key-prefix`.
* `--list` (inspect mode): List pairs of matching keys and logical values instead of shard `bucket_digest` values.
* `--list-limit` (inspect mode): The maximum number of pairs to print with `--list` (default `1000`). A value `<= 0` means unlimited.
* `--details` (inspect list mode): Include backend-specific version metadata.
* `--find-hash`: An optional 32-byte hex per-entry hash to search for. When two `bucket_digest` values differ by exactly one entry, their XOR is the hash of that entry. This flag prints every matching entry, so you can locate a single diverging row.

### Autobahn (GigaRouter) config generation

When you use the Autobahn (GigaRouter) networking layer, you can generate the Autobahn JSON config from a set of node directories. Each directory must contain `validator_pubkey.txt`, `node_pubkey.txt`, `autobahn_address.txt`, and `evmrpc_url.txt`. Unlike the key files, `evmrpc_url.txt` is not written automatically, so you must create it by hand with the node's EVM RPC URL. If the file is missing, the command fails with an error. The `mempool_size` field is no longer part of `autobahn.json`. Remove it from existing config files.

```bash theme={"dark"}
# Generate an autobahn JSON config from one or more node directories
seid tendermint gen-autobahn-config [node-dirs...] --output <path>

# The --output flag may be abbreviated as -o
seid tendermint gen-autobahn-config ./node0 ./node1 ./node2 -o autobahn.json

# Choose where autobahn consensus state and BlockDB are persisted (default: data/autobahn)
seid tendermint gen-autobahn-config ./node0 ./node1 --output autobahn.json --persistent-state-dir data/autobahn

# Pass an empty value to disable persistence and run in-memory only (memblock)
seid tendermint gen-autobahn-config ./node0 ./node1 --output autobahn.json --persistent-state-dir=

# Tune BlockDB retention and GC
seid tendermint gen-autobahn-config ./node0 ./node1 --output autobahn.json --blockdb-retention 30s --blockdb-gc-period 10s
```

The `--persistent-state-dir` flag controls where Autobahn persists its consensus state and BlockDB across restarts. The default is `data/autobahn`, so persistence is enabled by default without any operator action. Autobahn's durable block and quorum-certificate storage now lives in a LittDB-backed BlockDB opened under `<persistent_state_dir>/blockdb`, replacing the previous data write-ahead logs (the old `globalblocks/` and `fullcommitqcs/` subdirectories are no longer read). At config load time, a relative path is resolved against the node's `--home` directory, and an absolute path is used as is. An empty value (`--persistent-state-dir=`) disables persistence entirely and both the consensus and data layers run in memory only (memblock). When set, the flag populates the `PersistentStateDir` field in the generated config.

<Warning>
  Operators upgrading from a release that used the data WALs must be aware that Autobahn consensus state now lives under `<persistent_state_dir>/blockdb` (a LittDB BlockDB). The old `globalblocks/` and `fullcommitqcs/` WAL directories are no longer read.
</Warning>

Two additional flags tune the BlockDB:

* `--blockdb-retention`: Sets the BlockDB retention TTL written into the `block_db` section of the generated config. The default is `30s` because this helper targets local/docker clusters rather than production node bring-up. Pass an empty value (`--blockdb-retention=`) to omit the field and keep littblock's production default of 24h.
* `--blockdb-gc-period`: Sets the BlockDB garbage-collection period (for example `10s`). Omit it to keep littblock's default GC period.

When either flag is set, an optional `block_db` section is written into `autobahn.json`:

```json theme={"dark"}
"block_db": {
  "retention": "30s",
  "gc_period": "10s"
}
```

Each field is independently optional and is omitted from the JSON when empty; absent fields keep whatever littblock's default config uses. The `block_db` section overlays those defaults only when `persistent_state_dir` is set, and is ignored when persistence is disabled (memblock). When set, `retention` and `gc_period` must each be greater than zero.

The command reads these files from each node directory:

* `validator_pubkey.txt`: The validator public key in `validator:<pubkey>` format.
* `node_pubkey.txt`: The p2p node public key in `node:ed25519:public:<hex>` format.
* `autobahn_address.txt`: The network address (`host:port`) that the node advertises to peers.
* `evmrpc_url.txt`: The node's EVM RPC URL. It is written into the validator's `evmrpc` field for cross-shard transaction proxying.

The `validator_pubkey.txt` and `node_pubkey.txt` files are written automatically next to `priv_validator_key.json` and `node_key.json` whenever those keys are saved. They are therefore usually already present in each node's config directory.

The generated `autobahn.json` file describes the validator set, transaction limits, block interval, view timeout, and dial interval. Gas limits are not part of this file and come from the genesis block parameters instead. To make a node use the file, reference it from `config.toml` with the `autobahn-config-file` key.

#### Giga mode behavior and per-block limits

A node starts in Giga mode when `autobahn-config-file` is set in `config.toml`. In Giga mode, the block production and networking behavior differs significantly from standard Tendermint consensus:

* **The CometBFT `TxMempool` is not used.** Under Giga, the standard mempool and its gossip reactor are disabled entirely. Transactions route through the Autobahn producer-backed mempool instead.
* **Consensus reactor, state sync, and block sync are disabled.** In Giga mode, the node skips the consensus and state-sync reactors entirely. The block-sync reactor still runs without a syncer. State sync and block sync are both forced off, regardless of other configuration.
* **Transactions are admitted through the producer mempool.** The RPC broadcast endpoints call the producer's `InsertTx` or `TryInsertTx`, not the CometBFT mempool's `CheckTx`. `BroadcastTx` uses `InsertTx`, which blocks while the mempool is full. The async path calls `TryInsertTx` in the background and returns immediately. As a result, when the mempool is full, the transaction is silently dropped. The `mempool is full` error from `TryInsertTx` never reaches async callers.
* **Sequential EVM nonce ordering is enforced.** For EVM transactions, the producer mempool admits transactions strictly in nonce order per sender. A transaction whose nonce does not match the next expected nonce is rejected with a `bad nonce` error. Because admission is sequential, the mempool can track pending nonces (`EvmNextPendingNonce`) as callers submit them.

The producer enforces these limits on each Autobahn block payload as it fills a block:

* **Maximum transactions per block:** The lower of the configured `max_txs_per_block` and the built-in maximum of 2,000 (see the transaction payload caps below).
* **Maximum total transaction bytes per block:** A fixed per-block byte cap. A single transaction larger than this cap is rejected with a `transaction too large` error.
* **Wanted gas per block (`MaxGasWantedPerBlock`):** Derived from the genesis `MaxGasWanted` block param. A transaction whose `GasWanted` exceeds this per-block limit is rejected as too large.
* **Estimated gas per block (`MaxGasEstimatedPerBlock`):** Derived from the genesis `MaxGas` block param. A transaction whose (normalized) estimated gas exceeds this per-block limit is rejected as too large.

The producer seals the current block and starts a new one as soon as the next transaction would exceed a limit. This rule covers the transaction-count, byte, wanted-gas, and estimated-gas limits.

#### Autobahn committee and network message limits

Beyond the per-block payload limits, Giga mode enforces structural limits on the validator committee and on incoming consensus network messages:

* **Maximum validators per committee:** The Autobahn committee has a hard limit of 100 validators (`MaxValidators`). Committee creation rejects any validator set over this limit with a `too many validators` error. It does not silently truncate the set.
* **Bounded consensus network messages.** Autobahn consensus protobuf messages carry declared size and count constraints. These constraints are checked against the raw wire bytes before the message is decoded. Payloads that violate them are rejected during decoding, before any allocation. This protects nodes from oversized or malformed inputs that could otherwise decode into much larger in-memory structures.

The enforced message constraints include:

* **Per-field maximum sizes** on fixed-width fields such as hashes, signatures, and public keys.
* **Maximum repeated-field counts** on validator-related lists. Signature and quorum-certificate lists are capped at 100 entries, which matches the 100-validator committee cap.
* **Transaction payload caps:** A block payload may carry at most 2,000 transactions. The combined transaction byte budget is exactly 2,048,000 bytes (2,000 × 1,024), and it may be split arbitrarily across the transactions in the payload. These are the built-in maxima that the per-block limits above refer to.

Any message whose fields exceed these limits is rejected at decode time, so an oversized network payload never reaches the consensus logic.

<Note>
  Giga replaces the CometBFT mempool, so Giga does not support the `unsafe_flush_mempool` RPC endpoint. The endpoint returns `unsafe_flush_mempool is not supported with autobahn mempool`.
</Note>

#### HashVault app-hash equivocation guard

When a node runs under Autobahn (GigaRouter), the GigaRouter builds and owns an app-hash equivocation guard called HashVault. As each finalized height is executed, HashVault records that height's committed app hash before the implied state is committed. If the node ever attempts to commit a different app hash for a height it has already finalized, HashVault detects the conflict and halts the node. This protects a validator against externalizing two different app hashes for the same height, which could otherwise lead to slashing.

HashVault is enabled by default on both Autobahn validators and fullnodes. It stores committed app hashes in a durable Pebble DB under the Autobahn persistent state directory, at `<PersistentStateDir>/hashvault` (see the `--persistent-state-dir` flag and the `PersistentStateDir` config field above). When Autobahn runs in memory only (no persistent state directory, for example in tests), HashVault falls back to a no-op with no equivocation protection.

<Warning>
  If a node hits a startup panic reporting a HashVault app-hash mismatch, **do not restart it without human investigation** — the node attempted to change its mind about an already-finalized height. The panic and the preceding HashVault error log the conflicting hashes and the on-disk HashVault data directory (`hashVaultDir`).

  Only if you are certain there is no real equivocation, you can recover by stopping the node, deleting the HashVault data directory shown in the panic message, and restarting. **This removes equivocation protection**: if the node then commits a conflicting hash for an already-finalized height, the validator may be slashed.
</Warning>

You can disable HashVault entirely with the top-level `hash-vault-disabled-unsafe` field in `config.toml` (default `false`). This is an explicit, last-resort operator decision to run **without** equivocation protection; a node started with it enabled logs error-level warnings on every startup. Prefer the recovery steps above — only set `hash-vault-disabled-unsafe = true` if you are very sure the stored hashes are wrong and you keep hitting the same panic on new blocks.

```toml theme={"dark"}
# config.toml — top-level key, before any [section] header.
# Leave false to keep equivocation protection enabled (recommended).
hash-vault-disabled-unsafe = false
```

### Key management

Proper key management is critical for security. Use these commands to manage
your keys:

```bash theme={"dark"}
# Create new key
seid keys add <name> [flags]

# List all keys
seid keys list

# Delete key
seid keys delete <name>

# Export key (encrypted)
seid keys export <name>

# Import key
seid keys import <name> <keyfile>

# Show key address
seid keys show <name> -a
```

### Transaction commands

These commands let you interact with Sei:

```bash theme={"dark"}
# Send tokens
seid tx bank send <from-key> <to-address> <amount>usei [flags]

# Delegate tokens
seid tx staking delegate <validator-addr> <amount>usei --from <delegator-key>

# Withdraw rewards
seid tx distribution withdraw-rewards <validator-addr> --from <delegator-key>

# Edit validator
seid tx staking edit-validator [flags] --from <validator-key>
```

## Configuration parameters

Understanding configuration parameters is essential to optimize your node's
performance and security.

### App.toml parameters

The `app.toml` file controls application-specific settings:

<Accordion title="Complete app.toml configuration">
  ```toml theme={"dark"}
  # Minimum gas prices for transaction acceptance
  minimum-gas-prices = "0.02usei"

  # First block height a full node must not execute (read-only freeze mode).
  # 0 disables freeze mode. Full nodes only; see "Freeze mode" above.
  freeze-height = 0

  # API configuration
  [api]
  enable = true
  swagger = true
  address = "tcp://0.0.0.0:1317"
  max-open-connections = 1000

  # State sync configuration
  [state-sync]
  snapshot-interval = 1000
  snapshot-keep-recent = 2

  # State store configuration
  [state-store]
  ss-enable = true
  ss-backend = "pebbledb"
  ss-keep-recent = 100000
  ss-prune-interval = 600

  # Base-layer (IAVL/baseapp) pruning. These Cosmos SDK pruning settings are read
  # from the start flags/config and applied to the node's baseapp, so they are
  # honored when the node runs. `pruning` selects a strategy: "default", "nothing"
  # (archive node, keep all states), "everything" (keep only recent states), or
  # "custom" (use the pruning-keep-recent and pruning-interval values below).
  pruning = "default"
  # Number of recent heights to keep on disk. Used with pruning = "custom".
  pruning-keep-recent = "0"
  # How often (in blocks) to run a pruning pass. Used with pruning = "custom".
  pruning-interval = "0"

  # gRPC server configuration
  [grpc]
  enable = true
  address = "0.0.0.0:9090"
  # Maximum message size in bytes the server can receive. Bounds per-request memory
  # allocation before the rate limiter fires. Default 4 MB (4194304).
  max-recv-msg-size = 4194304
  # Maximum number of simultaneous open connections. 0 means unlimited.
  max-open-connections = 1000
  # Duration after which an idle connection is closed. 0 means infinity.
  max-connection-idle = "5m0s"
  # Maximum duration a connection may exist before it is closed. 0 means infinity.
  max-connection-age = "0s"
  # Additive grace period after max-connection-age during which the connection is
  # forcibly closed. 0 means infinity.
  max-connection-age-grace = "0s"
  # Interval after which, with no activity, the server pings the client for liveness.
  keepalive-time = "2h0m0s"
  # Duration the server waits for a keepalive ping ack before closing the connection.
  keepalive-timeout = "20s"
  # Minimum interval a client must wait between keepalive pings; more frequent pings
  # are penalized.
  keepalive-min-time = "5m0s"
  # Whether the server allows keepalive pings even when there are no active streams.
  keepalive-permit-without-stream = false
  ```
</Accordion>

<Note>
  The `[grpc]` server applies bounded defaults even when these keys are absent from an older `app.toml`. A node upgrading with a config file that predates these fields gets the in-code defaults (for example, a 4 MB `max-recv-msg-size`, `1000` `max-open-connections`, and a `5m` `max-connection-idle`) rather than running with unlimited connections or message sizes. A negative duration override for a keepalive or connection-age field is treated as a misconfiguration and clamped back to its safe default. The `max-connection-age` and `max-connection-age-grace` fields default to `0` (gRPC's "infinity"), and the keepalive-time/timeout/min-time defaults mirror gRPC's own defaults, so they are opt-in and do not change behavior unless configured.
</Note>

### Config.toml parameters

The `config.toml` file controls the core consensus engine and networking:

<Accordion title="Complete config.toml configuration">
  ```toml theme={"dark"}
  # P2P Configuration
  [p2p]
  laddr = "tcp://0.0.0.0:26656"
  external-address = ""
  # bootstrap-peers holds seed/peer addresses (NodeID@host:port, comma-separated)
  # dialled to populate the address book via PEX. As of v6.6.2, `seid init`
  # auto-populates this field with the built-in Sei Labs seed nodes for
  # well-known public networks (pacific-1, atlantic-2). Devnets (arctic-1) and
  # unknown/private chains get none, and an existing bootstrap-peers value is
  # never overwritten. The example below reflects a fresh init on an
  # unrecognised chain.
  bootstrap-peers = ""
  persistent-peers = ""
  upnp = false
  max-connections = 100
  max-outbound-connections = 20
  max-packet-msg-payload-size = 10240
  handshake-timeout = "10s"
  dial-timeout = "3s"
  # How often the node accepts a new inbound connection. A larger interval paces
  # the accept loop more slowly; if the kernel accept backlog outpaces it, arriving
  # peers wait past handshake-timeout and the node silently stops acquiring inbound
  # peers. In v6.6.2 this became configurable and the default accept rate rose from
  # ~1/s to ~100/s (a "10ms" interval). A value of 0 disables the limiter; negative
  # values are rejected during config validation.
  accept-interval = "10ms"

  # RPC Configuration
  [rpc]
  laddr = "tcp://0.0.0.0:26657"
  cors-allowed-origins = []
  cors-allowed-methods = ["HEAD", "GET", "POST"]
  cors-allowed-headers = ["Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time"]
  max-open-connections = 900
  # timeout-broadcast-tx-commit is now enforced by the BroadcastTxCommit RPC: when set
  # greater than 0 it is applied as a context timeout on the request, so a
  # BroadcastTxCommit call will be cancelled if it does not complete within this duration.
  timeout-broadcast-tx-commit = "10s"
  # Maximum number of results returned by tx_search and block_search.
  # Set to 0 to disable the cap (not recommended on public nodes).
  max-tx-search-results = 10000
  # max-search-scan-budget sets a process-wide cap on the total number of KV index
  # entries that all in-flight tx_search and block_search requests may visit at
  # once. It is shared across requests (not applied per-query) and bounds peak
  # memory and scan CPU under broad or highly concurrent search load. When the
  # budget is exhausted, in-flight searches fail with "kv indexer scan budget
  # exceeded; narrow the query or retry later" rather than continuing to accumulate.
  # The same budget is shared by the tx and block indexers, so the cap is
  # process-wide across both. Default 100000; set to 0 to disable the cap (not
  # recommended on public nodes).
  max-search-scan-budget = 100000

  # Mempool Configuration
  [mempool]
  size = 5000
  max-txs-bytes = 1073741824
  cache-size = 10000
  # ttl-duration and ttl-num-blocks now treat zero (or unset) as "TTL purging disabled".
  # A non-zero value defines the time / number of blocks after which a transaction
  # is removed from the mempool; leaving them unset or set to zero disables TTL-based
  # purging entirely.
  ttl-duration = "5s"
  ttl-num-blocks = 10

  # Consensus Configuration
  [consensus]
  wal-file = "data/tendermint/cs.wal/wal"
  # Consensus timeouts are governed by on-chain ConsensusParams; the old
  # timeout-* keys were removed and produce a startup error if present.
  # Local overrides are only possible via the unsafe-*-timeout-override
  # keys, which take effect only when unsafe-overrides-enabled = true.
  double-sign-check-height = 0
  ```
</Accordion>

<Note>
  The `[consensus]` section may still parse a `stateless-leader-election` field, but the field is **deprecated and ignored**. Stateless (seed-based) leader election is always enabled, regardless of the value, so the field has no effect. It remains only for config-parsing compatibility, and you can safely omit it.
</Note>

<Warning>
  Out-of-process ABCI support was removed. The full node now runs only with Tendermint in-process. External standalone ABCI processes (socket or gRPC) are no longer supported. As a result:

  * The `seid start` flags `--address` and `--transport` are **deprecated and ignored**.
  * The Tendermint node flags `--proxy-app` and `--abci` are **deprecated and ignored**.
  * The `proxy-app` and `abci` fields in `config.toml` are **deprecated and ignored**. Newly generated `config.toml` files no longer include them. If you upgrade a node and these lines are in your `config.toml`, delete them.
</Warning>

## Network parameters

Understanding network parameters helps you operate your node effectively.

### Chain parameters

These parameters define the network's behavior:

```text theme={"dark"}
Block Time: ~400ms target
Max Validators: 40
Unbonding Period: 21 days
Minimum Self Delegation: 1 SEI

Slashing Parameters:
  - signed_blocks_window:        108,000 blocks
  - min_signed_per_window:       5%   (validator must sign ≥5% of blocks in the window)
  - downtime_jail_duration:      10 minutes
  - slash_fraction_downtime:     0%   (no stake slash; jail only)
  - slash_fraction_double_sign:  0%   (no stake slash; double-signing still triggers
                                       permanent tombstoning)

Oracle Parameters:
  - min_valid_per_window:        0%   (default changed from 5% now that the Oracle
                                       Price Feeder is retired; distinct from the
                                       slashing module's min_signed_per_window above)
```

<Info>
  These values reflect the current on-chain parameters. For the source of truth, query them directly with `seid query staking params` and `seid query slashing params`. Per-validator settings (for example, commission rate and commission max change rate) are configured per validator and are not chain-level parameters.
</Info>

## File locations

Understanding the purpose and location of important files helps with maintenance
and troubleshooting:

```text theme={"dark"}
$HOME/.sei/
├── config/
│   ├── app.toml         # Application configuration
│   ├── client.toml      # Client configuration
│   ├── config.toml      # Tendermint configuration
│   ├── genesis.json     # Chain genesis file
│   ├── node_key.json    # Node identity key
│   ├── node_pubkey.txt  # Node public key in autobahn format ("node:ed25519:public:<hex>"), written when the node key is saved
│   ├── priv_validator_key.json  # Validator signing key
│   └── validator_pubkey.txt  # Validator public key in autobahn format ("validator:<pubkey>"), written when the validator key is saved
├── data/
│   ├── application.db   # Application state
│   └── tendermint/     # Tendermint consensus DBs (new subdirectory layout)
│       ├── blockstore.db    # Block data
│       ├── cs.wal/          # Consensus write-ahead logs
│       ├── evidence.db      # Evidence of misbehavior
│       ├── peerstore.db     # Peer store
│       ├── state.db         # Tendermint state
│       └── tx_index.db      # Transaction index
└── keyring-file/       # Local key storage
```

<Note>
  New nodes place the Tendermint consensus databases (blockstore, state, tx\_index, evidence, peerstore, and cs.wal) under `data/tendermint/`. Existing nodes with the legacy flat layout keep these databases directly under `data/` (for example, `data/blockstore.db` and `data/cs.wal/`). These nodes continue to use the legacy paths automatically. The legacy location takes precedence when it is present, so no migration is required.
</Note>

This reference guide gives essential technical information for operating Sei
nodes and validators. For API documentation and other detailed specifications,
see the relevant sections of this documentation.

## Build tags

### `historical_replay`

The `historical_replay` build tag produces a consensus-unsafe `seid` variant intended solely for replaying historical blocks. When you build with this tag, the block-execution transaction decoder is swapped for a lenient protobuf decoder (`NewTxConfigWithoutBodyBloatRejection`) that does **not** reject non-canonical (body-bloat) transaction bodies. This lets a node decode and execute historical blocks whose transaction bodies predate strict body-bloat rejection.

```bash theme={"dark"}
# Build a historical-replay seid variant
go build -tags historical_replay ...
```

<Warning>
  A binary built with the `historical_replay` tag is **consensus-unsafe** and must only be used for historical replay. Never run it on live or production paths. The lenient decoder is compiled in only when the tag is present, so an untagged (production) binary can never reach it — the lenient execution decoder stays off every mempool, `CheckTx`, and `DeliverTx` path. Do not use a `historical_replay` build to validate, produce blocks, or serve live traffic.
</Warning>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.