Shekyl Stats

Refreshing...

Network

Connected
Seed Nodes Active
--

Chain

Current Block
0
Target Height
0
Top Block Hash
--
Block Time Target
2 min

Rewards

Last Block Reward
0.000000 SKL
Difficulty
0
Estimated Hash Rate
0 H/s

Supply

Circulating Supply
--
Remaining Supply
--
Total Burned
0.000000 SKL

Economics

Release Multiplier
0
Burn Rate %
0

Staking

Stake Ratio
0
Staker Pool
0.000000 SKL
Staker Emission Share
0
Total Staked
N/A
Staking Height
N/A
Tier 0 Lock Blocks
N/A
Tier 1 Lock Blocks
N/A
Tier 2 Lock Blocks
N/A

Protocol

Transaction Format
TransactionV3
Membership Proof
FCMP++
Spend Auth
Ed25519 + ML-DSA-65
Confidentiality
Stealth + BP+

Node

TX Pool Size
0
Database Size
0 B
Node Version
--
Sync Status
Syncing
All Documentation

CLI User Guide

Comprehensive guide to the Shekyl CLI tools: daemon, wallet, staking, mining, multisig, and more.

Shekyl CLI User Guide

This guide covers the command-line tools shipped with shekyl-core. If you prefer a graphical interface, see the Shekyl GUI Wallet User Guide instead -- both guides share the same section structure so you can cross-reference equivalent features.


Table of Contents

  1. Introduction and Prerequisites
  2. Running a Node (shekyld)
  3. Wallet Basics (shekyl-cli)
  4. Sending and Receiving
  5. Staking
  6. Mining
  7. PQC Multisig
  8. Anonymity Networks (Tor and I2P)
  9. Network Selection
  10. Post-Quantum Security
  11. Wallet RPC Server
  12. Blockchain Utilities
  13. Security and Backup
  14. Troubleshooting
  15. Glossary
  16. Getting Help

Introduction and Prerequisites

What are the CLI tools?

Shekyl ships a set of command-line programs for running a node, managing wallets, and working with blockchain data. They are the same programs that the GUI wallet uses behind the scenes, and they give you full control over every feature.

Use the CLI tools when you want to:

  • Run a dedicated node on a server (headless, no desktop environment)
  • Script wallet operations or build integrations
  • Access advanced features not yet exposed in the GUI
  • Operate over SSH or inside containers

System requirements

  • OS / architecture: Linux (x86_64, ARM64 only), macOS (Intel, Apple Silicon), Windows (64-bit only, via MSYS2). 32-bit targets are not supported and must not be used: Shekyl's post-quantum primitives (ML-KEM-768, ML-DSA-65) require 64-bit arithmetic for their constant-time security property, and running a 32-bit Shekyl wallet exposes the wallet private key to a published timing-side-channel attack class (see docs/STRUCTURAL_TODO.md §"32-bit targets cannot safely run Shekyl" for the technical analysis). If your hardware is 32-bit-only — older Raspberry Pi Zero / Pi 1, pre-2005 x86 desktops, some embedded boards — Shekyl is not appropriate for it.
  • Disk: ~50 GB for a full node; ~10 GB with --prune-blockchain
  • RAM: 4 GB minimum, 8 GB recommended during initial sync
  • Network: Reliable broadband; the initial sync downloads the full chain

Shipped executables

BinaryPurpose
shekyldFull node daemon -- connects to the network, syncs the chain, relays transactions
shekyl-cliInteractive command-line wallet
shekyl-wallet-rpcWallet RPC server for programmatic access
shekyl-gen-ssl-certGenerate self-signed SSL certificates for RPC
shekyl-blockchain-importImport blockchain from a file
shekyl-blockchain-exportExport blockchain to a file
shekyl-mdb-copyCompact a stopped daemon's database
shekyl-blockchain-statsPrint blockchain statistics
shekyl-blockchain-ancestryTrace transaction ancestry chains
shekyl-blockchain-depthCompute minimum chain depth for outputs
shekyl-blockchain-usageHistogram of output reuse as ring members (legacy)

Getting the binaries

Download pre-built releases from the GitHub releases page, or build from source following the Installation Guide.


Running a Node (shekyld)

The daemon is the engine of the Shekyl network. It downloads and verifies every block, maintains the UTXO set, and relays transactions. Your wallet talks to the daemon -- it cannot function without one.

First launch

./shekyld

On first run, shekyld creates a data directory and begins downloading the blockchain from peers. The default locations are:

  • Linux: ~/.shekyl/
  • macOS: ~/.shekyl/
  • Windows: C:\ProgramData\shekyl\

Initial sync takes several hours depending on your hardware and network. You will see log lines showing block height, download rate, and verification progress. Let it run until you see "SYNCHRONIZED OK" in the output.

Configuration file

Instead of passing flags on the command line, you can write them in a config file. The syntax is optionname=value, one per line. Boolean flags use optionname=1.

# shekyld.conf
data-dir=/var/lib/shekyl
log-file=/var/log/shekyl/shekyld.log
log-level=0
prune-blockchain=1
# RPC defaults to loopback. For your own wallet on another machine you
# control, bind a view-only second listener (not a public remote node):
# rpc-restricted-bind-port=11030

Load a config file with --config-file /path/to/shekyld.conf. See utils/conf/shekyld.conf for a minimal example.

Key daemon flags

Data and logging

FlagDescription
--data-dir <path>Override the blockchain data directory
--log-file <path>Write logs to a specific file
--log-level <0-4>Verbosity: 0 = minimal, 4 = trace
--max-log-file-size <bytes>Rotate logs when they exceed this size (default 104857600)
--max-log-files <n>Number of rotated log files to keep

Synchronization and storage

FlagDescription
--prune-blockchainEnable pruning (~95% storage reduction for old prunable data)
--db-sync-mode <mode>LMDB sync mode: safe, fast, fastest
--block-sync-size <n>Number of blocks per sync batch

RPC

FlagDescription
--rpc-bind-port <port>HTTP RPC listen port (default: 11029 mainnet); Axum is the sole transport
--rpc-bind-ip <addr>Bind address for RPC (default: 127.0.0.1). IPv4 or IPv6 (::1). Loopback only: a wildcard (0.0.0.0, ::) is refused, and so is a network address — the daemon RPC has no authentication. --rpc-use-ipv6 also binds --rpc-bind-ipv6-address (default ::1) on the same start. Another machine of yours reaches this node through its onion service
--restricted-rpcDisable admin endpoints on the main listener (view-only for your wallet; not a public remote node)
--rpc-restricted-bind-port <port>Second view-only listener for a wallet you operate
--rpc-access-control-origins <list>Comma-separated CORS allow-list (default: deny)

shekyld does not accept --rpc-login or --rpc-ssl* (inbound RPC is plaintext). Use loopback locally; for remote access prefer an onion service or a reverse proxy. Wallet-RPC retains login/SSL flags for its own listener.

Peer-to-peer

FlagDescription
--p2p-bind-port <port>P2P listen port (default: 11021 mainnet)
--out-peers <n>Maximum outbound peer connections
--in-peers <n>Maximum inbound peer connections
--add-peer <host:port>Manually add a peer
--add-priority-node <host:port>Peer that is always maintained
--ban-list <path>File of banned IP addresses

Background operation

shekyld always runs in the foreground. Use your platform's service manager for background operation (systemd on Linux, launchd on macOS, Task Scheduler on Windows); the GUI wallet supervises its own bundled shekyld via the Tauri sidecar. An example systemd unit lives at contrib/packaging/linux/shekyld.service. The --detach, --pidfile, and --*-service flags were removed in V3.1.

FlagDescription
--non-interactiveDisable the interactive console (required under a service manager)

Interactive console

When shekyld is running in the foreground, you get an interactive console. Type help to list all commands. The most useful ones, grouped by purpose:

The same commands work from a second shell against a running daemon — shekyld status, shekyld exit — over its local RPC port (pass --rpc-bind-port / --testnet if the daemon is not on the defaults). The exit status is 0 when the daemon answered, 1 when the command is unknown or the request failed (daemon not running, connection refused, error reply), so scripts can check it without parsing the output.

Status and information

CommandDescription
statusOne-line summary: height, net hash, connections, sync state
print_heightCurrent blockchain height
diffCurrent mining difficulty
sync_infoDetailed sync and peer download state
hard_fork_infoCurrent and upcoming hard fork versions
versionDaemon version string

Mining

CommandDescription
start_mining <addr> [threads]Start the built-in CPU miner
stop_miningStop mining
mining_statusCurrent mining state, hash rate, address
show_hr / hide_hrToggle real-time hash rate display

Network and peers

CommandDescription
print_plFull peer list
print_pl_statsPeer list statistics
print_cnActive connections with data transfer stats
print_net_statsAggregate network bandwidth
bansList all banned peers
ban <ip> [seconds]Ban a peer
unban <ip>Remove a ban
limit_up <kB/s> / limit_down <kB/s>Set bandwidth limits
out_peers <n> / in_peers <n>Adjust peer count at runtime

Chain inspection

CommandDescription
print_bc <start> [end]Print block headers in a range
print_block <height or hash>Print a single block's details
print_tx <txid>Print transaction details
is_key_image_spent <key_image>Check if a key image is spent
print_poolFull transaction pool
print_pool_shTransaction pool (short format)
print_pool_statsPool statistics
alt_chain_infoShow alternative chain branches
bc_dyn_stats <last_n>Dynamic block stats for recent blocks
print_coinbase_tx_sum <start> <count>Sum of coinbase outputs in a range

Maintenance

CommandDescription
saveForce a blockchain save
flush_txpool [txid]Remove transaction(s) from the pool
flush_cache [bad-txs|bad-blocks]Clear internal caches
pop_blocks <n>Roll back the last N blocks
prune_blockchainEnable pruning on a non-pruned database

Exit

CommandDescription
stop_daemon / exitGracefully shut down

Wallet Basics (shekyl-cli)

shekyl-cli is an interactive shell: start it, then type commands at the prompt. The prompt always names the network — and the open wallet, when there is one:

mainnet>
testnet:miner>

Type help for the full command list and help <command> for one command's usage.

Wallet files live under ~/.shekyl/wallets/<network>/ by default, one directory per network, so a testnet wallet can never shadow a mainnet one. --wallet-dir <path> overrides the directory.

Creating a new wallet

At the prompt:

mainnet> create mywallet

You will be prompted for a password, and the wallet's 24-word BIP-39 seed phrase (English wordlist) is shown oncewrite it down on paper immediately. This seed is the only way to recover your funds if your wallet file is lost. To keep the seed out of logs and scrollback, create refuses to print it when output is piped or redirected.

For scripts there is a non-interactive subcommand that writes the seed to a file instead of the terminal (created 0600, refusing to overwrite):

./shekyl-cli create mywallet --seed-out /safe/path/seed.txt \
    [--password-file <path> | --password-stdin]

Your wallet is automatically a V3 wallet with full post-quantum key material (Ed25519 + ML-DSA-65). No extra steps are needed.

Restoring from a seed phrase

At the prompt:

mainnet> restore mywallet word1 word2 ... word24

or non-interactively, reading the seed from a file:

./shekyl-cli restore mywallet --seed-file /safe/path/seed.txt \
    [--password-file <path> | --password-stdin]

If you opted in to a BIP-39 passphrase at creation, you will need it here too. After a restore, run refresh to scan the chain for your funds.

The seed words are the only restore path: every wallet key (spend, view, message-signing, ML-KEM) derives from the master seed, so there is no separate restore-from-keys flow and no view-only wallet variant.

Opening an existing wallet

At the prompt:

mainnet> open mywallet

or open at startup:

./shekyl-cli --wallet mywallet

Connecting to a daemon

By default the wallet connects to a daemon on this machine, at the RPC port for the selected network (127.0.0.1:11029 on mainnet; --testnet and --stagenet pick the matching port automatically). If no daemon is answering there, the wallet says so when it starts and names the command that would fix it (shekyld --testnet, and the port it answers on).

shekyld serves RPC on loopback only, so a node of yours on another machine is reached through a Tor onion service you run in front of its loopback RPC (see "Wallet through Tor" below), over a SOCKS proxy:

./shekyl-cli --wallet mywallet \
    --proxy socks5h://127.0.0.1:9050 \
    --daemon-address <onion-address>:11029

Shekyl RPC is operator-to-operator: there is no recommended configuration in which the wallet talks to a daemon someone else controls, and shekyld does not advertise itself as a public node. A --daemon-address that is not loopback is said out loud when the wallet starts — whoever operates that daemon sees which blocks the wallet requests and what it broadcasts, and no proxy or encryption changes that. The warning reads the same for your own node: it states what a daemon is told, not who you are talking to. Without --proxy, a non-loopback address additionally warns that the connection itself is visible on the network path.

Understanding Bech32m addresses

Shekyl uses a segmented Bech32m address format with three parts:

  1. Classical segment (shekyl1...) -- ~113 characters, contains Ed25519 spend and view public keys
  2. PQC-A segment (skpq...) -- contains part of the ML-KEM-768 encapsulation key
  3. PQC-B segment (skpq2...) -- contains the rest of the ML-KEM-768 key

The full address is approximately 2,030 characters. When sharing addresses, use address --out, make_uri, or payment-request URIs — never retype it.

To see your address inside the wallet:

mainnet> address

By default this prints a short display form (the first 24 characters, an ellipsis, and the last 12) plus the full length — enough to recognize the address at a glance, and explicitly not valid for pasting. address --full prints the whole string; address --out <path> writes it to a new private file so the full form never has to transit terminal scrollback or shell history.


Sending and Receiving

Receiving SKL

Your wallet has one reusable primary address. On-chain privacy comes from per-output cryptography (stealth outputs, hybrid KEM) — not from generating a new address for every sender. Reuse your address freely; rotation does not improve chain privacy and is not recommended as default hygiene.

Display your primary address:

mainnet> address

(the short display form; address --full / address --out <path> for the real string — see "Understanding Bech32m addresses" above).

Merchants and invoicing: create a payment request (amount, label, optional expiry) and share a shekyl: URI with query parameters:

mainnet> request new <amount> <label> [--expiry <height>]
mainnet> requests list [pending|matched|all]

Attribution is cooperative (off-chain label + optional on-wire enc_label echo when the product flag is enabled). Unattributed inbound payments are normal — funds are still yours; history incoming --unattributed lists them. make_uri / parse_uri compose and decode shekyl: URIs directly.

Separate contexts (e.g. personal vs business) use separate wallet files — deliberate opsec, not a casual default. There are no accounts or subaddresses in the Shekyl model; payment requests carry the per-payer/per-purpose attribution that subaddresses carried elsewhere.

Checking your balance

mainnet> balance

shows the balance breakdown (spendable now vs still locked). wallet prints a one-shot summary — height, balance, address — and status shows wallet and daemon sync heights.

Sending SKL

Basic transfer (one recipient per transaction):

mainnet> transfer <amount> <address>

The wallet automatically constructs an FCMP++ membership proof for each spent input, signs with both Ed25519 and ML-DSA-65 (hybrid PQC), and broadcasts the transaction.

Priority controls the fee (higher priority = higher fee = faster confirmation): 01 economy, 2 standard, 3+ priority.

mainnet> transfer <amount> <address> --priority 3

There is no offline ("air-gapped") signing workflow, by design. An FCMP++ membership proof needs the live curve tree, so an offline signer cannot deliver the isolation such a workflow claims — the "cold" half would still need current chain data to build a valid transaction. Cold storage is the seed phrase on paper; a machine that signs is online. Every transfer shows the built transaction (destination, amount, fee) and waits for your confirmation before broadcasting — decline it and nothing leaves the wallet.

No sweep commands

The Monero-era sweep family (sweep_all, sweep_below, sweep_single, sweep_unmixable) does not exist: with a single primary address, no accounts, and no mixin rules there is nothing to consolidate toward. To move your funds, transfer the amount balance shows as spendable. (drain exists but is staking-side: it moves staking funds back to this wallet.)

Transaction verification and proofs

Prove to a third party that a payment was made. A multi-word [message] binds into the proof; the verifier must supply the identical string:

CommandDescription
get_tx_proof <txid> <address> [message]Prove a payment to <address> (sent or received; open wallet required)
check_tx_proof <txid> <address> <proof> [message]Verify a tx proof (no wallet needed)
get_reserve_proof [amount] [message]Prove unspent reserve (omit the amount to prove the full balance)
check_reserve_proof <address> <proof> [message]Verify a reserve proof (no wallet needed)

There is no get_tx_key / check_tx_key: the raw per-tx key is a bearer credential over the whole transaction, so its export is rejected in the proofs contract (WI-RPC-3) — the scoped get_tx_proof / check_tx_proof pair covers the use case.

Transaction history

mainnet> transfers
mainnet> show_transfer <txid>
mainnet> set_tx_note <txid> <note>
mainnet> get_tx_note <txid>

transfers lists recent transactions; abandon <txid> gives up on a dispatched send (funds stay locked until the network is confirmed to have dropped it).

Fees

Check fee quotes and the size estimate:

mainnet> fee [--inputs N] [--outputs N]

V3 transactions are larger than legacy transactions due to FCMP++ proofs and PQC authentication material. A typical 2-input, 2-output transaction is approximately 23 KB. Fees scale with transaction size.


Staking

Shekyl staking is archival pay-for-service. You bond SKL as on-chain collateral backing archival service, and you earn a share of the block reward-emission leg for the archival work your position verifiably performs. There are no duration tiers, no lock period, no claim transactions, and no minimum stake — those belonged to an earlier claim-based design that was retired before genesis. Your principal stays yours the whole time; the bond is an honesty anchor (slashable for misbehavior), not a custody transfer. When you are done staking, unstake posts the permanent exit and collect_unstaked returns the released collateral to your balance (PR-C, 2026-09-03).

For the economic model, see DESIGN_CONCEPTS.md §Component 3–4 and the canonical specs V3_STAKER_ARCHIVAL.md (mechanism) and design/REWARD_EMISSION_LEG.md (reward leg). If you intend to run a staker, read the STAKER_OPERATOR_GUIDE.md first — it covers the timing and funding footguns that matter for your privacy.

How it works

  • Activation is one-time. You activate staking once; the wallet then posts your archival bond and runs the background scan that answers challenges and tracks your service. You do not hand control of your coins to anyone.
  • Reward is by verified serve-work, not by amount or duration. The reward budget is divided among stakers in proportion to capped verified serve-work, so a larger position does not buy a proportionally larger share.
  • Rewards arrive automatically. They are paid through the loud reward-emission leg (public amounts) and received to a firewalled pseudonym. There is no manual "claim" step: the open wallet submits the claim transaction itself once each reward epoch settles. Very small rewards are held until enough accumulate to be worth the network fee, and a wallet that was closed for a while claims its backlog shortly after you reopen it. Automatic claiming currently requires the wallet's daemon to be on the same machine (a loopback address). If your wallet points at a remote daemon — even one you run yourself — rewards are not claimed automatically yet; they are held, and the wallet raises an operator alarm rather than claiming over the remote connection. Remote-daemon claiming arrives with remote-daemon support as a whole.
  • Principal stays liquid. You can release collateral by releasing, subject to a release cooldown. There is no fixed lock height to wait out.

Wallet support today

The full staking surface is live in interactive shekyl-cli (type help stake — or any staking command's name — for usage):

  • Activation: stake makes this wallet a staker (Foundation nodes only: stake --complete-tree-foundation, which states its terms and requires a typed phrase).
  • Status: staked_balance, staked_outputs, staking_info.
  • Principal movement: stake_in <amount> adds funds to the staking balance; drain_balance shows what can move back; drain <amount> moves it back (fee and destination are automatic).
  • Exit: unstake posts the permanent exit and collect_unstaked returns the released collateral, each with the irreversibility confirmation the exit warrants (PR-C).

Wallet RPC exposes the same verbs (stake, get_staked_balance, get_staked_outputs, staking_info, stake_in, drain, get_drain_balance, unstake, collect_unstaked); reward-related reads never conflate bonded principal with received rewards. The desktop GUI exposes staker activation directly.

Privacy considerations

  • Bond-post timing is a privacy surface, and the wallet handles it for you. The wallet draws a randomized entry standoff before posting your bond; do not override, batch, or schedule bond-posts to a clock or an external event (see the operator guide's footguns).
  • Reward reception is firewalled. Emission-leg rewards are attributed to a pseudonym, keeping your archival identity separate from your spending identity — provided you do not defeat the firewall by funding or draining through linked events.

Mining

Mining secures the network and earns you block rewards. Shekyl uses RandomX, an algorithm designed for ordinary CPUs.

From the daemon console

start_mining <address> [threads]
stop_mining
mining_status

From the wallet

mainnet> mine start          # default threads: min(cores, 4)
mainnet> mine start 8        # explicit thread count
mainnet> mine status         # active/idle, threads, hash rate
mainnet> mine stop

start_mining [threads], stop_mining, and mining_status work as aliases for the same verbs.

These commands control mining on the connected daemon — the daemon still does the hashing (no RandomX runs in the wallet process), and the daemon owns the mining threads, so mining keeps running after you close the CLI. Rewards pay to the open wallet's address automatically; you never paste an address the way the daemon-console form requires.

mine requires an open wallet, the unrestricted RPC listener, and a daemon on the same network as the CLI. The silent default is a daemon on this machine — the recommended posture. A remote --daemon-address (for example a node on your own network) is allowed: mine start reminds you that mining control is admin RPC, then continues. If the daemon is still syncing, mine start refuses — the daemon will not mine until it has caught up.

Background mining

Launch the daemon with background mining flags:

./shekyld --bg-mining-enable \
    --bg-mining-idle-threshold 90 \
    --bg-mining-min-idle-interval 10 \
    --start-mining <address> --mining-threads 2

Background mining runs at the lowest CPU priority and pauses when system utilisation exceeds the idle threshold.

The 60-block lock

Mined coins are locked for 60 blocks (~2 hours) before they become spendable. This protects against chain reorganisations that could invalidate the coinbase.

Coinbase PQC

When your daemon mines a block, the coinbase transaction automatically performs ML-KEM self-encapsulation to generate per-output PQC keys for the reward. This happens transparently -- no configuration needed.


PQC Multisig

Shekyl's multisig requires M-of-N participants to authorise a spend. It uses the same hybrid Ed25519 + ML-DSA-65 signature scheme as single-signer transactions, with a maximum of 7 participants.

How it works

All multisig coordination happens off-chain using file exchange. On-chain, the FCMP++ membership proof uses a single classical key -- the M-of-N threshold lives entirely in the PQC auth layer.

  1. Build: The coordinator creates the transaction body and FCMP++ proof.
  2. Export: The coordinator exports a signing request file (JSON) for each signer to review.
  3. Sign: Each of the M required signers independently produces a hybrid (Ed25519 + ML-DSA-65) signature over the canonical payload.
  4. Assemble: The coordinator collects all M signature files, assembles the pqc_auth container, and broadcasts the transaction.

Planned wallet-RPC surface

These methods are the v31 design's shape and are not yet in the wallet-RPC contract registry (docs/api/wallet_rpc.yaml); they land with the multisig implementation:

MethodDescription
create_pqc_multisig_groupCreate a group with N total, M required, participant keys
get_pqc_multisig_infoCheck if wallet is part of a multisig group and its parameters
export_multisig_signing_requestExport a transaction for co-signers
sign_multisig_partialProduce a partial signature over the signing request
import_multisig_signaturesImport M signatures and assemble the final transaction

Transaction size

Each additional signer adds roughly another hybrid key + signature of authentication material (~5.4 KB solo baseline via pqc_auth_weight()).

ConfigurationAuth Sizevs. Single
Single signer~5.4 KBbaseline
2-of-3~12.8 KB~2.4×
3-of-5~20.2 KB~3.7×
5-of-5~27 KB~5.0×

Whole-tx weights (FCMP++/Bp+/KEM dominate) are closer to ~2.4× solo for 5-of-5 — see V3_ROLLOUT.md. Target cap is MAX_MULTISIG_PARTICIPANTS = 5 (MSW-G; code still = 7 until MSW-1).

Use cases

  • Treasury management: 2-of-3 or 3-of-5 ensures no single person can spend development or community funds.
  • Staking security: A bonded archival position keeps significant collateral committed over a long service life and is slashable for misbehavior, so controlling it with a single key is a single point of failure. Use multisig to protect the keys that control bonded value.
  • Inheritance and recovery: 2-of-3 where the owner holds two keys and a trusted party holds one.
  • Escrow: Buyer, seller, and arbitrator each hold a key in a 2-of-3.

Anonymity Networks (Tor and I2P)

Status: Experimental. There are known metadata leak vectors. See ANONYMITY_NETWORKS.md for the full threat matrix.

Tor-zone cover is an operator choice, not a protocol default (TRC, recorded 2026-09-12). Wire-observer resistance on Tor comes from running a non-exit Tor relay in a separate process from shekyld, with the node's Tor client that same process (originated cells on the relay's OR connections). A sidecar client Tor — including the default managed ephemeral instance — is uncovered. See TOR_RELAY.md. The daemon does not check, warn, or refuse on that basis. The contract is design/TOR_COVER_POSTURE.md.

Shekyl can broadcast transactions over Tor or I2P so that observers cannot link your IP address to your transactions. Regular block sync and peer communication still uses IPv4 to resist Sybil attacks.

Daemon: outbound transaction proxy

./shekyld --tx-proxy tor,127.0.0.1:9050 \
          --tx-proxy i2p,127.0.0.1:9000

Omit the outbound count to use the default. An explicit count below 12 is refused at start (the relay-embargo floor). For Tor-zone cover, that SocksPort must be the non-exit relay process — see TOR_RELAY.md — not a second client Tor.

Daemon: inbound hidden service

To receive connections over Tor:

./shekyld --anonymous-inbound <your-onion>.onion:11021,127.0.0.1:11021,25

For I2P:

./shekyld --anonymous-inbound <your-b32>.b32.i2p:11021,127.0.0.1:11021,25

Wallet through Tor

./shekyl-cli --wallet mywallet \
    --proxy socks5h://127.0.0.1:9050 \
    --daemon-address <onion-address>:11029

The daemon must expose a hidden service for RPC (separate from the P2P hidden service). The wallet still states at startup that the daemon's operator sees what it asks for — true of your own node too; see "Connecting to a daemon".

Key behaviours

  • When any anonymity mode is active, locally-originated transactions are only sent to peers on anonymity networks.
  • If no anonymity peers are available, the transaction is held -- it will never be broadcast over a public connection.
  • V3 transactions are larger (~7-8 KB vs ~2-3 KB pre-PQC), creating a more distinctive traffic burst. On Tor that burst is mixed by operator relay volume (TOR_RELAY.md), not by dummy traffic. Dummy and fragmentation remain the cover for encrypted zones other than Tor.

Network Selection

Shekyl runs four networks. Use the right one for your purpose:

NetworkFlagP2P PortRPC PortUse
Mainnet(default)1102111029Production -- real money
Testnet--testnet1202112029Protocol experiments
Stagenet--stagenet1302113029Integration testing (exchange, wallet)
Fakechain--regtestn/an/aLocal deterministic testing

Both the daemon and wallet must be started with the same network flag:

./shekyld --testnet
./shekyl-cli --testnet

The wallet keeps the networks from mixing on its own:

  • Wallet files live in a per-network directory (~/.shekyl/wallets/testnet/), so a testnet wallet can never shadow a mainnet one.
  • The prompt names the network (testnet:miner>), so you always know which money you are holding.
  • The default daemon address follows the flag (127.0.0.1:12029 on testnet), and mine start refuses if the daemon reports a different network than the CLI was started with.

Switching networks is a restart (exit, then start again with the other flag) — a deliberate speed bump between test money and real money.


Post-Quantum Security

Every Shekyl transaction is protected by two layers of cryptography:

  1. Ed25519 -- a battle-tested classical signature algorithm.
  2. ML-DSA-65 (FIPS 204) -- a NIST-standardized post-quantum lattice-based signature at security level 3.

Both signatures must be valid for a transaction to be accepted. An attacker would need to break both classical and post-quantum assumptions simultaneously.

Additionally, each output has its own post-quantum keypair derived through a hybrid KEM (X25519 + ML-KEM-768). When someone sends you coins, the transaction includes an encrypted key exchange that produces a unique ML-DSA-65 signing key for that output. Compromising one output's key does not affect any other.

All of this happens automatically. You do not need to enable anything or understand the cryptographic details. The wallet handles key generation, encapsulation, signing, and verification behind the scenes.

For the full technical specification, see POST_QUANTUM_CRYPTOGRAPHY.md.


Wallet RPC Server (shekyl-wallet-rpc)

shekyl-wallet-rpc is the JSON-RPC server behind every Shekyl wallet client: shekyl-cli hosts one in-process for you over a private local endpoint, so most users never start it directly. Run it yourself for tooling that speaks JSON-RPC over HTTP, or to serve several wallets from one process. The contract is docs/api/wallet_rpc.yaml; the flag reference is docs/EXECUTABLES.md §3.

Launching

./shekyl-wallet-rpc \
    --wallet-dir /path/to/wallets \
    --rpc-bind 127.0.0.1:29500 \
    --rpc-login user:password \
    --daemon-address http://127.0.0.1:11029

Key flags

FlagDescription
--wallet-dir <path>Directory of wallet files; create_wallet / open_wallet work here
--rpc-bind <addr>A numeric IP:PORT (default 127.0.0.1:29500; IPv6 as [::1]:29500; hostnames are not resolved) or uds:///path/to.sock (Unix). Wildcard addresses (0.0.0.0, ::, [::]) are refused — bind a specific IP address; a non-loopback address requires --rpc-login
--rpc-login <user:pass>HTTP basic auth, both halves non-empty (anything else is refused at startup). Mandatory off loopback; on loopback or a UDS socket it may be omitted
--disable-rpc-loginRun without auth — refused off loopback, and refused together with --rpc-login (pass one)
--daemon-address <url>Your node's RPC URL. Default: this machine's daemon at the RPC port for --network. A daemon that is not loopback is disclosed in the log at startup, --proxy or not (see "Connecting to a daemon" above)
--proxy <socks5h://…>Route the daemon connection through a SOCKS5h proxy
--network <name>mainnet (default), testnet, or stagenet

Three things the server will refuse, on purpose. Binding 0.0.0.0 or :: binds every interface your machine has and every one it gains later (a VPN, a hotspot, a container bridge), so the server asks you to name the one interface you mean. And it will not serve without authentication on any address other than loopback: a wallet RPC the network can reach honours every request, spends included. The network being yours is not what provides the security — the TV, the smart plug and a guest's phone are on your home network too. And it answers only requests whose Content-Type is application/json (anything else gets HTTP 415): that is what stops a web page in your browser from driving the wallet on 127.0.0.1 — every real client sends it anyway — and, when you run without --rpc-login, it answers only to its IP address or localhost, not to a hostname a web page could have pointed at your machine.

Method categories

All methods are called via POST /json_rpc. The specified surface (per the contract's x-shekyl-method-registry):

  • Wallet lifecycle: create_wallet, restore_wallet, open_wallet, close_wallet, change_password, get_wallet_info
  • Balance and address: get_balance, get_primary_address
  • Sending: build_pending_tx, submit_pending_tx, discard_pending_tx, abandon_tx, estimate_tx_size_and_weight, get_default_fee_priority
  • History: get_transfers, get_transfer_by_id, set_tx_note, get_tx_note
  • Sync: refresh, rescan_blockchain, get_height, get_version
  • Payment requests and URIs: create_payment_request, list_payment_requests, make_uri, parse_uri
  • Staking: stake (activation), get_staked_balance, get_staked_outputs, staking_info (reads); stake_in, drain, get_drain_balance (WI-RPC-5); unstake and collect_unstaked (the composed exit, PR-C). There is no claim RPC — emission claims are engine-side automation, and that name is rejected, not reserved.
  • Proofs and signing: get_tx_proof, check_tx_proof, get_reserve_proof, check_reserve_proof, sign_message, verify_message

There are no wallet-RPC mining methods: the daemon does the hashing, and mining is controlled through the daemon's own restricted-off endpoints (which the shekyl-cli mine verbs drive). Monero-era names the registry refuses outright — get_tx_key/check_tx_key, spend proofs, sweep_*/accounts, cold-signing export/import — are recorded as REJECTED in the contract so they cannot be silently re-minted.

For the full RPC reference, see the wallet-RPC contract docs/api/wallet_rpc.yaml.


Blockchain Utilities

These standalone tools operate directly on the blockchain database. Stop shekyld before using them (they need exclusive access to the LMDB files).

shekyl-blockchain-import

Import a blockchain file (from blockchain-export or a trusted source):

./shekyl-blockchain-import --input-file blockchain.raw --batch-size 5000

Use --dangerous-unverified-import only with files you trust completely -- it skips all verification for speed.

shekyl-blockchain-export

Export the blockchain to a portable file:

./shekyl-blockchain-export --output-file blockchain.raw
./shekyl-blockchain-export --output-file partial.raw --block-start 0 --block-stop 100000

shekyl-mdb-copy

Pruning happens inside the daemon: start shekyld with --prune-blockchain, or run the prune_blockchain command in the daemon console. An in-place prune marks database pages as free without shrinking the file. To reclaim the disk space, stop shekyld and compact the database:

mkdir /path/to/compacted
./shekyl-mdb-copy -c ~/.shekyl/lmdb /path/to/compacted/

then replace the old lmdb directory with the compacted copy (you temporarily need disk space for both). The destination directory must already exist and be empty, and the tool must run as the user that owns the data directory — the copy opens the database read-only but still needs write access to the source's reader-lock file (lock.mdb, created if missing), and a sudo run leaves the compacted copy root-owned, so the daemon cannot use it after the swap. Always pass both paths: with the destination omitted, the tool streams the entire database to standard output.

Note that pruning removes only transaction proof and PQC-auth data; curve-tree leaf data is not prunable and grows with the chain.

shekyl-blockchain-stats

Print statistics about the blockchain:

./shekyl-blockchain-stats --with-emission --with-fees --with-diff

Additional flags: --with-inputs, --with-outputs, --with-hours.

Other utilities

ToolPurpose
shekyl-blockchain-ancestryTrace the input ancestry of a transaction
shekyl-blockchain-depthCompute minimum chain depth for an output or transaction
shekyl-blockchain-usageHistogram of output reuse as ring members (legacy)

All tools accept --data-dir, --testnet, --stagenet, and --log-level flags.

Note: shekyl-blockchain-ancestry, shekyl-blockchain-depth, and shekyl-blockchain-usage all analyze spend-graph relationships that FCMP++ transactions do not reveal (a spend never discloses which output it consumes, and inputs carry no ring-member references). They have no substrate on Shekyl chain data and are scheduled for a deletion audit (see docs/EXECUTABLES.md and docs/FOLLOWUPS.md; shekyl-blockchain-prune-known-spent-data was already deleted under that audit).


Security and Backup

Your mnemonic seed

Your 24-word BIP-39 seed phrase is the only way to recover your wallet. No company, no foundation, no developer can recover it for you.

The seed is shown exactly once, when the wallet is created or restored; there is no seed command to redisplay it afterwards (see "No secret export" below). If you did not record it then, and the wallet file is still healthy, create a new wallet and transfer the funds to it.

If you opted in to a BIP-39 passphrase at creation time, the seed words alone are not sufficient -- you must also retain the passphrase. There is no on-device "encrypted seed" variant; the passphrase is never stored.

Write your seed on paper. Store it offline. Never share it.

No secret export, no view-only wallets

The wallet deliberately has no secret-egress surface: there are no viewkey / spendkey display commands, no key-image export, and no view-only ("watch-only") wallet variant. Your seed backup is shown exactly once, at create/restore time — everything else derives from it. Under FCMP++ a view key is not a chain-scanning credential the way it was in CryptoNote-era coins, so a view-only wallet would not deliver the third-party-auditor use case; balance disclosure to a third party is served by reserve proofs (get_reserve_proof / check_reserve_proof) with scoped, cryptographic disclosure instead of a standing credential.

Changing your password

mainnet> password

prompts for the current password, then the new one twice.


Troubleshooting

Daemon won't sync

  • Firewall: Ensure port 11021 (P2P) is open for inbound connections, or use --out-peers to increase outbound connections.
  • Disk space: A full node needs ~50 GB. Use --prune-blockchain to reduce to ~5 GB.
  • Corrupted database: Try pop_blocks 100 in the daemon console to roll back recent blocks. As a last resort, delete the LMDB directory and resync.

Wallet balance is wrong or zero

  • Not synced: Run refresh in the wallet. status shows the wallet's height next to the daemon's — make sure the daemon is fully synchronised first.
  • Full rescan: rescan rebuilds your transaction history from the chain (transaction keys, notes, payment requests and staking records are kept). This can take a while.

Transaction not confirming

  • Make sure the daemon is synced (status in the wallet shows both heights; status in the daemon console shows peers and sync state).
  • The transaction pool can be checked with print_pool in the daemon.
  • Check the transaction's state with transfers / show_transfer <txid> in the wallet. A dispatched send that the network has dropped can be given up with abandon <txid>.

Reading logs

Increase daemon verbosity for debugging:

./shekyld --log-level 2

Or change it at runtime in the console:

set_log 2

shekyl-cli logs to stderr only (warnings and up by default; tune with SHEKYL_LOG, see "Logging" below) — it writes no log file, so nothing about your wallet activity lands on disk beside the wallet.


Logging

Shekyl has completed the migration from the unmaintained C++ easylogging++ library to a unified Rust logger — the shekyl-logging crate, built on tracing and tracing-subscriber. The migration shipped in two chores: V3.1 alpha.4 landed Chore #1 (Rust-side consolidation) and V3.x alpha.0 landed Chore #2 (C++ shim + easylogging++ retirement + MONERO_* env-var sweep). All binaries — shekyl-cli, shekyl-wallet-rpc, and shekyld — now share a single subscriber, a single env var, and a single on-disk layout.

This section has two distinct audiences. If you just run Shekyl binaries and want to know which env var tunes log verbosity, read only "For operators." If you maintain downstream code or script shekyld-style deployments, read "For integrators" too.

For operators

SHEKYL_LOG is the single env var that controls logging across every Shekyl binary. Shipping a change to this section of the docs is a tell that the knob changed; nothing else is needed.

SHEKYL_LOG accepts standard tracing-subscriber EnvFilter syntax:

SHEKYL_LOG=warn                                  # bare level: all targets at WARN
SHEKYL_LOG=info,shekyl_wallet_rpc=debug          # level per target
SHEKYL_LOG=shekyl_wallet_rpc::scanner=trace      # nested module target

The legacy easylogging++ category grammar is also accepted (net.p2p:DEBUG,wallet.wallet2:INFO, numeric 0..=4 presets, +/- modifiers) because the C++ shim still round-trips through the Rust-side translator. Suffix globs (*y.z:TRACE) are rejected with a structured error and, where possible, a rewrite suggestion — no silent acceptance.

Defaults (when SHEKYL_LOG is unset) are per-binary and match the pre-migration behavior: shekyl-cli defaults to WARN, shekyl-wallet-rpc to INFO, and shekyld applies the rich level-0 preset (*:WARNING,net:FATAL,net.http:FATAL,…,global:INFO) that the daemon has always used. RUST_LOG and the historical MONERO_LOGS / MONERO_LOG_FORMAT variables are intentionally not honored — SHEKYL_LOG is the single supported knob. Scripts or systemd units that still set the retired MONERO_* names must be updated before upgrading past V3.x alpha.0.

File sinks follow the binary's role:

  • shekyl-cli writes stderr only. Redirect with 2> path if you want a file.
  • shekyl-wallet-rpc writes stderr only unless you pass --log-file <PATH>. When --log-file is supplied, the parent directory is created with 0700 perms and the file with 0600 perms on POSIX. No rotation is performed on the opt-in path — the operator owns file lifecycle.
  • shekyld writes both stderr and a rotated file sink. The default file lives at ~/.shekyl/logs/shekyld.log (suffixed -testnet / -stagenet / -regtest for non-mainnet runs), rotated at ~100 MB with 50 archives retained. The live file and every rotated archive are forced to POSIX mode 0600 by the Rust side before the filename becomes visible. Override the path with --log-file, rotation with --max-log-file-size and --max-log-files (set max-log-files to 0 to disable pruning).

For integrators

The Rust shekyl-logging crate is the hinge. Read rust/shekyl-logging/README.md before writing new Rust binaries; in particular, note the LoggerGuard footgun (dropping the guard silently loses buffered events; the crate uses #[must_use] + a workspace-wide clippy::let_underscore_must_use = "deny" lint to catch the common shape).

C++ callers route through the existing MINFO / MDEBUG / MWARNING / etc. macros in contrib/epee/include/misc_log_ex.h. The macros now expand to shekyl_log_emit / shekyl_log_level_enabled via the FFI declared in src/shekyl/shekyl_log.h; the vendored external/easylogging++/ tree and the easylogging++.h umbrella header have been removed. The el:: namespace survives only as a thin compatibility shim (el::Level, el::Color, el::base::DispatchAction) to keep existing call sites compiling — new code must not rely on it.

Behavior changes to be aware of when upgrading:

  1. Output format. The default fmt::layer from tracing-subscriber replaces the custom easylogging++ format string. Timestamps are RFC 3339 UTC (not local time with microseconds), the level token is full words (ERROR rather than E), and the category appears as a structured target. Log-scraping tooling that parsed the prior format byte-for-byte must be updated.
  2. MLOG_SET_THREAD_NAME is a no-op. The macro still accepts its argument so existing call sites (abstract_tcp_server2.inl, miner.cpp) keep compiling, but the label ([SRV_MAIN], [miner 3]) no longer appears in the log stream. Restoring semantic thread labels via pthread_setname_np / equivalent is tracked in docs/FOLLOWUPS.md as a minor follow-up.
  3. MONERO_LOGS / MONERO_LOG_FORMAT are retired. The C++ shim no longer reads either name; SHEKYL_LOG is the single input. MONERO_LOG_FORMAT in particular has no replacement — formatting is owned by the subscriber's layer stack and is not operator-tunable.

Glossary

TermMeaning
AddressYour Bech32m-encoded public identifier for receiving SKL. Contains both classical (~113 char) and PQC (~1,750 char) key segments. Safe to share.
Atomic unitThe smallest unit of SKL. 1 SKL = 1,000,000,000 atomic units.
BlockA bundle of transactions added to the blockchain roughly every 2 minutes.
Block heightThe sequential number of a block, starting from 0.
Block rewardNew SKL created and given to the miner who finds a valid block.
Config fileA text file (shekyld.conf) containing daemon options in name=value format, loaded with --config-file.
ConsoleThe interactive command prompt inside shekyld when running in foreground mode.
DaemonThe background program (shekyld) that connects to the Shekyl network and maintains the blockchain.
DifficultyA measure of how hard mining puzzles are. Adjusts automatically to target ~2-minute blocks.
EmissionThe schedule by which new SKL is created. The total supply is mathematically capped.
FCMP++ membership proofA zero-knowledge proof that the spent output exists in the full UTXO set without revealing which one. The anonymity set is every output on the blockchain.
Hybrid signatureTwo signatures on every transaction: Ed25519 (classical) and ML-DSA-65 (quantum-resistant).
Key imagesCryptographic markers that prevent double-spending. Derived and tracked internally by the wallet; there is no export surface.
KDF roundsKey derivation function iterations; higher values make wallet password brute-forcing harder.
Mnemonic seedThe 24 BIP-39 words that fully restore your wallet (plus your passphrase, if you opted in to one). Treat as a master password you can never change.
ML-DSA-65A quantum-resistant signature algorithm standardized by NIST (FIPS 204).
PrivacyFCMP++ proofs, stealth addresses, and per-output PQC keys (hybrid X25519 + ML-KEM-768) hide who sends, who receives, and how much. Automatic.
PruningRemoving old prunable transaction data to reduce storage. The node can still verify new blocks.
RandomXShekyl's mining algorithm, designed for regular CPUs.
RPCRemote Procedure Call -- the JSON-based API exposed by the daemon and wallet RPC server.
StakingLocking SKL for a period to earn yield from the emission pool.
Stealth addressA one-time address generated for each transaction so only sender and receiver know the destination.
Payment requestA merchant invoice record (amount, label, expiry) tied to your primary address — replaces per-sender subaddress rotation.

Getting Help


This guide covers Shekyl CLI tools v0.4.x. TransactionV3 with FCMP++ membership proofs and hybrid PQC spend authorization is the required transaction format on the rebooted chain.