Protocol documentationSource snapshot: 0.1.0 alphaNetwork: testnet / devnet

Interfaces

Explorer operations

Enable and operate the read-only API and SQLite index, monitor backfill, rebuild safely, expose it behind a reverse proxy, and connect the Astro explorer without leaking private RPC.

Operator runbook · API v1 / index schema v1Reviewed 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.

Deployment shape

The public explorer has two independently deployable parts:

Browser
  └── luracoin.com (Astro static frontend on Cloudflare Pages)
        └── HTTPS GET -> public explorer API origin
                         └── Luracoin full node
                               ├── block files + RocksDB
                               └── optional explorer.sqlite3

The static website does not run the Python node or SQLite index. The API must run next to a full node on suitable persistent compute, then be published through a controlled HTTPS reverse proxy. Private RPC stays on loopback and is never part of this path.

Core-only startup

The Explorer API is enabled by default and binds to loopback:

luracoin node --network testnet

Verify locally:

curl -fsS http://127.0.0.1:18000/health
curl -fsS http://127.0.0.1:18000/api/v1/status
curl -fsS 'http://127.0.0.1:18000/api/v1/blocks?limit=5'

This supplies block list/detail, block transactions, transaction lookup, and address state without a second database.

Enable the historical index

Start with:

luracoin node --network testnet --explorer-index

The node creates the default projection at:

~/.luracoin/testnet/explorer.sqlite3

It backfills existing canonical blocks in the background, then follows the tip. Core API routes remain live while indexed routes return a state-specific 503.

The miner command accepts the same explorer options, so an operator can run one mining full-node process with both services when that topology is intentional.

CLI and environment controls

CLI Environment Default Meaning
--explorer-api / --no-explorer-api LURACOIN_EXPLORER_API true start or suppress public read-only API
--explorer-host, --api-host LURACOIN_EXPLORER_HOST 127.0.0.1 listen address
--explorer-port, --api-port LURACOIN_EXPLORER_PORT network-specific listen port
--explorer-index / --no-explorer-index LURACOIN_EXPLORER_INDEX false enable SQLite projection and indexer
--explorer-db LURACOIN_EXPLORER_DB data-dir explorer.sqlite3 projection path
LURACOIN_EXPLORER_INDEX_INTERVAL 2 seconds base indexer polling interval

CLI flags take precedence over environment-derived defaults. The CLI does not automatically load a .env file; export values or inject them with the service manager.

Strict boolean environment values are:

true:  1, true, yes, on
false: 0, false, no, off

Other values fail configuration. Port must be 1–65,535 and polling interval must be positive.

Enabling the index while disabling the API is contradictory and rejected.

Isolated custom example

export LURACOIN_NETWORK=testnet
export LURACOIN_DATA_DIR=/srv/luracoin/testnet
export LURACOIN_EXPLORER_API=true
export LURACOIN_EXPLORER_INDEX=true
export LURACOIN_EXPLORER_DB=/srv/luracoin-explorer/testnet.sqlite3
export LURACOIN_EXPLORER_PORT=19000

luracoin node --host 127.0.0.1 --port 9999

Keep the network data directory and index on persistent storage. Make their ownership explicit and do not share one database across testnet and devnet.

What SQLite contains

Schema v1 uses these conceptual tables:

Table Purpose
metadata schema, network, chain ID, indexed tip identity and progress
blocks header fields, counts, sizes, fees, rewards
transactions public transaction fields and canonical locations
addresses current derived balances, nonces, totals, activity ranges
address_transactions many-to-many address/history relationships and direction
chain_stats global counts and monetary aggregates

Raw blocks, complete unlocking signatures, Redis pending entries, RPC tokens, mnemonics, and private keys are not copied into the projection.

Amounts are stored as decimal text, not SQLite signed integers, so the full unsigned 64-bit protocol range survives.

SQLite uses WAL. During operation, expect:

explorer.sqlite3
explorer.sqlite3-wal
explorer.sqlite3-shm

The database may exceed raw block-file size because it denormalizes address relationships and aggregates for fast public queries.

Backfill algorithm

The indexer advances one canonical block at a time. Each block projection is one SQLite transaction:

  1. load block at next height from canonical storage;
  2. derive its summary and transaction rows;
  3. update address aggregates and relationships in transaction order;
  4. update chain-wide statistics;
  5. publish indexed height and block ID metadata;
  6. commit the SQLite transaction.

If the process exits mid-block, SQLite rolls that block back. Restart resumes from the last complete height.

After reaching the observed target, the indexer polls for new blocks. A newly advanced tip moves it briefly back through catch-up before ready state.

Identity and divergence checks

Before continuing an existing projection, the index verifies:

  • schema version;
  • configured network name;
  • chain ID;
  • last indexed height;
  • last indexed block ID against the canonical source.

A database for another network is rejected rather than mixed.

When the prefix belongs to the same network but no longer matches canonical local history, the projection is disposable and rebuilt from the full-node source. This protects public query consistency; it does not add full-node reorganization support.

Monitoring backfill

Poll status conservatively:

curl -fsS http://127.0.0.1:18000/api/v1/status

Key fields:

{
  "height": 5000,
  "tip_id": "...",
  "ready": true,
  "index": {
    "enabled": true,
    "ready": false,
    "state": "building",
    "indexed_height": 1249,
    "target_height": 5000,
    "progress": 24.99
  }
}

Alert on:

  • index.state == "error";
  • indexed height not advancing while canonical height is stable/ahead;
  • repeated 503 explorer_index_unavailable responses;
  • database, WAL, or filesystem growth near capacity;
  • process restarts or Uvicorn bind failures;
  • canonical tip identity disagreements between expected nodes;
  • API latency and response-size outliers.

An indexer failure does not stop the full node. Retries use exponential backoff capped at 60 seconds, so process health alone is insufficient.

Safe manual rebuild

There is no standalone reindex command. To rebuild:

  1. stop the node cleanly;
  2. confirm no other process uses the database;
  3. move the main SQLite file and matching -wal/-shm files together into a timestamped backup directory;
  4. keep canonical block files and RocksDB untouched;
  5. restart with --explorer-index;
  6. watch status until indexed height and ID match the canonical tip;
  7. test indexed routes before discarding the backup.

Do not delete only the main .sqlite3 file while leaving WAL companions. Do not manipulate a live database by hand.

Because the projection is not consensus authority, rebuilding it must never change balances, block acceptance, node height, or Redis state.

Backup policy

Choose one of two explicit policies:

  • omit SQLite from backups and accept a full backfill after restore; or
  • stop the node and snapshot the SQLite file plus WAL/SHM companions consistently.

Always preserve the canonical node data separately. A SQLite-only backup cannot recreate a full node.

Public reverse proxy

Keep Uvicorn on loopback and terminate public HTTPS in a hardened proxy. A production proxy should enforce:

  • GET/HEAD-only access to public routes;
  • TLS and modern HTTP policy;
  • connection, request, and upstream timeouts;
  • request rate and concurrent-request limits;
  • maximum response buffering/size policy;
  • structured access logs and origin health checks;
  • cache rules for immutable block/transaction IDs;
  • short or no caching for status, latest blocks, rankings, and stats.

Never proxy the RPC port. A safe hostname such as api.luracoin.com should route only to the Explorer API port.

Before publication, test from outside the origin network that these fail:

/v1/status
/v1/transactions
any request carrying or attempting to discover rpc.token
non-GET mutation methods
direct access to Redis, RocksDB, SQLite, or the P2P administrative host

CORS behavior

The API returns CORS permission for credential-free GET from any origin. That makes it consumable from luracoin.com, but it also makes every public response available to any website.

Do not add cookies, bearer credentials, private account data, or administrative endpoints behind this same permissive policy.

Connect the Astro website

The website reads a build-time public variable:

PUBLIC_EXPLORER_API_URL=https://api.luracoin.com npm run build

For Cloudflare Pages, set PUBLIC_EXPLORER_API_URL in the Pages project’s production environment, then trigger a new deployment. Astro substitutes public variables during the static build.

Use the base origin only:

correct:   https://api.luracoin.com
incorrect: https://api.luracoin.com/api/v1/status
incorrect: http://127.0.0.1:18444

The frontend adds endpoint paths and must never receive the RPC token.

Placeholder phase

Until a stable public API origin exists, the explorer page intentionally communicates that live data is not connected. It must not display fabricated heights, hashes, transactions, prices, or address balances.

The integration sequence is:

  1. run a dedicated, synchronized testnet full node;
  2. enable the explorer index and wait for ready;
  3. publish only the read-only API through HTTPS;
  4. validate CORS, limits, errors, and schema using /openapi.json;
  5. set the Pages build variable;
  6. deploy the frontend;
  7. run smoke tests for status, list, detail, search, empty, building, error, and timeout states.

API smoke test

LURA_EXPLORER_ORIGIN=https://api.luracoin.com

curl -fsS "$LURA_EXPLORER_ORIGIN/health"
curl -fsS "$LURA_EXPLORER_ORIGIN/api/v1/status"
curl -fsS "$LURA_EXPLORER_ORIGIN/api/v1/features"
curl -fsS "$LURA_EXPLORER_ORIGIN/api/v1/blocks?limit=2"
curl -fsS "$LURA_EXPLORER_ORIGIN/openapi.json"

unset LURA_EXPLORER_ORIGIN

Then verify an existing block by height and ID, its transaction pagination, an existing transaction, an unused valid address, an invalid address, and every index-not-ready state.

Incident response

If public responses appear inconsistent:

  1. preserve logs and current status payload;
  2. compare canonical height/tip ID with the index metadata;
  3. compare the node with trusted controlled peers by both height and tip ID;
  4. take the public origin out of rotation if it may return false data;
  5. leave canonical stores untouched;
  6. rebuild only the SQLite projection when its prefix is the problem;
  7. start a clean testnet data directory when canonical storage itself is unsupported or corrupt.

Because the alpha has no branch/reorg recovery, a canonical fork disagreement is not fixed by rebuilding SQLite. It requires controlled node/network recovery.

Source anchors

Primary implementation files used for this chapter: