How it works

BitPlan publishes encrypted HTML drafts as versioned 1Sat Ordinals. Private drafts are encrypted by the wallet. For sharing, the CLI uses the SDK to encrypt the document once and asks the wallet to wrap its key for each reader. The wallet always owns the identity keys, signs, and publishes.

BITPLANPUBLISHbitplan CLIvalidate · scan · envelopeBRC-100 walletencrypt/wrap · sign · unwrapBRC-100BSV chainencrypted 1Sat OrdinalREAD + DECRYPTindexer / OrdFSreturns ciphertextbitplan.dev viewerrenders browser plaintextpublishwallet.decrypt ↕ plaintext / key
The CLI validates and packages the draft. The wallet performs identity key operations and publishes. In the browser, the wallet decrypts a private draft or unwraps a shared document key; the SDK opens the shared payload.

bitplan.dev fetches public ciphertext from 1Sat. In the browser, the connected wallet decrypts a private draft or unwraps a shared document key. The site has no drafts database, and plaintext never reaches its server.

Encryption

A BitPlan envelope is a container. Its BPLN marker, version, and JSON header tell a reader how to open the encrypted body. The header is public and contains no secret key.

The envelope is a container around the encrypted data. It is not an encryption algorithm.

Private

The wallet encrypts the complete plan through the BRC-100 encrypt method. It derives the key from the wallet root, [2, "bitplan"], the draft's keyID, and counterparty: "self". The root key and derived key stay in the wallet. The keyID is a public label, not a key.

Shared

The CLI creates a fresh random 32-byte document key and encrypts the plan once with the @bsv/sdk AES-256-GCM implementation. The wallet then encrypts that small key for the owner and each reader. A reader's identity key selects their copy; their wallet derives the matching key with the publisher as counterparty.

The SDK gets both the document key and a fresh AES-GCM IV from the operating system's secure random generator. It stops with an error if secure randomness is unavailable. The encrypted plan also commits the exact public header. Changes made without the document key fail authentication.

What this protects

AES-GCM hides the plan and detects changes to its ciphertext. A wrong wallet, key, or counterparty cannot decrypt it. The chain still reveals the envelope size and version. Shared envelopes also reveal the publisher and reader identity keys. Access to an older shared version cannot be revoked because that inscription is permanent.

Publishing

  1. Check locally. The CLI validates the HTML and scans the document and metadata for secrets. A failure stops the upload before the wallet is called.
  2. Confirm. The CLI shows an approximate envelope size and content fee, then asks for confirmation.
  3. Encrypt. A private draft is one BRC-100 wallet wallet.encrypt call. A shared draft is encrypted once with the SDK, then the wallet wraps its 32-byte key for the owner and each reader. The wallet keeps every identity private key. The CLI builds the envelope.
  4. Publish through the wallet. The wallet signs and broadcasts a new inscription, or spends the current draft coin to create its next version.
  5. Optional relay. With --relay, the CLI sends the wallet-returned Atomic BEEF to 1Sat. 1Sat attempts to capture it for OrdFS and forwards the transaction to Arcade. The wallet publish remains authoritative.

Sharing

--share-with <identity-key> adds a reader to the next version. The recipient's wallet unwraps the document key using the publisher's public identity key. These public keys identify the counterparties; they are not the symmetric document key. BRC-42 derivation and identity-key operations stay inside each BRC-100 wallet.

The access list is public. Each reader adds only a small wrapped key, not another copy of the document. --private makes a later version wallet-only, but no transaction can revoke access to an older shared inscription.

Versions

The first inscription's origin outpoint is the draft ID. Each update spends the current 1-sat output and carries the next encrypted envelope forward. /d/<origin> resolves the latest version; ?v=n pins one version.

The first transaction creates the draft coin. Each later version spends its current output and creates a replacement carrying a new envelope; the origin outpoint remains the draft ID.

Reading

The viewer fetches and validates the encrypted envelope, then calls wallet.decrypt. For a shared draft the wallet returns the document key and the SDK decrypts the payload in the browser. The HTML stays in the browser and renders in a sandboxed iframe. The CLI equivalent is bitplan fetch <origin|url>.

bitplan list finds drafts by asking the wallet for outputs in its 1sat basket tagged type:application/x-bitplan. Local state is only a metadata cache; it contains no wallet keys.

Control and recovery

The current coin controls publishing; wallet keys control reading. Those capabilities can diverge. Sharing grants read access; it does not grant the recipient the draft coin or permission to publish the next version. The envelope does not sign authorship; the draft's origin and transaction chain establish who controls publishing.

Ciphertext is public and permanent. Encryption does not restore a delete button, which is why every upload is scanned for secrets.

Start with CLI setup, or inspect the envelope format.

Search BitPlan

Jump to a BitPlan page, docs, or the npm CLI.