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
- Introduction and Prerequisites
- Running a Node (shekyld)
- Wallet Basics (shekyl-cli)
- Sending and Receiving
- Staking
- Mining
- PQC Multisig
- Anonymity Networks (Tor and I2P)
- Network Selection
- Post-Quantum Security
- Wallet RPC Server
- Blockchain Utilities
- Security and Backup
- Troubleshooting
- Glossary
- 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
| Binary | Purpose |
|---|---|
shekyld | Full node daemon -- connects to the network, syncs the chain, relays transactions |
shekyl-cli | Interactive command-line wallet |
shekyl-wallet-rpc | Wallet RPC server for programmatic access |
shekyl-gen-ssl-cert | Generate self-signed SSL certificates for RPC |
shekyl-blockchain-import | Import blockchain from a file |
shekyl-blockchain-export | Export blockchain to a file |
shekyl-mdb-copy | Compact a stopped daemon's database |
shekyl-blockchain-stats | Print blockchain statistics |
shekyl-blockchain-ancestry | Trace transaction ancestry chains |
shekyl-blockchain-depth | Compute minimum chain depth for outputs |
shekyl-blockchain-usage | Histogram 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
| Flag | Description |
|---|---|
--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
| Flag | Description |
|---|---|
--prune-blockchain | Enable 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
| Flag | Description |
|---|---|
--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-rpc | Disable 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
| Flag | Description |
|---|---|
--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.
| Flag | Description |
|---|---|
--non-interactive | Disable 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
| Command | Description |
|---|---|
status | One-line summary: height, net hash, connections, sync state |
print_height | Current blockchain height |
diff | Current mining difficulty |
sync_info | Detailed sync and peer download state |
hard_fork_info | Current and upcoming hard fork versions |
version | Daemon version string |
Mining
| Command | Description |
|---|---|
start_mining <addr> [threads] | Start the built-in CPU miner |
stop_mining | Stop mining |
mining_status | Current mining state, hash rate, address |
show_hr / hide_hr | Toggle real-time hash rate display |
Network and peers
| Command | Description |
|---|---|
print_pl | Full peer list |
print_pl_stats | Peer list statistics |
print_cn | Active connections with data transfer stats |
print_net_stats | Aggregate network bandwidth |
bans | List 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
| Command | Description |
|---|---|
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_pool | Full transaction pool |
print_pool_sh | Transaction pool (short format) |
print_pool_stats | Pool statistics |
alt_chain_info | Show 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
| Command | Description |
|---|---|
save | Force 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_blockchain | Enable pruning on a non-pruned database |
Exit
| Command | Description |
|---|---|
stop_daemon / exit | Gracefully 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 once — write 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:
- Classical segment (
shekyl1...) -- ~113 characters, contains Ed25519 spend and view public keys - PQC-A segment (
skpq...) -- contains part of the ML-KEM-768 encapsulation key - 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): 0–1 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:
| Command | Description |
|---|---|
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:
stakemakes 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_balanceshows what can move back;drain <amount>moves it back (fee and destination are automatic). - Exit:
unstakeposts the permanent exit andcollect_unstakedreturns 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.
- Build: The coordinator creates the transaction body and FCMP++ proof.
- Export: The coordinator exports a signing request file (JSON) for each signer to review.
- Sign: Each of the M required signers independently produces a hybrid (Ed25519 + ML-DSA-65) signature over the canonical payload.
- Assemble: The coordinator collects all M signature files, assembles the
pqc_authcontainer, 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:
| Method | Description |
|---|---|
create_pqc_multisig_group | Create a group with N total, M required, participant keys |
get_pqc_multisig_info | Check if wallet is part of a multisig group and its parameters |
export_multisig_signing_request | Export a transaction for co-signers |
sign_multisig_partial | Produce a partial signature over the signing request |
import_multisig_signatures | Import 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()).
| Configuration | Auth Size | vs. Single |
|---|---|---|
| Single signer | ~5.4 KB | baseline |
| 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:
| Network | Flag | P2P Port | RPC Port | Use |
|---|---|---|---|---|
| Mainnet | (default) | 11021 | 11029 | Production -- real money |
| Testnet | --testnet | 12021 | 12029 | Protocol experiments |
| Stagenet | --stagenet | 13021 | 13029 | Integration testing (exchange, wallet) |
| Fakechain | --regtest | n/a | n/a | Local 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:12029on testnet), andmine startrefuses 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:
- Ed25519 -- a battle-tested classical signature algorithm.
- 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
| Flag | Description |
|---|---|
--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-login | Run 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);unstakeandcollect_unstaked(the composed exit, PR-C). There is noclaimRPC — 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
| Tool | Purpose |
|---|---|
shekyl-blockchain-ancestry | Trace the input ancestry of a transaction |
shekyl-blockchain-depth | Compute minimum chain depth for an output or transaction |
shekyl-blockchain-usage | Histogram 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, andshekyl-blockchain-usageall 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 (seedocs/EXECUTABLES.mdanddocs/FOLLOWUPS.md;shekyl-blockchain-prune-known-spent-datawas 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-peersto increase outbound connections. - Disk space: A full node needs ~50 GB. Use
--prune-blockchainto reduce to ~5 GB. - Corrupted database: Try
pop_blocks 100in 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
refreshin the wallet.statusshows the wallet's height next to the daemon's — make sure the daemon is fully synchronised first. - Full rescan:
rescanrebuilds 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 (
statusin the wallet shows both heights;statusin the daemon console shows peers and sync state). - The transaction pool can be checked with
print_poolin 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 withabandon <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-cliwrites stderr only. Redirect with2> pathif you want a file.shekyl-wallet-rpcwrites stderr only unless you pass--log-file <PATH>. When--log-fileis supplied, the parent directory is created with0700perms and the file with0600perms on POSIX. No rotation is performed on the opt-in path — the operator owns file lifecycle.shekyldwrites both stderr and a rotated file sink. The default file lives at~/.shekyl/logs/shekyld.log(suffixed-testnet/-stagenet/-regtestfor non-mainnet runs), rotated at ~100 MB with 50 archives retained. The live file and every rotated archive are forced to POSIX mode0600by the Rust side before the filename becomes visible. Override the path with--log-file, rotation with--max-log-file-sizeand--max-log-files(set max-log-files to0to 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:
- Output format. The default
fmt::layerfromtracing-subscriberreplaces the custom easylogging++ format string. Timestamps are RFC 3339 UTC (not local time with microseconds), the level token is full words (ERRORrather thanE), and the category appears as a structured target. Log-scraping tooling that parsed the prior format byte-for-byte must be updated. MLOG_SET_THREAD_NAMEis 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 viapthread_setname_np/ equivalent is tracked indocs/FOLLOWUPS.mdas a minor follow-up.MONERO_LOGS/MONERO_LOG_FORMATare retired. The C++ shim no longer reads either name;SHEKYL_LOGis the single input.MONERO_LOG_FORMATin particular has no replacement — formatting is owned by the subscriber's layer stack and is not operator-tunable.
Glossary
| Term | Meaning |
|---|---|
| Address | Your Bech32m-encoded public identifier for receiving SKL. Contains both classical (~113 char) and PQC (~1,750 char) key segments. Safe to share. |
| Atomic unit | The smallest unit of SKL. 1 SKL = 1,000,000,000 atomic units. |
| Block | A bundle of transactions added to the blockchain roughly every 2 minutes. |
| Block height | The sequential number of a block, starting from 0. |
| Block reward | New SKL created and given to the miner who finds a valid block. |
| Config file | A text file (shekyld.conf) containing daemon options in name=value format, loaded with --config-file. |
| Console | The interactive command prompt inside shekyld when running in foreground mode. |
| Daemon | The background program (shekyld) that connects to the Shekyl network and maintains the blockchain. |
| Difficulty | A measure of how hard mining puzzles are. Adjusts automatically to target ~2-minute blocks. |
| Emission | The schedule by which new SKL is created. The total supply is mathematically capped. |
| FCMP++ membership proof | A 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 signature | Two signatures on every transaction: Ed25519 (classical) and ML-DSA-65 (quantum-resistant). |
| Key images | Cryptographic markers that prevent double-spending. Derived and tracked internally by the wallet; there is no export surface. |
| KDF rounds | Key derivation function iterations; higher values make wallet password brute-forcing harder. |
| Mnemonic seed | The 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-65 | A quantum-resistant signature algorithm standardized by NIST (FIPS 204). |
| Privacy | FCMP++ proofs, stealth addresses, and per-output PQC keys (hybrid X25519 + ML-KEM-768) hide who sends, who receives, and how much. Automatic. |
| Pruning | Removing old prunable transaction data to reduce storage. The node can still verify new blocks. |
| RandomX | Shekyl's mining algorithm, designed for regular CPUs. |
| RPC | Remote Procedure Call -- the JSON-based API exposed by the daemon and wallet RPC server. |
| Staking | Locking SKL for a period to earn yield from the emission pool. |
| Stealth address | A one-time address generated for each transaction so only sender and receiver know the destination. |
| Payment request | A merchant invoice record (amount, label, expiry) tied to your primary address — replaces per-sender subaddress rotation. |
Getting Help
- In-repo documentation: Browse the docs/ directory or the online documentation browser.
- GUI wallet guide: USER_GUIDE.md covers the same features from the graphical interface.
- Source code: shekyl-core on GitHub
- Built-in help: Run
shekyld --help,shekyl-cli --help, or typehelpinside any interactive console.
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.