bittensormcp

Security & self-custody

Your coldkey signs locally.
I only broadcast.

Every write that goes through me is composed unsigned, signed by your key on your machine, and submitted with nothing but the signature. There is no code path anywhere in the server, the SDK, or the protocol that accepts a private key, seed, or mnemonic. This page is the exact mechanism — verifiable, not marketing.

The two-step signing protocol

What happens when your agent writes.

  1. 01

    I compose, unsigned

    Your agent calls a write tool (stake, transfer, register…). I assemble the exact substrate call and return an UNSIGNED_PAYLOAD plus an intent_id. Nothing is on-chain yet, and nothing can be — the transaction does not exist without your signature.

  2. 02

    You sign, locally

    The payload bytes are signed with your sr25519 coldkey — in your own process, your browser extension, or your agent's signer callback. The key never crosses a network boundary. The open-source @bittensormcp/sign SDK takes a signer function, never key material.

  3. 03

    I verify, then broadcast

    I check the signature against the stored payload and your account before anything leaves — then I rebuild the byte-identical call, inject your signature, and submit. What the chain sees is indistinguishable from a transaction you signed in btcli.

Blast radius

What I can and cannot do.

I can

  • → Read live chain state and stamp every answer with the block it came from.
  • → Compose unsigned transactions exactly as your agent requests them.
  • → Broadcast a transaction after your signature verifies against the stored payload.
  • → Log every write your account makes and show it back to you via bittensor_activity_log, txHash included.

I cannot

  • ✕ Sign anything. No key material exists server-side — there is nothing to steal that could move your TAO.
  • ✕ Alter a transaction after you sign. The signature covers the exact payload bytes; changing one bit invalidates it, and Step 2 replays the stored call byte-identically.
  • ✕ Replay or reuse a signature. Each intent is single-use, bound to your account, and expires after 120 seconds.
  • ✕ Accept a key even if you offered one. Neither the API nor the SDK has a parameter for seed, mnemonic, or private key.

Single-use, 120 s intents

Every unsigned payload is stored as a signing intent scoped to your account, consumed on first use, and dead after two minutes. A leaked payload is worthless without your key; a leaked signature is worthless after one use.

Rate limits & a kill switch

Writes are rate-limited per account, and you can pause all write access on your own account at any time (premiumDisabled) — re-enable only with your own signature-authenticated session.

A complete audit trail

Every write your account makes is recorded with tool, arguments, txHash and timestamp, queryable by you — and only you — through bittensor_activity_log. If it isn't in your log, it didn't happen through me.

Don't take my word for it

Verify, don't trust.

The signer is open source

The signing half — the only part that ever touches your key's process — is public at github.com/btmcp/SignSDK and published to npm as @bittensormcp/sign with provenance attestation. Its API is a signer callback: there is literally no parameter that would accept your key.

Every write is a public extrinsic

Each transaction lands on-chain with a txHash you can inspect on any Bittensor explorer. Compare the extrinsic against what your agent requested — they are the same call, because your signature would not verify over anything else.

Your history is queryable

Call bittensor_activity_log with your own account and cross-check it against the chain. The log only ever shows your own entries.

Integration detail — the full A2 flow, payload format, and runnable examples — is in llms.txt and the SignSDK README. A gentler introduction lives at /about#keys.

Writes, without
handing over the key.

Free reads, forever. Self-custody writes for 0.1 TAO a month — every extrinsic signed by you, on your machine.