Hashi Node Operator Runbook
This runbook is for Sui validators joining the Hashi committee. It covers prerequisites, configuration, genesis, ongoing operations, and governance.
Hashi is a Sui-native asset orchestration protocol that secures and manages native BTC for use on Sui through threshold cryptography. Committee members run the hashi service alongside their Sui validator to participate in MPC threshold Schnorr signing for Bitcoin custody. The committee collectively manages a Bitcoin master key, and no single validator ever holds the full key.
This is a living document. Hashi launches on Testnet first. This document adds Mainnet parameters to the Networks table below as the team finalizes them. Wherever the runbook shows a network-specific value, use the column for your target network (the worked examples use Testnet).
This document assumes familiarity with Sui validator operations, Linux system administration, and basic Bitcoin infrastructure.
Networks
Hashi is network-specific: the Sui chain, the Bitcoin chain, and their endpoints all differ per deployment. The runbook's examples use Testnet (Sui Testnet + Bitcoin Signet); for any other network, substitute the values from this table. Adding a network later is just a matter of filling in a column here.
| Parameter | Testnet (current) | Mainnet |
|---|---|---|
| Sui network | Testnet | Mainnet |
sui-chain-id | 69WiPg3DAQiwdxfncX6wYQ2siKwAe6L9BZthQea3JNMD | 4btiuiMPvEENsttpZC7CZ53DruC3MAgfznDbASZ7DR6S |
| Sui public fullnode | https://fullnode.testnet.sui.io:443 | https://fullnode.mainnet.sui.io:443 |
| Bitcoin network | Signet | Mainnet |
bitcoin-chain-id (genesis block hash) | 00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6 | 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f |
[bitcoin].network | signet | mainnet |
| Bitcoin RPC port (default) | 38332 | 8332 |
| Bitcoin P2P port (default) | 38333 | 8333 |
package-id / hashi-object-id | provided at launch | TBD |
A self-hosted Sui fullnode is reached at its RPC port (default
http://127.0.0.1:9000) on any network. Hashi's built-in default forbitcoin-rpcishttp://localhost:8332(the Mainnet port), so on Signet you must set the port explicitly.
1. Prerequisites
1.1 Resources
The requirements for running a Hashi node are low compared with those of a Sui node. To reduce blast radius, avoid colocating the Hashi process with a Sui validator. There should be no performance concerns with containerizing the process or running it in Kubernetes.
| Resource | Recommendation |
|---|---|
| CPU | 3 cores |
| Memory | 2 GB |
| Storage | 10 GB persistent |
| Network | 1 Gbps NIC / link |
Sizing notes (preliminary, derived from June test runs and pending revalidation after recent fixes):
- CPU: 3 cores for headroom, mainly the parallel-signing burst on the leader.
- Memory: 2 GB is comfortable headroom; steady-state is well under this (a one-off ~3.7 GB spike came from a since-identified bug, not normal load).
- Network: ~66 Mbps ingress / ~40 Mbps egress with the default MPC
(t, f)parameters; a higher-throughput ("hybrid") configuration likely needs considerably more, so provision a 1 Gbps link for headroom. Bandwidth tracks MPC protocol rounds (DKG, key rotation, presigning, signing), not individual deposit/withdrawal transactions; egress can be a meaningful cost, and the team is actively reducing this.- Storage: peaks around 1 GB; 10 GB is generous but fine.
These figures reflect Testnet observations and might change for Mainnet.
1.2 External service dependencies
A Hashi node depends on two external services that you provide:
- Bitcoin node for your target network, reachable on both P2P and JSON-RPC (default ports per the Networks table; Signet uses 38333 / 38332), running as a full archival node with:
txindex=1(Hashi callsgetrawtransactionon arbitrary deposit txids)blockfilterindex=1+peerblockfilters=1(BIP-157/158 compact block filter serving for the embedded light client)server=1plus RPC auth
- Sui fullnode with gRPC enabled. As a Sui validator you already operate one; ensure its gRPC v2 API is enabled.
Run both dependencies on separate infrastructure from the Hashi node, not colocated on the same host, so a fault or compromise in one does not cascade to the others.
1.3 Software dependencies
Hashi binary. Build the Linux x86-64 binary with the deterministic Docker build. It compiles in a pinned, reproducible environment, extracts the binary, and writes its sha256:
# Reproducible build (requires Docker); writes out/hashi and out/hashi.sha256
bash docker/hashi/build.sh --no-cache
The build pins its base images, sets SOURCE_DATE_EPOCH=0, links statically, and compiles --frozen with networking disabled, so it is bit-for-bit reproducible. CI confirms the same binary hash across runners. Each release publishes the sha256 of these deterministic binaries, so you can reproduce the build yourself and confirm your binary matches the published checksum:
sha256sum -c out/hashi.sha256
Backup tooling. Node backups are OpenPGP (Sequoia). To generate a backup key and decrypt backups you need an OpenPGP toolchain:
gpg+gpg-agent, for decrypting backups, including with a YubiKey (works over a forwardedgpg-agentsocket so you can restore on a server using a YubiKey plugged into your laptop)- Optionally Sequoia
sq(local keys) oroct/ openpgp-card-tools (YubiKey key generation)
See the Hashi Node Backups guide for the full key-generation procedure.
1.4 Sui RPC
Hashi connects to Sui over the gRPC v2 API (gRPC over HTTP/2), not JSON-RPC. The fullnode must expose the sui.rpc.v2 services, including the streaming SubscriptionService (subscribe_checkpoints) the node uses to follow the chain. A fullnode that serves only JSON-RPC does not work.
| Setup | sui-rpc value | Transport |
|---|---|---|
| Public Sui Foundation fullnode | https://fullnode.testnet.sui.io:443 | gRPC over TLS (443) |
| Self-hosted fullnode (separate host) | http://<fullnode-host>:9000 | plaintext gRPC/h2c (default 9000) |
As a Sui validator you already run a fullnode, so point Hashi at it and confirm gRPC is enabled in the fullnode config. The node verifies the endpoint's reported chain ID matches sui-chain-id at startup and refuses to start on mismatch.
Set sui-chain-id and sui-rpc to match your target network, as shown in the Networks table.
1.5 Bitcoin node
Hashi talks to Bitcoin over two independent channels, both of which must be reachable:
- P2P: an embedded Kyoto BIP-157 light client connects over P2P to fetch headers, compact block filters (BIP-158), and full blocks. This requires the serving node to advertise filters (
peerblockfilters=1) and build the index (blockfilterindex=1). - JSON-RPC: used for transaction lookup (
getrawtransaction), fee estimation (estimatesmartfee), UTXO checks (gettxout), chain info (getblockchaininfo), and broadcasting withdrawals (sendrawtransaction).
For the JSON-RPC channel you can run your own archival Signet node or use a verified RPC provider such as QuickNode (or another provider that serves Bitcoin Signet). Whichever you choose, the endpoint must be archival (Hashi calls getrawtransaction on arbitrary deposit txids) and must permit sendrawtransaction.
The P2P / BIP-157 channel (the embedded light client's compact-filter sync) needs a peer that serves filters (peerblockfilters=1). Run your own Signet node for this, or use a provider that exposes Signet P2P with compact-filter serving. The simplest setup that satisfies both channels is a single self-hosted archival Signet node.
RPC methods Hashi calls (so you can scope RPC permissions): getrawtransaction (verbose), sendrawtransaction, estimatesmartfee, gettxout, getblockchaininfo, getblockheader (verbose). Hashi targets Bitcoin Core v29.
bitcoin.conf, run as an archival, non-pruned node (Signet shown; for another network use its ports and network flag from the Networks table):
# Hashi Testnet — Bitcoin SIGNET serving node
# ARCHIVAL: do NOT set prune=
signet=1
server=1
txindex=1 # getrawtransaction on arbitrary deposit txids
blockfilterindex=1 # build BIP-158 compact block filter index
peerblockfilters=1 # serve BIP-157 filters to the embedded light client
listen=1 # accept P2P (Signet default port 38333)
# JSON-RPC auth — use rpcauth (hashed) in production, or rpcuser/rpcpassword
rpcuser=YOUR_RPC_USER
rpcpassword=YOUR_STRONG_RPC_PASSWORD
# rpcauth=YOUR_RPC_USER:SALT$HASH # generate with Bitcoin Core's rpcauth.py
[signet]
bind=0.0.0.0:38333 # P2P
rpcbind=127.0.0.1:38332 # JSON-RPC (Signet default port)
rpcallowip=127.0.0.1 # restrict to where Hashi runs
The corresponding Hashi config pointers (full template in 3.1):
bitcoin-chain-id = "00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6" # signet genesis
bitcoin-rpc = "http://127.0.0.1:38332" # Hashi's own default is 8332 — you MUST override it
bitcoin-trusted-peers = ["127.0.0.1:38333"] # host:port (port is mandatory)
# bitcoin-start-height = <a height at/below the first relevant Signet deposit>
[bitcoin-rpc-auth]
UserPass = ["YOUR_RPC_USER", "YOUR_STRONG_RPC_PASSWORD"]
Notes: ports
38332(RPC) /38333(P2P) are Bitcoin Core Signet defaults. Hashi does not default to them (its defaultbitcoin-rpcishttp://localhost:8332), so set them explicitly. The light client runs in whitelist-only mode and connects only tobitcoin-trusted-peers, so that list must be correct and reachable.bitcoin-start-heightdefaults to800000(Mainnet-oriented); set it for Signet so the light client does not skip early deposits.
Verify your node:
bitcoin-cli -signet getblockchaininfo | grep '"chain"' # must be "signet"
bitcoin-cli -signet getindexinfo # block filter index present
bitcoin-cli -signet getnetworkinfo # NODE_COMPACT_FILTERS advertised
2. Key management and security
2.1 Keys overview
| Key | Algorithm | Provisioning | Purpose |
|---|---|---|---|
| Sui validator account key | Any Sui scheme (Ed25519, multisig, secp256k1, zkLogin) | Existing (Sui validator) | Authorizes initial Hashi registration. See 2.3. |
| TLS private key | Ed25519 | Generate fresh (dedicated) | Peer-to-peer authentication through a self-signed cert. The node registers the public key onchain. |
| Operator private key | Ed25519 | Generate fresh | Signs the node's day-to-day Sui transactions (cert submission, governance, metadata). See 2.4. |
| BLS12-381 key | BLS12-381 | Auto-generated at startup | MPC protocol participation. Stored in local DB. |
| Encryption key | ECIES | Auto-generated at startup | Encrypts MPC share exchange between validators. Stored in local DB. |
| Backup key | OpenPGP (Sequoia) | Operator-generated (file or YubiKey) | Encrypts automatic epoch backups. Strongly recommended. |
2.2 TLS key (dedicated)
Generate a fresh Ed25519 TLS key dedicated to your Hashi node, separate from your Sui validator's TLS key, for clean separation of concerns. This key authenticates your Hashi node to other committee members, and the node registers the corresponding public key onchain during registration. Set tls-private-key to a PKCS#8 PEM/DER file path or an inline PEM string.
tls-private-key = "/path/to/tls-private-key.pem"
A planned enhancement is for the node to generate its TLS key on startup, removing this manual step.
2.3 Registration must be signed by the account key
Your Sui validator account key must sign and broadcast the initial registration transaction. Onchain, validator::register sets the member's validator_address = ctx.sender() and asserts the sender is in the Sui active validator set, so no operator key can substitute for this first transaction. Registration places no constraint on the account's key scheme (Ed25519, multisig, secp256k1, or zkLogin all work); it requires only that the sender is an active Sui validator.
Because that key is typically in cold storage or a hardware wallet, use the offline signing flow: hashi register --print-only builds the unsigned transaction and prints it as base64 without needing any private key. Sign it externally with the account key and broadcast.
# Emit the unsigned registration tx (no private key required)
hashi register --config /path/to/validator.toml \
--operator-address 0x<your-operator-address> \
--print-only
# -> sign the printed base64 tx with the validator account key (cold/hardware) and broadcast
Do not rely on the plain
hashi register(execute) path for the first registration unless your configuredoperator-private-keyis the validator account key. The builder forces the sender to the validator address while the execute path signs with the operator key, producing a signer/sender mismatch.
2.4 Operator key (day-to-day signing)
Generate a fresh Ed25519 keypair for the operator and fund its address with SUI for gas. The running node (hashi server) signs all its day-to-day Sui transactions with this operator-private-key: MPC certificate submission, governance, and metadata updates.
operator-private-key = "/path/to/hashi-operator-key.pem"
Delegating to the operator key. After registration (signed by the validator account key, see 2.3), authorize the operator key by setting it as the member's operator address with --operator-address:
hashi register --config /path/to/validator.toml \
--operator-address 0x<operator-address> \
--print-only
# sign with the validator account key (cold/hardware) and broadcast
Onchain, every member action is authorized by member_authorized(validator, sender), where the sender must be the member's validator key or its registered operator address. So once delegated, the operator key performs cert submission, governance voting, and metadata updates on its own, and the validator account key can return to cold storage. You need it again only to rotate the operator key or change registration metadata.
Operator-key rotation is validator-only. Only the validator account key can change the operator address (onchain, set_operator_address requires the sender to be the validator). A compromised or lost operator key therefore cannot re-delegate or lock you out: pull the validator key from cold storage, run hashi register --operator-address 0x<new-operator> --print-only, sign offline, and this action revokes the previous operator key.
Break-glass / backup. Because the validator key always retains full authority, it doubles as a recovery key. If the operator key is unavailable, the validator key can perform any member action directly, such as signing the node's transactions or voting.
2.5 Backup key (OpenPGP)
Hashi creates encrypted backups of critical state (MPC key shares, DB, config) every epoch. Encryption needs only the public OpenPGP certificate, so the private key never has to be on the server during normal operation.
- Option 1 (recommended): YubiKey. Generate the key on the device; the private key never leaves it.
- Option 2: local OpenPGP key. Generate with
sqorgpg.
Set the armored public certificate (inline or a path) in the node config:
backup-pgp-cert = "/path/to/hashi-backup-cert.asc" # armored OpenPGP public cert, or inline armored text
backup-dir = "/var/lib/hashi/backups"
Key generation and restore are covered in the Hashi Node Backups guide. Store the private key / YubiKey securely and separately from the backup files.
3. Configuration
Hashi uses two configuration files:
- Validator config, used by
hashi serverandhashi register. Contains keys, endpoints, and all node parameters. - CLI config (
hashi-cli.toml), used by governance, query, and deposit/withdraw commands.
3.1 Validator config
All fields use kebab-case. Optional fields show their defaults in comments.
# ── Identity ──────────────────────────────────────────────────────────
# Ed25519 TLS private key (PEM file path, DER file path, or inline PEM)
tls-private-key = "/path/to/tls-private-key.pem"
# Ed25519 operator private key for signing the node's Sui transactions
# (see the delegation caveat in section 2.4)
operator-private-key = "/path/to/operator-key.pem"
# Your Sui validator address
validator-address = "0x<your-validator-address>"
# ── Network ───────────────────────────────────────────────────────────
# Local bind address for gRPC+TLS server (default: 0.0.0.0:443)
# listen-address = "0.0.0.0:443"
# Public URL where other validators can reach this node
endpoint-url = "https://your-hashi-node.example.com:443"
# Prometheus metrics endpoint (default: 127.0.0.1:9180)
# metrics-http-address = "127.0.0.1:9180"
# ── Sui (gRPC v2) ─────────────────────────────────────────────────────
# Sui chain ID — genesis checkpoint digest (Base58). Use your network's value
# from the Networks table; the example below is Testnet.
sui-chain-id = "69WiPg3DAQiwdxfncX6wYQ2siKwAe6L9BZthQea3JNMD"
# Sui gRPC endpoint. Public fullnode (TLS:443) or self-hosted (plaintext gRPC:9000)
sui-rpc = "https://fullnode.testnet.sui.io:443"
# sui-rpc = "http://127.0.0.1:9000"
# Hashi package and object IDs (provided after package publication)
[hashi-ids]
package-id = "0x<package-id>"
hashi-object-id = "0x<hashi-object-id>"
# ── Bitcoin ───────────────────────────────────────────────────────────
# Bitcoin chain ID — genesis block hash. Use your network's value from the
# Networks table; the example below is Signet (Testnet).
bitcoin-chain-id = "00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6"
# Bitcoin Core RPC endpoint (Hashi default is http://localhost:8332 — override for Signet)
bitcoin-rpc = "http://127.0.0.1:38332"
# RPC authentication: None, UserPass, or CookieFile
[bitcoin-rpc-auth]
UserPass = ["YOUR_RPC_USER", "YOUR_STRONG_RPC_PASSWORD"]
# CookieFile = "/home/USER/.bitcoin/signet/.cookie"
# Trusted Bitcoin P2P peers for the embedded BIP-157 light client (host:port; port mandatory)
bitcoin-trusted-peers = ["127.0.0.1:38333"]
# Block height the light client begins syncing from (default: 800000, mainnet-oriented)
# bitcoin-start-height = <height at/below first relevant Signet deposit>
# ── Storage ───────────────────────────────────────────────────────────
# Local database path (required)
db = "/var/lib/hashi/db"
# ── Backup (OpenPGP / Sequoia) ────────────────────────────────────────
# Armored OpenPGP public certificate (file path or inline) for encrypted backups
# backup-pgp-cert = "/path/to/hashi-backup-cert.asc"
# Backup output directory (default: /tmp)
# backup-dir = "/var/lib/hashi/backups"
# ── Optional Integrations ─────────────────────────────────────────────
# Screener gRPC endpoint for AML/sanctions checking (when unset, screening is skipped)
# screener-endpoint = "https://screener.example.com"
# Guardian gRPC endpoint (when unset, the guardian integration is bypassed)
# guardian-endpoint = "https://hashi-guardian.example.com"
# ── Advanced Tuning (defaults are fine for most operators) ────────────
# Max gRPC message size in bytes (default: 16777216 = 16 MiB)
# grpc-max-decoding-message-size = 16777216
# Withdrawal batching delay in ms (default: 300000 = 5 min)
# withdrawal-batching-delay-ms = 300000
# Max withdrawals per Bitcoin transaction (default: 70)
# withdrawal-max-batch-size = 70
# Max unconfirmed transaction chain depth (default: 5)
# max-mempool-chain-depth = 5
# Fee estimation confirmation target in blocks (default: 3)
# withdrawal-fee-conf-target = 3
# Concurrency for per-input withdrawal signing (default: 25)
# withdrawal-signing-concurrency = 25
# Concurrency for leader job tasks, e.g. deposit processing (default: 32)
# max-concurrent-leader-job-tasks = 32
3.2 CLI config
For governance commands, queries, and user-facing operations. Generate a template:
hashi config template -o hashi-cli.toml
# Sui gRPC endpoint
sui_rpc_url = "https://fullnode.testnet.sui.io:443"
# Hashi package ID (original package address)
package_id = "0x<package-id>"
# Hashi shared object ID
hashi_object_id = "0x<hashi-object-id>"
# Path to Ed25519 keypair for signing CLI transactions
keypair_path = "/path/to/operator-key.pem"
[bitcoin]
rpc_url = "http://127.0.0.1:38332"
rpc_user = "YOUR_RPC_USER"
rpc_password = "YOUR_STRONG_RPC_PASSWORD"
network = "signet"
3.3 Environment variables
All environment variables override their corresponding config file values.
| Variable | Overrides | Purpose |
|---|---|---|
HASHI_CLI_CONFIG | n/a | Path to CLI config file |
SUI_RPC_URL | sui_rpc_url / sui-rpc | Sui gRPC endpoint |
HASHI_PACKAGE_ID | package_id | Hashi package ID |
HASHI_OBJECT_ID | hashi_object_id | Hashi shared object ID |
HASHI_KEYPAIR | keypair_path | Path to signing keypair |
BTC_RPC_URL | bitcoin.rpc_url | Bitcoin RPC URL |
BTC_RPC_USER | bitcoin.rpc_user | Bitcoin RPC username |
BTC_RPC_PASSWORD | bitcoin.rpc_password | Bitcoin RPC password |
BTC_NETWORK | bitcoin.network | Bitcoin network (regtest, signet, testnet4, mainnet) |
BTC_PRIVATE_KEY | bitcoin.private_key_path | Path to BTC private key (WIF) |
HASHI_GAS_BUDGET | n/a | Gas budget for transactions (MIST) |
4. Network and firewall
4.1 Inbound
| Port | Protocol | Source | Purpose |
|---|---|---|---|
| 443 (default) | TLS 1.3 + gRPC | Other committee validators | MPC protocol messages, DKG, signing, deposit/withdrawal coordination |
The port is configurable through listen-address. It must be publicly reachable at the URL specified in endpoint-url, which is registered onchain.
4.2 Outbound
| Destination | Port | Protocol | Purpose |
|---|---|---|---|
| Sui fullnode | 443 (public) / 9000 (self-hosted) | gRPC over HTTP/2 | Chain state, checkpoint subscription, tx submission |
| Bitcoin RPC | per network (38332 on Signet) | HTTP/JSON-RPC | Tx lookup, fee estimation, UTXO checks, withdrawal broadcast |
| Bitcoin P2P | per network (38333 on Signet) | TCP | Kyoto BIP-157 light client (headers, filters, blocks) |
| Peer validators | 443 | TLS 1.3 + gRPC | Outbound connections to other committee members |
- Sui: the connection is HTTP/2 gRPC. HTTP/1.1-only proxies (or anything that strips HTTP/2) break the checkpoint subscription.
- Bitcoin RPC: the node broadcasts withdrawals through
sendrawtransaction, so the RPC user/whitelist must permit it, not only read calls.
4.3 Metrics (localhost only)
| Port | Protocol | Bind | Purpose |
|---|---|---|---|
| 9180 (default) | HTTP | 127.0.0.1 | Prometheus scrape endpoint at /metrics |
Not exposed externally by default. Scrape from localhost or through a reverse proxy with access controls.
4.4 TLS
- Hashi uses TLS 1.3 exclusively with Ed25519 self-signed certificates.
- The TLS public key is registered onchain, so peers verify certificates against the onchain registry, with no certificate authority requirement.
- The node auto-generates certificates from the
tls-private-keyat startup.
5. Genesis procedure
Genesis forms the initial Hashi committee and generates the shared Bitcoin master key through Distributed Key Generation (DKG).
5.1 Timeline
Package Published → Validators Register → 95% Stake Threshold → DKG (Automatic) → End Reconfig → Presignatures → Operational
From DKG trigger to operational typically takes 30 to 120 seconds, assuming validators are online and reachable.
5.2 Step 1: package publication (admin)
The admin publishes the Hashi Move package. This produces the package-id and hashi-object-id you need in both configs. The admin communicates these values to you.
5.3 Step 2: register your validator
Registration must be authorized by your Sui validator account key (see 2.3). Use the offline flow:
hashi register --config /path/to/validator.toml \
--operator-address 0x<your-operator-address> \
--print-only
# sign the printed base64 tx with the account key (cold/hardware) and broadcast
The registration transaction writes your BLS12-381 public key, Ed25519 TLS public key, ECIES encryption public key, endpoint URL, and operator address to the chain.
5.4 Step 3: start the server
hashi server --config /path/to/validator.toml
The node starts the gRPC+TLS server, connects to Sui (gRPC) and Bitcoin (RPC + P2P), and begins monitoring for the genesis trigger.
5.5 Step 4: DKG (automatic)
Once 95% of total Sui validator stake has registered with Hashi, the first node to detect this submits start_reconfig. All registered validators then run DKG concurrently. No operator action needed; monitor logs for start_reconfig, DKG started, DKG completed. DKG times out after 600 s per attempt and retries until it succeeds or a newer epoch supersedes it.
5.6 Step 5: end reconfig (automatic)
After DKG, validators exchange BLS signatures over the result. Once 2/3 of committee weight has signed, end_reconfig is submitted onchain and the committee generates presignatures. No operator action needed; monitor logs for end_reconfig submitted and presignature generation.
5.7 Post-genesis verification
hashi committee list
hashi committee epoch
hashi committee view 0x<your-validator-address>
hashi config on-chain
curl -s http://localhost:9180/metrics | grep -E 'hashi_epoch|hashi_reconfig_in_progress|hashi_presig_pool_remaining'
Expected after success: hashi_epoch > 0, hashi_reconfig_in_progress = 0, hashi_presig_pool_remaining > 0.
6. CLI reference
6.1 Global options
--config, -c CLI config file path (env: HASHI_CLI_CONFIG)
--sui-rpc-url Sui gRPC endpoint (env: SUI_RPC_URL)
--package-id Hashi package ID (env: HASHI_PACKAGE_ID)
--hashi-object-id Hashi shared object ID (env: HASHI_OBJECT_ID)
--keypair, -k Signing keypair path (env: HASHI_KEYPAIR)
--verbose, -v Enable verbose output
--yes, -y Skip confirmation prompts
--gas-budget Gas budget in MIST (env: HASHI_GAS_BUDGET)
--dry-run Simulate without executing
6.2 Server
hashi server [--config <path>]
Run the Hashi validator daemon. Uses the validator config.
6.3 Register
hashi register --config <path> [--operator-address <addr>] [--print-only] [-v] [-y]
Register or update your validator onchain. Initial registration must be signed by the validator account key; use --print-only for offline signing. Idempotent.
6.4 Committee
hashi committee list [--epoch <N>] # List committee members (default: current epoch)
hashi committee view <address> # View details for a specific validator
hashi committee epoch # Show current epoch info
6.5 Config
hashi config template [-o <path>] # Generate CLI config template (default: hashi-cli.toml)
hashi config show # Show effective CLI configuration
hashi config on-chain # View on-chain protocol configuration
6.6 Proposals (governance)
hashi proposal list [--type <type>] [--detailed]
hashi proposal view <proposal-id>
hashi proposal vote <proposal-id> [--execute]
hashi proposal remove-vote <proposal-id>
hashi proposal execute <proposal-id>
Create proposals:
hashi proposal create update-config <key> <value> [-m key=value]
hashi proposal create update-mpc-config [--threshold-bps <N>] [--max-faulty-bps <N>] [--weight-reduction-allowed-delta <N>] [-m key=value]
hashi proposal create upgrade [--package-path <path> | --digest <hex>] [-m key=value]
hashi proposal create enable-version <version> [-m key=value]
hashi proposal create disable-version <version> [-m key=value]
hashi proposal create abort-reconfig [--epoch <N>] [-m key=value]
hashi proposal create update-guardian [--url <url>] [--public-key <hex>] [-m key=value]
hashi proposal create emergency-pause [--unpause] [-m key=value]
Config values are type-prefixed: u64:30000, bool:true.
6.7 Backup
Backups are OpenPGP (Sequoia): encrypted archives use the suffix .tar.asc; unencrypted archives use .tar (format auto-detected on restore).
# Create a backup (cert taken from the node config's backup-pgp-cert, or override inline/by path)
hashi backup save <node-config-path> [--backup-pgp-cert <armored-cert-or-path>] [--output-dir .]
# Restore an encrypted .tar.asc — exactly one decryptor is required:
hashi backup restore <tarball> --backup-pgp-secret-key <secret-key-file> [--output-dir .] [--copy-to-original-paths]
hashi backup restore <tarball> --use-gpg-agent [--gpg-homedir <dir>] [--output-dir .] [--copy-to-original-paths]
# Restore an unencrypted .tar — no key needed
hashi backup restore <tarball.tar> [--output-dir .]
--use-gpg-agent supports YubiKeys, including over a forwarded gpg-agent socket. --copy-to-original-paths writes files back to the absolute paths captured at backup time (same-host in-place recovery only).
6.8 Deposit, withdraw, and balance
# Deposits
hashi deposit generate-address --recipient <sui-address>
hashi deposit request --txid <btc-txid> [--outputs <json>] [--recipient <sui-addr>]
hashi deposit request-single --txid <btc-txid> --vout <n> --amount <sats> [--recipient <sui-addr>]
hashi deposit status <request-id>
hashi deposit list [--json]
# Withdrawals
hashi withdraw request --amount <sats> --btc-address <addr> [--count <N>] # --count submits N identical requests (default 1)
hashi withdraw cancel <request-id>
hashi withdraw status <request-id>
hashi withdraw list [--json]
# Balance
hashi balance <sui-address> [--json]
These are primarily for testing or admin use, not routine operator tasks.
6.9 Publish (admin only)
hashi publish ...
Builds, publishes, and initializes the Hashi Move package (including its onchain protocol and custody configuration). The admin runs this, not individual operators.
7. Governance participation
7.1 How proposals work
- Any committee member can create a proposal. Quorum is stake-weighted: most proposals require 2/3 of committee weight, while an emergency pause uses a lower threshold (5%) so the committee can halt the protocol quickly (resuming again requires 2/3). See the threshold column in 7.2.
- Votes can be added/removed before execution. Once quorum is reached, any committee member can execute.
- The
--executeflag onvoteauto-executes if your vote reaches quorum (except for Upgrade proposals). - Proposals carry optional
-m key=valuemetadata for human context.
Governance actions are authorized by member: the signer must be the committee member's validator key or its delegated operator key, and the system records the vote under the validator for stake-weight purposes. So your operator key can vote on the validator's behalf.
7.2 Proposal types
| Type | Threshold | Purpose | Parameters |
|---|---|---|---|
| UpdateConfig | 2/3 | Change protocol parameters | key and type-prefixed value |
| UpdateMpcConfig | 2/3 | Change MPC threshold/faulty/delta | --threshold-bps, --max-faulty-bps, --weight-reduction-allowed-delta |
| Upgrade | 2/3 | Publish a new package version | --package-path or --digest |
| EnableVersion | 2/3 | Re-enable a disabled package version | version number |
| DisableVersion | 2/3 | Disable a package version | version number (cannot disable active version) |
| AbortReconfig | 2/3 | Abort a stuck reconfiguration | --epoch of pending reconfig |
| UpdateGuardian | 2/3 | Change guardian endpoint and key | --url, --public-key |
| EmergencyPause | 5% to pause, 2/3 to unpause | Quickly pause or resume the protocol in an emergency | n/a |
EmergencyPause uses a deliberately low threshold so a small fraction of committee weight can halt the protocol fast; resuming requires the normal 2/3. Create it with
hashi proposal create emergency-pause(add--unpauseto propose resuming).
Configurable protocol parameters:
| Key | Type | Default | Description |
|---|---|---|---|
bitcoin_deposit_minimum | u64 | 30,000 sats | Minimum deposit amount (floor: 546 sats) |
bitcoin_withdrawal_minimum | u64 | 30,000 sats | Minimum withdrawal amount (floor: 547 sats) |
bitcoin_confirmation_threshold | u64 | 6 blocks | Confirmations required before the deposit becomes final |
bitcoin_deposit_time_delay_ms | u64 | 600,000 ms (10 min) | Delay between deposit approval and confirmation (fraud window) |
withdrawal_cancellation_cooldown_ms | u64 | 3,600,000 ms (1 hr) | Cooldown before a withdrawal can be cancelled |
paused | bool | false | Pause all deposit/withdrawal processing |
The CLI
update-config --helplists a subset of keys (bitcoin_deposit_minimum,bitcoin_withdrawal_minimum,bitcoin_confirmation_threshold,withdrawal_cancellation_cooldown_ms,paused);bitcoin_deposit_time_delay_msis still settable.
7.3 Lifecycle example: UpdateConfig
# 1. Create the proposal
hashi proposal create update-config bitcoin_deposit_minimum u64:50000 \
-m reason="Increase minimum to reduce small-value UTXO accumulation"
# note the proposal ID: 0x<proposal-id>
# 2. Share the proposal ID with other committee members
# 3. Vote (--execute auto-executes if quorum is reached)
hashi proposal vote 0x<proposal-id> --execute
# 4. Check status
hashi proposal view 0x<proposal-id>
# 5. Execute manually if needed
hashi proposal execute 0x<proposal-id>
# 6. Verify
hashi config on-chain
7.4 Package upgrade flow
# Build and propose (CLI verifies PACKAGE_VERSION is incremented)
hashi proposal create upgrade --package-path /path/to/packages/hashi
# Vote (--execute is NOT supported for Upgrade proposals)
hashi proposal vote 0x<proposal-id>
# After quorum: execute the upgrade
hashi proposal execute 0x<proposal-id>
8. Monitoring and metrics
8.1 Prometheus endpoint
Metrics are served at http://127.0.0.1:9180/metrics (configurable through metrics-http-address).
curl -s http://localhost:9180/metrics | grep hashi_
8.2 Key metrics
| Metric | Type | What to Watch |
|---|---|---|
uptime | gauge | Node uptime (s). Labels: version, sui_chain_id, bitcoin_chain_id, package_id. |
hashi_epoch | gauge | Current Hashi epoch. Should advance with Sui epochs. |
hashi_sui_epoch | gauge | Current Sui epoch. |
hashi_reconfig_in_progress | gauge | 1 during reconfiguration. Extended periods = problem. |
hashi_paused | gauge | 1 when deposits/withdrawals are paused. |
hashi_presig_pool_remaining | gauge | Presignatures available. Should not reach 0. |
hashi_is_leader | gauge | 1 when this node is the current leader. |
hashi_kyoto_synced | gauge | 1 when the Bitcoin light client is synced. |
hashi_kyoto_best_height | gauge | Bitcoin tip block height. |
hashi_kyoto_connected_peers | gauge | Bitcoin P2P peer count. |
hashi_deposit_queue_size | gauge | Pending deposit requests. |
hashi_withdrawal_queue_size | gauge | Pending withdrawals (by status). |
hashi_utxo_pool_size | gauge | Managed UTXOs (available / unconfirmed_change / locked). |
hashi_mpc_sign_failures_total | counter | MPC signing failures. Investigate if increasing. |
hashi_mpc_reconfig_total_duration_seconds | histogram | DKG/rotation duration. |
hashi_sui_tx_submissions_total | counter | Sui tx submissions (by operation, status). |
hashi_leader_retries_total | counter | Leader operation retries (by operation, error kind). |
8.3 Suggested alerts
| Condition | Severity | Action |
|---|---|---|
hashi_reconfig_in_progress == 1 for > 10 min | Critical | Check logs for DKG/rotation errors. Might need AbortReconfig. |
hashi_presig_pool_remaining == 0 | Critical | Node cannot sign. Check reconfig status and MPC logs. |
hashi_kyoto_synced == 0 for > 5 min | Warning | Light client lost sync. Check Bitcoin RPC and P2P connectivity. |
hashi_kyoto_connected_peers == 0 | Warning | No Bitcoin P2P peers. Check bitcoin-trusted-peers and reachability. |
hashi_mpc_sign_failures_total increasing | Warning | Investigate MPC protocol errors in logs. |
up == 0 (Prometheus target down) | Critical | Node is down. Restart and check logs. |
Coming soon: A metrics push service for centralized collection. Details follow once it lands.
9. Epoch changes (reconfiguration)
9.1 What happens automatically
On every Sui epoch change, the Hashi committee reconfigures:
- A node detects the Sui epoch change and submits
start_reconfig. - The system pauses deposit and withdrawal processing.
- Key rotation runs. The old committee redistributes key shares to the new committee (the Bitcoin master public key is unchanged). The genesis epoch runs DKG instead.
- Validators exchange BLS signatures; once 2/3 weight signs,
end_reconfigis submitted. - The new committee generates presignatures.
- Normal operations resume.
9.2 Operator responsibilities
- Keep your node online across epoch boundaries. It must participate in key rotation.
- Keep registration metadata current. If you change endpoint URL, TLS key, or other registration info, re-run
hashi registerbefore the next epoch change. Metadata updates accept either the operator or the validator key. - Monitor reconfiguration. Watch
hashi_reconfig_in_progressandhashi_mpc_reconfig_total_duration_seconds.
9.3 Stuck reconfigurations
If reconfiguration stalls (offline validators, misconfigured endpoints, protocol failures), the committee can abort and retry:
hashi proposal create abort-reconfig --epoch <pending-epoch>
hashi proposal vote 0x<proposal-id> --execute
After execution, the previous committee resumes; the system can trigger a new reconfiguration on the next Sui epoch boundary. Common causes: too much registered stake offline/unreachable, invalid endpoints or stale TLS keys, network partitions preventing MPC message exchange.
10. Backup and restore
The full key-generation and restore procedures (including YubiKey setup with oct and decrypting over SSH) are in the Hashi Node Backups guide. This section is a quick summary.
When backup-pgp-cert is configured, the node automatically writes an encrypted backup every epoch: it packs the DB (MPC key shares, signing/encryption keys), config, and referenced key files into a tar archive and wraps it as an ASCII-armored OpenPGP (Sequoia) message. Encrypted archives use the suffix .tar.asc; unencrypted archives use .tar. The node writes backups to backup-dir (default /tmp, so set a persistent path). Encryption needs only the public certificate, so the private key need not be present during normal operation.
Because backups use OpenPGP, you can restore over SSH: plug the YubiKey into your laptop, forward your gpg-agent to the server, and decrypt the backup there. The private key never leaves the YubiKey.
# Manual backup
hashi backup save /path/to/validator.toml \
--backup-pgp-cert /path/to/hashi-backup-cert.asc \
--output-dir /var/lib/hashi/backups
# Restore with a local OpenPGP secret key
hashi backup restore /var/lib/hashi/backups/hashi-backup-*.tar.asc \
--backup-pgp-secret-key /path/to/secret-key.asc \
--output-dir /var/lib/hashi/restore
# Restore via gpg-agent / YubiKey (works over a forwarded agent)
hashi backup restore /var/lib/hashi/backups/hashi-backup-*.tar.asc \
--use-gpg-agent [--gpg-homedir /path/to/gnupghome] \
--output-dir /var/lib/hashi/restore
11. Coming soon
The following capabilities are planned but not yet available.
Screener setup
Coming soon: Guidance on AML/sanctions screening. The
screener-endpointconfig field points to a gRPC screener service; when unset, screening is skipped. Setup instructions to follow.
Metrics push service
Coming soon: A centralized metrics push service, to push Prometheus metrics to a shared endpoint for aggregate monitoring.
Cloud backup integration
Coming soon: Support for automatically uploading encrypted backups to cloud storage (for example, S3).