{
  "schemaVersion": "1.0",
  "item": {
    "slug": "bitkit-cli",
    "name": "Bitkit Cli",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/ovitrif/bitkit-cli",
    "canonicalUrl": "https://clawhub.ai/ovitrif/bitkit-cli",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/bitkit-cli",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=bitkit-cli",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "CONTRIBUTING.md",
      "README.md",
      "SKILL.md",
      "install.sh",
      "rustfmt.toml"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-23T16:43:11.935Z",
      "expiresAt": "2026-04-30T16:43:11.935Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
        "contentDisposition": "attachment; filename=\"4claw-imageboard-1.0.1.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/bitkit-cli"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/bitkit-cli",
    "agentPageUrl": "https://openagent3.xyz/skills/bitkit-cli/agent",
    "manifestUrl": "https://openagent3.xyz/skills/bitkit-cli/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/bitkit-cli/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "bitkit-cli -- AI Agent Skill",
        "body": "Bitcoin Lightning payment CLI for agents. Lowest LSP fees. Self-custody wallet with LNURL/Lightning Address support, typed exit codes, JSON envelope output, encrypted Pubky messaging, and daemon mode.\n\nInstall: curl -sSL https://raw.githubusercontent.com/synonymdev/bitkit-cli/main/install.sh | sh\nBinary names: bitkit or bk (identical alias)\nAlways use: --json flag on every invocation for parseable output."
      },
      {
        "title": "JSON Envelope",
        "body": "All --json output uses a consistent envelope:\n\nSuccess (stdout):\n\n{ \"ok\": true, \"data\": { ... } }\n\nError (stderr):\n\n{ \"ok\": false, \"error\": \"message\", \"code\": 1 }\n\nParse with: jq -r '.data.field' for success data, check .ok first."
      },
      {
        "title": "Quick Start",
        "body": "# 1. Create wallet (no encryption for agent use)\nbk init --no-password --json\n\n# 2. Start daemon for instant command execution\nbk start --json\n\n# 3. Get on-chain address and fund it\nADDRESS=$(bk address --json | jq -r '.data.address')\n\n# 4. Order inbound Lightning liquidity via LSP\nORDER=$(bk lsp create-order 500000 --json)\nORDER_ID=$(echo \"$ORDER\" | jq -r '.data.order_id')\n\n# 5. Pay the order (on-chain to payment_address), then open channel\nbk lsp open-channel \"$ORDER_ID\" --listen 9735 --json\n\n# 6. Create an invoice and receive payment\nbk invoice 5000 --description \"agent service\" --wait --listen 9735 --json\n\n# 7. Pay someone else's invoice\nbk pay lnbc50u1p... --json\n\n# 8. Check balance and history\nbk balance --json\nbk history --json\n\n# 9. Stop daemon when done\nbk stop --json"
      },
      {
        "title": "Daemon Mode",
        "body": "By default each command cold-starts the LDK node (slow). Start a persistent daemon for instant execution:\n\nbk start --json           # start daemon (default port 3457)\nbk status --json          # check if running\nbk balance --json         # instant -- proxied through daemon\nbk stop --json            # stop daemon\n\nWhen the daemon is running, all commands automatically proxy through its HTTP API. When stopped, commands fall back to per-command cold-start. No code changes needed."
      },
      {
        "title": "start",
        "body": "Start the background daemon. Idempotent -- returns current PID if already running.\n\nbk start --json\nbk start --port 8080 --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"status\": \"started\",\n    \"pid\": 12345,\n    \"port\": 3457\n  }\n}\n\nArgDefaultDescription--port <port>3457HTTP API port\n\nstatus is \"started\" or \"already_running\"."
      },
      {
        "title": "stop",
        "body": "Stop the running daemon.\n\nbk stop --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"status\": \"stopped\",\n    \"pid\": 12345\n  }\n}\n\nErrors if no daemon is running (exit code 1)."
      },
      {
        "title": "status",
        "body": "Check daemon status.\n\nbk status --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"running\": true,\n    \"pid\": 12345,\n    \"port\": 3457,\n    \"started_at\": \"2026-02-19T10:00:00+00:00\",\n    \"version\": \"0.1.0\"\n  }\n}\n\nWhen stopped: running is false, all other fields are null."
      },
      {
        "title": "Global Flags",
        "body": "FlagEnv VariableDefaultDescription--json--offMachine-readable JSON to stdout--dir <path>BITKIT_DIR~/.bitkit/Wallet data directory--network <net>BITKIT_NETWORKmainnetmainnet or regtest--listen <port>BITKIT_LISTENoffP2P listen port on 0.0.0.0:<port>--password <pw>BITKIT_PASSWORD--Wallet password (for encrypted seeds)"
      },
      {
        "title": "Wallet",
        "body": "init\n\nCreate a new wallet. Idempotent -- re-running prints existing wallet info.\n\nbk init --no-password --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"node_id\": \"02abc123...\",\n    \"seed_phrase\": \"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\",\n    \"wallet_dir\": \"/root/.bitkit\",\n    \"network\": \"mainnet\",\n    \"pubky_id\": \"8pinxrz9tuxfz3qo5gkhdebuhtq6mrimh3matdncsrsno7kg45mo\"\n  }\n}\n\nArgRequiredDescription--no-passwordone ofStore seed as plaintext (for agents)--password <pw>one ofEncrypt seed with AES-256-GCM + Argon2id\n\ninfo\n\nShow node status, channel counts, sync state.\n\nbk info --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"node_id\": \"02abc123...\",\n    \"network\": \"mainnet\",\n    \"channels_active\": 1,\n    \"channels_pending\": 0,\n    \"block_height\": 880000,\n    \"synced\": true,\n    \"wallet_dir\": \"/root/.bitkit\"\n  }\n}\n\nbalance\n\nShow Lightning and on-chain balances in satoshis.\n\nbk balance --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"lightning_sats\": 450000,\n    \"onchain_sats\": 50000,\n    \"total_sats\": 500000,\n    \"total_onchain_sats\": 55000,\n    \"anchor_reserve_sats\": 5000,\n    \"pending_sweep_sats\": 0,\n    \"pending_sweeps\": []\n  }\n}\n\nonchain_sats -- spendable on-chain balance\ntotal_onchain_sats -- all on-chain funds including reserved\nanchor_reserve_sats -- sats reserved for anchor channel fees\npending_sweep_sats -- funds sweeping from closed channels (not yet spendable)\npending_sweeps -- array of sweep entries with amount_sats, status, spending_txid, confirmation_height\n\nArgDescription--btcDisplay in BTC instead of sats (human output only)\n\nconfig\n\nShow resolved configuration with source tracking. Does not start the node.\n\nbk config --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"network\": { \"value\": \"mainnet\", \"source\": \"default\" },\n    \"wallet_dir\": { \"value\": \"/root/.bitkit\", \"source\": \"default\" },\n    \"chain_source\": { \"value\": \"esplora\", \"source\": \"file\" },\n    \"esplora_url\": { \"value\": \"https://blockstream.info/api\", \"source\": \"file\" },\n    \"electrum_url\": { \"value\": \"\", \"source\": \"file\" },\n    \"rgs_url\": { \"value\": \"https://rapidsync.lightningdevkit.org/snapshot\", \"source\": \"file\" },\n    \"blocktank_url\": { \"value\": \"https://api1.blocktank.to/api\", \"source\": \"file\" },\n    \"listen_port\": { \"value\": \"off\", \"source\": \"default\" }\n  }\n}\n\nEach entry has value (resolved value) and source (cli, env, file, or default).\n\naddress\n\nGet a new on-chain receive address. Generates a fresh address each call. Optionally validate an existing address.\n\nbk address --json\nbk address --type taproot --json\nbk address --validate bc1q... --json\n\nGenerate address:\n\n{\n  \"ok\": true,\n  \"data\": { \"address\": \"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4\", \"address_type\": \"native-segwit\" }\n}\n\nValidate address (--validate):\n\n{\n  \"ok\": true,\n  \"data\": { \"address\": \"bc1q...\", \"valid\": true, \"network\": \"mainnet\", \"reason\": null }\n}\n\nOn invalid address: valid is false and reason explains why.\n\nArgDefaultDescription--type <type>native-segwitAddress type: legacy, nested-segwit, native-segwit, taproot--validate <addr>--Validate <addr> for the current network; no node start needed--qroffDisplay address as QR code in terminal (human output only)\n\nsend\n\nSend bitcoin on-chain to an address.\n\nbk send bc1q... 50000 --json\nbk send bc1q... 50000 --fee-rate 5 --json\nbk send bc1q... --drain --json\nbk send bc1q... 50000 --utxo <txid>:<vout> --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"txid\": \"abc123...\",\n    \"address\": \"bc1q...\",\n    \"amount_sats\": 50000,\n    \"drain\": false,\n    \"fee_rate_sat_per_vb\": 5,\n    \"utxos_used\": [\"abc123...:0\"]\n  }\n}\n\nWhen --drain is used, amount_sats is null. When no --fee-rate is set, fee_rate_sat_per_vb is null.\n\nArgDefaultDescription<address>requiredDestination Bitcoin address<amount_sats>--Amount in satoshis (omit with --drain)--drainoffSend all spendable funds (mutually exclusive with --utxo)--fee-rate <sat/vb>autoFee rate in sat/vb--utxo <txid:vout>--Spend a specific UTXO (repeatable; incompatible with --drain)\n\nlist-utxos\n\nList all spendable UTXOs in the on-chain wallet.\n\nbk list-utxos --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"utxos\": [\n      {\n        \"txid\": \"abc123...\",\n        \"vout\": 0,\n        \"value_sats\": 1000000,\n        \"outpoint\": \"abc123...:0\"\n      }\n    ],\n    \"total_sats\": 1000000,\n    \"count\": 1\n  }\n}\n\nUse outpoint values (e.g. abc123...:0) as --utxo arguments for send and estimate-fee.\n\nestimate-fee\n\nPreview the fee for an on-chain send without broadcasting.\n\nbk estimate-fee bc1q... 50000 --json\nbk estimate-fee bc1q... 50000 --fee-rate 5 --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"fee_sats\": 250,\n    \"address\": \"bc1q...\",\n    \"amount_sats\": 50000,\n    \"fee_rate_sat_per_vb\": 5\n  }\n}\n\nfee_rate_sat_per_vb is null when not specified (node chooses automatically).\n\nArgDefaultDescription<address>requiredDestination Bitcoin address<amount_sats>requiredAmount to send in satoshis--fee-rate <sat/vb>autoOverride fee rate in sat/vb--utxo <txid:vout>--Restrict coin selection to specific UTXOs\n\nbump-fee\n\nReplace an unconfirmed transaction with a higher-fee version (RBF). The original transaction must be in the mempool and RBF-enabled.\n\nbk bump-fee <txid> <fee_rate> --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"original_txid\": \"abc123...\",\n    \"new_txid\": \"def456...\",\n    \"fee_rate_sat_per_vb\": 20\n  }\n}\n\nArgDescription<txid>TXID of the unconfirmed transaction to replace<fee_rate>New fee rate in sat/vb (must be higher than the original)\n\nOn failure (invalid txid, not in mempool, RBF not signalled): exits with code 1.\n\ncpfp\n\nAccelerate a stuck transaction by spending one of its outputs at a higher fee rate (Child-Pays-For-Parent).\n\nbk cpfp <txid> --json\nbk cpfp <txid> --fee-rate 30 --json\nbk cpfp <txid> --urgent --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"parent_txid\": \"abc123...\",\n    \"child_txid\": \"def456...\",\n    \"fee_rate_sat_per_vb\": 30,\n    \"urgent\": false\n  }\n}\n\nfee_rate_sat_per_vb is null when neither --fee-rate nor --urgent is set.\n\nArgDefaultDescription<txid>requiredTXID of the parent transaction to accelerate--fee-rate <sat/vb>autoExplicit fee rate for the child transaction--urgentoffAutomatically compute a high fee rate to confirm next block\n\nOn failure (invalid txid, no spendable output found): exits with code 1.\n\nhistory\n\nList recent transactions, newest first.\n\nbk history --limit 5 --type send --json\n\n{\n  \"ok\": true,\n  \"data\": [\n    {\n      \"type\": \"send\",\n      \"amount_sat\": 5000,\n      \"fee_sat\": 3,\n      \"status\": \"complete\",\n      \"timestamp\": \"2026-02-15 12:00:00\",\n      \"description\": \"coffee\"\n    }\n  ]\n}\n\nArgDefaultDescription--limit <n>20Number of entries--type <send|recv>allFilter by direction\n\nStatus values: complete, pending, failed\nType values: send, recv"
      },
      {
        "title": "Lightning Payments",
        "body": "invoice\n\nGenerate a BOLT 11 invoice to receive payment.\n\nbk invoice 5000 --description \"agent service\" --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"bolt11\": \"lnbc50u1p...\",\n    \"payment_hash\": \"a1b2c3d4e5f6...\",\n    \"amount_sat\": 5000,\n    \"description\": \"agent service\",\n    \"expires_at\": \"2026-02-15T14:30:00+00:00\"\n  }\n}\n\nArgDefaultDescription<amount_sats>requiredAmount in satoshis--description <text>\"\"Invoice memo--expiry <secs>3600Invoice expiry in seconds--waitoffBlock until payment received--timeout <secs>300Timeout when using --wait--qroffDisplay invoice as QR code in terminal (human output only)\n\nTwo-phase output with --wait: First prints the invoice envelope above, then blocks. When payment arrives, prints a second envelope to stdout:\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"status\": \"received\",\n    \"amount_sat\": 5000,\n    \"payment_hash\": \"a1b2c3d4e5f6...\"\n  }\n}\n\nIf timeout expires, exits with code 2 (network error). Always use --listen <port> with --wait so the payer can connect.\n\npay\n\nPay a BOLT 11 invoice, LNURL, or Lightning Address. Polls until success, failure, or timeout.\n\nbk pay lnbc50u1p... --json\nbk pay lnurl1dp68gurn... --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"status\": \"success\",\n    \"amount_sat\": 5000,\n    \"fee_sat\": 3,\n    \"payment_hash\": \"a1b2c3d4e5f6...\",\n    \"preimage\": \"1a2b3c4d5e6f...\"\n  }\n}\n\nArgDefaultDescription<bolt11>requiredBOLT 11 invoice, LNURL, or Lightning Address--max-fee <sats>unlimitedMaximum routing fee in satoshis--timeout <secs>60Payment timeout\n\nLNURL/Lightning Address support: pay accepts LNURL (lnurl1...) and resolves to a BOLT11 invoice. For LNURL-pay with variable amounts (where min_sendable != max_sendable), the CLI currently requires a fixed-amount LNURL. Lightning Address (user@domain.tld) requires a --amount flag which is not yet implemented — use a BOLT11 invoice instead.\n\nOn failure: exits with code 1 (\"Payment failed\"). On timeout: exits with code 2.\n\nwithdraw\n\nWithdraw from an LNURL-withdraw endpoint. Creates an invoice and submits it to the LNURL service.\n\nbk withdraw lnurl1dp68gurn... --json\nbk withdraw lnurl1dp68gurn... --amount 10000 --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"status\": \"submitted\",\n    \"amount_sat\": 10000,\n    \"bolt11\": \"lnbc100u1p...\"\n  }\n}\n\nArgDefaultDescription<lnurl>requiredLNURL-withdraw bech32 string--amount <sats>max withdrawableAmount in satoshis"
      },
      {
        "title": "Channels",
        "body": "open-channel\n\nOpen a Lightning channel to a peer. Requires on-chain funds.\n\nbk open-channel 02ab...@127.0.0.1:9735 100000 --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"channel_id\": \"12345678901234567890\",\n    \"peer\": \"02ab...\",\n    \"amount_sats\": 100000\n  }\n}\n\nArgDescription<peer>Peer in pubkey@host:port format<amount_sats>Channel capacity in satoshis--push <sats>Give the peer initial balance\n\nclose-channel\n\nClose a Lightning channel. Get channel_id and peer from list-channels.\n\nbk close-channel 12345678901234567890 02ab... --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"channel_id\": \"12345678901234567890\",\n    \"status\": \"closing\"\n  }\n}\n\nArgDescription<channel_id>Channel ID (decimal, from list-channels)<peer>Counterparty node public key--forceForce close (use when peer is unresponsive)\n\nStatus values: closing (cooperative), force_closing\n\nlist-channels\n\nList all Lightning channels with capacity, balances, and status.\n\nbk list-channels --json\n\n{\n  \"ok\": true,\n  \"data\": [\n    {\n      \"channel_id\": \"12345678901234567890\",\n      \"peer\": \"02ab...\",\n      \"capacity_sats\": 500000,\n      \"local_balance_msat\": 250000000,\n      \"remote_balance_msat\": 250000000,\n      \"is_usable\": true,\n      \"is_channel_ready\": true,\n      \"is_outbound\": false\n    }\n  ]\n}\n\nNote: balances are in millisatoshis. Divide by 1000 for satoshis."
      },
      {
        "title": "LSP (Blocktank Liquidity)",
        "body": "Use the Blocktank LSP to get inbound Lightning liquidity without manually finding peers.\n\nlsp info\n\nGet LSP service info: available nodes, channel limits, fee rates.\n\nbk lsp info --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"nodes\": [\n      {\n        \"alias\": \"blocktank-lsp\",\n        \"pubkey\": \"02abc...\",\n        \"connection_strings\": [\"02abc...@1.2.3.4:9735\"]\n      }\n    ],\n    \"min_channel_size_sats\": 20000,\n    \"max_channel_size_sats\": 5000000,\n    \"min_expiry_weeks\": 2,\n    \"max_expiry_weeks\": 52,\n    \"network\": \"Mainnet\",\n    \"fee_rates_fast\": 10,\n    \"fee_rates_mid\": 5,\n    \"fee_rates_slow\": 1\n  }\n}\n\nlsp estimate-fee\n\nEstimate the cost of a channel order before creating it.\n\nbk lsp estimate-fee 500000 --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"fee_sats\": 500,\n    \"network_fee_sats\": 300,\n    \"service_fee_sats\": 200\n  }\n}\n\nArgDefaultDescription<lsp_balance_sats>requiredInbound liquidity amount--expiry <weeks>6Channel expiry in weeks--client-balance-sats <sats>0Outbound liquidity (your side)\n\nlsp create-order\n\nCreate a channel order. Returns payment details.\n\nbk lsp create-order 500000 --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"order_id\": \"ord-abc123...\",\n    \"state\": \"Created\",\n    \"fee_sats\": 500,\n    \"lsp_balance_sats\": 500000,\n    \"client_balance_sats\": 0,\n    \"payment_address\": \"bc1q...\",\n    \"payment_bolt11\": \"lnbc...\",\n    \"order_expires_at\": \"2026-03-01T00:00:00Z\"\n  }\n}\n\nArgDefaultDescription<lsp_balance_sats>requiredInbound liquidity amount--expiry <weeks>6Channel expiry in weeks--client-balance-sats <sats>0Your outbound balance--zero-confoffEnable zero-conf channel\n\nAfter creating, pay to payment_address (on-chain) or payment_bolt11 (Lightning). Then poll get-order until state becomes Paid.\n\nlsp get-order\n\nCheck the status of an order.\n\nbk lsp get-order ord-abc123 --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"order_id\": \"ord-abc123...\",\n    \"state\": \"Open\",\n    \"state2\": \"Paid\",\n    \"fee_sats\": 500,\n    \"lsp_balance_sats\": 500000,\n    \"client_balance_sats\": 0,\n    \"channel_expiry_weeks\": 6,\n    \"channel_state\": \"Opening\",\n    \"funding_tx\": \"abc123def...\",\n    \"payment_state\": \"Paid\",\n    \"order_expires_at\": \"2026-03-01T00:00:00Z\",\n    \"created_at\": \"2026-02-17T00:00:00Z\"\n  }\n}\n\nchannel_state and funding_tx are null until the channel is being opened.\n\nState enum values for lsp get-order:\n\nFieldValuesDescriptionstateCreated, Open, Expired, ClosedLegacy order statestate2Created, Paid, Executed, ExpiredCurrent order state (prefer this)channel_stateOpening, Open, ClosedChannel lifecycle statepayment_stateCreated, Paid, Refunded, RefundAvailable, CanceledPayment lifecycle state\n\nPoll state2 for order progress: Created → Paid (after on-chain payment) → Executed (channel opened).\n\nlsp open-channel\n\nTell Blocktank to open a channel to your node. The order must be in Paid state. Requires --listen so Blocktank can connect.\n\nbk lsp open-channel ord-abc123 --listen 9735 --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"order_id\": \"ord-abc123...\",\n    \"state\": \"Open\",\n    \"channel_state\": \"Opening\",\n    \"funding_tx\": \"abc123def...\"\n  }\n}"
      },
      {
        "title": "Messaging (Pubky Encrypted)",
        "body": "End-to-end encrypted messaging via the Pubky network. Identity is created during init.\n\nmessage whoami\n\nPrint your Pubky messaging ID. Does not require network access.\n\nbk message whoami --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"pubky_id\": \"8pinxrz9tuxfz3qo5gkhdebuhtq6mrimh3matdncsrsno7kg45mo\"\n  }\n}\n\nmessage send\n\nSend an encrypted message to a peer.\n\nbk message send 8pin...xyz '{\"type\":\"invoice\",\"bolt11\":\"lnbc...\",\"amount_sats\":5000,\"description\":\"service\"}' --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"message_id\": \"abc-123-def\",\n    \"recipient\": \"8pin...xyz\"\n  }\n}\n\nArgDescription<pubky>Recipient's Pubky ID<text>Message content (plain text or JSON string)\n\nmessage read\n\nRead the full conversation with a peer, sorted by timestamp.\n\nbk message read 8pin...xyz --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"messages\": [\n      {\n        \"sender\": \"8pin...abc\",\n        \"content\": \"{\\\"type\\\":\\\"invoice\\\",\\\"bolt11\\\":\\\"lnbc...\\\",\\\"amount_sats\\\":5000,\\\"description\\\":\\\"service\\\"}\",\n        \"timestamp\": 1708000000,\n        \"verified\": true\n      }\n    ]\n  }\n}\n\nFieldDescriptionsenderPubky ID of the message authorcontentMessage text (may contain JSON -- parse it)timestampUnix timestamp in secondsverifiedtrue if cryptographic signature verified\n\nmessage listen\n\nPoll for new messages from a peer. Streams one envelope per line to stdout as messages arrive.\n\nbk message listen 8pin...xyz --interval 3 --timeout 120 --json\n\nEach new message is printed as a single-line envelope:\n\n{\"ok\":true,\"data\":{\"sender\":\"8pin...abc\",\"content\":\"hello\",\"timestamp\":1708000001,\"verified\":true}}\n\nArgDefaultDescription<pubky>requiredPeer's Pubky ID--interval <secs>3Poll interval--timeout <secs>0 (forever)Stop after this many seconds\n\nStatus messages go to stderr. Only message JSON goes to stdout."
      },
      {
        "title": "Daemon HTTP API",
        "body": "When the daemon is running (bk start), all commands proxy through an HTTP API on localhost:3457 (configurable via --port). The API uses HTTP Basic Auth with auto-generated credentials.\n\nAuthentication: HTTP Basic Auth (bitkit:<password>). Password is auto-generated on first bk start and stored in <wallet_dir>/api-password.\n\nEndpoints:\n\nMethodPathDescriptionGET/healthHealth check (no auth)GET/balanceWallet balanceGET/infoNode infoGET/history?limit=20&type=sendPayment historyGET/channelsList channelsGET/utxosList UTXOsPOST/addressGenerate receive addressPOST/sendSend on-chainPOST/invoiceCreate Lightning invoicePOST/payPay Lightning invoicePOST/estimate-feeEstimate on-chain feePOST/bump-feeRBF fee bumpPOST/cpfpCPFP accelerationPOST/channels/openOpen channelPOST/channels/closeClose channelGET/eventsWebSocket event stream\n\nAll responses use the same JSON envelope as CLI --json output: {\"ok\": true, \"data\": {...}}.\n\nPOST Request Body Schemas:\n\nPOST /address:\n\n{ \"address_type\": \"native-segwit\" }\n\naddress_type is optional. Values: legacy, nested-segwit, native-segwit, taproot.\n\nPOST /send:\n\n{ \"address\": \"bc1q...\", \"amount_sats\": 50000, \"drain\": false, \"fee_rate\": 5, \"utxos\": [\"txid:0\"] }\n\namount_sats is required unless drain is true. fee_rate, drain, utxos are optional.\n\nPOST /estimate-fee:\n\n{ \"address\": \"bc1q...\", \"amount_sats\": 50000, \"fee_rate\": 5, \"utxos\": [\"txid:0\"] }\n\nfee_rate and utxos are optional.\n\nPOST /bump-fee:\n\n{ \"txid\": \"abc123...\", \"fee_rate\": 20 }\n\nBoth fields required.\n\nPOST /cpfp:\n\n{ \"txid\": \"abc123...\", \"fee_rate\": 30, \"urgent\": false }\n\ntxid is required. fee_rate and urgent are optional (mutually exclusive).\n\nPOST /invoice:\n\n{ \"amount_sats\": 5000, \"description\": \"agent service\", \"expiry\": 3600 }\n\namount_sats is required. description and expiry are optional.\n\nPOST /pay:\n\n{ \"bolt11\": \"lnbc50u1p...\", \"max_fee\": 100, \"timeout\": 60 }\n\nbolt11 is required. max_fee and timeout are optional.\n\nPOST /channels/open:\n\n{ \"peer\": \"02ab...@127.0.0.1:9735\", \"amount_sats\": 100000, \"push_sats\": 0 }\n\npeer and amount_sats are required. push_sats is optional.\n\nPOST /channels/close:\n\n{ \"channel_id\": \"12345678901234567890\", \"peer\": \"02ab...\", \"force\": false }\n\nchannel_id and peer are required. force is optional (default false)."
      },
      {
        "title": "Webhooks",
        "body": "Configure in ~/.bitkit/<network>/config.toml:\n\nwebhook_url = \"https://your-agent.example.com/webhook\"\nwebhook_secret = \"your-hmac-secret\"\n\nEvents and Payloads:\n\nEach webhook POST body contains event, data, and timestamp (Unix ms):\n\npayment_received — Lightning payment received:\n\n{\n  \"event\": \"payment_received\",\n  \"data\": { \"payment_hash\": \"abc...\", \"amount_sat\": 1000 },\n  \"timestamp\": 1708444800000\n}\n\npayment_sent — Outbound Lightning payment succeeded:\n\n{\n  \"event\": \"payment_sent\",\n  \"data\": { \"payment_hash\": \"abc...\", \"amount_sat\": 5000, \"fee_sat\": 3 },\n  \"timestamp\": 1708444800000\n}\n\npayment_failed — Outbound Lightning payment failed:\n\n{\n  \"event\": \"payment_failed\",\n  \"data\": { \"payment_hash\": \"abc...\", \"reason\": \"RouteNotFound\" },\n  \"timestamp\": 1708444800000\n}\n\npayment_hash and reason are nullable (may be null).\n\nchannel_ready — Channel fully open and usable:\n\n{\n  \"event\": \"channel_ready\",\n  \"data\": { \"channel_id\": \"4242...\", \"counterparty_node_id\": \"02ab...\" },\n  \"timestamp\": 1708444800000\n}\n\ncounterparty_node_id is nullable.\n\nchannel_closed — Channel closed:\n\n{\n  \"event\": \"channel_closed\",\n  \"data\": { \"channel_id\": \"4242...\", \"counterparty_node_id\": \"02ab...\", \"reason\": \"CooperativeClosure\" },\n  \"timestamp\": 1708444800000\n}\n\ncounterparty_node_id and reason are nullable.\n\nSignature: X-Bitkit-Signature: hmac-sha256=<hex> header (HMAC-SHA256 of body using webhook_secret).\n\nRetry: 3 attempts with exponential backoff (1s, 4s, 16s)."
      },
      {
        "title": "WebSocket Events",
        "body": "Connect to ws://localhost:3457/events with Basic Auth. Receives the same events as webhooks in real-time as JSON text frames.\n\n# Example with websocat\nwebsocat ws://localhost:3457/events -H \"Authorization: Basic $(echo -n 'bitkit:PASSWORD' | base64)\"\n\nFrame format: Each event is a JSON text frame with the same structure as webhook payloads:\n\n{\"event\":\"payment_received\",\"data\":{\"payment_hash\":\"abc...\",\"amount_sat\":1000},\"timestamp\":1708444800000}\n\nBehavior:\n\nServer sends a WebSocket Ping every 10 seconds as a keepalive\nIf the client falls behind, the server sends a warning frame: {\"warning\":\"missed 3 events\"}\nConnection closes when: client sends a Close frame, any WebSocket error occurs, or the daemon shuts down\nNo client-to-server messages are expected (receive-only stream)"
      },
      {
        "title": "Configuration Reference",
        "body": "Config file: ~/.bitkit/<network>/config.toml (created by bk init). All fields are under [node]:\n\n[node]\nnetwork = \"mainnet\"                    # \"mainnet\" or \"regtest\"\nchain_source = \"esplora\"               # \"esplora\" or \"electrum\"\nesplora_url = \"https://blockstream.info/api\"\nelectrum_url = \"\"                      # used when chain_source = \"electrum\"\nrgs_url = \"https://rapidsync.lightningdevkit.org/snapshot\"\nblocktank_url = \"https://api1.blocktank.to/api\"\nwebhook_url = \"\"                       # POST target for events\nwebhook_secret = \"\"                    # HMAC-SHA256 signing key\nauto_liquidity = false                 # enable background inbound capacity monitor\nauto_liquidity_threshold_sats = 100000 # order channel when inbound < this\nauto_liquidity_channel_size_sats = 500000  # channel size to order\n\nFieldTypeDefaultDescriptionnetworkstring\"mainnet\"Bitcoin networkchain_sourcestring\"esplora\"Chain sync backend: esplora or electrumesplora_urlstring\"https://blockstream.info/api\"Esplora API URLelectrum_urlstring\"\"Electrum server URL (regtest: tcp://127.0.0.1:60001)rgs_urlstring(LDK default)Rapid Gossip Sync URL for Lightning routing graphblocktank_urlstring\"https://api1.blocktank.to/api\"Blocktank LSP API URLwebhook_urlstring\"\"Webhook POST target (empty = disabled)webhook_secretstring\"\"HMAC-SHA256 key for X-Bitkit-Signature headerauto_liquidityboolfalseEnable background inbound capacity monitorauto_liquidity_threshold_satsu64100000Order channel when inbound capacity drops below thisauto_liquidity_channel_size_satsu64500000Size of auto-ordered channels"
      },
      {
        "title": "Auto-Liquidity",
        "body": "When auto_liquidity = true in config.toml, the daemon monitors inbound channel capacity every 5 minutes. If total inbound capacity drops below auto_liquidity_threshold_sats, it automatically creates a Blocktank channel order for auto_liquidity_channel_size_sats.\n\nImportant: The auto-created order still requires on-chain payment to payment_address from the order. The daemon does not pay automatically — an agent or human must fund the order.\n\nWorkflow:\n\nEnable in config: auto_liquidity = true\nStart daemon: bk start --json\nMonitor for channel_ready events via webhook or WebSocket\nWhen a Blocktank order is created, check bk lsp get-order <id> for the payment_address\nPay the order on-chain, then the channel opens automatically"
      },
      {
        "title": "API Password",
        "body": "The daemon HTTP API uses HTTP Basic Auth. The password is auto-generated on first bk start.\n\nLocation: <wallet_dir>/api-password (e.g., ~/.bitkit/api-password)\nFormat: 32 random bytes encoded as 64 hex characters\nPermissions: File mode 0600 (owner read/write only)\n\n# Read the password\ncat ~/.bitkit/api-password\n\n# Use with curl\ncurl -u \"bitkit:$(cat ~/.bitkit/api-password)\" http://localhost:3457/balance\n\n# Use with WebSocket\nwebsocat ws://localhost:3457/events -H \"Authorization: Basic $(echo -n \"bitkit:$(cat ~/.bitkit/api-password)\" | base64)\"\n\nThe password persists across daemon restarts. Delete the file to force regeneration on next bk start."
      },
      {
        "title": "Exit Codes",
        "body": "CodeNameCommon CausesRecovery0Success----1User errorBad input, missing wallet, invalid invoice, expired invoiceFix input and retry2Network errorConnection failed, sync timeout, payment timeoutRetry after delay3Insufficient fundsNot enough on-chain or Lightning balanceFund wallet or open channel"
      },
      {
        "title": "Error Envelope",
        "body": "When --json is used and an error occurs, a structured error envelope is written to stderr:\n\n{\n  \"ok\": false,\n  \"error\": \"Invalid invoice: ...\",\n  \"code\": 1\n}"
      },
      {
        "title": "Bash Pattern",
        "body": "OUTPUT=$(bk pay \"$BOLT11\" --json 2>/dev/null)\nEXIT_CODE=$?\n\ncase $EXIT_CODE in\n  0) echo \"Success: $(echo \"$OUTPUT\" | jq -r '.data.payment_hash')\" ;;\n  1) echo \"Bad input -- check the invoice\" >&2; exit 1 ;;\n  2) echo \"Network issue -- retrying...\" >&2; sleep 5; bk pay \"$BOLT11\" --json ;;\n  3) echo \"Insufficient funds\" >&2; exit 3 ;;\nesac\n\nImportant: Errors print to stderr (as JSON envelope when --json is used). Success output prints to stdout. Always capture them separately."
      },
      {
        "title": "Multi-Wallet Pattern",
        "body": "Run multiple independent agent wallets by using separate data directories.\n\nexport BITKIT_DIR=\"/tmp/agent-${SESSION_ID}\"\nbk init --no-password --json\nbk address --json\n# ... each agent operates independently\n\nOr use the --dir flag:\n\nbk --dir /tmp/agent-alice init --no-password --json\nbk --dir /tmp/agent-bob init --no-password --json\n\nEach directory gets its own seed, node identity, Pubky ID, and channel state. Agents sharing a machine can run concurrently with different --dir values."
      },
      {
        "title": "Input Validation Reference",
        "body": "FormatPatternExampleNode pubkey66-char hex (compressed secp256k1)02abc123...Peer addresspubkey@host:port02abc...@127.0.0.1:9735Transaction ID64-char hex4a5e1e4baab89f3a...Outpointtxid:vout (decimal vout)4a5e1e4b...:0Channel IDDecimal u12812345678901234567890Bitcoin addressNetwork-validated bech32/base58bc1q..., bcrt1q...BOLT11 invoicelnbc... or lnbcrt... prefixlnbc50u1p...LNURLBech32 with lnurl HRPlnurl1dp68gurn...Lightning Addressuser@domain.tldagent@pay.bitkit.toPubky ID52-char z-base-328pinxrz9tux...45moFee ratePositive integer (sat/vb)5AmountNon-negative integer (satoshis)50000\n\nAll validation errors exit with code 1 and include a descriptive error message."
      },
      {
        "title": "Agent Message Protocol",
        "body": "Agents communicate structured data by sending JSON strings as message content. The convention uses a type field to distinguish message kinds:\n\nInvoice request:\n\n{\n  \"type\": \"invoice\",\n  \"bolt11\": \"lnbc50u1p...\",\n  \"amount_sats\": 5000,\n  \"description\": \"data analysis service\"\n}\n\nPayment confirmation:\n\n{\n  \"type\": \"payment_confirmation\",\n  \"payment_hash\": \"a1b2c3d4e5f6...\",\n  \"amount_sats\": 5000\n}"
      },
      {
        "title": "Workflow: Discover, Message, Pay",
        "body": "# 1. Get your own Pubky ID to share with other agents\nMY_ID=$(bk message whoami --json | jq -r '.data.pubky_id')\n\n# 2. Send a message to another agent\nbk message send \"$PEER_PUBKY\" \"Hello, I need data analysis\" --json\n\n# 3. Listen for their reply (they'll send an invoice)\nbk message listen \"$PEER_PUBKY\" --timeout 120 --json | while IFS= read -r MSG; do\n  TYPE=$(echo \"$MSG\" | jq -r '.data.content' | jq -r '.type // empty' 2>/dev/null)\n  if [ \"$TYPE\" = \"invoice\" ]; then\n    BOLT11=$(echo \"$MSG\" | jq -r '.data.content' | jq -r '.bolt11')\n    echo \"Received invoice: $BOLT11\"\n    break\n  fi\ndone\n\n# 4. Pay the invoice\nbk pay \"$BOLT11\" --json"
      },
      {
        "title": "End-to-End: Pay Another Agent",
        "body": "Complete flow where Agent A requests a service, Agent B invoices for it, Agent A pays, and Agent B confirms."
      },
      {
        "title": "Agent B (service provider)",
        "body": "#!/usr/bin/env bash\nset -euo pipefail\nexport BITKIT_DIR=/tmp/agent-b\n\n# Initialize and share identity\nbk init --no-password --json >/dev/null\nMY_ID=$(bk message whoami --json | jq -r '.data.pubky_id')\necho \"Agent B Pubky ID: $MY_ID\"\n\n# Listen for requests from Agent A\nbk message listen \"$AGENT_A_PUBKY\" --timeout 300 --json | while IFS= read -r MSG; do\n  CONTENT=$(echo \"$MSG\" | jq -r '.data.content')\n\n  # Create invoice for the requested service\n  INVOICE_JSON=$(bk invoice 5000 --description \"data analysis\" --json)\n  BOLT11=$(echo \"$INVOICE_JSON\" | jq -r '.data.bolt11')\n\n  # Send invoice to Agent A\n  PAYLOAD=$(jq -n --arg b \"$BOLT11\" '{type:\"invoice\",bolt11:$b,amount_sats:5000,description:\"data analysis\"}')\n  bk message send \"$AGENT_A_PUBKY\" \"$PAYLOAD\" --json\n\n  # Wait for payment\n  RECEIVED=$(bk invoice 5000 --description \"data analysis\" --wait --listen 9735 --json | tail -1)\n  HASH=$(echo \"$RECEIVED\" | jq -r '.data.payment_hash')\n\n  # Confirm payment\n  CONFIRM=$(jq -n --arg h \"$HASH\" '{type:\"payment_confirmation\",payment_hash:$h,amount_sats:5000}')\n  bk message send \"$AGENT_A_PUBKY\" \"$CONFIRM\" --json\n  break\ndone"
      },
      {
        "title": "Agent A (client)",
        "body": "#!/usr/bin/env bash\nset -euo pipefail\nexport BITKIT_DIR=/tmp/agent-a\n\n# Initialize\nbk init --no-password --json >/dev/null\nMY_ID=$(bk message whoami --json | jq -r '.data.pubky_id')\n\n# Request service from Agent B\nbk message send \"$AGENT_B_PUBKY\" \"Please analyze dataset X\" --json\n\n# Wait for invoice\nBOLT11=\"\"\nbk message listen \"$AGENT_B_PUBKY\" --timeout 120 --json | while IFS= read -r MSG; do\n  TYPE=$(echo \"$MSG\" | jq -r '.data.content' | jq -r '.type // empty' 2>/dev/null)\n  if [ \"$TYPE\" = \"invoice\" ]; then\n    BOLT11=$(echo \"$MSG\" | jq -r '.data.content' | jq -r '.bolt11')\n    break\n  fi\ndone\n\n# Pay the invoice\nPAY_RESULT=$(bk pay \"$BOLT11\" --json)\necho \"Payment status: $(echo \"$PAY_RESULT\" | jq -r '.data.status')\"\necho \"Fee paid: $(echo \"$PAY_RESULT\" | jq -r '.data.fee_sat') sats\"\n\n# Wait for confirmation\nbk message listen \"$AGENT_B_PUBKY\" --timeout 60 --json | while IFS= read -r MSG; do\n  TYPE=$(echo \"$MSG\" | jq -r '.data.content' | jq -r '.type // empty' 2>/dev/null)\n  if [ \"$TYPE\" = \"payment_confirmation\" ]; then\n    echo \"Payment confirmed by Agent B\"\n    break\n  fi\ndone"
      },
      {
        "title": "Security & Privacy",
        "body": "Trust model: Fully self-custodial. The BIP39 seed never leaves the local machine. No telemetry, analytics, or tracking of any kind.\n\nExternal endpoints contacted:\n\nEndpointPurposeData SentConfigurableEsplora serverChain sync (mainnet)Transaction queriesYes (esplora_url)Electrum serverChain sync (regtest)Transaction queriesYes (electrum_url)RGS serverLightning gossip syncNone (download only)Yes (rgs_url)Blocktank APILSP channel ordersNode ID, order paramsYes (blocktank_url)LNURL servicePay/withdraw resolutionAmount, callback URLUser-initiated onlyPubky DHTEncrypted messagingE2E encrypted payloadsBuilt-inWebhook URLEvent notificationsPayment/channel eventsYes (webhook_url)\n\nSeed storage: Custom binary format with magic bytes (BKIT), version byte, and encrypted (AES-256-GCM + Argon2id KDF) or plaintext flag. File: ~/.bitkit/<network>/seed.enc.\n\nAPI password: Auto-generated 32-byte random hex, stored with 0600 permissions. Only used for local daemon HTTP API access."
      },
      {
        "title": "LSP Workflow: Fresh Wallet to Lightning-Ready",
        "body": "From a brand new wallet to being able to send and receive Lightning payments:\n\n# 1. Create wallet\nbk init --no-password --json\n\n# 2. Get on-chain address\nADDRESS=$(bk address --json | jq -r '.data.address')\necho \"Fund this address: $ADDRESS\"\n\n# 3. (Fund the address externally, wait for confirmation)\n\n# 4. Estimate channel fees\nbk lsp estimate-fee 500000 --json\n\n# 5. Create channel order\nORDER_ID=$(bk lsp create-order 500000 --json | jq -r '.data.order_id')\n\n# 6. Pay the order on-chain (the payment_address from create-order)\n# ... or wait for on-chain funding to cover it\n\n# 7. Poll until order is paid\nwhile true; do\n  STATE=$(bk lsp get-order \"$ORDER_ID\" --json | jq -r '.data.state2')\n  [ \"$STATE\" = \"Paid\" ] && break\n  sleep 10\ndone\n\n# 8. Open the channel\nbk lsp open-channel \"$ORDER_ID\" --listen 9735 --json\n\n# 9. Verify channel is active\nbk list-channels --json | jq '.data[0].is_usable'\n\n# Now ready to send and receive Lightning payments"
      },
      {
        "title": "Tags",
        "body": "bitcoin, lightning, payment, invoice, self-custody, wallet, CLI, agent, LNURL, lightning-address, BOLT11, LSP, lowest-fees, daemon, webhooks, websocket, encrypted-messaging, on-chain, channels, liquidity"
      }
    ],
    "body": "bitkit-cli -- AI Agent Skill\n\nBitcoin Lightning payment CLI for agents. Lowest LSP fees. Self-custody wallet with LNURL/Lightning Address support, typed exit codes, JSON envelope output, encrypted Pubky messaging, and daemon mode.\n\nInstall: curl -sSL https://raw.githubusercontent.com/synonymdev/bitkit-cli/main/install.sh | sh Binary names: bitkit or bk (identical alias) Always use: --json flag on every invocation for parseable output.\n\nJSON Envelope\n\nAll --json output uses a consistent envelope:\n\nSuccess (stdout):\n\n{ \"ok\": true, \"data\": { ... } }\n\n\nError (stderr):\n\n{ \"ok\": false, \"error\": \"message\", \"code\": 1 }\n\n\nParse with: jq -r '.data.field' for success data, check .ok first.\n\nQuick Start\n# 1. Create wallet (no encryption for agent use)\nbk init --no-password --json\n\n# 2. Start daemon for instant command execution\nbk start --json\n\n# 3. Get on-chain address and fund it\nADDRESS=$(bk address --json | jq -r '.data.address')\n\n# 4. Order inbound Lightning liquidity via LSP\nORDER=$(bk lsp create-order 500000 --json)\nORDER_ID=$(echo \"$ORDER\" | jq -r '.data.order_id')\n\n# 5. Pay the order (on-chain to payment_address), then open channel\nbk lsp open-channel \"$ORDER_ID\" --listen 9735 --json\n\n# 6. Create an invoice and receive payment\nbk invoice 5000 --description \"agent service\" --wait --listen 9735 --json\n\n# 7. Pay someone else's invoice\nbk pay lnbc50u1p... --json\n\n# 8. Check balance and history\nbk balance --json\nbk history --json\n\n# 9. Stop daemon when done\nbk stop --json\n\nDaemon Mode\n\nBy default each command cold-starts the LDK node (slow). Start a persistent daemon for instant execution:\n\nbk start --json           # start daemon (default port 3457)\nbk status --json          # check if running\nbk balance --json         # instant -- proxied through daemon\nbk stop --json            # stop daemon\n\n\nWhen the daemon is running, all commands automatically proxy through its HTTP API. When stopped, commands fall back to per-command cold-start. No code changes needed.\n\nstart\n\nStart the background daemon. Idempotent -- returns current PID if already running.\n\nbk start --json\nbk start --port 8080 --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"status\": \"started\",\n    \"pid\": 12345,\n    \"port\": 3457\n  }\n}\n\nArg\tDefault\tDescription\n--port <port>\t3457\tHTTP API port\n\nstatus is \"started\" or \"already_running\".\n\nstop\n\nStop the running daemon.\n\nbk stop --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"status\": \"stopped\",\n    \"pid\": 12345\n  }\n}\n\n\nErrors if no daemon is running (exit code 1).\n\nstatus\n\nCheck daemon status.\n\nbk status --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"running\": true,\n    \"pid\": 12345,\n    \"port\": 3457,\n    \"started_at\": \"2026-02-19T10:00:00+00:00\",\n    \"version\": \"0.1.0\"\n  }\n}\n\n\nWhen stopped: running is false, all other fields are null.\n\nGlobal Flags\nFlag\tEnv Variable\tDefault\tDescription\n--json\t--\toff\tMachine-readable JSON to stdout\n--dir <path>\tBITKIT_DIR\t~/.bitkit/\tWallet data directory\n--network <net>\tBITKIT_NETWORK\tmainnet\tmainnet or regtest\n--listen <port>\tBITKIT_LISTEN\toff\tP2P listen port on 0.0.0.0:<port>\n--password <pw>\tBITKIT_PASSWORD\t--\tWallet password (for encrypted seeds)\nCommand Reference\nWallet\ninit\n\nCreate a new wallet. Idempotent -- re-running prints existing wallet info.\n\nbk init --no-password --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"node_id\": \"02abc123...\",\n    \"seed_phrase\": \"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\",\n    \"wallet_dir\": \"/root/.bitkit\",\n    \"network\": \"mainnet\",\n    \"pubky_id\": \"8pinxrz9tuxfz3qo5gkhdebuhtq6mrimh3matdncsrsno7kg45mo\"\n  }\n}\n\nArg\tRequired\tDescription\n--no-password\tone of\tStore seed as plaintext (for agents)\n--password <pw>\tone of\tEncrypt seed with AES-256-GCM + Argon2id\ninfo\n\nShow node status, channel counts, sync state.\n\nbk info --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"node_id\": \"02abc123...\",\n    \"network\": \"mainnet\",\n    \"channels_active\": 1,\n    \"channels_pending\": 0,\n    \"block_height\": 880000,\n    \"synced\": true,\n    \"wallet_dir\": \"/root/.bitkit\"\n  }\n}\n\nbalance\n\nShow Lightning and on-chain balances in satoshis.\n\nbk balance --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"lightning_sats\": 450000,\n    \"onchain_sats\": 50000,\n    \"total_sats\": 500000,\n    \"total_onchain_sats\": 55000,\n    \"anchor_reserve_sats\": 5000,\n    \"pending_sweep_sats\": 0,\n    \"pending_sweeps\": []\n  }\n}\n\nonchain_sats -- spendable on-chain balance\ntotal_onchain_sats -- all on-chain funds including reserved\nanchor_reserve_sats -- sats reserved for anchor channel fees\npending_sweep_sats -- funds sweeping from closed channels (not yet spendable)\npending_sweeps -- array of sweep entries with amount_sats, status, spending_txid, confirmation_height\nArg\tDescription\n--btc\tDisplay in BTC instead of sats (human output only)\nconfig\n\nShow resolved configuration with source tracking. Does not start the node.\n\nbk config --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"network\": { \"value\": \"mainnet\", \"source\": \"default\" },\n    \"wallet_dir\": { \"value\": \"/root/.bitkit\", \"source\": \"default\" },\n    \"chain_source\": { \"value\": \"esplora\", \"source\": \"file\" },\n    \"esplora_url\": { \"value\": \"https://blockstream.info/api\", \"source\": \"file\" },\n    \"electrum_url\": { \"value\": \"\", \"source\": \"file\" },\n    \"rgs_url\": { \"value\": \"https://rapidsync.lightningdevkit.org/snapshot\", \"source\": \"file\" },\n    \"blocktank_url\": { \"value\": \"https://api1.blocktank.to/api\", \"source\": \"file\" },\n    \"listen_port\": { \"value\": \"off\", \"source\": \"default\" }\n  }\n}\n\n\nEach entry has value (resolved value) and source (cli, env, file, or default).\n\naddress\n\nGet a new on-chain receive address. Generates a fresh address each call. Optionally validate an existing address.\n\nbk address --json\nbk address --type taproot --json\nbk address --validate bc1q... --json\n\n\nGenerate address:\n\n{\n  \"ok\": true,\n  \"data\": { \"address\": \"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4\", \"address_type\": \"native-segwit\" }\n}\n\n\nValidate address (--validate):\n\n{\n  \"ok\": true,\n  \"data\": { \"address\": \"bc1q...\", \"valid\": true, \"network\": \"mainnet\", \"reason\": null }\n}\n\n\nOn invalid address: valid is false and reason explains why.\n\nArg\tDefault\tDescription\n--type <type>\tnative-segwit\tAddress type: legacy, nested-segwit, native-segwit, taproot\n--validate <addr>\t--\tValidate <addr> for the current network; no node start needed\n--qr\toff\tDisplay address as QR code in terminal (human output only)\nsend\n\nSend bitcoin on-chain to an address.\n\nbk send bc1q... 50000 --json\nbk send bc1q... 50000 --fee-rate 5 --json\nbk send bc1q... --drain --json\nbk send bc1q... 50000 --utxo <txid>:<vout> --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"txid\": \"abc123...\",\n    \"address\": \"bc1q...\",\n    \"amount_sats\": 50000,\n    \"drain\": false,\n    \"fee_rate_sat_per_vb\": 5,\n    \"utxos_used\": [\"abc123...:0\"]\n  }\n}\n\n\nWhen --drain is used, amount_sats is null. When no --fee-rate is set, fee_rate_sat_per_vb is null.\n\nArg\tDefault\tDescription\n<address>\trequired\tDestination Bitcoin address\n<amount_sats>\t--\tAmount in satoshis (omit with --drain)\n--drain\toff\tSend all spendable funds (mutually exclusive with --utxo)\n--fee-rate <sat/vb>\tauto\tFee rate in sat/vb\n--utxo <txid:vout>\t--\tSpend a specific UTXO (repeatable; incompatible with --drain)\nlist-utxos\n\nList all spendable UTXOs in the on-chain wallet.\n\nbk list-utxos --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"utxos\": [\n      {\n        \"txid\": \"abc123...\",\n        \"vout\": 0,\n        \"value_sats\": 1000000,\n        \"outpoint\": \"abc123...:0\"\n      }\n    ],\n    \"total_sats\": 1000000,\n    \"count\": 1\n  }\n}\n\n\nUse outpoint values (e.g. abc123...:0) as --utxo arguments for send and estimate-fee.\n\nestimate-fee\n\nPreview the fee for an on-chain send without broadcasting.\n\nbk estimate-fee bc1q... 50000 --json\nbk estimate-fee bc1q... 50000 --fee-rate 5 --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"fee_sats\": 250,\n    \"address\": \"bc1q...\",\n    \"amount_sats\": 50000,\n    \"fee_rate_sat_per_vb\": 5\n  }\n}\n\n\nfee_rate_sat_per_vb is null when not specified (node chooses automatically).\n\nArg\tDefault\tDescription\n<address>\trequired\tDestination Bitcoin address\n<amount_sats>\trequired\tAmount to send in satoshis\n--fee-rate <sat/vb>\tauto\tOverride fee rate in sat/vb\n--utxo <txid:vout>\t--\tRestrict coin selection to specific UTXOs\nbump-fee\n\nReplace an unconfirmed transaction with a higher-fee version (RBF). The original transaction must be in the mempool and RBF-enabled.\n\nbk bump-fee <txid> <fee_rate> --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"original_txid\": \"abc123...\",\n    \"new_txid\": \"def456...\",\n    \"fee_rate_sat_per_vb\": 20\n  }\n}\n\nArg\tDescription\n<txid>\tTXID of the unconfirmed transaction to replace\n<fee_rate>\tNew fee rate in sat/vb (must be higher than the original)\n\nOn failure (invalid txid, not in mempool, RBF not signalled): exits with code 1.\n\ncpfp\n\nAccelerate a stuck transaction by spending one of its outputs at a higher fee rate (Child-Pays-For-Parent).\n\nbk cpfp <txid> --json\nbk cpfp <txid> --fee-rate 30 --json\nbk cpfp <txid> --urgent --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"parent_txid\": \"abc123...\",\n    \"child_txid\": \"def456...\",\n    \"fee_rate_sat_per_vb\": 30,\n    \"urgent\": false\n  }\n}\n\n\nfee_rate_sat_per_vb is null when neither --fee-rate nor --urgent is set.\n\nArg\tDefault\tDescription\n<txid>\trequired\tTXID of the parent transaction to accelerate\n--fee-rate <sat/vb>\tauto\tExplicit fee rate for the child transaction\n--urgent\toff\tAutomatically compute a high fee rate to confirm next block\n\nOn failure (invalid txid, no spendable output found): exits with code 1.\n\nhistory\n\nList recent transactions, newest first.\n\nbk history --limit 5 --type send --json\n\n{\n  \"ok\": true,\n  \"data\": [\n    {\n      \"type\": \"send\",\n      \"amount_sat\": 5000,\n      \"fee_sat\": 3,\n      \"status\": \"complete\",\n      \"timestamp\": \"2026-02-15 12:00:00\",\n      \"description\": \"coffee\"\n    }\n  ]\n}\n\nArg\tDefault\tDescription\n--limit <n>\t20\tNumber of entries\n--type <send|recv>\tall\tFilter by direction\n\nStatus values: complete, pending, failed Type values: send, recv\n\nLightning Payments\ninvoice\n\nGenerate a BOLT 11 invoice to receive payment.\n\nbk invoice 5000 --description \"agent service\" --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"bolt11\": \"lnbc50u1p...\",\n    \"payment_hash\": \"a1b2c3d4e5f6...\",\n    \"amount_sat\": 5000,\n    \"description\": \"agent service\",\n    \"expires_at\": \"2026-02-15T14:30:00+00:00\"\n  }\n}\n\nArg\tDefault\tDescription\n<amount_sats>\trequired\tAmount in satoshis\n--description <text>\t\"\"\tInvoice memo\n--expiry <secs>\t3600\tInvoice expiry in seconds\n--wait\toff\tBlock until payment received\n--timeout <secs>\t300\tTimeout when using --wait\n--qr\toff\tDisplay invoice as QR code in terminal (human output only)\n\nTwo-phase output with --wait: First prints the invoice envelope above, then blocks. When payment arrives, prints a second envelope to stdout:\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"status\": \"received\",\n    \"amount_sat\": 5000,\n    \"payment_hash\": \"a1b2c3d4e5f6...\"\n  }\n}\n\n\nIf timeout expires, exits with code 2 (network error). Always use --listen <port> with --wait so the payer can connect.\n\npay\n\nPay a BOLT 11 invoice, LNURL, or Lightning Address. Polls until success, failure, or timeout.\n\nbk pay lnbc50u1p... --json\nbk pay lnurl1dp68gurn... --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"status\": \"success\",\n    \"amount_sat\": 5000,\n    \"fee_sat\": 3,\n    \"payment_hash\": \"a1b2c3d4e5f6...\",\n    \"preimage\": \"1a2b3c4d5e6f...\"\n  }\n}\n\nArg\tDefault\tDescription\n<bolt11>\trequired\tBOLT 11 invoice, LNURL, or Lightning Address\n--max-fee <sats>\tunlimited\tMaximum routing fee in satoshis\n--timeout <secs>\t60\tPayment timeout\n\nLNURL/Lightning Address support: pay accepts LNURL (lnurl1...) and resolves to a BOLT11 invoice. For LNURL-pay with variable amounts (where min_sendable != max_sendable), the CLI currently requires a fixed-amount LNURL. Lightning Address (user@domain.tld) requires a --amount flag which is not yet implemented — use a BOLT11 invoice instead.\n\nOn failure: exits with code 1 (\"Payment failed\"). On timeout: exits with code 2.\n\nwithdraw\n\nWithdraw from an LNURL-withdraw endpoint. Creates an invoice and submits it to the LNURL service.\n\nbk withdraw lnurl1dp68gurn... --json\nbk withdraw lnurl1dp68gurn... --amount 10000 --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"status\": \"submitted\",\n    \"amount_sat\": 10000,\n    \"bolt11\": \"lnbc100u1p...\"\n  }\n}\n\nArg\tDefault\tDescription\n<lnurl>\trequired\tLNURL-withdraw bech32 string\n--amount <sats>\tmax withdrawable\tAmount in satoshis\nChannels\nopen-channel\n\nOpen a Lightning channel to a peer. Requires on-chain funds.\n\nbk open-channel 02ab...@127.0.0.1:9735 100000 --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"channel_id\": \"12345678901234567890\",\n    \"peer\": \"02ab...\",\n    \"amount_sats\": 100000\n  }\n}\n\nArg\tDescription\n<peer>\tPeer in pubkey@host:port format\n<amount_sats>\tChannel capacity in satoshis\n--push <sats>\tGive the peer initial balance\nclose-channel\n\nClose a Lightning channel. Get channel_id and peer from list-channels.\n\nbk close-channel 12345678901234567890 02ab... --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"channel_id\": \"12345678901234567890\",\n    \"status\": \"closing\"\n  }\n}\n\nArg\tDescription\n<channel_id>\tChannel ID (decimal, from list-channels)\n<peer>\tCounterparty node public key\n--force\tForce close (use when peer is unresponsive)\n\nStatus values: closing (cooperative), force_closing\n\nlist-channels\n\nList all Lightning channels with capacity, balances, and status.\n\nbk list-channels --json\n\n{\n  \"ok\": true,\n  \"data\": [\n    {\n      \"channel_id\": \"12345678901234567890\",\n      \"peer\": \"02ab...\",\n      \"capacity_sats\": 500000,\n      \"local_balance_msat\": 250000000,\n      \"remote_balance_msat\": 250000000,\n      \"is_usable\": true,\n      \"is_channel_ready\": true,\n      \"is_outbound\": false\n    }\n  ]\n}\n\n\nNote: balances are in millisatoshis. Divide by 1000 for satoshis.\n\nLSP (Blocktank Liquidity)\n\nUse the Blocktank LSP to get inbound Lightning liquidity without manually finding peers.\n\nlsp info\n\nGet LSP service info: available nodes, channel limits, fee rates.\n\nbk lsp info --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"nodes\": [\n      {\n        \"alias\": \"blocktank-lsp\",\n        \"pubkey\": \"02abc...\",\n        \"connection_strings\": [\"02abc...@1.2.3.4:9735\"]\n      }\n    ],\n    \"min_channel_size_sats\": 20000,\n    \"max_channel_size_sats\": 5000000,\n    \"min_expiry_weeks\": 2,\n    \"max_expiry_weeks\": 52,\n    \"network\": \"Mainnet\",\n    \"fee_rates_fast\": 10,\n    \"fee_rates_mid\": 5,\n    \"fee_rates_slow\": 1\n  }\n}\n\nlsp estimate-fee\n\nEstimate the cost of a channel order before creating it.\n\nbk lsp estimate-fee 500000 --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"fee_sats\": 500,\n    \"network_fee_sats\": 300,\n    \"service_fee_sats\": 200\n  }\n}\n\nArg\tDefault\tDescription\n<lsp_balance_sats>\trequired\tInbound liquidity amount\n--expiry <weeks>\t6\tChannel expiry in weeks\n--client-balance-sats <sats>\t0\tOutbound liquidity (your side)\nlsp create-order\n\nCreate a channel order. Returns payment details.\n\nbk lsp create-order 500000 --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"order_id\": \"ord-abc123...\",\n    \"state\": \"Created\",\n    \"fee_sats\": 500,\n    \"lsp_balance_sats\": 500000,\n    \"client_balance_sats\": 0,\n    \"payment_address\": \"bc1q...\",\n    \"payment_bolt11\": \"lnbc...\",\n    \"order_expires_at\": \"2026-03-01T00:00:00Z\"\n  }\n}\n\nArg\tDefault\tDescription\n<lsp_balance_sats>\trequired\tInbound liquidity amount\n--expiry <weeks>\t6\tChannel expiry in weeks\n--client-balance-sats <sats>\t0\tYour outbound balance\n--zero-conf\toff\tEnable zero-conf channel\n\nAfter creating, pay to payment_address (on-chain) or payment_bolt11 (Lightning). Then poll get-order until state becomes Paid.\n\nlsp get-order\n\nCheck the status of an order.\n\nbk lsp get-order ord-abc123 --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"order_id\": \"ord-abc123...\",\n    \"state\": \"Open\",\n    \"state2\": \"Paid\",\n    \"fee_sats\": 500,\n    \"lsp_balance_sats\": 500000,\n    \"client_balance_sats\": 0,\n    \"channel_expiry_weeks\": 6,\n    \"channel_state\": \"Opening\",\n    \"funding_tx\": \"abc123def...\",\n    \"payment_state\": \"Paid\",\n    \"order_expires_at\": \"2026-03-01T00:00:00Z\",\n    \"created_at\": \"2026-02-17T00:00:00Z\"\n  }\n}\n\n\nchannel_state and funding_tx are null until the channel is being opened.\n\nState enum values for lsp get-order:\n\nField\tValues\tDescription\nstate\tCreated, Open, Expired, Closed\tLegacy order state\nstate2\tCreated, Paid, Executed, Expired\tCurrent order state (prefer this)\nchannel_state\tOpening, Open, Closed\tChannel lifecycle state\npayment_state\tCreated, Paid, Refunded, RefundAvailable, Canceled\tPayment lifecycle state\n\nPoll state2 for order progress: Created → Paid (after on-chain payment) → Executed (channel opened).\n\nlsp open-channel\n\nTell Blocktank to open a channel to your node. The order must be in Paid state. Requires --listen so Blocktank can connect.\n\nbk lsp open-channel ord-abc123 --listen 9735 --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"order_id\": \"ord-abc123...\",\n    \"state\": \"Open\",\n    \"channel_state\": \"Opening\",\n    \"funding_tx\": \"abc123def...\"\n  }\n}\n\nMessaging (Pubky Encrypted)\n\nEnd-to-end encrypted messaging via the Pubky network. Identity is created during init.\n\nmessage whoami\n\nPrint your Pubky messaging ID. Does not require network access.\n\nbk message whoami --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"pubky_id\": \"8pinxrz9tuxfz3qo5gkhdebuhtq6mrimh3matdncsrsno7kg45mo\"\n  }\n}\n\nmessage send\n\nSend an encrypted message to a peer.\n\nbk message send 8pin...xyz '{\"type\":\"invoice\",\"bolt11\":\"lnbc...\",\"amount_sats\":5000,\"description\":\"service\"}' --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"message_id\": \"abc-123-def\",\n    \"recipient\": \"8pin...xyz\"\n  }\n}\n\nArg\tDescription\n<pubky>\tRecipient's Pubky ID\n<text>\tMessage content (plain text or JSON string)\nmessage read\n\nRead the full conversation with a peer, sorted by timestamp.\n\nbk message read 8pin...xyz --json\n\n{\n  \"ok\": true,\n  \"data\": {\n    \"messages\": [\n      {\n        \"sender\": \"8pin...abc\",\n        \"content\": \"{\\\"type\\\":\\\"invoice\\\",\\\"bolt11\\\":\\\"lnbc...\\\",\\\"amount_sats\\\":5000,\\\"description\\\":\\\"service\\\"}\",\n        \"timestamp\": 1708000000,\n        \"verified\": true\n      }\n    ]\n  }\n}\n\nField\tDescription\nsender\tPubky ID of the message author\ncontent\tMessage text (may contain JSON -- parse it)\ntimestamp\tUnix timestamp in seconds\nverified\ttrue if cryptographic signature verified\nmessage listen\n\nPoll for new messages from a peer. Streams one envelope per line to stdout as messages arrive.\n\nbk message listen 8pin...xyz --interval 3 --timeout 120 --json\n\n\nEach new message is printed as a single-line envelope:\n\n{\"ok\":true,\"data\":{\"sender\":\"8pin...abc\",\"content\":\"hello\",\"timestamp\":1708000001,\"verified\":true}}\n\nArg\tDefault\tDescription\n<pubky>\trequired\tPeer's Pubky ID\n--interval <secs>\t3\tPoll interval\n--timeout <secs>\t0 (forever)\tStop after this many seconds\n\nStatus messages go to stderr. Only message JSON goes to stdout.\n\nAgent Integration\nDaemon HTTP API\n\nWhen the daemon is running (bk start), all commands proxy through an HTTP API on localhost:3457 (configurable via --port). The API uses HTTP Basic Auth with auto-generated credentials.\n\nAuthentication: HTTP Basic Auth (bitkit:<password>). Password is auto-generated on first bk start and stored in <wallet_dir>/api-password.\n\nEndpoints:\n\nMethod\tPath\tDescription\nGET\t/health\tHealth check (no auth)\nGET\t/balance\tWallet balance\nGET\t/info\tNode info\nGET\t/history?limit=20&type=send\tPayment history\nGET\t/channels\tList channels\nGET\t/utxos\tList UTXOs\nPOST\t/address\tGenerate receive address\nPOST\t/send\tSend on-chain\nPOST\t/invoice\tCreate Lightning invoice\nPOST\t/pay\tPay Lightning invoice\nPOST\t/estimate-fee\tEstimate on-chain fee\nPOST\t/bump-fee\tRBF fee bump\nPOST\t/cpfp\tCPFP acceleration\nPOST\t/channels/open\tOpen channel\nPOST\t/channels/close\tClose channel\nGET\t/events\tWebSocket event stream\n\nAll responses use the same JSON envelope as CLI --json output: {\"ok\": true, \"data\": {...}}.\n\nPOST Request Body Schemas:\n\nPOST /address:\n\n{ \"address_type\": \"native-segwit\" }\n\n\naddress_type is optional. Values: legacy, nested-segwit, native-segwit, taproot.\n\nPOST /send:\n\n{ \"address\": \"bc1q...\", \"amount_sats\": 50000, \"drain\": false, \"fee_rate\": 5, \"utxos\": [\"txid:0\"] }\n\n\namount_sats is required unless drain is true. fee_rate, drain, utxos are optional.\n\nPOST /estimate-fee:\n\n{ \"address\": \"bc1q...\", \"amount_sats\": 50000, \"fee_rate\": 5, \"utxos\": [\"txid:0\"] }\n\n\nfee_rate and utxos are optional.\n\nPOST /bump-fee:\n\n{ \"txid\": \"abc123...\", \"fee_rate\": 20 }\n\n\nBoth fields required.\n\nPOST /cpfp:\n\n{ \"txid\": \"abc123...\", \"fee_rate\": 30, \"urgent\": false }\n\n\ntxid is required. fee_rate and urgent are optional (mutually exclusive).\n\nPOST /invoice:\n\n{ \"amount_sats\": 5000, \"description\": \"agent service\", \"expiry\": 3600 }\n\n\namount_sats is required. description and expiry are optional.\n\nPOST /pay:\n\n{ \"bolt11\": \"lnbc50u1p...\", \"max_fee\": 100, \"timeout\": 60 }\n\n\nbolt11 is required. max_fee and timeout are optional.\n\nPOST /channels/open:\n\n{ \"peer\": \"02ab...@127.0.0.1:9735\", \"amount_sats\": 100000, \"push_sats\": 0 }\n\n\npeer and amount_sats are required. push_sats is optional.\n\nPOST /channels/close:\n\n{ \"channel_id\": \"12345678901234567890\", \"peer\": \"02ab...\", \"force\": false }\n\n\nchannel_id and peer are required. force is optional (default false).\n\nWebhooks\n\nConfigure in ~/.bitkit/<network>/config.toml:\n\nwebhook_url = \"https://your-agent.example.com/webhook\"\nwebhook_secret = \"your-hmac-secret\"\n\n\nEvents and Payloads:\n\nEach webhook POST body contains event, data, and timestamp (Unix ms):\n\npayment_received — Lightning payment received:\n\n{\n  \"event\": \"payment_received\",\n  \"data\": { \"payment_hash\": \"abc...\", \"amount_sat\": 1000 },\n  \"timestamp\": 1708444800000\n}\n\n\npayment_sent — Outbound Lightning payment succeeded:\n\n{\n  \"event\": \"payment_sent\",\n  \"data\": { \"payment_hash\": \"abc...\", \"amount_sat\": 5000, \"fee_sat\": 3 },\n  \"timestamp\": 1708444800000\n}\n\n\npayment_failed — Outbound Lightning payment failed:\n\n{\n  \"event\": \"payment_failed\",\n  \"data\": { \"payment_hash\": \"abc...\", \"reason\": \"RouteNotFound\" },\n  \"timestamp\": 1708444800000\n}\n\n\npayment_hash and reason are nullable (may be null).\n\nchannel_ready — Channel fully open and usable:\n\n{\n  \"event\": \"channel_ready\",\n  \"data\": { \"channel_id\": \"4242...\", \"counterparty_node_id\": \"02ab...\" },\n  \"timestamp\": 1708444800000\n}\n\n\ncounterparty_node_id is nullable.\n\nchannel_closed — Channel closed:\n\n{\n  \"event\": \"channel_closed\",\n  \"data\": { \"channel_id\": \"4242...\", \"counterparty_node_id\": \"02ab...\", \"reason\": \"CooperativeClosure\" },\n  \"timestamp\": 1708444800000\n}\n\n\ncounterparty_node_id and reason are nullable.\n\nSignature: X-Bitkit-Signature: hmac-sha256=<hex> header (HMAC-SHA256 of body using webhook_secret).\n\nRetry: 3 attempts with exponential backoff (1s, 4s, 16s).\n\nWebSocket Events\n\nConnect to ws://localhost:3457/events with Basic Auth. Receives the same events as webhooks in real-time as JSON text frames.\n\n# Example with websocat\nwebsocat ws://localhost:3457/events -H \"Authorization: Basic $(echo -n 'bitkit:PASSWORD' | base64)\"\n\n\nFrame format: Each event is a JSON text frame with the same structure as webhook payloads:\n\n{\"event\":\"payment_received\",\"data\":{\"payment_hash\":\"abc...\",\"amount_sat\":1000},\"timestamp\":1708444800000}\n\n\nBehavior:\n\nServer sends a WebSocket Ping every 10 seconds as a keepalive\nIf the client falls behind, the server sends a warning frame: {\"warning\":\"missed 3 events\"}\nConnection closes when: client sends a Close frame, any WebSocket error occurs, or the daemon shuts down\nNo client-to-server messages are expected (receive-only stream)\nConfiguration Reference\n\nConfig file: ~/.bitkit/<network>/config.toml (created by bk init). All fields are under [node]:\n\n[node]\nnetwork = \"mainnet\"                    # \"mainnet\" or \"regtest\"\nchain_source = \"esplora\"               # \"esplora\" or \"electrum\"\nesplora_url = \"https://blockstream.info/api\"\nelectrum_url = \"\"                      # used when chain_source = \"electrum\"\nrgs_url = \"https://rapidsync.lightningdevkit.org/snapshot\"\nblocktank_url = \"https://api1.blocktank.to/api\"\nwebhook_url = \"\"                       # POST target for events\nwebhook_secret = \"\"                    # HMAC-SHA256 signing key\nauto_liquidity = false                 # enable background inbound capacity monitor\nauto_liquidity_threshold_sats = 100000 # order channel when inbound < this\nauto_liquidity_channel_size_sats = 500000  # channel size to order\n\nField\tType\tDefault\tDescription\nnetwork\tstring\t\"mainnet\"\tBitcoin network\nchain_source\tstring\t\"esplora\"\tChain sync backend: esplora or electrum\nesplora_url\tstring\t\"https://blockstream.info/api\"\tEsplora API URL\nelectrum_url\tstring\t\"\"\tElectrum server URL (regtest: tcp://127.0.0.1:60001)\nrgs_url\tstring\t(LDK default)\tRapid Gossip Sync URL for Lightning routing graph\nblocktank_url\tstring\t\"https://api1.blocktank.to/api\"\tBlocktank LSP API URL\nwebhook_url\tstring\t\"\"\tWebhook POST target (empty = disabled)\nwebhook_secret\tstring\t\"\"\tHMAC-SHA256 key for X-Bitkit-Signature header\nauto_liquidity\tbool\tfalse\tEnable background inbound capacity monitor\nauto_liquidity_threshold_sats\tu64\t100000\tOrder channel when inbound capacity drops below this\nauto_liquidity_channel_size_sats\tu64\t500000\tSize of auto-ordered channels\nAuto-Liquidity\n\nWhen auto_liquidity = true in config.toml, the daemon monitors inbound channel capacity every 5 minutes. If total inbound capacity drops below auto_liquidity_threshold_sats, it automatically creates a Blocktank channel order for auto_liquidity_channel_size_sats.\n\nImportant: The auto-created order still requires on-chain payment to payment_address from the order. The daemon does not pay automatically — an agent or human must fund the order.\n\nWorkflow:\n\nEnable in config: auto_liquidity = true\nStart daemon: bk start --json\nMonitor for channel_ready events via webhook or WebSocket\nWhen a Blocktank order is created, check bk lsp get-order <id> for the payment_address\nPay the order on-chain, then the channel opens automatically\nAPI Password\n\nThe daemon HTTP API uses HTTP Basic Auth. The password is auto-generated on first bk start.\n\nLocation: <wallet_dir>/api-password (e.g., ~/.bitkit/api-password) Format: 32 random bytes encoded as 64 hex characters Permissions: File mode 0600 (owner read/write only)\n\n# Read the password\ncat ~/.bitkit/api-password\n\n# Use with curl\ncurl -u \"bitkit:$(cat ~/.bitkit/api-password)\" http://localhost:3457/balance\n\n# Use with WebSocket\nwebsocat ws://localhost:3457/events -H \"Authorization: Basic $(echo -n \"bitkit:$(cat ~/.bitkit/api-password)\" | base64)\"\n\n\nThe password persists across daemon restarts. Delete the file to force regeneration on next bk start.\n\nError Handling\nExit Codes\nCode\tName\tCommon Causes\tRecovery\n0\tSuccess\t--\t--\n1\tUser error\tBad input, missing wallet, invalid invoice, expired invoice\tFix input and retry\n2\tNetwork error\tConnection failed, sync timeout, payment timeout\tRetry after delay\n3\tInsufficient funds\tNot enough on-chain or Lightning balance\tFund wallet or open channel\nError Envelope\n\nWhen --json is used and an error occurs, a structured error envelope is written to stderr:\n\n{\n  \"ok\": false,\n  \"error\": \"Invalid invoice: ...\",\n  \"code\": 1\n}\n\nBash Pattern\nOUTPUT=$(bk pay \"$BOLT11\" --json 2>/dev/null)\nEXIT_CODE=$?\n\ncase $EXIT_CODE in\n  0) echo \"Success: $(echo \"$OUTPUT\" | jq -r '.data.payment_hash')\" ;;\n  1) echo \"Bad input -- check the invoice\" >&2; exit 1 ;;\n  2) echo \"Network issue -- retrying...\" >&2; sleep 5; bk pay \"$BOLT11\" --json ;;\n  3) echo \"Insufficient funds\" >&2; exit 3 ;;\nesac\n\n\nImportant: Errors print to stderr (as JSON envelope when --json is used). Success output prints to stdout. Always capture them separately.\n\nMulti-Wallet Pattern\n\nRun multiple independent agent wallets by using separate data directories.\n\nexport BITKIT_DIR=\"/tmp/agent-${SESSION_ID}\"\nbk init --no-password --json\nbk address --json\n# ... each agent operates independently\n\n\nOr use the --dir flag:\n\nbk --dir /tmp/agent-alice init --no-password --json\nbk --dir /tmp/agent-bob init --no-password --json\n\n\nEach directory gets its own seed, node identity, Pubky ID, and channel state. Agents sharing a machine can run concurrently with different --dir values.\n\nInput Validation Reference\nFormat\tPattern\tExample\nNode pubkey\t66-char hex (compressed secp256k1)\t02abc123...\nPeer address\tpubkey@host:port\t02abc...@127.0.0.1:9735\nTransaction ID\t64-char hex\t4a5e1e4baab89f3a...\nOutpoint\ttxid:vout (decimal vout)\t4a5e1e4b...:0\nChannel ID\tDecimal u128\t12345678901234567890\nBitcoin address\tNetwork-validated bech32/base58\tbc1q..., bcrt1q...\nBOLT11 invoice\tlnbc... or lnbcrt... prefix\tlnbc50u1p...\nLNURL\tBech32 with lnurl HRP\tlnurl1dp68gurn...\nLightning Address\tuser@domain.tld\tagent@pay.bitkit.to\nPubky ID\t52-char z-base-32\t8pinxrz9tux...45mo\nFee rate\tPositive integer (sat/vb)\t5\nAmount\tNon-negative integer (satoshis)\t50000\n\nAll validation errors exit with code 1 and include a descriptive error message.\n\nMessaging Workflow\nAgent Message Protocol\n\nAgents communicate structured data by sending JSON strings as message content. The convention uses a type field to distinguish message kinds:\n\nInvoice request:\n\n{\n  \"type\": \"invoice\",\n  \"bolt11\": \"lnbc50u1p...\",\n  \"amount_sats\": 5000,\n  \"description\": \"data analysis service\"\n}\n\n\nPayment confirmation:\n\n{\n  \"type\": \"payment_confirmation\",\n  \"payment_hash\": \"a1b2c3d4e5f6...\",\n  \"amount_sats\": 5000\n}\n\nWorkflow: Discover, Message, Pay\n# 1. Get your own Pubky ID to share with other agents\nMY_ID=$(bk message whoami --json | jq -r '.data.pubky_id')\n\n# 2. Send a message to another agent\nbk message send \"$PEER_PUBKY\" \"Hello, I need data analysis\" --json\n\n# 3. Listen for their reply (they'll send an invoice)\nbk message listen \"$PEER_PUBKY\" --timeout 120 --json | while IFS= read -r MSG; do\n  TYPE=$(echo \"$MSG\" | jq -r '.data.content' | jq -r '.type // empty' 2>/dev/null)\n  if [ \"$TYPE\" = \"invoice\" ]; then\n    BOLT11=$(echo \"$MSG\" | jq -r '.data.content' | jq -r '.bolt11')\n    echo \"Received invoice: $BOLT11\"\n    break\n  fi\ndone\n\n# 4. Pay the invoice\nbk pay \"$BOLT11\" --json\n\nEnd-to-End: Pay Another Agent\n\nComplete flow where Agent A requests a service, Agent B invoices for it, Agent A pays, and Agent B confirms.\n\nAgent B (service provider)\n#!/usr/bin/env bash\nset -euo pipefail\nexport BITKIT_DIR=/tmp/agent-b\n\n# Initialize and share identity\nbk init --no-password --json >/dev/null\nMY_ID=$(bk message whoami --json | jq -r '.data.pubky_id')\necho \"Agent B Pubky ID: $MY_ID\"\n\n# Listen for requests from Agent A\nbk message listen \"$AGENT_A_PUBKY\" --timeout 300 --json | while IFS= read -r MSG; do\n  CONTENT=$(echo \"$MSG\" | jq -r '.data.content')\n\n  # Create invoice for the requested service\n  INVOICE_JSON=$(bk invoice 5000 --description \"data analysis\" --json)\n  BOLT11=$(echo \"$INVOICE_JSON\" | jq -r '.data.bolt11')\n\n  # Send invoice to Agent A\n  PAYLOAD=$(jq -n --arg b \"$BOLT11\" '{type:\"invoice\",bolt11:$b,amount_sats:5000,description:\"data analysis\"}')\n  bk message send \"$AGENT_A_PUBKY\" \"$PAYLOAD\" --json\n\n  # Wait for payment\n  RECEIVED=$(bk invoice 5000 --description \"data analysis\" --wait --listen 9735 --json | tail -1)\n  HASH=$(echo \"$RECEIVED\" | jq -r '.data.payment_hash')\n\n  # Confirm payment\n  CONFIRM=$(jq -n --arg h \"$HASH\" '{type:\"payment_confirmation\",payment_hash:$h,amount_sats:5000}')\n  bk message send \"$AGENT_A_PUBKY\" \"$CONFIRM\" --json\n  break\ndone\n\nAgent A (client)\n#!/usr/bin/env bash\nset -euo pipefail\nexport BITKIT_DIR=/tmp/agent-a\n\n# Initialize\nbk init --no-password --json >/dev/null\nMY_ID=$(bk message whoami --json | jq -r '.data.pubky_id')\n\n# Request service from Agent B\nbk message send \"$AGENT_B_PUBKY\" \"Please analyze dataset X\" --json\n\n# Wait for invoice\nBOLT11=\"\"\nbk message listen \"$AGENT_B_PUBKY\" --timeout 120 --json | while IFS= read -r MSG; do\n  TYPE=$(echo \"$MSG\" | jq -r '.data.content' | jq -r '.type // empty' 2>/dev/null)\n  if [ \"$TYPE\" = \"invoice\" ]; then\n    BOLT11=$(echo \"$MSG\" | jq -r '.data.content' | jq -r '.bolt11')\n    break\n  fi\ndone\n\n# Pay the invoice\nPAY_RESULT=$(bk pay \"$BOLT11\" --json)\necho \"Payment status: $(echo \"$PAY_RESULT\" | jq -r '.data.status')\"\necho \"Fee paid: $(echo \"$PAY_RESULT\" | jq -r '.data.fee_sat') sats\"\n\n# Wait for confirmation\nbk message listen \"$AGENT_B_PUBKY\" --timeout 60 --json | while IFS= read -r MSG; do\n  TYPE=$(echo \"$MSG\" | jq -r '.data.content' | jq -r '.type // empty' 2>/dev/null)\n  if [ \"$TYPE\" = \"payment_confirmation\" ]; then\n    echo \"Payment confirmed by Agent B\"\n    break\n  fi\ndone\n\nSecurity & Privacy\n\nTrust model: Fully self-custodial. The BIP39 seed never leaves the local machine. No telemetry, analytics, or tracking of any kind.\n\nExternal endpoints contacted:\n\nEndpoint\tPurpose\tData Sent\tConfigurable\nEsplora server\tChain sync (mainnet)\tTransaction queries\tYes (esplora_url)\nElectrum server\tChain sync (regtest)\tTransaction queries\tYes (electrum_url)\nRGS server\tLightning gossip sync\tNone (download only)\tYes (rgs_url)\nBlocktank API\tLSP channel orders\tNode ID, order params\tYes (blocktank_url)\nLNURL service\tPay/withdraw resolution\tAmount, callback URL\tUser-initiated only\nPubky DHT\tEncrypted messaging\tE2E encrypted payloads\tBuilt-in\nWebhook URL\tEvent notifications\tPayment/channel events\tYes (webhook_url)\n\nSeed storage: Custom binary format with magic bytes (BKIT), version byte, and encrypted (AES-256-GCM + Argon2id KDF) or plaintext flag. File: ~/.bitkit/<network>/seed.enc.\n\nAPI password: Auto-generated 32-byte random hex, stored with 0600 permissions. Only used for local daemon HTTP API access.\n\nLSP Workflow: Fresh Wallet to Lightning-Ready\n\nFrom a brand new wallet to being able to send and receive Lightning payments:\n\n# 1. Create wallet\nbk init --no-password --json\n\n# 2. Get on-chain address\nADDRESS=$(bk address --json | jq -r '.data.address')\necho \"Fund this address: $ADDRESS\"\n\n# 3. (Fund the address externally, wait for confirmation)\n\n# 4. Estimate channel fees\nbk lsp estimate-fee 500000 --json\n\n# 5. Create channel order\nORDER_ID=$(bk lsp create-order 500000 --json | jq -r '.data.order_id')\n\n# 6. Pay the order on-chain (the payment_address from create-order)\n# ... or wait for on-chain funding to cover it\n\n# 7. Poll until order is paid\nwhile true; do\n  STATE=$(bk lsp get-order \"$ORDER_ID\" --json | jq -r '.data.state2')\n  [ \"$STATE\" = \"Paid\" ] && break\n  sleep 10\ndone\n\n# 8. Open the channel\nbk lsp open-channel \"$ORDER_ID\" --listen 9735 --json\n\n# 9. Verify channel is active\nbk list-channels --json | jq '.data[0].is_usable'\n\n# Now ready to send and receive Lightning payments\n\nTags\n\nbitcoin, lightning, payment, invoice, self-custody, wallet, CLI, agent, LNURL, lightning-address, BOLT11, LSP, lowest-fees, daemon, webhooks, websocket, encrypted-messaging, on-chain, channels, liquidity"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/ovitrif/bitkit-cli",
    "publisherUrl": "https://clawhub.ai/ovitrif/bitkit-cli",
    "owner": "ovitrif",
    "version": "0.2.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/bitkit-cli",
    "downloadUrl": "https://openagent3.xyz/downloads/bitkit-cli",
    "agentUrl": "https://openagent3.xyz/skills/bitkit-cli/agent",
    "manifestUrl": "https://openagent3.xyz/skills/bitkit-cli/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/bitkit-cli/agent.md"
  }
}