Protocol documentationSource snapshot: 0.1.0 alphaNetwork: testnet / devnet

Start here

Full node operations

A source-level runbook for configuring, running, inspecting, backing up, recovering, and safely exposing the services inside a Luracoin full node.

Operator reference · alpha constraints applyReviewed August 31, 2026
Specification status

This page documents the implemented alpha. Executable source, interoperability vectors, and tests remain authoritative if prose and code ever diverge.

What a full node owns

luracoin node is one long-running Python process that owns several cooperating services:

  • the canonical testnet or devnet account state;
  • length-prefixed block files and RocksDB indexes;
  • a TCP P2P v2 listener and outbound connections;
  • a Redis-backed pending transaction pool;
  • an authenticated loopback RPC for local wallets;
  • a read-only explorer API, enabled by default on loopback;
  • optionally, a SQLite explorer projection and its background indexer;
  • optionally, a local Vite wallet server and allowlisted gateway.

luracoin mine embeds the same full node and adds an interruptible CPU proof-of-work loop. It is not a client of a separately running node.

Command surface

The canonical CLI command names use hyphens. Legacy camelCase forms remain compatibility aliases for a small set of read/generate commands.

Command Purpose Long-running
generate-wallet Print new BIP39/BIP32 recovery material once as JSON. No
node Run P2P, private RPC, explorer API, and optional wallet gateway. Yes
mine --address ADDRESS Run the node services plus CPU mining to one reward address. Yes
get-info Read local network, height, next reward, and configured RPC port. No
get-balance ADDRESS Read confirmed account state from RocksDB. No
get-block HEIGHT Read and deserialize one indexed canonical block. No

Every command accepts --network, --data-dir, and --log-level. node and mine share the service flags documented below.

Network namespaces

Network selection changes more than a label. It selects the chain ID committed into transactions and handshakes, P2P magic, Redis database default, explorer port, data directory, and genesis manifest.

Network Chain ID P2P magic Redis DB RPC Explorer Initialization
mainnet 0 ba77d89f 0 8444 8000 Rejected: no genesis manifest.
testnet 1 fbc0b6db 1 18444 18000 Fixed educational genesis.
devnet 2 faceb00c 2 28444 28000 Fixed educational genesis.

Never reuse one directory or Redis DB between networks. A transaction, P2P handshake, or message from another chain ID is rejected, and the explorer projection stores network identity metadata to prevent accidental mixing.

Configuration precedence

The CLI does not load .env automatically. Configuration enters through the parent process environment or explicit flags.

  1. Explicit CLI flags select values for that invocation.
  2. Environment variables initialize the runtime defaults.
  3. Built-in network defaults fill everything else.

--data-dir is the exact directory for one node. Without it, LURACOIN_DATA_DIR is used. Otherwise the node resolves LURACOIN_HOME/<network>, with ~/.luracoin as the root default.

Important environment variables:

Variable Default Meaning
LURACOIN_NETWORK testnet Initial network choice.
LURACOIN_HOME ~/.luracoin Root below which network directories are created.
LURACOIN_DATA_DIR unset Exact private directory for the process.
LURACOIN_REDIS_HOST localhost Redis host.
LURACOIN_REDIS_PORT 6379 Redis port.
LURACOIN_REDIS_DB network-specific Redis logical DB containing the mempool.
LURACOIN_EXPLORER_API true Enable the read-only API.
LURACOIN_EXPLORER_HOST 127.0.0.1 Explorer bind address.
LURACOIN_EXPLORER_PORT network-specific Explorer TCP port.
LURACOIN_EXPLORER_INDEX false Create and follow the SQLite query projection.
LURACOIN_EXPLORER_DB <data-dir>/explorer.sqlite3 Projection path.
LURACOIN_EXPLORER_INDEX_INTERVAL 2 Base seconds between indexer passes.

The advanced sync and relay limits are consolidated in Constants & limits.

Service flags

--host ADDRESS                 P2P bind; default 0.0.0.0
--port PORT                    P2P port; default 9999
--seed HOST:PORT               Initial peer; repeatable
--rpc-host LOOPBACK            127.0.0.1, ::1, or localhost only
--rpc-port PORT                Network-specific default when omitted
--explorer-api / --no-explorer-api
--explorer-host, --api-host ADDRESS
--explorer-port, --api-port PORT
--explorer-index / --no-explorer-index
--explorer-db PATH
--wallet-dir PATH              Dedicated built SPA directory
--wallet-host 127.0.0.1|0.0.0.0
--wallet-port PORT             Default 8080

--explorer-index requires the explorer API. The CLI rejects --explorer-index --no-explorer-api instead of silently creating a database with no query surface.

The RPC host is constrained by the parser and again by RpcServer: only loopback names/addresses are accepted. There is no supported flag to expose it remotely.

Local development node

luracoin node --network testnet \
  --host 127.0.0.1 --port 9999 \
  --rpc-port 18444 --explorer-port 18000

This profile is intentionally isolated unless you add controlled seeds.

Controlled multi-node lab

Give each node unique directories, Redis DBs, and ports. Bind P2P to an explicit lab interface or loopback. Seed every new process from at least one node you control. Do not expose the alpha P2P listener to untrusted Internet traffic: it has framing limits and cooldowns, but no durable misbehavior score, per-IP rate limiter, or Sybil resistance.

Explorer-index node

luracoin node --network testnet --explorer-index

Core explorer routes become available immediately. Historical feeds, rankings, address histories, and aggregate statistics return a stable 503 until SQLite reaches the observed tip. See Explorer operations.

Node plus localhost wallet

luracoin node --network testnet \
  --wallet-dir ../luracoin-wallet/dist \
  --wallet-host 127.0.0.1 --wallet-port 8080

The directory must be a dedicated frontend artifact. The wallet server rejects a static root that contains the node’s private data directory and blocks any file named rpc.token.

Mining node

luracoin mine --network testnet \
  --address <VALID_TESTNET_ADDRESS> \
  --host 127.0.0.1 --port 9999 --rpc-port 18444

Mining begins only while node.ready is true. A higher peer interrupts stale proof of work; a received valid next block also interrupts the current template.

Data directory anatomy

A normal initialized directory contains:

<data-dir>/
├── accounts.db/          RocksDB account balances, nonces, apply markers
├── blocks.db/            RocksDB height → block location index
├── chainstate.db/        RocksDB tip, file number, hash and history indexes
├── blocks/
│   ├── blk000000.dat     length-prefixed canonical block records
│   └── ...
├── rpc.token             private bearer, normally mode 0600
└── explorer.sqlite3      only when the optional index is enabled
    ├── -wal              transient SQLite WAL sidecar
    └── -shm              transient SQLite shared-memory sidecar

The *.db entries are RocksDB directories, not single files. Block files rotate around 128 MiB. Redis is external and contains only pending data; it is not the canonical chain.

Startup sequence and readiness

The node initializes or validates genesis before opening sockets. It then starts P2P, RPC, optional wallet service, and explorer service. The startup summary reports:

  • selected network and chain ID;
  • local height and best connected peer height;
  • P2P, RPC, wallet, and explorer endpoints;
  • whether the explorer index was requested;
  • whether mining is active;
  • resolved data directory.

ready has a deliberately narrow meaning:

genesis exists
AND no synchronization task is active
AND local height >= highest height advertised by connected peers

An untrusted peer height is currently part of that decision. The alpha does not download and rank header chains by cumulative work, so ready is an operational signal—not a finality or trust guarantee.

Health and inspection

Use interfaces according to their intended audience:

# Local canonical state
luracoin get-info --network testnet
luracoin get-block --network testnet 0
luracoin get-balance --network testnet <ADDRESS>

# Public-data service health
curl -s http://127.0.0.1:18000/health
curl -s http://127.0.0.1:18000/api/v1/status

# Private RPC health does not require the token
curl -s http://127.0.0.1:18444/health

The explorer status separates node readiness from index readiness. Treat status.ready and status.index.ready as independent booleans.

Current logging uses Python logging with selectable DEBUG, INFO, WARNING, or ERROR. Structured metrics, disk alerts, peer scoring inspection, and production-grade observability remain roadmap work.

Graceful shutdown

Send SIGINT or SIGTERM. The CLI sets an asyncio event, then stops services in dependency-safe order:

  1. explorer API and indexer;
  2. optional wallet server;
  3. private RPC;
  4. P2P node, listener, peer readers, sync tasks, ping loop, and maintenance loop.

The miner also cancels its stop task and interrupts its proof-of-work worker. Avoid SIGKILL except when the process cannot respond: the persistence code has idempotent recovery boundaries, but the project does not claim fully journaled crash consistency across every store.

Backups

There is no live-backup command. For a consistent manual backup:

  1. Stop the node cleanly.
  2. Copy the entire data directory, preserving permissions and directory structure.
  3. If the explorer index is enabled, copy explorer.sqlite3, -wal, and -shm together—or omit all three and rebuild the projection later.
  4. Do not rely on Redis as backup material. Pending transactions are disposable and the mempool is not currently persisted or rebuilt automatically.
  5. Protect rpc.token like a credential. A restored token grants local RPC access to any process that can read it.

Wallet recovery material is not stored in the full node directory. Back up the wallet mnemonic separately and offline.

Recovery model

Block persistence is deliberately idempotent within its implemented boundaries:

  • each record is appended and fsynced before its location is published;
  • account updates and their applied-block marker share one RocksDB batch;
  • chain metadata and transaction/address indexes share another batch;
  • retrying the same block ID at the same height is a no-op where possible;
  • a record appended immediately after the tip but not indexed before a crash can be recovered from one of two adjacent positions;
  • conflicting or malformed records fail closed.

This does not provide rollback, branch storage, or a universal repair tool. There is no verify, reindex, or restore command for the canonical stores. If a long-lived alpha directory is suspect, preserve it read-only for diagnosis and start a clean directory from the fixed genesis rather than editing RocksDB or block files by hand.

The SQLite explorer projection is different: it is derived and rebuildable. Stop the node, move the database and sidecars as one unit, then restart with --explorer-index.

Common failure modes

Redis is unavailable

The node can still expose confirmed chain data, but transaction admission, mempool reads, selection, and normal wallet sending fail or report mempool_available: false. Confirm Redis host, port, DB number, and redis-cli ping.

Address already in use

Another node, miner, or stale development process owns the P2P, RPC, explorer, or wallet port. Stop it or choose a unique port. Never point two processes at the same data directory merely to work around a port collision.

Node reports ready with zero peers

This is expected for an isolated initialized node. Add controlled --seed endpoints and inspect peer_count and best_peer_height. No built-in seed list exists.

Node remains not ready

Inspect connected peer heights and logs. Synchronization is sequential in batches of 50. A peer that times out or supplies a rejected block is disconnected and enters an in-memory exponential host cooldown. The node then attempts the best remaining peer.

Mainnet fails at startup

This is intentional. Mainnet has no manifest. Use testnet or devnet; do not fabricate a genesis as an operational workaround.

Explorer indexed routes return 503

Read /api/v1/status. The index may be disabled, building, retrying after an error, or unavailable. Core block, transaction lookup, and address state routes remain independent of SQLite.

Wallet cannot find the token

Electron and the node are resolving different directories. Export the same LURACOIN_HOME root, or set LURACOIN_DATA_DIR to the exact directory in the shell that starts Electron.

Public exposure checklist

The private RPC and wallet gateway are local-only interfaces. Do not publish them.

The explorer API was designed for read-only public data, but the process itself does not include rate limiting, response quotas, disk quotas, TLS, or production observability. If you expose it:

  • bind explicitly and place it behind a TLS reverse proxy;
  • rate-limit by route and client;
  • cap request/response sizes and upstream timeouts;
  • monitor process memory, file descriptors, RocksDB reads, and SQLite growth;
  • serve only the intended testnet hostname;
  • verify /health and /api/v1/status network identity before adding it to the frontend;
  • never forward /v1/*, the wallet port, the P2P control surface, or rpc.token.

The current P2P listener should remain restricted to controlled experiments until peer scoring, rate limiting, Sybil defenses, headers-first synchronization, and multi-node soak testing are implemented.

Source anchors

Primary implementation files used for this chapter: