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.
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:
- load block at next height from canonical storage;
- derive its summary and transaction rows;
- update address aggregates and relationships in transaction order;
- update chain-wide statistics;
- publish indexed height and block ID metadata;
- 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_unavailableresponses; - 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:
- stop the node cleanly;
- confirm no other process uses the database;
- move the main SQLite file and matching
-wal/-shmfiles together into a timestamped backup directory; - keep canonical block files and RocksDB untouched;
- restart with
--explorer-index; - watch status until indexed height and ID match the canonical tip;
- 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:
- run a dedicated, synchronized testnet full node;
- enable the explorer index and wait for ready;
- publish only the read-only API through HTTPS;
- validate CORS, limits, errors, and schema using
/openapi.json; - set the Pages build variable;
- deploy the frontend;
- 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:
- preserve logs and current status payload;
- compare canonical height/tip ID with the index metadata;
- compare the node with trusted controlled peers by both height and tip ID;
- take the public origin out of rotation if it may return false data;
- leave canonical stores untouched;
- rebuild only the SQLite projection when its prefix is the problem;
- 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:
