This repo is a fork of upstream Intercom (Trac-Systems/intercom): a reference implementation of the Intercom stack on Trac Network for an internet of agents.
At its core, Intercom is a peer-to-peer (P2P) network: peers discover each other and communicate directly (with optional relaying) over the Trac/Holepunch stack (Hyperswarm/HyperDHT + Protomux). There is no central server required for sidechannel messaging.
This fork adds a non-custodial swap harness:
- Negotiate via request-for-quote (RFQ) messages over Intercom sidechannels (P2P).
- Settle BTC over Lightning <> USDT on Solana using a shared Solana escrow program (HTLC-style).
Links:
- Upstream Intercom:
https://github.com/Trac-Systems/intercom - This fork:
https://github.com/TracSystems/intercom-swap
Intercom Swap is a local-first P2P system with one core runtime and multiple optional control/settlement paths.
Humans + Autonomous Agents
|
+--------------+--------------+
| |
Structured control Natural language
(UI + tool calls) (optional prompting)
| |
+--------------+--------------+
v
Intercom runtime peer
(identity + local state store)
|
+-----------------+-------------------+
| |
v v
P2P coordination fabric Optional app extension
- Sidechannels (RFQ + swap) - Local-first contracts/features
- Subnet replication - Trac Network tx path (TNK gas)
|
+--------------+--------------+
| |
v v
Lightning settlement Solana settlement
(BTC leg) (USDT leg)
Key idea:
- Intercom handles coordination and agent communication.
- Settlement happens on Lightning + Solana.
- Contract usage on Trac Network is optional and extensible.
Intercom is a Trac stack for autonomous agents:
- Sidechannels: fast, ephemeral P2P messaging (Hyperswarm + Noise).
- Features: integrate non-agent services/tools into the same network.
- Contracts (optional): deterministic state + optional chat.
- MSB (optional): value-settled transactions.
This fork keeps Intercom intact and layers swap + ops tooling on top.
- Run Strategy Matrix
- Install And Operate From
SKILL.md - How To Use
SKILL.mdWith An Agent - Conceptual Flow (BTC(LN) <> USDT(Solana))
- External APIs / RPCs (Defaults)
- Command Surface (Scripts = "Function Calls")
- Start Intercom Peers (
run-swap-*) - SC-Bridge Control (
swapctl) - RFQ Bots (
rfq-maker/rfq-taker) - Recovery (
swaprecover) - Solana Wallet Tooling (
solctl) - Solana Escrow Program Tooling (
escrowctl) - Lightning Operator Tooling (
lnctl) - Optional LND Local Lifecycle (
lndctl/lndpw) - Prompt Router (Optional)
- Tests (Mandatory)
- Secrets + Repo Hygiene
Choose one path before running commands. Do not mix paths in a single instance.
| Goal | Path | Typical Network | Data Isolation Rule |
|---|---|---|---|
| Validate code and workflows | Test path | LN regtest + Solana local/devnet | test stores + test receipts DB + test promptd port |
| Upgrade an existing deployment | Upgrade path | same as current deployment | keep backup, preserve current stores, rerun tests |
| Operate with real funds | Mainnet path | LN mainnet + Solana mainnet | separate mainnet stores/receipts/ports; never reuse test data |
| Human-first operation | Collin path | any | Collin talks to one promptd instance at a time |
| Agent-first automation | Headless path | any | prefer deterministic scripts/tool calls over free-form prompting |
Minimal rule set:
- Always decide
testvsmainnetfirst. - Keep test and mainnet fully separated (store names, DB paths, ports, audit dirs).
- For mainnet, use public DHT bootstraps (local DHT is test-only).
- Run tests before first live settlement.
SKILL.md is the canonical installer + runbook for this repo. If you are an agent, treat it as the source of truth for:
- installation steps
- runtime requirements (Pear, Node)
- first-run decisions (sidechannels, invites, PoW)
- operations (LN/Solana, recovery, tests)
Installation and large merges are easiest with a top-tier coding model.
Recommended:
- OpenAI: GPT-5.3+ (Codex,
xhigh) - Anthropic: Claude Opus 4.6+
OpenClaw can use and control this stack autonomously (install/upgrade via SKILL.md, ops via scripts and optional promptd tool calls, including backend worker tools intercomswap_tradeauto_*).
Local/open-weight models can work too, but use a high-grade one.
Example prompts (copy/paste):
- Install
Install this repo using SKILL.md. Run all tests (unit + e2e). Report what you ran and any failures.
- Install + staging tests
Install this repo using SKILL.md. Run unit + local e2e. Then run a smoke test on test networks (LN regtest + Solana devnet) if supported. Report results.
2b. Decide and execute one run path first
Read SKILL.md and pick exactly one run path (test / upgrade / mainnet / collin / headless). Explain why that path matches the goal, then execute it end-to-end.
- Update workflow
Pull the latest version of this fork, resolve merge conflicts, and run all tests (unit + e2e). If testnet smoke tests exist, run them too. Only then proceed to mainnet checks.
3b. Switch to mainnet (fresh instance)
Create a clean mainnet instance: do NOT reuse any test stores or receipts DBs. Wipe/rotate test data only, keep mainnet keys separate. Then bring up mainnet peer + promptd + Collin and run a mainnet readiness checklist (funding + LN channel ready + Solana RPC reachable).
- Mainnet start
Install this repo using SKILL.md, run all tests (unit + e2e), then run the mainnet bring-up checklist and start maker+taker peers on mainnet (with user-provided Solana RPC + Solana keypairs + LN node configuration). Report the exact commands run and any failures.
- Enable Collin prompting (LLM mode)
Enable LLM prompting for Collin. Tell me exactly what config you need (OpenAI-compatible base_url, api_key or token file, model, max_tokens, temperature) and where it must be stored (gitignored). Validate by running a prompt that posts an Offer and confirm it appears in the UI.
- Operator support mode
I’m operating Collin and I’m stuck: “<paste error>”. Explain what it means and the exact next click/command to fix it. Do not guess; inspect the repo and logs.
Rendezvous sidechannel(s) (any; examples: 0000intercom, 0000intercomswapbtcusdt, my-swap-room)
|
| swap.svc_announce (service + offers[]) [periodic rebroadcast; sidechannels have no history]
| Offer (optional) -> RFQ (manual or backend-auto-from-offer) -> QUOTE -> QUOTE_ACCEPT
| - pre-filter by app_hash + fee caps + refund window
v
per-trade invite-only swap:<trade_id>
|
| TERMS (binding: fees, mint, refund_after_unix, ...)
| ACCEPT
| LN_INVOICE (payment_hash)
| SOL_ESCROW_CREATED (escrow PDA + vault ATA)
v
Settlement (BTC over Lightning <> USDT on Solana)
1) Maker creates + posts LN invoice (receiver inbound liquidity check must pass)
2) Taker runs LN route precheck and posts `ln_route_precheck_ok` (swap.status)
3) Maker escrows USDT (Solana) only after taker precheck is OK
4) Taker verifies escrow on-chain (hard rule: no escrow, no pay)
5) Taker pays LN invoice -> learns preimage
6) Taker claims USDT on Solana using preimage
7) Refund path after sol_refund_after_unix if LN payment never happens
This stack touches a few external endpoints. Defaults are chosen so local e2e is easy, and live ops are configurable:
- Price oracle (HTTP): by default uses public exchange APIs (no keys):
binance,coinbase,gate,kucoin,okx,bitstamp,kraken.- Enabled on peers via
--price-oracle 1(included inscripts/run-swap-*.sh). - Configure providers via
--price-providers "<csv>".
- Enabled on peers via
- Solana (JSON-RPC over HTTP): bots/tools default to local validator
http://127.0.0.1:8899.- Configure via
--solana-rpc-url "<url[,url2,...]>"(comma-separated failover pool).
- Configure via
- Bitcoin/LN: the BTC leg is Lightning (CLN or LND).
- Local e2e uses docker regtest stacks under
dev/(includesbitcoind). - Mainnet uses your local LN node (CLN via
bitcoindRPC, or LND vianeutrinoorbitcoindbackend). - This repo does not require a separate public Bitcoin explorer API by default.
- Local e2e uses docker regtest stacks under
If any of your HTTP/RPC endpoints require auth headers (Bearer/API tokens), see Authenticated API Endpoints near the end of this README.
After installation, day-to-day operation should be done by invoking scripts (macOS/Linux .sh, Windows .ps1). The .mjs files are the canonical CLIs; wrappers exist to keep invocation stable and tool-call friendly.
| Area | macOS/Linux | Windows | Canonical | Purpose |
|---|---|---|---|---|
| Bootstrap | scripts/bootstrap.sh |
n/a | bash | Install Pear runtime + deps |
| Start peer (maker/service) | scripts/run-swap-maker.sh |
scripts/run-swap-maker.ps1 |
shell | Start a peer with SC-Bridge + price oracle and join an RFQ channel |
| Start peer (taker/client) | scripts/run-swap-taker.sh |
scripts/run-swap-taker.ps1 |
shell | Start a peer with SC-Bridge + price oracle and join an RFQ channel; pins SWAP_INVITER_KEYS for swap:* |
| Peer lifecycle supervisor | scripts/peermgr.sh |
scripts/peermgr.ps1 |
scripts/peermgr.mjs |
Start/stop/restart background peers (headless) without keeping a terminal open |
| SC-Bridge control | scripts/swapctl.sh |
scripts/swapctl.ps1 |
scripts/swapctl.mjs |
Sidechannel ops + signed message helpers |
| SC-Bridge control (token auto) | scripts/swapctl-peer.sh |
scripts/swapctl-peer.ps1 |
wrapper | Same as swapctl, but reads token from onchain/sc-bridge/<store>.token |
| RFQ maker bot | scripts/rfq-maker-peer.sh |
scripts/rfq-maker-peer.ps1 |
scripts/rfq-maker.mjs |
Quote RFQs; optionally run full swap state machine |
| RFQ taker bot | scripts/rfq-taker-peer.sh |
scripts/rfq-taker-peer.ps1 |
scripts/rfq-taker.mjs |
Send RFQ; accept quote; optionally run full swap state machine |
| RFQ bot control | scripts/rfqbotmgr.sh |
scripts/rfqbotmgr.ps1 |
scripts/rfqbotmgr.mjs |
Start/stop/restart RFQ bot instances without stopping the peer |
| Recovery | scripts/swaprecover.sh |
scripts/swaprecover.ps1 |
scripts/swaprecover.mjs |
List/show receipts; claim/refund escrows |
| Solana wallet ops | scripts/solctl.sh |
scripts/solctl.ps1 |
scripts/solctl.mjs |
Keypairs, balances, ATA, token transfers |
| Solana escrow ops | scripts/escrowctl.sh |
scripts/escrowctl.ps1 |
scripts/escrowctl.mjs |
Program config, fee vaults, escrow inspection |
| Solana program ops (maintainers) | scripts/solprogctl.sh |
scripts/solprogctl.ps1 |
scripts/solprogctl.mjs |
Build/deploy the Solana program |
| Lightning ops | scripts/lnctl.sh |
scripts/lnctl.ps1 |
scripts/lnctl.mjs |
Addresses, channels, invoices, payments |
| LND local lifecycle (optional) | scripts/lndctl.sh |
scripts/lndctl.ps1 |
scripts/lndctl.mjs |
Generate lnd.conf, start/stop, create/unlock wallet |
| LND password helper (optional) | scripts/lndpw.sh |
scripts/lndpw.ps1 |
shell | Write an LND wallet password file (no trailing newline) |
| Function call | What it does | Parameters |
|---|---|---|
scripts/run-swap-maker.sh [storeName] [scBridgePort] [rfqChannel] [...extra peer flags] |
Starts a maker/service peer, enables SC-Bridge + price oracle, joins the RFQ channel | Positional args; optional env: SIDECHANNEL_POW (default 1), SIDECHANNEL_POW_DIFFICULTY (default 12) |
SWAP_INVITER_KEYS="<makerPeerPubkeyHex[,more]>" scripts/run-swap-taker.sh [storeName] [scBridgePort] [rfqChannel] [...extra peer flags] |
Starts a taker/client peer and pins inviter key(s) for swap:* invite-only channels |
Requires SWAP_INVITER_KEYS; same optional env vars as maker |
Notes:
| Item | Details |
|---|---|
| Token files | Created under onchain/sc-bridge/<storeName>.token (gitignored). |
| RFQ channel | Any sidechannel works. Many operators use a dedicated rendezvous (example: 0000intercomswapbtcusdt) to reduce noise, but 0000intercom works too. |
| Subnet channel | Keep --subnet-channel consistent across peers (mismatches can prevent connections). |
peermgr is a local supervisor for starting/stopping pear run peers in the background (so you don’t need to keep a terminal open).
Notes:
- It enforces: never run the same peer store twice.
- It stores state + logs under
onchain/peers/(gitignored). - It always starts the peer in headless mode (
--terminal 0).
| Command | What it does |
|---|---|
scripts/peermgr.sh start --name <id> --store <peerStoreName> --sc-port <n> --sidechannels <csv> |
Start a peer and join one or more extra sidechannels on startup |
scripts/peermgr.sh stop --name <id> |
Stop the peer process |
scripts/peermgr.sh restart --name <id> |
Restart using the last saved config |
scripts/peermgr.sh status [--name <id>] |
Show state + PID + liveness |
swapctl is the SC-Bridge client CLI. It controls a running peer over WebSocket, and (when needed) signs locally using the peer keypair file (SC-Bridge never signs).
| Flag | Required | Meaning |
|---|---|---|
--url ws://127.0.0.1:<scPort> |
yes | SC-Bridge websocket URL |
--token <hex> |
yes | SC-Bridge token (from onchain/sc-bridge/<store>.token) |
--peer-keypair <path> |
signing only | Peer keypair.json (usually stores/<store>/db/keypair.json) for commands that create signed payloads |
| Wrapper | What it does |
|---|---|
scripts/swapctl-peer.sh <storeName> <scPort> <swapctl command...> |
Reads onchain/sc-bridge/<storeName>.token and calls swapctl with --url/--token |
scripts/swapctl-peer.ps1 <storeName> <scPort> <swapctl command...> |
Same for Windows |
| Command | What it does | Important flags |
|---|---|---|
info |
Peer info (pubkey, joined channels, SC-Bridge status) | none |
stats |
Peer runtime stats | none |
price-get |
Price snapshot from the peer's price feature | none |
watch |
Stream messages for debugging/observability | --channels <a,b,c>, --kinds <k1,k2>, --trade-id <id>, --pretty 0/1, --raw 0/1 |
| Command | What it does | Flags |
|---|---|---|
join |
Join a sidechannel | --channel <name>; optional: --invite <b64/json/@file>, --welcome <b64/json/@file> |
leave |
Leave a sidechannel | --channel <name> |
open |
Request others to open a channel (via the entry channel) | --channel <name> --via <entryChannel>; optional: --invite <...>, --welcome <...> |
send |
Send plaintext or JSON to a channel | --channel <name> and one of: --text <msg> or --json <obj/@file>; optional: --invite <...>, --welcome <...> |
| Command | What it does | Flags |
|---|---|---|
svc-announce |
Broadcast a signed service announcement | Required: --channels <a,b,c> --name <label>; optional: --pairs <p1,p2>, --rfq-channels <a,b,c>, --note <text>, --offers-json <json/@file>, --trade-id <id>, --ttl-sec <sec>, --join 0/1 |
svc-announce-loop |
Periodically re-broadcast announcements (sidechannels have no history) | Required: --channels <a,b,c> --config <json/@file>; optional: --interval-sec <sec>, --watch 0/1, --ttl-sec <sec>, --trade-id <id>, --join 0/1 |
| Command | What it does | Flags |
|---|---|---|
make-welcome |
Create a signed welcome payload | --channel <name> --text <welcomeText> |
make-invite |
Create a signed invite payload | --channel <name> --invitee-pubkey <hex32>; optional: --ttl-sec <sec>, --welcome <b64/json/@file> |
| Command | What it does | Flags |
|---|---|---|
rfq |
Send RFQ to an RFQ channel | --channel <rfqChannel> --trade-id <id> --btc-sats <n> --usdt-amount <atomicStr>; optional: --valid-until-unix <sec> |
quote |
Send quote | --channel <rfqChannel> --trade-id <id> --rfq-id <id> --btc-sats <n> --usdt-amount <atomicStr> --valid-until-unix <sec> |
quote-from-rfq |
Build + send a quote from an RFQ envelope | --channel <rfqChannel> --rfq-json <envelope/@file>; optional: --btc-sats <n>, --usdt-amount <atomicStr>, --valid-until-unix <sec> |
quote-accept |
Accept a quote | --channel <rfqChannel> --quote-json <envelope/@file> |
swap-invite-from-accept |
Create and send a swap:<trade_id> invite after acceptance |
--channel <rfqChannel> --accept-json <envelope/@file>; optional: --swap-channel <name>, --welcome-text <text>, --ttl-sec <sec> |
join-from-swap-invite |
Join a swap channel using a swap-invite envelope | --swap-invite-json <envelope/@file> |
terms |
Send swap terms into swap:<id> |
Required: --channel <swapChannel> --trade-id <id> --btc-sats <n> --usdt-amount <atomicStr> --sol-mint <base58> --sol-recipient <base58> --sol-refund <base58> --sol-refund-after-unix <sec> --ln-receiver-peer <hex32> --ln-payer-peer <hex32> --platform-fee-bps <n> --trade-fee-bps <n> --trade-fee-collector <base58>; optional: --platform-fee-collector <base58>, --terms-valid-until-unix <sec> |
accept |
Accept swap terms | --channel <swapChannel> --trade-id <id> and one of: --terms-hash <hex> or --terms-json <envelope/body/@file> |
| Command | What it does | Flags |
|---|---|---|
verify-prepay |
Validate that terms, invoice, and escrow match; optionally validate escrow on-chain | Required: --terms-json <envelope/body/@file> --invoice-json <envelope/body/@file> --escrow-json <envelope/body/@file>; optional: --now-unix <sec>, --solana-rpc-url <url[,url2,...]>, --solana-commitment <confirmed/finalized/processed> |
These are long-running bots that sit in an RFQ channel and negotiate RFQ/quotes. With --run-swap 1 they run the full swap state machine inside an invite-only swap:<trade_id> channel.
| Wrapper | What it does |
|---|---|
scripts/rfq-maker-peer.sh <storeName> <scPort> [...flags] |
Runs the maker bot against a running peer (reads token from onchain/sc-bridge/<storeName>.token) |
scripts/rfq-maker-peer.ps1 <storeName> <scPort> [...flags] |
Same for Windows |
scripts/rfq-taker-peer.sh <storeName> <scPort> [...flags] |
Runs the taker bot against a running peer (reads token from onchain/sc-bridge/<storeName>.token) |
scripts/rfq-taker-peer.ps1 <storeName> <scPort> [...flags] |
Same for Windows |
Prefer rfqbotmgr for tool-call operation: stop/restart individual bot instances without touching pear run.
| Function call | What it does |
|---|---|
scripts/rfqbotmgr.sh start-maker --name <id> --store <peerStore> --sc-port <n> -- [...rfq-maker flags] |
Start a maker bot in the background (logs under onchain/rfq-bots/) |
scripts/rfqbotmgr.sh start-taker --name <id> --store <peerStore> --sc-port <n> -- [...rfq-taker flags] |
Start a taker bot in the background |
scripts/rfqbotmgr.sh stop --name <id> |
Stop a running bot |
scripts/rfqbotmgr.sh restart --name <id> |
Restart a bot with the last saved args |
scripts/rfqbotmgr.sh status [--name <id>] |
Show bot state + PID + liveness |
| Flag | Meaning |
|---|---|
--rfq-channel <name> |
RFQ negotiation channel (default 0000intercomswapbtcusdt) |
--swap-channel-template <tmpl> |
Swap channel name template (default swap:{trade_id}) |
--quote-valid-sec <n> |
Quote validity window (default 60) |
--invite-ttl-sec <n> |
Invite TTL (default 604800) |
--once 0/1 |
Exit after one completed swap (default 0) |
--once-exit-delay-ms <n> |
Delay before exiting when --once 1 (default 750) |
--debug 0/1 |
Verbose logs (default 0) |
--receipts-db <path> |
Receipts DB path (recommended: onchain/receipts/rfq-bots/<store>/<bot>.sqlite) |
- Price is negotiated strictly from RFQ/Offer terms (
btc_sats,usdt_amount). - Oracle snapshots are informational only (UI/ops visibility), not settlement gates.
- Open RFQs (
usdt_amount=0) are not supported in bot flow; amounts must be explicit.
| Flag | Meaning |
|---|---|
--run-swap 0/1 |
Execute the full swap state machine (default 0) |
--swap-timeout-sec <n> |
Per-swap timeout (default 300) |
--swap-resend-ms <n> |
Proof resend interval (default 1200) |
--terms-valid-sec <n> |
Terms validity window (default 300) |
--solana-refund-after-sec <n> |
Solana refund timelock from terms send time (default 259200 = 72h) |
--ln-invoice-expiry-sec <n> |
LN invoice expiry seconds (default 3600) |
| Flag | Meaning |
|---|---|
--solana-rpc-url <url[,url2,...]> |
Solana RPC pool (default http://127.0.0.1:8899) |
--solana-keypair <path> |
Maker Solana keypair (required when --run-swap 1) |
--solana-mint <pubkey> |
SPL mint for escrow (required when --run-swap 1) |
--solana-decimals <n> |
Mint decimals (default 6 for mainnet USDT) |
--solana-program-id <pubkey> |
Override program id (defaults to the compiled-in shared program id) |
--solana-cu-limit <units> |
Optional compute unit limit |
--solana-cu-price <microLamports> |
Optional priority fee |
--solana-trade-fee-collector <pubkey> |
Which trade-fee config PDA to use (defaults to platform fee collector) |
| Flag | Meaning |
|---|---|
--ln-impl <cln/lnd> |
Lightning implementation (default cln) |
--ln-backend <docker/cli> |
Lightning backend (default docker) |
--ln-compose-file <path> |
Docker compose file (default dev/ln-regtest/docker-compose.yml) |
--ln-service <name> |
Docker service name (required when --ln-backend docker) |
--ln-network <regtest/signet/mainnet/...> |
Lightning network (default regtest) |
--ln-cli-bin <path> |
CLI binary override (for --ln-backend cli) |
| Flag | Meaning |
|---|---|
--lnd-rpcserver <host:port> |
LND RPC server (for lncli) |
--lnd-tlscert <path> |
TLS cert path |
--lnd-macaroon <path> |
Macaroon path |
--lnd-dir <path> |
LND dir |
| Flag | Meaning |
|---|---|
--trade-id <id> |
Trade id (default random) |
--rfq-channel <name> |
RFQ negotiation channel (default 0000intercomswapbtcusdt) |
--btc-sats <n> |
Sats requested (default 50000) |
--usdt-amount <atomicStr> |
USDT requested (base units, must be > 0) |
--rfq-valid-sec <n> |
RFQ validity window (default 60) |
--timeout-sec <n> |
RFQ/quote negotiation timeout (default 30) |
--rfq-resend-ms <n> |
RFQ resend interval (default 1200) |
--accept-resend-ms <n> |
Quote accept resend interval (default 1200) |
--once 0/1 |
Exit after one completed swap (default 0) |
--once-exit-delay-ms <n> |
Delay before exiting when --once 1 (default 200) |
--debug 0/1 |
Verbose logs (default 0) |
--receipts-db <path> |
Receipts DB path (recommended: onchain/receipts/rfq-bots/<store>/<bot>.sqlite) |
--persist-preimage 0/1 |
Persist ln_preimage_hex into receipts (default 1 when receipts enabled) |
--stop-after-ln-pay 0/1 |
Testing/recovery hook: stop after paying LN (default 0) |
- Taker accepts/rejects quotes by negotiated terms + protocol guardrails (fees/windows/signers/app binding), not oracle thresholds.
- Oracle remains informational only.
| Flag | Meaning |
|---|---|
--run-swap 0/1 |
Execute the full swap state machine (default 0) |
--swap-timeout-sec <n> |
Per-swap timeout (default 300) |
--swap-resend-ms <n> |
Proof resend interval (default 1200) |
--min-solana-refund-window-sec <n> |
Reject TERMS where sol_refund_after_unix - now is below this (default 3600 = 1h) |
--max-solana-refund-window-sec <n> |
Reject TERMS where sol_refund_after_unix - now is above this (default 604800 = 1w) |
--max-platform-fee-bps <n> |
Reject TERMS with platform fee above this (default 500) |
--max-trade-fee-bps <n> |
Reject TERMS with trade fee above this (default 1000) |
--max-total-fee-bps <n> |
Reject TERMS with total fee above this (default 1500) |
| Flag | Meaning |
|---|---|
--solana-rpc-url <url[,url2,...]> |
Solana RPC pool (default http://127.0.0.1:8899) |
--solana-keypair <path> |
Taker Solana keypair (required when --run-swap 1) |
--solana-mint <pubkey> |
SPL mint for escrow (required when --run-swap 1) |
--solana-decimals <n> |
Mint decimals (default 6) |
--solana-program-id <pubkey> |
Override program id (defaults to the compiled-in shared program id) |
--solana-cu-limit <units> |
Optional compute unit limit |
--solana-cu-price <microLamports> |
Optional priority fee |
| Flag | Meaning |
|---|---|
--ln-impl <cln/lnd> |
Lightning implementation (default cln) |
--ln-backend <docker/cli> |
Lightning backend (default docker) |
--ln-compose-file <path> |
Docker compose file (default dev/ln-regtest/docker-compose.yml) |
--ln-service <name> |
Docker service name (required when --ln-backend docker) |
--ln-network <regtest/signet/mainnet/...> |
Lightning network (default regtest) |
--ln-cli-bin <path> |
CLI binary override (for --ln-backend cli) |
| Flag | Meaning |
|---|---|
--lnd-rpcserver <host:port> |
LND RPC server (for lncli) |
--lnd-tlscert <path> |
TLS cert path |
--lnd-macaroon <path> |
Macaroon path |
--lnd-dir <path> |
LND dir |
swaprecover provides a deterministic recovery path using the local receipts DB.
| Flag | Meaning |
|---|---|
--receipts-db <path> |
Receipts DB (SQLite; should live under onchain/) |
| Command | What it does | Flags |
|---|---|---|
list |
List trades in receipts | Optional: --limit <n> |
show |
Show one trade | One of: --trade-id <id>, --payment-hash <hex32> |
claim |
Claim Solana escrow if LN was paid but agent crashed | One of: --trade-id <id>, --payment-hash <hex32>; required: --solana-rpc-url <csv>, --solana-keypair <path> |
refund |
Refund Solana escrow after timeout | One of: --trade-id <id>, --payment-hash <hex32>; required: --solana-rpc-url <csv>, --solana-keypair <path> |
| Flag | Meaning |
|---|---|
--rpc-url <url[,url2,...]> |
RPC pool (default http://127.0.0.1:8899) |
--commitment <processed/confirmed/finalized> |
Commitment (default confirmed) |
| Command | What it does | Flags |
|---|---|---|
keygen |
Create a keypair | --out <path>; optional: --seed-hex <hex32>, --force 0/1 |
address |
Print pubkey | --keypair <path> |
balance |
SOL balance | --keypair <path> |
airdrop |
Devnet/testnet airdrop | --keypair <path> --sol <n> |
transfer-sol |
Send SOL | --keypair <path> --to <pubkey> --sol <n> |
mint-create |
Create a test mint | --keypair <path> --decimals <n>; optional: --out <path> |
mint-info |
Inspect mint | --mint <pubkey> |
token-ata |
Print or create ATA | --keypair <path> --mint <pubkey>; optional: --owner <pubkey>, --create 0/1 |
token-balance |
SPL token balance | --keypair <path> --mint <pubkey>; optional: --owner <pubkey> |
token-transfer |
Transfer SPL tokens | --keypair <path> --mint <pubkey> --to <pubkey> --amount <u64>; optional: --create-ata 0/1 |
mint-to |
Mint test tokens | --keypair <path> --mint <pubkey> --to <pubkey> --amount <u64>; optional: --create-ata 0/1 |
inventory |
Print balances across mints | --keypair <path>; optional: --mints <csvPubkeys> |
| Flag | Meaning |
|---|---|
--solana-rpc-url <url[,url2,...]> |
RPC pool (default http://127.0.0.1:8899) |
--commitment <processed/confirmed/finalized> |
Commitment (default confirmed) |
--program-id <base58> |
Override program id (default is the shared program id compiled into the client) |
--solana-cu-limit <units> |
Optional compute unit limit |
--solana-cu-price <microLamports> |
Optional priority fee |
--solana-keypair <path> |
Required for signing commands (config-init, config-set, withdrawals, trade config init/set) |
| Command | What it does | Flags |
|---|---|---|
config-get |
Read platform config | none |
config-init |
Initialize platform fee config (fixed 10 bps / 0.1%) |
optional: --fee-collector <pubkey>, --simulate 0/1, --fee-bps 10 |
config-set |
Update platform fee collector (fee stays fixed at 10 bps / 0.1%) |
optional: --fee-collector <pubkey>, --simulate 0/1, --fee-bps 10 |
fees-balance |
Platform fee vault balance | --mint <pubkey> |
fees-withdraw |
Withdraw platform fees | --mint <pubkey>; optional: --amount <u64>, --create-ata 0/1, --simulate 0/1 |
trade-config-get |
Read trade fee config | --fee-collector <pubkey> |
trade-config-init |
Initialize trade fee config (default 10 bps / 0.1% when omitted) |
optional: --fee-bps <n>, --fee-collector <pubkey>, --simulate 0/1 |
trade-config-set |
Update trade fee config (default 10 bps / 0.1% when omitted) |
optional: --fee-bps <n>, --fee-collector <pubkey>, --simulate 0/1 |
trade-fees-balance |
Trade fee vault balance | --fee-collector <pubkey> --mint <pubkey> |
trade-fees-withdraw |
Withdraw trade fees (for the signer fee collector) | --mint <pubkey>; optional: --amount <u64>, --create-ata 0/1, --simulate 0/1 |
escrow-get |
Inspect escrow state | --payment-hash <hex32> |
| Command | What it does | Flags |
|---|---|---|
id |
Print the program id used by the codebase | none |
build |
Build the SBF program | none (requires Rust + Solana CLI toolchain) |
deploy |
Deploy/upgrade the program | Required: --rpc-url <url> --payer <keypair.json> --program-keypair <keypair.json>; optional: --upgrade-authority <keypair.json>, --so <path>, --dry-run 0/1 |
keypair-pubkey |
Print a program pubkey from a keypair file | --program-keypair <keypair.json> |
| Flag | Meaning |
|---|---|
--impl <cln/lnd> |
Implementation (default cln) |
--backend <cli/docker> |
Backend (default cli) |
--network <bitcoin/mainnet/testnet/regtest/signet> |
Network (default regtest) |
--compose-file <path> |
Docker backend compose (default dev/ln-regtest/docker-compose.yml) |
--service <name> |
Docker service name (required for docker backend) |
--cli-bin <path> |
CLI binary override |
--lnd-rpcserver <host:port> |
LND CLI backend extra |
--lnd-tlscert <path> |
LND CLI backend extra |
--lnd-macaroon <path> |
LND CLI backend extra |
--lnd-dir <path> |
LND CLI backend extra |
| Command | What it does | Flags |
|---|---|---|
info |
Node info | none |
newaddr |
New on-chain address | none |
listpeers |
List connected peers (and advertised addresses) | none |
listfunds |
Wallet + channel balances | none |
balance |
Alias of listfunds wallet balance | none |
connect |
Connect to a peer | --peer <nodeid@host:port> |
fundchannel |
Open a channel | --node-id <hex> --amount-sats <n> |
closechannel |
Close a channel (returns liquidity to on-chain wallet) | --channel-id <id>; optional: --force 0/1, --sat-per-vbyte <n> |
invoice |
Create invoice | --msat <amountmsat> --label <label> --desc <text>; optional: --expiry <sec> |
decodepay |
Decode a BOLT11 invoice | --bolt11 <invoice> |
pay |
Pay invoice | --bolt11 <invoice> |
pay-status |
Payment status | --payment-hash <hex32> |
preimage-get |
Preimage lookup (for recovery) | --payment-hash <hex32> |
This is only for running LND from a local directory under onchain/ (not required if you use the docker deployments in dev/).
| Command | What it does | Synopsis |
|---|---|---|
init |
Generate lnd.conf under onchain/ |
init --node <name> [--network <mainnet/testnet/signet/regtest>] [--lnd-dir <path>] [--alias <str>] [--p2p-port <n>] [--rpc-port <n>] [--rest-port <n>] [--bitcoin-node <neutrino/bitcoind>] [--neutrino-peers <host:port[,..]>] [--wallet-password-file <path>] |
start |
Start lnd |
start --node <name> [--network <...>] [--lnd-dir <path>] [--lnd-bin <path>] |
stop |
Stop lnd |
stop --node <name> [--network <...>] [--lnd-dir <path>] [--lncli-bin <path>] |
create-wallet |
Create wallet (interactive) | create-wallet --node <name> [--network <...>] [--lnd-dir <path>] [--lncli-bin <path>] |
unlock |
Unlock wallet (interactive) | unlock --node <name> [--network <...>] [--lnd-dir <path>] [--lncli-bin <path>] |
paths |
Print TLS/macaroon paths | paths --node <name> [--network <...>] [--lnd-dir <path>] |
| Function call | What it does | Parameters |
|---|---|---|
scripts/lndpw.sh <outFile> |
Writes a password file (no trailing newline) | Positional: <outFile> (example: onchain/lnd/mainnet/maker/wallet.pw) |
This repo includes an optional prompt router + tool executor (promptd) that:
- calls an OpenAI-compatible LLM endpoint
- executes only the safe tool surface (SC-Bridge safe RPC + deterministic scripts)
- writes an audit trail under
onchain/ - keeps swap secrets out of the model context (preimages, invites/welcomes) by using opaque
secret:<id>handles
Canonical sources (always up to date):
- Tool schemas + parameters:
src/prompt/tools.js - Validation + runtime behavior:
src/prompt/executor.js - LLM system/tool policy:
src/prompt/system.js
Tool index (complete):
Complete prompt-tool index (source of truth: src/prompt/tools.js, runtime behavior: src/prompt/executor.js).
| Tool | Description |
|---|---|
intercomswap_app_info |
Get app binding info (app_tag, Solana program id, derived app_hash). |
intercomswap_env_get |
Get local environment/config summary (LN network, Solana RPC, receipts DB path). Does not touch the network. |
intercomswap_stack_start |
Start/bootstrap the local stack (peer + SC-Bridge, LN regtest channel on docker, Solana local validator, receipts DB). |
intercomswap_stack_stop |
Stop the local stack (peer + Solana local validator + LN docker). Does not delete state. |
| Tool | Description |
|---|---|
intercomswap_sc_info |
Get peer info via SC-Bridge (safe fields only). |
intercomswap_sc_stats |
Get SC-Bridge stats. |
intercomswap_sc_price_get |
Get latest price snapshot from local price feature/oracle. |
intercomswap_sc_subscribe |
Subscribe this prompt session to sidechannel message events for specific channels. |
intercomswap_sc_wait_envelope |
Wait for the next signed swap envelope seen on subscribed sidechannels. Returns a handle to the full envelope. |
intercomswap_sc_join |
Join a sidechannel (invite/welcome optional). |
intercomswap_sc_join_many |
Join multiple sidechannels (public rendezvous style). |
intercomswap_sc_leave |
Leave a sidechannel locally (channel hygiene). |
intercomswap_sc_leave_many |
Leave multiple sidechannels locally (channel hygiene). |
intercomswap_sc_open |
Request/open a sidechannel via an entry channel (invite/welcome optional). |
intercomswap_sc_send_text |
Send a plain text message to a channel. |
intercomswap_sc_send_json |
Send a JSON message to a channel (structured payload). |
| Tool | Description |
|---|---|
intercomswap_peer_status |
List local peer instances started via prompt tools (reads onchain/peers). |
intercomswap_peer_start |
Start a local peer instance (detached pear run process). |
intercomswap_peer_stop |
Stop a local peer instance (by instance name). |
intercomswap_peer_restart |
Restart a local peer instance (stop then start using last config). |
| Tool | Description |
|---|---|
intercomswap_offer_post |
Post a signed Offer announcement (swap.svc_announce) into rendezvous channels (advertise: have USDT, want BTC; prompts BTC sellers to post matching RFQs). |
intercomswap_rfq_post |
Post a signed RFQ envelope into an RFQ rendezvous channel (BTC_LN->USDT_SOL: sell BTC for USDT). |
intercomswap_quote_post |
Post a signed QUOTE envelope into an RFQ channel (references an RFQ id). Fees are read from on-chain config/trade-config (not negotiated). Provide either valid_until_unix or valid_for_sec. |
intercomswap_quote_post_from_rfq |
Maker: post a signed QUOTE that matches an RFQ envelope (no manual rfq_id/btc_sats/usdt_amount required). Fees are read from on-chain config/trade-config (not negotiated). Provide either valid_until_unix or valid_for_sec. |
intercomswap_quote_accept |
Post a signed QUOTE_ACCEPT envelope into the RFQ channel (accept a quote). |
intercomswap_swap_invite_from_accept |
Maker: generate welcome+invite and post SWAP_INVITE into the RFQ channel, based on an accepted quote. |
intercomswap_join_from_swap_invite |
Taker: join swap: channel using SWAP_INVITE envelope. |
| Tool | Description |
|---|---|
intercomswap_tradeauto_status |
Get backend trade-automation worker status and memory counters. |
intercomswap_tradeauto_trace_set |
Enable/disable backend trade-automation trace emission (off by default). |
intercomswap_tradeauto_start |
Start backend multi-trade automation worker for subscribed sidechannels. |
intercomswap_tradeauto_stop |
Stop backend multi-trade automation worker. |
| Tool | Description |
|---|---|
intercomswap_rfqbot_status |
List local RFQ bot instances started via prompt tools (reads onchain/rfq-bots). |
intercomswap_rfqbot_start_maker |
Start a maker RFQ bot instance (detached background process). |
intercomswap_rfqbot_start_taker |
Start a taker RFQ bot instance (detached background process). |
intercomswap_rfqbot_stop |
Stop an RFQ bot instance (by instance name). |
intercomswap_rfqbot_restart |
Restart an RFQ bot instance (stop then start using last config). |
| Tool | Description |
|---|---|
intercomswap_autopost_status |
List in-process autopost jobs started via tools (offer/rfq repost schedulers). |
intercomswap_autopost_start |
Start a periodic repost scheduler for offer_post or rfq_post. |
intercomswap_autopost_stop |
Stop an autopost job (by name). |
| Tool | Description |
|---|---|
intercomswap_terms_post |
Maker: post signed TERMS envelope inside swap:. |
intercomswap_terms_accept |
Taker: post signed ACCEPT inside swap: referencing the terms hash. |
intercomswap_swap_status_post |
Post signed STATUS envelope inside swap: (informational; used for liveness handshakes). |
intercomswap_swap_cancel_post |
Post signed CANCEL envelope inside swap: (only allowed before escrow is created). |
intercomswap_terms_accept_from_terms |
Taker: post signed ACCEPT inside swap: from a TERMS envelope (computes terms hash). |
| Tool | Description |
|---|---|
intercomswap_ln_docker_ps |
Docker-only: show docker compose service status for the configured LN compose stack. |
intercomswap_ln_docker_up |
Docker-only: start LN docker services (defaults to bitcoind + configured LN service). |
intercomswap_ln_docker_down |
Docker-only: stop the LN docker compose stack. |
intercomswap_ln_regtest_init |
Docker-only (regtest): bootstrap a funded Lightning channel between the two nodes in the compose stack (mine, fund, connect, open channel). |
intercomswap_ln_unlock |
LND-only: unlock the wallet (docker backend). Uses ln.wallet_password_file from prompt setup, or an inferred file under onchain/lnd// (gitignored). |
| Tool | Description |
|---|---|
intercomswap_ln_info |
Get Lightning node info (impl/backend configured locally). |
intercomswap_ln_newaddr |
Get a new on-chain BTC address from the LN node wallet. |
intercomswap_ln_listpeers |
List connected Lightning peers (used to suggest peer URIs). |
intercomswap_ln_listfunds |
Get on-chain + channel balances. |
intercomswap_ln_listchannels |
List Lightning channels with peer/state/balance details (for channel management). |
intercomswap_ln_closechannel |
Close a Lightning channel (cooperative by default) to return liquidity to on-chain BTC. |
intercomswap_ln_withdraw |
Send on-chain BTC from the LN node wallet to a BTC address. |
intercomswap_ln_connect |
Connect to a Lightning peer (nodeid@host:port). |
intercomswap_ln_peer_probe |
Probe a Lightning peer (TCP reachability + LN connect state). Optionally attempts reconnect. |
intercomswap_ln_fundchannel |
Open a public Lightning channel to a peer. |
intercomswap_ln_splice |
Splice a Lightning channel in/out (CLN experimental splicing only). Use positive sats to add liquidity and negative sats to remove liquidity. |
intercomswap_ln_invoice_create |
Create a standard BOLT11 invoice (no hodl invoices). |
intercomswap_ln_decodepay |
Decode a BOLT11 invoice offline. |
intercomswap_ln_pay |
Pay a BOLT11 invoice. |
intercomswap_ln_rebalance_selfpay |
Best-effort inbound rebalance: create an invoice on this node and pay it from this same node. Works best with LND using allow_self_payment; routing outcome depends on available channels/routes. |
intercomswap_ln_pay_status |
Query payment status by payment_hash. |
intercomswap_ln_preimage_get |
Get a payment preimage by payment_hash (for recovery). |
| Tool | Description |
|---|---|
intercomswap_swap_ln_invoice_create_and_post |
Maker: create an LN invoice and post LN_INVOICE into swap:. |
intercomswap_swap_sol_escrow_init_and_post |
Maker: init Solana escrow and post SOL_ESCROW_CREATED into swap:. Requires taker to post ln_route_precheck_ok (swap.status) after LN invoice is posted. Fees are read from on-chain config/trade-config (not negotiated). |
intercomswap_swap_verify_pre_pay |
Taker: verify (terms + LN invoice + Sol escrow) and validate the escrow exists on-chain before paying. Also enforces safety margins: rejects if the invoice expires too soon (<60s) or the escrow refund_after is too soon (<10min). |
intercomswap_swap_ln_route_precheck_from_terms_invoice |
Taker: decode invoice and run LN route/liquidity precheck from signed TERMS + LN_INVOICE before maker escrow is created. |
intercomswap_swap_ln_pay_and_post |
Taker: pay the LN invoice and post LN_PAID into swap:. |
intercomswap_swap_ln_pay_and_post_from_invoice |
Taker: pay an LN invoice from an LN_INVOICE envelope and post LN_PAID into swap: (no manual bolt11/payment_hash copying). |
intercomswap_swap_ln_pay_and_post_verified |
Taker: verify (terms + invoice + escrow on-chain), then pay the LN invoice and post LN_PAID into swap:. Enforces the same safety margins as verify_pre_pay (invoice expiry + refund_after). |
intercomswap_swap_sol_claim_and_post |
Taker: claim Solana escrow and post SOL_CLAIMED into swap:. |
| Tool | Description |
|---|---|
intercomswap_sol_local_status |
Local-only: show whether a solana-test-validator RPC is listening on the configured localhost port (and whether it was started by this repo). |
intercomswap_sol_local_start |
Local-only: start solana-test-validator with the escrow program loaded (writes ledger/logs under onchain/). |
intercomswap_sol_local_stop |
Local-only: stop the managed solana-test-validator process. |
intercomswap_sol_signer_pubkey |
Get the configured Solana signer pubkey for this promptd instance. |
intercomswap_sol_keygen |
Generate a new Solana keypair JSON file under onchain/ (gitignored). |
intercomswap_sol_keypair_pubkey |
Get the pubkey for a Solana keypair JSON file (path must be under onchain/). |
intercomswap_sol_airdrop |
Request an airdrop (local validator/test only; mainnet will fail). |
intercomswap_sol_transfer_sol |
Transfer SOL from the configured signer to a recipient pubkey. |
intercomswap_sol_token_transfer |
Transfer an SPL token (ATA->ATA) from the signer to a recipient owner pubkey. |
intercomswap_sol_mint_create |
Create a new SPL mint where the signer is mint+freeze authority (test/dev convenience). |
intercomswap_sol_mint_to |
Mint SPL tokens from a signer-controlled mint to a recipient owner pubkey (test/dev convenience). |
intercomswap_sol_balance |
Get SOL balance for a pubkey. |
intercomswap_sol_token_balance |
Get SPL token balance for a (owner,mint) pair (ATA). |
intercomswap_sol_escrow_get |
Fetch escrow state by payment_hash (and mint). |
intercomswap_sol_escrow_init |
Initialize an escrow locked to LN payment_hash. Fees are read from on-chain config/trade-config (not negotiated). |
intercomswap_sol_escrow_claim |
Claim escrow by submitting LN preimage (recipient signature required). |
intercomswap_sol_escrow_refund |
Refund escrow after timeout (refund signature required). |
intercomswap_sol_config_get |
Get program fee config (platform config PDA). |
intercomswap_sol_config_set |
Set program fee config (admin authority required; platform fee is fixed at 10 bps). |
intercomswap_sol_fees_withdraw |
Withdraw accrued platform fees from fee vault (admin authority required). |
intercomswap_sol_trade_config_get |
Get trade fee config (per fee_collector). |
intercomswap_sol_trade_config_set |
Init/set trade fee config (fee_collector authority required; defaults to 10 bps when omitted). |
intercomswap_sol_trade_fees_withdraw |
Withdraw accrued trade fees for the configured fee_collector. |
| Tool | Description |
|---|---|
intercomswap_receipts_list |
List local trade receipts (sqlite). |
intercomswap_receipts_show |
Show a local receipt by trade_id. |
intercomswap_receipts_list_open_claims |
List trades that look claimable (state=ln_paid and preimage present). |
intercomswap_receipts_list_open_refunds |
List trades that look refundable (state=escrow and refund_after <= now). Uses local receipt data only. |
intercomswap_swaprecover_claim |
Recover: claim a stuck Solana escrow using local receipts + signer. |
intercomswap_swaprecover_refund |
Recover: refund an expired Solana escrow using local receipts + signer. |
Total tools documented: 102.
When function signatures change:
- Update this README command/tool references.
- Update
SKILL.mdguidance. - Keep
src/prompt/tools.jsandsrc/prompt/executor.jsin sync (schema vs execution).
All prompt configuration lives in a local JSON file (recommended path: onchain/prompt/setup.json), which is gitignored by default.
No environment variables are required for promptd configuration.
Generate a template:
./scripts/promptd.sh --print-template > onchain/prompt/setup.jsonEdit onchain/prompt/setup.json:
llm.base_url: your OpenAI-compatible REST API base (typically ends with/v1)llm.model: model id to usellm.api_key: optional (use""if not required)llm.call_style:openaiorfunctiongemma- if omitted, promptd auto-detects
functiongemmawhenllm.modelcontainsfunctiongemma; otherwiseopenai
- if omitted, promptd auto-detects
llm.prompt_profile:default(full IntercomSwap system/tool policy)functiongemma_minimal(lean prompt intended for FunctionGemma-style tool callers)
llm.tool_schema_profile:full(entire tool schemas)compact(default foropenai; reduced schema verbosity)minimal(default forfunctiongemma; required fields only)names_only(tool names + empty parameter schema; smallest payload)
- legacy:
llm.tools_compact(defaulttrue): maps totool_schema_profile=compact(orfullwhen set tofalse) ifllm.tool_schema_profileis not set.llm.tools_compact_keep_tool_descriptions(defaulttrue)llm.tools_compact_keep_schema_descriptions(defaultfalse)
llm.tools_select_pass(defaultfalse): optional two-pass prompting (select tool names first, then run tool-calling with only those tools).llm.tools_select_max_tools(default16)
- FunctionGemma tip: do not force OpenAI
response_formatJSON mode unless you specifically need it. - FunctionGemma runtime profile (recommended explicit config):
llm.call_style: "functiongemma"llm.prompt_profile: "functiongemma_minimal"llm.tool_schema_profile: "minimal"- omit
llm.response_formatunless required by your backend
peer.keypair: path to the peer wallet keypair file (usuallystores/<store>/db/keypair.json) so tools can sign sidechannel envelopes locally- optional sampling params:
max_tokens,temperature,top_p,top_k,min_p,repetition_penalty sc_bridge.tokenorsc_bridge.token_filereceipts.db(optional, forintercomswap_receipts_*tools)ln.*,solana.*(optional, depending on which tools you want enabled)- trade automation bootstrap (optional, defaults shown):
server.tradeauto_autostart(defaulttrue): backend trade worker starts automatically on promptd startup.server.tradeauto_channels(default["0000intercomswapbtcusdt","0000intercom"]).server.tradeauto_trace_enabled(defaultfalse): trace is off by default.server.tradeauto_autostart_retry_ms(default5000),server.tradeauto_autostart_max_attempts(default24).
Start the service:
./scripts/promptd.sh --config onchain/prompt/setup.jsonOptional server hardening (recommended if you expose promptd beyond localhost, e.g. via ngrok):
server.auth_token: requiresAuthorization: Bearer <token>on all/v1/*endpointsserver.tls: serve HTTPS instead of HTTP (providekey+certpaths underonchain/)
Run prompts:
./scripts/promptctl.sh --prompt "Show SC-Bridge info"
./scripts/promptctl.sh --auto-approve 1 --prompt "Post an RFQ in 0000intercomswapbtcusdt"If server.auth_token is set, add --auth-token:
./scripts/promptctl.sh --auth-token "<token>" --prompt "Show SC-Bridge info"Some tool outputs are sensitive (LN preimages, swap invites/welcomes). promptd will replace these values with secret:<id> handles before sending tool results back to the model. Later tool calls can pass those handles back, and the executor will resolve them server-side.
promptd also exposes NDJSON streaming endpoints for memory-safe UIs:
POST /v1/run/stream(stream prompt execution events)GET /v1/sc/stream(stream sidechannel events received via SC-Bridge)
This repo includes Collin, a local-first control center UI (prompting is only one part of it).
- Source:
ui/collin/ - Served by:
promptd(same origin as/v1/*, no CORS issues) - UI feeds are virtualized and use backscroll paging to keep the DOM/memory stable.
Important: Collin’s live sidechannel stream (/v1/sc/stream) requires a running peer with SC-Bridge enabled.
Start a peer first (or start it from Collin via the peer_* tools once promptd is running).
Collin also enforces a hard STACK READY gate for trade tools (RFQ/Offer/Bots/Swap protocol):
- peer + SC-Bridge running
sc/streamconnected- Lightning has at least one channel
- Solana signer + program config reachable
- receipts DB configured (for recovery)
For docker regtest, Collin includes a one-click Lightning bootstrap (intercomswap_ln_regtest_init) that mines, funds both LN nodes, and opens a channel.
Current Collin wallet/trading guardrails:
- Sell USDT / Sell BTC line editors show live wallet snapshots (LN liquidity, USDT atomic balance, SOL lamports).
- Posting is blocked when balances/liquidity are insufficient:
- LN route buffer included for BTC send checks.
- USDT requirement includes fee-cap headroom.
- SOL tx-fee buffer required for claim/refund/transfer paths.
- Channel Manager defaults to ACINQ on mainnet (reduces isolated topology
NO_ROUTEincidents).- Manual peer URI selection is available under an Advanced expander.
- Quick peer URI suggestions come from
intercomswap_ln_listpeers.
- Autopost bots stop automatically on insufficient-funds/liquidity errors (and stop on expiry/fill as before).
- Trade automation now runs server-side (backend worker via
intercomswap_tradeauto_*), not in browser state. Collin no longer owns client-side settlement loops. - Collin sidechannel stream processing deduplicates repeated SC events (including reconnect backlog duplicates) before inserting into the local event store to keep browser CPU/load bounded.
- dedupe uses signed envelope identity first (not seq-first), so re-sent identical envelopes with fresh seq values are dropped.
- UI stream rendering is batched (instead of per-event re-render) and dedupe-map pruning runs periodically (instead of per-event full scans) to prevent browser lockups during high-volume replay windows.
- Collin keeps invite-only
swap:*trade channels separate from rendezvous settings:- trade channels can be auto-watched for stream visibility after join/invite,
- but they are never promoted into the global rendezvous channel configuration used for stack start or Offer/RFQ broadcast.
- If backend trace shows
stopped, start/stop it directly in Collin Overview (Trade Automation Trace) or callintercomswap_tradeauto_start/intercomswap_tradeauto_stop.
Examples:
# Background peer (recommended; doesn’t require keeping a terminal open)
scripts/peermgr.sh start --name swap-maker-peer --store swap-maker --sc-port 49222 --sidechannels 0000intercomswapbtcusdt
# Foreground peer (dev convenience)
scripts/run-swap-maker.sh swap-maker 49222 0000intercomswapbtcusdtBuild the UI:
cd ui/collin
npm install
npm run buildRun the UI (via promptd):
./scripts/promptd.sh --config onchain/prompt/setup.jsonOpen:
http://127.0.0.1:9333/
Dev mode (HMR) with a built-in proxy for /v1 and /healthz:
cd ui/collin
npm run devDo not “toggle” one running instance between test and mainnet. Run two separate instances so you never mix:
- peer stores / keys
- promptd ports (also isolates Collin’s browser DB by origin)
- SC‑Bridge ports + tokens
- receipts sqlite DBs (
receipts.db) - prompt audit logs (
server.audit_dir)
Recommended conventions:
- Test rendezvous channel:
0000intercomswapbtcusdt_test - Mainnet rendezvous channel:
0000intercomswapbtcusdt
Example promptd configs (all under onchain/ so they are gitignored):
- Test:
onchain/prompt/test/setup.jsonserver.port:9333receipts.db:onchain/receipts/test/swap-maker.sqliteserver.audit_dir:onchain/prompt/audit-testln.network:regtest(orsignet)solana.rpc_url: local validator / devnet
- Mainnet:
onchain/prompt/mainnet/setup.jsonserver.port:9334receipts.db:onchain/receipts/mainnet/swap-maker.sqliteserver.audit_dir:onchain/prompt/audit-mainnetln.network:bitcoinsolana.rpc_url: mainnet RPC(s)
Collin shows an ENV indicator (TEST/MAINNET/MIXED) from intercomswap_env_get and displays the active receipts.db path so you can sanity-check before moving funds.
Run all tests after changes:
npm test
npm run test:e2eonchain/contains local wallets, node data, tokens, and other secrets/runtime state and must never be committed.progress.mdis a local handoff log and is gitignored.
Some price/RPC/API providers require auth headers (for example Authorization: Bearer ...).
This repo supports URL-prefix based header injection via one of:
HTTP_HEADERS_JSON(JSON string)HTTP_HEADERS_FILE(path to JSON file)onchain/http/headers.json(default, if present; gitignored)
Example onchain/http/headers.json:
{
"rules": [
{
"match": "https://rpc.example.com/",
"headers": { "Authorization": "Bearer YOUR_TOKEN" }
}
]
}Matching rules:
matchis a simple string prefix (or*for all URLs).- If multiple rules match, longer prefixes override shorter ones.