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:
| File | Services | Profile |
|---|---|---|
all_svc/compose/core.yml | arrekta-node-1..3 | always |
all_svc/compose/platform.yml | Postgres, Redis, indexer, gateway, wallet, issuer | platform |
all_svc/compose/services.yml | arrekta-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
/v1proxy, 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
| Metric | Meaning |
|---|---|
arrekta_block_height | active chain height |
arrekta_finality_height | highest final block (signed checkpoint or snapshot base) |
arrekta_checkpoints_pending | checkpoints still collecting signatures |
arrekta_last_reorg_depth, arrekta_reorgs_total | depth of the latest reorg, and the count |
arrekta_blocks_connected_total | connected 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_total | fee-bump replacements |
Alerts and what to do
| Alert | First steps |
|---|---|
ArrektaNodeDown | Check the container or service and its disk; restart; the node resumes from its last committed state |
ArrektaChainStalled | Look 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 |
ArrektaBlockRateLow | Merged-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 |
ArrektaNodesDiverged | Compare /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, ArrektaCheckpointStuck | Too few signers online. Check signer nodes (ARREKTA_CHECKPOINT_SEED set, connected, synced) |
ArrektaFewPeers, ArrektaNoOutbound | Check connectivity and ARREKTA_PEERS. Low peer counts raise eclipse risk |
ArrektaRejectSpike | Look 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>returnshash,chainid(0x0D07),_targetandheight;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 POSTheader ‖ auxpow ‖ txsto/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.
- Each signer runs a synced node with
-
Rotation:
- Build the new group's encoded
SignerSet. - Have m current signers sign a checkpoint carrying it as
next_set(for example witharrekta_core.checkpoint.sign(...)). - POST each partial signature to
/v1/checkpoints.
From the next height on, only the new group counts. No release is needed.
- Build the new group's encoded
-
Never sign two different hashes at one height, and never reuse the public regtest seeds (
0x31…,0x32…,0x33…). -
GET /v1/checkpointsshows 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_LIMITEDinstead ofNODE_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/chainreportspruned_height; Prometheus hasarrekta_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:
| URL | Returns |
|---|---|
/v1/supply | JSON: 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/total | total supply in whole ARRK, plain text (12345.67890000) |
/v1/supply/circulating | circulating 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).
| Variable | Meaning |
|---|---|
ARREKTA_PROXY | SOCKS5 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_PROXY | SOCKS5 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_SAM | I2P router SAM v3.1 bridge (e.g. 127.0.0.1:7656) |
ARREKTA_I2P_ACCEPT | accept 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_ONLYNET | automatic 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
ADDRreplies. - 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=onionalone 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| Concern | Practice |
|---|---|
| Backups | the 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) |
| Exposure | 9736 (P2P) public only when routing for others; 9737 (REST) bound to the host's loopback, never public |
| Wallet | keep ARRK in the node's wallet: closes and claim sweeps pay fees by CPFP |
| Watchtower | the testnet node serves as a tower (ARREKTA_CHANNELD_TOWER=true); operators' own nodes point ARREKTA_CHANNELD_TOWERS at one or more towers |
| Public graph | ARREKTA_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 |
| Monitoring | Prometheus job arrekta-channeld. Alert on arrekta_channeld_force_closes_total or arrekta_channeld_justice_broadcast_total increasing, and on arrekta_channeld_peers == 0 |
| Upgrades | stop, upgrade, start. The store is versioned; channels reconnect and resume interrupted rounds |
Releases
- Tag the release commit.
tools/release/build-release.sh --checkbuilds the five binaries twice in clean, network-less containers and fails unless they are bit-for-bit identical. It writesdist/<version>/SHA256SUMS.- 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). - Users verify with
sign_release.py verify dist/<version>/SHA256SUMS --trusted release-keys.txt --threshold 2.
Incident response
- Detect: an alert fires or a report arrives via SECURITY.md.
- 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.
- Diagnose: collect
/v1/chain,/v1/checkpoints,/v1/admin/integrity, logs and metrics from every node; reproduce on regtest. - 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.
- Disclose: follow SECURITY.md timelines; publish an advisory signed with the release keys.