How it works
BitPlan encrypts an HTML plan before it leaves your computer. BRC-100 is the interface BitPlan uses to talk to your wallet; it is not an inscription format. The plan can stay hosted as ciphertext while it changes, then become a permanent 1Sat Ordinal when it is ready.
The viewer loads ciphertext from bitplan.dev or 1Sat. In the browser, an authorized wallet or reader link unwraps the document key. Plaintext never reaches the 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.
Plan JSON
fresh random 32-byte document key
SDK AES-256-GCM
one authenticated ciphertext
wallet.encrypt
[2, "bitplan"] · keyID · self or reader
BPLN 0x02
document ciphertext + one wrapped key per reader
Only you
The wallet encrypts the 32-byte document key to itself, not the whole document. It derives that wrap 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.
You and named readers
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, version, and the publisher and reader identity keys. Access to an older 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. The plan is encrypted once with the SDK, then the wallet wraps its 32-byte document 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.
- Notify OrdFS. By default, 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;
--no-relayopts out.
Hosted drafts
A hosted draft uses the same envelope as an on-chain draft. bitplan.dev stores that ciphertext instead of writing it to the chain. CLI uploads use a wallet; browser guest starters use a throwaway reader identity. Neither needs BSV for hosting. bitplan inscribe puts the draft on chain, and the hosted link redirects to that origin forever. Stored draft objects contain only ciphertext and the public envelope header.
A hosted ID is a random 120-bit locator. It does not decrypt anything. A separate random 256-bit update secret stays in the CLI's local state. Update requests carry that bearer over HTTPS; the server stores only its hash and requires the bearer for every new version. Losing the local secret means that machine can no longer update the hosted draft.
Reader links
See sharing and access for guest links, team invitations, and the current limits of moving to wallet-only access.
A reader link contains a separate random 256-bit private key. Its public half is a normal reader slot. Its private half sits after the# in the URL, which browsers do not send in HTTP requests. Anyone with the complete link can read, so copy and share it as carefully as a password. --private removes that reader from the next version, but it cannot revoke versions already shared.
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. 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.