Skip to content
LogoLogo

Operating Arrekta nodes

How to monitor nodes, run the checkpoint group, use snapshots, ship releases and respond to incidents. Protocol details are in core/PROTOCOL.md.

Local container stack

./all_svc/arrekta-compose.sh runs everything from local images under the compose project arrekta. The services are defined in three files, and you can comment out any service block you don't run:

FileServicesProfile
all_svc/compose/core.ymlarrekta-node-1..3always
all_svc/compose/platform.ymlPostgres, Redis, indexer, gateway, wallet, issuerplatform
all_svc/compose/services.ymlarrekta-explorer (:28080), arrekta-docs (:28081)web
arrekta-channeld (API :29737, P2P :29736)channels
arrekta-faucet (:28090)faucet
arrekta-mesh (:28085)mesh
arrekta-prometheus (:29090)monitoring
./all_svc/arrekta-compose.sh build all-images      # or: build web | explorer docs channeld …
./all_svc/arrekta-compose.sh up web                # core + explorer + docs
./all_svc/arrekta-compose.sh up all                # core + every profile
./all_svc/arrekta-compose.sh up arrekta-docs         # a single service, any time
./all_svc/arrekta-compose.sh stop arrekta-explorer
  • Host ports are loopback-only, except channeld's P2P port.
  • The explorer image is built once. It reaches the node through a same-origin /v1 proxy, and its environment chooses the node, docs link, channel graph and faucet when it starts.
  • Secret files for channeld and the faucet are created in .secrets/ on first use and never overwritten.
  • Fund the faucet's address (it is logged at start) before using it.

Monitoring

Every node serves Prometheus metrics at /metrics. Config and alert rules are in ops/monitoring/:

prometheus --config.file=ops/monitoring/prometheus.yml      # scrapes arrekta-node-1..3, loads alerts.yml
MetricMeaning
arrekta_block_heightactive chain height
arrekta_finality_heighthighest final block (signed checkpoint or snapshot base)
arrekta_checkpoints_pendingcheckpoints still collecting signatures
arrekta_last_reorg_depth, arrekta_reorgs_totaldepth of the latest reorg, and the count
arrekta_blocks_connected_totalconnected blocks (its rate is the effective hashrate signal)
arrekta_peers{direction}inbound and outbound peers
arrekta_rejects_total{kind,code}rejected blocks, transactions, packages, checkpoints
arrekta_txs_replaced_totalfee-bump replacements

Alerts and what to do

AlertFirst steps
ArrektaNodeDownCheck the container or service and its disk; restart; the node resumes from its last committed state
ArrektaChainStalledLook at arrekta_blocks_connected_total on all nodes. If every node stalled, the miners or merged-mining pools stopped: contact them. If one node stalled, check its peers
ArrektaBlockRateLowMerged-mining hashrate dropped. Contact pools. Ask issuers and exchanges to raise their confirmation depth until it recovers, or rely on checkpoints
ArrektaDeepReorg (≥ 6)Incident. Freeze large withdrawals at integrated services; compare tips (/v1/chain) and finality floors (/v1/checkpoints) across nodes; capture logs; follow Incident response
ArrektaNodesDivergedCompare /v1/chain and /v1/admin/integrity per node. A node on a minority branch that disagrees with the finality floor is either misconfigured or attacked
ArrektaFinalityLagging, ArrektaCheckpointStuckToo few signers online. Check signer nodes (ARREKTA_CHECKPOINT_SEED set, connected, synced)
ArrektaFewPeers, ArrektaNoOutboundCheck connectivity and ARREKTA_PEERS. Low peer counts raise eclipse risk
ArrektaRejectSpikeLook at the code label: spam (policy codes) or a misbehaving peer (consensus codes; it gets banned automatically)

Merged mining (pools)

  • Pools call the node's JSON-RPC at POST /rpc:

    • createauxblock <address> returns hash, chainid (0x0D07), _target and height;
    • submitauxblock <hash> <auxpow-hex> submits the solution.

    This is the same interface Namecoin and Syscoin expose, so existing merged-mining pool software works with Arrekta as an extra aux chain.

  • Solo miners use /v1/mining/template (header_hex, txs_hex), build a standalone AuxPoW (PROTOCOL.md §9.6.3), and POST header ‖ auxpow ‖ txs to /v1/mining/submit.

Checkpoint group

  • Setup:

    • Each signer runs a synced node with ARREKTA_CHECKPOINT_SEED=<64 hex chars> (default interval 10, depth 6).
    • Generate seeds offline, one per signer, from a hardware RNG.
    • The group (threshold m, public keys) is set in the network parameters at the genesis ceremony.
  • Rotation:

    1. Build the new group's encoded SignerSet.
    2. Have m current signers sign a checkpoint carrying it as next_set (for example with arrekta_core.checkpoint.sign(...)).
    3. POST each partial signature to /v1/checkpoints.

    From the next height on, only the new group counts. No release is needed.

  • Never sign two different hashes at one height, and never reuse the public regtest seeds (0x31…, 0x32…, 0x33…).

  • GET /v1/checkpoints shows the group, the latest final checkpoint, pending signatures and the finality floor.

Snapshots

arrekta-node-rs snapshot export snap.arrks         # on a synced node (stop it first: the database is exclusive)
arrekta-node-rs snapshot import snap.arrks         # into an EMPTY data directory, then start the node
  • What import checks: every header's proof of work, the state hash against the commitment, and it sets a finality floor at the snapshot height.
  • When it's proven: once the node connects block H + 100, the snapshot is proven by proof of work.
  • Distribution: snapshots carry a checksum, but treat their source like any download. Consensus rejects a forged one at H + 100.
  • Interoperability: the Python reference has the same commands (arrekta-node snapshot …). Files are interchangeable.

Pruned mode

ARREKTA_PRUNE=<blocks> (Rust node) keeps the bodies of only the most recent blocks; 0, the default, keeps everything. The minimum is 288. At the 8 MB block limit the chain can grow by up to 5.8 GB a day, so wallets' and exchanges' own nodes usually want this.

  • Kept: headers, the UTXO set, the transaction index, compact filters and state commitments. Validation, sync, mining, light-client serving and balance lookups work as usual.
  • Deleted: block bodies and undo data more than N blocks below the tip, and never above the finality floor. A background task removes them in batches of 500.
  • To peers: the node advertises NODE_NETWORK_LIMITED instead of NODE_NETWORK. Peers fetch older blocks from archival nodes (PROTOCOL.md §12.2).
  • API: block transactions, raw blocks and transactions in pruned blocks return 410 block-pruned. /v1/chain reports pruned_height; Prometheus has arrekta_pruned_height.
  • Limits:
    • Run the indexer, explorer and gateway against an archival node.
    • A reorg deeper than the pruned height cannot be followed. The node logs an error and stays put; resync it. Finality checkpoints make this practically impossible on public networks.
    • Snapshot (fast start) plus pruning gives a full-validating node without the history.

Peer bandwidth (Rust node)

ARREKTA_P2P_PEER_BANDWIDTH_KBPS (default 8192, i.e. 8 MiB/s) caps what each peer may use, in each direction. It is a one-second token bucket: a peer over budget is not disconnected, the node simply waits before reading its next message (TCP backpressure slows the sender) or sending it the next one. An honest peer during initial sync only slows down; a peer flooding us, or asking for blocks without end, cannot take more than its share. 0 turns it off. GET /v1/admin/peers shows bytes_recv, bytes_sent and throttled_secs per peer.

assumevalid (Rust node)

ARREKTA_ASSUME_VALID=<block hash> speeds up a first sync the way Bitcoin Core's -assumevalid does: blocks that are ancestors of that block are connected without checking witness signatures. Everything else is still checked: proof of work, amounts, double spends, asset and issuer rules, state and filter commitments. It applies only while:

  • the block is on the most-work header chain, and
  • it is buried at least two weeks of blocks under the best header (6 blocks on regtest).

A fork that does not contain it, or a block above it, is fully verified. Take the hash from the release notes of a build you trust; empty (the default) verifies everything. The node logs assumevalid: skipping signature checks … when it starts using it.

Supply endpoints (listing sites, exchanges)

CoinGecko and CoinMarketCap read supply from URLs the project gives them and refresh it every few minutes. Every node serves them:

URLReturns
/v1/supplyJSON: total_supply, circulating_supply, max_supply (null: no hard cap, tail emission), issued, burned, immature, non_circulating wallets, and emission (current reward, next halving, annual issuance and inflation)
/v1/supply/totaltotal supply in whole ARRK, plain text (12345.67890000)
/v1/supply/circulatingcirculating supply in whole ARRK, plain text
  • Total supply is every ARRK in existence: issued minus burned, read from the chain state.
  • Circulating supply is total minus immature coinbase outputs (not spendable for 100 blocks) minus the wallets listed in ARREKTA_SUPPLY_NONCIRCULATING="foundation=arrk1…,locked=arrk1…". The JSON lists those wallets with their balances, which CoinGecko requires before it accepts a circulating figure below the total.
  • Give listing sites the public API host's URLs (https://api.<domain>/v1/supply/circulating).

Tor and I2P

The Rust node can reach and be reached over Tor and I2P, as Bitcoin Core does. Every connection, whatever carries it, still runs the post-quantum handshake (PROTOCOL.md §12.1).

VariableMeaning
ARREKTA_PROXYSOCKS5 proxy for all outbound connections (e.g. Tor at 127.0.0.1:9050). Names are resolved by the proxy, so DNS never leaks
ARREKTA_ONION_PROXYSOCKS5 proxy for .onion peers only (defaults to ARREKTA_PROXY)
ARREKTA_TOR_CONTROL / ARREKTA_TOR_PASSWORD(_FILE)Tor control port. The node publishes a v3 onion service for inbound connections and keeps its key in onion_v3_private_key in the data dir, so the address is stable
ARREKTA_I2P_SAMI2P router SAM v3.1 bridge (e.g. 127.0.0.1:7656)
ARREKTA_I2P_ACCEPTaccept inbound I2P (default true when the SAM bridge is set). The persistent destination is in i2p_private_key; outbound uses a fresh transient identity per connection
ARREKTA_ONLYNETautomatic outbound only to these networks: ipv4, ipv6, onion, i2p (manual and inbound connections are not affected)

How it behaves:

  • Each outbound SOCKS5 connection uses random credentials, so Tor builds a separate circuit for it (stream isolation).
  • Without a proxy for .onion, onion addresses are refused rather than looked up in clearnet DNS.
  • Our onion and I2P addresses are advertised in ADDR replies.
  • Address buckets treat onion and I2P addresses as their own network groups. The node first fills an outbound slot on any reachable network it has no peer on yet, so one network's failure cannot isolate it.
  • onlynet=onion alone makes a node easier to eclipse (all its peers come through one network). Prefer adding Tor next to clearnet, or combining onion and I2P.

DNS seeds: peer entries that are DNS names (ARREKTA_PEERS, the network's seeds) are dialled by name. Without a proxy, every address the name resolves to also joins the address table. Seeders run by several independent operators are listed in the network's seed list. With a proxy, names are only resolved through it.

The Python reference node does not implement Tor or I2P. It is a consensus reference, not an edge node.

Payment channel node

arrekta-channeld runs Arrekta's L2 next to a node (design: CHANNELD.md, user guide on the docs site: Payment channels). In the testnet deployment (deploy/) it is switched on with ARREKTA_CHANNELS=true in .env.production:

cd deploy
./deploy.sh secrets            # creates secrets/channeld_seed and secrets/channeld_token (0600)
./deploy.sh deploy
TOKEN=$(cat secrets/channeld_token)
curl -s -H "authorization: Bearer $TOKEN" http://127.0.0.1:9737/v1/info
curl -s -H "authorization: Bearer $TOKEN" http://127.0.0.1:9737/v1/wallet   # fund this address
ConcernPractice
Backupsthe seed and the channeld-data volume, continuously. A stale backup must never be used to close a channel (the node detects it on reconnect and refuses)
Exposure9736 (P2P) public only when routing for others; 9737 (REST) bound to the host's loopback, never public
Walletkeep ARRK in the node's wallet: closes and claim sweeps pay fees by CPFP
Watchtowerthe testnet node serves as a tower (ARREKTA_CHANNELD_TOWER=true); operators' own nodes point ARREKTA_CHANNELD_TOWERS at one or more towers
Public graphARREKTA_CHANNELD_PUBLIC_GRAPH=true in the bundle; Caddy routes only /public/v1/* on the explorer origin to it, so the explorer's Channels page works and the owner API stays unreachable
MonitoringPrometheus job arrekta-channeld. Alert on arrekta_channeld_force_closes_total or arrekta_channeld_justice_broadcast_total increasing, and on arrekta_channeld_peers == 0
Upgradesstop, upgrade, start. The store is versioned; channels reconnect and resume interrupted rounds

Releases

  1. Tag the release commit.
  2. tools/release/build-release.sh --check builds the five binaries twice in clean, network-less containers and fails unless they are bit-for-bit identical. It writes dist/<version>/SHA256SUMS.
  3. At least two release managers or independent builders reproduce the build, then co-sign with tools/release/sign_release.py sign <their-key> dist/<version>/SHA256SUMS (ML-DSA-65, post-quantum).
  4. Users verify with sign_release.py verify dist/<version>/SHA256SUMS --trusted release-keys.txt --threshold 2.

Incident response

  1. Detect: an alert fires or a report arrives via SECURITY.md.
  2. Contain:
    • for consensus or chain incidents, ask integrated services (exchanges, issuers, the gateway's operators) to pause large withdrawals and raise confirmation depth;
    • signers may checkpoint the honest chain early (/v1/checkpoints) to end a reorg attack.
  3. Diagnose: collect /v1/chain, /v1/checkpoints, /v1/admin/integrity, logs and metrics from every node; reproduce on regtest.
  4. Fix:
    • policy bugs ship as a normal release;
    • consensus bugs follow PROTOCOL.md §15: a soft fork via a reserved lock kind or version-bits deployment, with both implementations and vectors updated together.
  5. Disclose: follow SECURITY.md timelines; publish an advisory signed with the release keys.