Protocol documentationSource snapshot: 0.1.0 alphaNetwork: testnet / devnet

Interfaces

Explorer API

The public read-only API contract: core and indexed capabilities, exact routes, pagination, response models, precision rules, index states, CORS, errors, and frontend integration.

Public read-only API · version 1Reviewed 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.

API role

The Explorer API is a public, read-only view of one full node’s canonical chain. It powers block explorers, status pages, transaction lookups, address views, and aggregate dashboards.

It has no transaction submission, wallet secret, mining, peer-control, or administrative route. It never reads or exposes rpc.token.

The service has two capability tiers:

Tier Data source Availability
core canonical block files and RocksDB indexes enabled with the API
indexed optional denormalized SQLite projection only after index reaches ready state

The core tier keeps an ordinary full node useful without paying SQLite storage and backfill cost. Collection-wide history, rankings, and aggregates require the index.

Startup and discovery

A standard testnet node starts the core API on loopback:

luracoin node --network testnet

Default service URLs:

Swagger UI   http://127.0.0.1:18000/docs
ReDoc        http://127.0.0.1:18000/redoc
OpenAPI      http://127.0.0.1:18000/openapi.json
Health       http://127.0.0.1:18000/health
Status       http://127.0.0.1:18000/api/v1/status

Network ports:

Network Default explorer port
mainnet 8000
testnet 18000
devnet 28000

Mainnet’s port is reserved while mainnet initialization remains unavailable.

Route capability matrix

Core routes work without SQLite:

Method Route Result
GET /health process, network, chain ID, API version
GET /api/v1/status canonical tip, node state, index state, capabilities
GET /api/v1/features compact core/index availability
GET /api/v1/blocks newest-first canonical block summaries
GET /api/v1/blocks/{height-or-hash} one block summary
GET /api/v1/blocks/{height-or-hash}/transactions paginated transactions in one block
GET /api/v1/transactions/{txid} one confirmed transaction
GET /api/v1/addresses/{address} current confirmed balance and nonce

Indexed routes require a ready SQLite projection:

Method Route Result
GET /api/v1/transactions global confirmed transaction feed
GET /api/v1/addresses addresses ranked by balance
GET /api/v1/addresses/{address}/transactions full indexed address history
GET /api/v1/stats global counts and monetary aggregates

The exact machine-readable contract is generated at /openapi.json from response models and parameter bounds.

JSON precision

All monetary fields are decimal strings, including:

balance, fee, value, reward, total_fees, total_received,
total_sent, total_transferred, total_rewards

Example:

{
  "value": "18446744073709551615",
  "fee": "4294967295"
}

This is deliberate. JavaScript’s number cannot represent the entire uint64 range exactly. UI formatting should parse with BigInt, then divide into LURA/lurashi units without floating-point consensus arithmetic.

Health and status

GET /health:

{
  "ok": true,
  "network": "testnet",
  "chain_id": 1,
  "api_version": 1
}

GET /api/v1/status:

{
  "network": "testnet",
  "chain_id": 1,
  "api_version": 1,
  "initialized": true,
  "height": 2499,
  "tip_id": "000000...",
  "ready": true,
  "syncing": false,
  "index": {
    "enabled": true,
    "ready": false,
    "state": "building",
    "indexed_height": 1249,
    "target_height": 2499,
    "progress": 50.0
  },
  "capabilities": {
    "block_list": true,
    "block_lookup": true,
    "block_transactions": true,
    "transaction_lookup": true,
    "address_state": true,
    "transaction_feed": false,
    "address_ranking": false,
    "address_history": false,
    "aggregate_stats": false
  }
}

ready refers to the full node. index.ready refers to the SQLite projection. They answer different questions and may differ.

An uninitialized canonical chain reports height -1 and omits tip_id.

Feature state

GET /api/v1/features returns:

{
  "api_version": 1,
  "features": {
    "core": { "enabled": true, "ready": true },
    "explorer_index": {
      "enabled": false,
      "ready": false,
      "state": "disabled"
    }
  }
}

Clients should inspect capabilities or feature state before showing an indexed view. Do not treat a 503 as an empty transaction feed.

Block summary model

Every block list/detail item contains:

Field Type Meaning
height integer canonical height
prev_block_hash string 64-hex predecessor ID
miner string 34-character reward address
id string 64-hex block ID
nonce integer PoW nonce
version integer block format version
timestamp integer Unix seconds
bits string four compact-target bytes as eight hex characters
transaction_count integer coinbase plus normal transactions
size_bytes integer 118 + transaction_count × 213
total_fees decimal string sum of non-coinbase fees
reward decimal string coinbase value
confirmations integer local depth including the block itself

Block detail deliberately omits full transaction bodies; use its transactions subroute.

Listing blocks

GET /api/v1/blocks?limit=10&offset=0&before_height=2500

Constraints:

  • limit: 1–10, default 10;
  • offset: 0–2,147,483,647;
  • before_height: optional 0–uint32.

Items are newest first. The server loads and discards one block at a time to avoid materializing up to ten 8 MiB blocks in memory.

Response:

{
  "network": "testnet",
  "items": [],
  "pagination": {
    "limit": 10,
    "offset": 0,
    "count": 10,
    "total": 2500,
    "before_height": null,
    "next_before_height": 2490
  }
}

For stable backward traversal, pass next_before_height into the next call. This avoids a growing offset while the tip advances. before_height is exclusive: an anchor of 2490 begins below it.

Resolving one block

Both are valid:

GET /api/v1/blocks/42
GET /api/v1/blocks/000000...64 hex characters...

A decimal reference must fit uint32. Hash input accepts either hex case and is normalized for lookup. Any other syntax returns invalid_block_reference; a valid reference with no canonical object returns block_not_found.

Response envelope:

{
  "network": "testnet",
  "block": { "height": 42, "id": "..." }
}

The actual block object follows the complete block-summary model above.

Transaction model

Explorer transactions contain:

Field Type Meaning
id string canonical transaction ID
chain integer network chain ID
nonce integer sender nonce or coinbase block height
fee decimal string lurashis
value decimal string lurashis
from_address string sender or zero coinbase sentinel
to_address string recipient
unlock_sig string/null 128 bytes as hex when included
block_height integer containing canonical height
block_id string containing block ID
block_timestamp integer containing block Unix time
transaction_index integer zero-based position
confirmations integer local canonical depth
is_coinbase boolean, optional projection/core classification
direction string, optional address-history context

Core transaction responses may include the unlocking field because the canonical transaction object contains it. Treat it as public signature/key material, never as a secret.

Block transactions

GET /api/v1/blocks/{block_ref}/transactions?limit=25&offset=0

limit is 1–100, default 25. The response includes a compact block reference, items, and total transaction count:

{
  "network": "testnet",
  "block": { "height": 42, "id": "..." },
  "items": [],
  "pagination": {
    "limit": 25,
    "offset": 0,
    "count": 25,
    "total": 80
  }
}

Transaction indices reflect their absolute positions even on later pages. Coinbase is index zero.

Transaction lookup and feed

Confirmed lookup is core:

GET /api/v1/transactions/{64-hex-txid}

It validates the ID, resolves the RocksDB transaction location, loads the block, bounds the transaction index, and rechecks that the transaction at that location has the requested ID. Stale/mismatched lookup state does not produce a false result.

The global feed is indexed:

GET /api/v1/transactions?limit=25&offset=0

It supports limit 1–100 and bounded non-negative offset. Results are confirmed transactions only.

Address state

Core route:

GET /api/v1/addresses/{address}

Response:

{
  "network": "testnet",
  "address": "L...",
  "exists": true,
  "balance": "700000000",
  "nonce": 3
}

A syntactically valid address with no canonical account returns exists: false, zero balance, and nonce zero. This is distinct from an invalid address (400).

This public view is confirmed-only. It does not expose private RPC’s Redis-derived spendable balance or pending nonce.

Address summary and ranking

Indexed ranking:

GET /api/v1/addresses?limit=25&offset=0

Each summary contains:

address
balance
nonce
total_received
total_sent
total_fees
transaction_count
first_seen_height
last_seen_height

All monetary totals are strings. Ranking is derived from the current SQLite projection, not a consensus commitment.

Address history

Indexed route:

GET /api/v1/addresses/{address}/transactions?limit=25&offset=0

The envelope includes network, address, transaction items, and pagination. A transaction involving the address can include an optional contextual direction; do not use that label as a consensus field.

Aggregate statistics

GET /api/v1/stats returns:

{
  "network": "testnet",
  "stats": {
    "network": "testnet",
    "chain_id": 1,
    "indexed_height": 2499,
    "indexed_block_id": "...",
    "blocks": 2500,
    "transactions": 8120,
    "addresses": 445,
    "total_fees": "10000",
    "total_transferred": "920000000000",
    "total_rewards": "12500000000000"
  }
}

Statistics describe the indexed canonical prefix. Consumers should compare indexed_height and block ID with status rather than assuming they equal the live tip.

Index states and 503 behavior

Normal index states:

State Enabled Ready Meaning
disabled no no index not requested
building yes no backfill or catch-up in progress
ready yes yes reached observed target
error yes no latest indexing/status operation failed

Indexed routes return one of:

{ "error": "explorer_index_disabled", "index": { "state": "disabled", "enabled": false, "ready": false } }
{ "error": "explorer_index_building", "index": { "state": "building", "enabled": true, "ready": false } }
{ "error": "explorer_index_error", "index": { "state": "error", "enabled": true, "ready": false } }
{ "error": "explorer_index_unavailable" }

All use HTTP 503. Core routes remain available.

Other errors

Status Error/case
400 invalid_block_reference
400 invalid_transaction_id
400 invalid_address
404 block_not_found
404 transaction_not_found
422 FastAPI parameter validation failure
503 index state/unavailable errors above

FastAPI’s 422 body follows its validation-error schema. Clients should tolerate additional fields in future compatible API responses.

CORS and publication

The service enables cross-origin GET requests from any origin, without credentials, with a 600-second preflight cache. It remains bound to 127.0.0.1 by default.

Publishing it requires an explicit host change and an external reverse proxy with:

  • TLS;
  • per-IP and global rate limits;
  • request/response size controls;
  • upstream timeouts and concurrency bounds;
  • caching policy for immutable block/transaction lookups;
  • health checks and logs;
  • abuse and disk-pressure monitoring.

The application itself provides no authentication, rate limiting, or SQLite quota.

Website configuration

The Astro explorer reads a public build-time base origin:

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

Configure only the origin/base URL. The browser appends /api/v1/status, /api/v1/blocks, and other routes.

If this variable is absent, the current public website stays in a transparent preview/placeholder state rather than inventing chain data. That is the correct behavior until a stable public API endpoint exists.

Confirmation disclaimer

The API calculates:

confirmations = local_chain_height - block_height + 1

This is depth in one node’s linear canonical history. Because the alpha has no cumulative-work fork choice or reorganization support, confirmations are not public-network finality. Explorer UI must retain that distinction.

Source anchors

Primary implementation files used for this chapter: