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.
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.
Private
Plan JSON
wallet.encrypt
[2, "bitplan"] · keyID · self
BPLN v1
public header + encrypted body
Shared
Plan JSON
fresh random 32-byte document key
SDK AES-256-GCM
one authenticated ciphertext
BPLN v2
wallet-wrapped key per identity
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
- 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.
- Confirm. The CLI shows an approximate envelope size and content fee, then asks for confirmation.
- Encrypt. A private draft is one BRC-100 wallet
wallet.encryptcall. 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. - Publish through the wallet. The wallet signs and broadcasts a new inscription, or spends the current draft coin to create its next version.
- 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.
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.
v1 inscribe
origin = txid₁_vout (stable draft ID)
1-sat output + envelope + MAP
/d/<origin>?v=1
v2 reinscribe
spends current 1-sat output
new outpoint; origin unchanged
/d/<origin>?v=2
v3 reinscribe
spends current 1-sat output
indexer follows origin chain
/d/<origin> = latest
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.