Message signing and verification — SIP-018 structured Clarity data signing (on-chain verifiable), Stacks plain-text message signing (SIWS-compatible), Bitcoin BIP-137 message signing, and BIP-340 Schnorr signing for Taproot multisig. All signing requires an unlocked wallet; hash and verify operations do not.
Provides cryptographic message signing for the Stacks and Bitcoin ecosystems. Four signing standards are supported:
secp256k1-recover?.m/44'/1237'/0'/0/0) by default, or from a wallet key path via keySource.bun run signing/signing.ts <subcommand> [options]
Sign structured Clarity data using the SIP-018 standard. The domain binding (name + version + chain-id) prevents cross-app and cross-chain replay attacks. Requires an unlocked wallet.
bun run signing/signing.ts sip018-sign \
--message '{"amount":{"type":"uint","value":100}}' \
--domain-name "My App" \
--domain-version "1.0.0"
Options:
--message (required) — Structured data as a JSON string. Use type hints for explicit Clarity types:
{"type":"uint","value":100} → uint{"type":"int","value":-50} → int{"type":"principal","value":"SP..."} → principal{"type":"ascii","value":"hello"} → string-ascii{"type":"utf8","value":"hello"} → string-utf8{"type":"buff","value":"0x1234"} → buff{"type":"bool","value":true} → bool{"type":"none"} → none{"type":"some","value":...} → (some ...){"type":"list","value":[...]} → list{"type":"tuple","value":{...}} → tuplestring → string-utf8, number → int, boolean → bool, null → none--domain-name + --domain-version (required together) — Flat CLI domain fields--domain (alternative) — MCP-style JSON object: {"name":"My App","version":"1.0.0"} (optional chainId)Output:
{
"success": true,
"signature": "abc123...",
"signatureFormat": "RSV (65 bytes hex)",
"signer": "SP...",
"network": "testnet",
"chainId": 2147483648,
"hashes": {
"message": "...",
"domain": "...",
"encoded": "...",
"verification": "...",
"prefix": "0x534950303138"
},
"domain": { "name": "My App", "version": "1.0.0", "chainId": 2147483648 },
"verificationNote": "Use sip018-verify with the 'verification' hash..."
}
Verify a SIP-018 signature and recover the signer's Stacks address. Provide the verification hash from sip018-sign or sip018-hash.
bun run signing/signing.ts sip018-verify \
--message-hash <verificationHash> \
--signature <rsv65BytesHex> \
[--expected-signer <address>]
Options:
--message-hash (required) — The SIP-018 verification hash (from sip018-sign/sip018-hash)--signature (required) — Signature in RSV format (65 bytes hex)--expected-signer (optional) — Expected signer address to verify againstOutput:
{
"success": true,
"recoveredPublicKey": "03...",
"recoveredAddress": "SP...",
"network": "testnet",
"verification": {
"expectedSigner": "SP...",
"isValid": true,
"message": "Signature is valid for the expected signer"
}
}
Compute the SIP-018 message hash without signing. Returns all hash components needed for off-chain or on-chain verification. Does not require an unlocked wallet.
bun run signing/signing.ts sip018-hash \
--message '{"amount":{"type":"uint","value":100}}' \
--domain-name "My App" \
--domain-version "1.0.0" \
[--chain-id <id>]
Options:
--message (required) — Structured data as a JSON string (same format as sip018-sign)--domain-name + --domain-version (required together) — Flat CLI domain fields--domain (alternative) — MCP-style JSON object: {"name":"My App","version":"1.0.0"} (optional chainId)--chain-id (optional) — Chain ID override (takes precedence over domain.chainId)Output:
{
"success": true,
"hashes": {
"message": "...",
"domain": "...",
"encoded": "...",
"verification": "..."
},
"hashConstruction": {
"prefix": "0x534950303138",
"formula": "verification = sha256(prefix || domainHash || messageHash)"
},
"domain": { "name": "My App", "version": "1.0.0", "chainId": 2147483648 },
"clarityVerification": {
"example": "(secp256k1-recover? (sha256 encoded-data) signature)"
}
}
Sign a plain text message using the Stacks message signing format. The message is prefixed with \x17Stacks Signed Message:\n before hashing (SIWS-compatible). Requires an unlocked wallet.
bun run signing/signing.ts stacks-sign --message "Hello, Stacks!"
Options:
--message (required) — Plain text message to signOutput:
{
"success": true,
"signature": "abc123...",
"signatureFormat": "RSV (65 bytes hex)",
"signer": "SP...",
"network": "testnet",
"message": {
"original": "Hello, Stacks!",
"prefix": "\u0017Stacks Signed Message:\n",
"prefixHex": "...",
"hash": "..."
},
"verificationNote": "Use stacks-verify with the original message and signature to verify."
}
Verify a Stacks message signature and recover the signer's Stacks address. Compatible with SIWS authentication flows.
bun run signing/signing.ts stacks-verify \
--message "Hello, Stacks!" \
--signature <rsv65BytesHex> \
[--expected-signer <address>]
Options:
--message (required) — The original plain text message that was signed--signature (required) — Signature in RSV format (65 bytes hex)--expected-signer (optional) — Expected signer Stacks addressOutput:
{
"success": true,
"signatureValid": true,
"recoveredPublicKey": "03...",
"recoveredAddress": "SP...",
"network": "testnet",
"message": {
"original": "Hello, Stacks!",
"prefix": "\u0017Stacks Signed Message:\n",
"hash": "..."
},
"verification": {
"expectedSigner": "SP...",
"signerMatches": true,
"isFullyValid": true,
"message": "Signature is valid and matches expected signer"
}
}
Sign a plain text message using Bitcoin message signing. Automatically selects the signing format based on address type: BIP-137 (65-byte compact signature) for legacy (1...) and wrapped SegWit (3...) addresses; BIP-322 "simple" (witness-serialized) for native SegWit (bc1q) and Taproot (bc1p) addresses. Compatible with Electrum, Bitcoin Core, and modern wallets. Requires an unlocked wallet with Bitcoin keys.
bun run signing/signing.ts btc-sign --message "Hello, Bitcoin!"
Options:
--message (required) — Plain text message to signOutput:
{
"success": true,
"signature": "abc123...",
"signatureBase64": "...",
"signatureFormat": "BIP-137 (65 bytes: 1 header + 32 r + 32 s)",
"signer": "bc1q...",
"network": "mainnet",
"addressType": "P2WPKH (native SegWit)",
"message": {
"original": "Hello, Bitcoin!",
"prefix": "\u0018Bitcoin Signed Message:\n",
"prefixHex": "...",
"formattedHex": "...",
"hash": "..."
},
"header": { "value": 39, "recoveryId": 0, "addressType": "P2WPKH (native SegWit)" },
"verificationNote": "Use btc-verify with the original message and signature to verify."
}
Verify a Bitcoin message signature (BIP-137 or BIP-322) and recover the signer's Bitcoin address. Automatically detects the format: BIP-137 (65-byte compact, hex 130 chars or base64 88 chars) for legacy/wrapped-SegWit addresses, and BIP-322 "simple" (witness-serialized, base64) for native SegWit (bc1q) and Taproot (bc1p) addresses.
bun run signing/signing.ts btc-verify \
--message "Hello, Bitcoin!" \
--signature <hexOrBase64Sig> \
[--expected-signer <btcAddress>]
Options:
--message (required) — The original plain text message that was signed--signature (required) — Bitcoin signature: BIP-137 (65 bytes as hex [130 chars] or base64 [88 chars]) for legacy/wrapped-SegWit, or BIP-322 "simple" (witness-serialized, base64) for bc1q/bc1p addresses--expected-signer (optional) — Expected signer Bitcoin address to verify againstOutput:
{
"success": true,
"signatureValid": true,
"recoveredPublicKey": "03...",
"recoveredAddress": "bc1q...",
"network": "mainnet",
"message": { "original": "Hello, Bitcoin!", "prefix": "...", "hash": "..." },
"header": { "value": 39, "recoveryId": 0, "addressType": "P2WPKH (native SegWit)" },
"verification": {
"expectedSigner": "bc1q...",
"signerMatches": true,
"isFullyValid": true,
"message": "Signature is valid and matches expected signer"
}
}
Sign a raw 32-byte digest with Schnorr (BIP-340) using the wallet's Taproot private key. Use for Taproot script-path spending, multisig coordination, or any case where you need a BIP-340 Schnorr signature over a pre-computed hash (e.g., BIP-341 sighash). Includes a blind-signing safety gate — the first call without --confirm-blind-sign returns the digest for review. Requires an unlocked wallet with Taproot keys.
bun run signing/signing.ts schnorr-sign-digest \
--digest <64-char-hex> \
[--aux-rand <64-char-hex>] \
[--confirm-blind-sign]
Options:
--digest (required) — 32-byte hex-encoded digest to sign (e.g., BIP-341 transaction sighash)--aux-rand (optional) — 32-byte hex auxiliary randomness for BIP-340 (improves side-channel resistance)--confirm-blind-sign (optional) — Set to confirm you have reviewed the digest and accept blind-signing risk. Without this flag, returns a warning with the digest for review.Output (without --confirm-blind-sign):
{
"warning": "schnorr-sign-digest signs a raw 32-byte digest...",
"digestToReview": "abc123...",
"instructions": "Review the digest above. If you trust its origin..."
}
Output (with --confirm-blind-sign):
{
"success": true,
"signature": "abc123...",
"publicKey": "def456...",
"address": "bc1p...",
"network": "mainnet",
"signatureFormat": "BIP-340 Schnorr (64 bytes)",
"publicKeyFormat": "x-only (32 bytes)",
"note": "For Taproot script-path spending, append sighash type byte..."
}
Verify a BIP-340 Schnorr signature over a 32-byte digest. Takes the digest, signature, and x-only public key, returns whether the signature is valid. Use for verifying Taproot signatures from other agents in multisig coordination.
bun run signing/signing.ts schnorr-verify-digest \
--digest <64-char-hex> \
--signature <128-char-hex> \
--public-key <64-char-hex>
Options:
--digest (required) — 32-byte hex-encoded digest that was signed--signature (required) — 64-byte hex-encoded BIP-340 Schnorr signature--public-key (required) — 32-byte hex-encoded x-only public key of the signerOutput:
{
"success": true,
"isValid": true,
"digest": "abc123...",
"signature": "def456...",
"publicKey": "789abc...",
"message": "Signature is valid for the given digest and public key",
"note": "BIP-340 Schnorr verification. Use for validating signatures in Taproot multisig coordination."
}
Sign a Nostr event object (NIP-01 format) using the wallet's Nostr key. By default the key is derived via NIP-06 (m/44'/1237'/0'/0/0) from the wallet mnemonic, producing an npub that matches NIP-06 compliant Nostr clients (e.g. Amethyst, Damus, Snort). Requires an unlocked wallet.
bun run signing/signing.ts nostr-sign-event \
--event '{"kind":1,"created_at":1700000000,"tags":[],"content":"Hello, Nostr!"}' \
[--key-source nostr|taproot|segwit]
Options:
--event (required) — Nostr event JSON object (NIP-01 format). Fields id, pubkey, and sig are computed and returned; do not include them in the input.--key-source (optional, default: "nostr") — Which wallet key to use for signing:
"nostr" (default) — NIP-06 derived key (m/44'/1237'/0'/0/0). Use this for new identities and compatibility with all NIP-06 Nostr clients."taproot" — BIP-86 Taproot key (m/86'/0'/0'/0/0). Use if you already have an existing Nostr identity on your Taproot key."segwit" — BIP-84 native SegWit key (m/84'/0'/0'/0/0). Use if you already have an existing Nostr identity on your SegWit key.Output:
{
"success": true,
"event": {
"id": "abc123...",
"pubkey": "def456...",
"created_at": 1700000000,
"kind": 1,
"tags": [],
"content": "Hello, Nostr!",
"sig": "789abc..."
},
"npub": "npub1...",
"keySource": "nostr",
"derivationPath": "m/44'/1237'/0'/0/0",
"note": "Key derived via NIP-06. npub matches NIP-06 compliant Nostr clients."
}
Key derivation note: The default "nostr" source uses NIP-06 (m/44'/1237'/0'/0/0), the standard Nostr derivation path defined in the NIP-06 spec. Agents should use this default for all Nostr interactions. The keySource override is only needed when an existing Nostr identity was previously established on a different key path.
| Standard | Prefix | Use Case | On-Chain Verifiable? |
|----------|--------|----------|---------------------|
| SIP-018 | SIP018 (hex) | Structured Clarity data | Yes (secp256k1-recover?) |
| Stacks | \x17Stacks Signed Message:\n | Auth, ownership proof | No (off-chain only) |
| BIP-137 / BIP-322 | \x18Bitcoin Signed Message:\n | Bitcoin auth, ownership proof (BIP-137 for 1.../3...; BIP-322 for bc1q/bc1p) | No (off-chain only) |
| BIP-340 | None (raw digest) | Taproot multisig, script-path spending | Yes (OP_CHECKSIG/OP_CHECKSIGADD) |
| NIP-06 (Nostr) | None (event hash) | Nostr event signing (NIP-01) | No (Nostr network only) |
bun run wallet/wallet.ts unlock)sip018-hash, both *-verify subcommands, and schnorr-verify-digest do NOT require an unlocked walletSearch for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer