Vault Sub-Accounts
Someone running a wallet app for several separate purposes, a personal account, a shop, a brand, normally ends up juggling a different seed phrase or backup for each one, which is tedious to secure and easy to lose track of. This describes a way for one single backed-up secret to safely produce as many separate, unlinkable accounts as needed, each behaving like its own independent wallet, and for all of them to be found again from that one secret alone.
Reference for an AI
Everything an assistant needs to answer questions about BRC-208 accurately, including what it depends on.
Summary
- Why
- People want several separate wallet identities for separate purposes without backing up and securing a different seed for each one.
- What
- BRC-208 is a standard for deriving, allocating, and recovering any number of independent BRC-100 wallet accounts from a single backed-up vault secret.
- How
- A wallet picks one derivation for the whole vault (BRC-157 hardened profiles for new entropy-based wallets, or a BRC-42 child under a reserved protocol for wallets with an existing root key), allocates account indices sequentially starting at 0, and on restore walks indices in order checking each for signs of use…
What this lets you do
- Derive any number of isolated accounts from one backed-up secret
- Recover every account from the master alone, with no separate account list to lose
- Refuse app requests that would leak another account's key or the vault master
- Give each account its own complete BRC-100 wallet: outputs, identities, history
- Tell apart which derivation scheme a restored backup uses before opening accounts
Written by claude-sonnet-5 from the specification text. Where the two differ, the original is correct.
The specification
Abstract
This standard defines how a wallet keeps any number of accounts under one vault master, the single secret the user backs up. Each account is a complete BRC-100 wallet with its own identity key, outputs, and history. How account roots derive from the master depends on what the master is:
- Entropy masters (BRC-157): account
nis BRC-157 profilem/0'/n'. The master never signs. New wallets SHOULD use this derivation. - Root-key masters (a BRC-75 key, an imported key): account 0 is the root key, unchanged, and account
n ≥ 1is its BRC-42 child under the reserved protocol[2, "account"], key IDaccount-n, counterpartyself. Deployed wallets keep their coins and identities.
On top of either derivation this standard defines the account layer: sequential allocation, discovery, recovery from the master alone, local naming, the requests a wallet refuses, and how a restore tells the derivations apart.
Motivation
People want separate on-chain identities for separate purposes, such as a personal wallet, a shop, an issuer and a website, without keeping a separate seed phrase for each one.
BRC-157 already derives any number of profile keys from one entropy backup, at hardened BIP-32 paths. A profile key reveals nothing about the master or about any other profile, and no BRC-100 request can reach a hardened path. That is the isolation a multi-account wallet wants. BRC-157 does not say how a wallet allocates, finds, or recovers the profiles it has used, so two wallets restoring the same backup can show different accounts.
Many deployed wallets also hold a single root key that is not BRC-157 entropy:
- a BRC-75 key, which is SHA-256 of the BIP-39 seed;
- a key recovered from an older HD wallet;
- a raw private key imported without any mnemonic.
These wallets have no BIP-32 master node, so they cannot derive BRC-157 profiles, and moving them to a new root would move every coin and identity they hold.
This standard gives both kinds of wallet the same account layer. It uses BRC-157 profiles wherever the master is entropy, and a BRC-42 derivation from the root key wherever it is not.
Specification
The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY" are to be interpreted as described in RFC 2119.
Vault master
The vault master is the one secret from which every account of a vault derives. It is either:
- BRC-157 entropy, recovered from its mnemonic or its BRC-140 shares as BRC-157 specifies; or
- a root key: a secp256k1 private key whose public key was the wallet's BRC-100 identity key before any account existed. This standard does not constrain how the root key was produced.
Each vault has exactly one derivation, profile for entropy masters and self-child for root-key masters. A wallet MUST record the derivation with the vault and MUST NOT change it: switching derivations moves every account to new keys.
New wallets SHOULD generate BRC-157 entropy (32 bytes, 24 words) and use the profile derivation.
Account roots
Every account root is a secp256k1 private key and signs with ECDSA. The account's identity key is the compressed public key of its account root. From then on the account root is an ordinary BRC-100 root key. Every key the wallet derives for that account, including its baskets, its certificates and any BAP identity, is derived from the account root alone. Accounts do not share outputs, actions or permissions.
profile derivation
Account n ≥ 0 is BRC-157 profile n:
const accountRoot = HD.fromSeed(Mnemonic.fromEntropy(entropy).toSeed())
.derive(`m/0'/${n}'`).privKey
Account 0 is the BRC-157 root key (m/0'/0'). The entropy key, the seed and the BIP-32 nodes above the profiles MUST NOT be used as an account, to sign, or to encrypt. The seed uses the empty BIP-39 passphrase, as BRC-157 requires by default.
self-child derivation
Account 0 is the root key. Implementations MUST NOT derive account 0, and MUST NOT change the root key when accounts are added.
For an integer n ≥ 1, the account root is the BRC-42 child of the root key, derived with the BRC-43 invoice number 2-account-account-n:
| Field | Value |
|---|---|
| Security level | 2 |
| Protocol name | account |
| Key ID | account-n in decimal, with no leading zeros (account-1, account-2, …) |
| Counterparty | self |
In the BSV TypeScript SDK:
const accountRoot = new KeyDeriver(rootKey)
.derivePrivateKey([2, 'account'], `account-${n}`, 'self')
Reserved requests
The protocol name account is reserved for the wallet at every security level, as BRC-44 reserves names beginning with admin. A wallet implementing this standard, whatever its derivation:
- MUST refuse every application request whose
protocolIDnamesaccount. The name is compared after the BRC-43 normalisation (lowercase, surrounding whitespace removed). This coversgetPublicKey,createSignature,verifySignature,encrypt,decrypt,createHmac,verifyHmac, andrevealSpecificKeyLinkage. - MUST refuse
revealCounterpartyKeyLinkageandrevealSpecificKeyLinkagewhen the counterparty is the active account's own identity key orself, under any protocol. Counterparty linkage with itself yields the BRC-42 offset of everyselfchild of the account root, and specific linkage yields one child's offset. Aselfchild is its root plus its offset, so an offset together with that child's private key gives up the root. Any key the wallet exports as aselfchild (aself-childaccount root, a server key, a BAP signing key) is enough for that. - MUST refuse these requests without a permission prompt. A user cannot meaningfully consent to handing out another account's key or its own root.
Under self-child derivation, rule 1 is what keeps accounts apart. Without it, an application approved on account 0 could ask for a signature by [2, "account"], account-1, self over a transaction sighash and spend account 1. Rule 1 also stops the application linking account identity keys to one vault through getPublicKey. Under profile derivation no BRC-100 request reaches another account, and rule 1 keeps applications behaving the same against every wallet. Rule 2 protects each account's own root under both derivations.
Allocation and naming
Implementations MUST allocate indices in order. Account 0 exists from the start under both derivations, and new accounts take 1, 2, and so on. A new account takes one more than the highest index ever allocated under the vault master. An index is its key: implementations MUST NOT reassign an index to a different account, including after the user hides or archives one. After recovery, the highest discovered index counts as the highest allocated. An index allocated before recovery but never used holds no state, so handing it out again is harmless.
Account names are local metadata. They MUST NOT affect derivation. A wallet SHOULD keep { index, name, identityKey } for each account, keyed by account 0's identity key.
Recovery
The vault master alone MUST be sufficient to recover every account. No account list, name or other backup may be required to find an account or derive its keys.
- Recover the vault master by its usual means (BRC-157 mnemonic or shares, BRC-75 mnemonic, imported key) and determine its derivation (next section). Account 0 is now known.
- Discover the other accounts. Starting at index 1, derive each account's identity key in order and check it for use: any record the wallet would itself rely on to recover that account. Examples are a history backup stored for the identity key, or messages waiting for it at its messagebox. Requests to those hosts are signed by the candidate account's own root key, so no host learns any key.
- An index is used when a record exists, and unused when every source answers that none does.
- A wallet MUST NOT stop before five consecutive unused indices. Every index up to the highest used one is an allocated account, because allocation is sequential.
- When a source cannot answer, the wallet MUST NOT treat the index as unused. It keeps the accounts found so far and resumes discovery from that index later.
- Derive each account root as above and open it as its own wallet. An account that opens with empty local state recovers its history exactly as account 0 does (for example, from its history backup), then reconciles with the chain.
Account names are not recoverable from the vault master. A wallet MAY back them up beside each account's state; otherwise discovered accounts take default names.
Telling the derivations apart
A backup does not always say what it is. The same 12 to 24 words are BRC-157 entropy, a BRC-75 phrase, or the seed of an older HD wallet. A key reconstructed from BRC-140 shares, or typed in as an emergency key, is either BRC-157 entropy or a root key. Each reading opens different accounts. A wallet restoring a backup whose derivation it does not know:
- MUST open the reading for which this device already holds local state.
- Otherwise MUST check account 0 of each reading for use, as discovery does. Coins at account 0's own address, and a handle bound to its identity key, MAY also count. The reading whose account 0 is used wins. If several are used, the wallet's default for that kind of backup wins among them.
- When no reading is used and any source could not answer, the wallet MUST NOT guess. Opening the wrong reading shows an empty wallet over the real one. It reports the failure and lets the user retry.
- When every source answered and no reading is used, nothing is at stake, and the wallet opens its default reading for that kind of backup. A 24-word phrase without a passphrase SHOULD default to BRC-157.
The wallet then records the derivation it opened, so later unlocks never ask again. A wallet that copies a vault to another device (for example, a sealed recovery copy) MUST carry the derivation with the master.
Test vectors
profile derivation
The BIP-39 test-vector entropy 7f7f…7f (32 bytes), whose phrase is legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title, with the empty passphrase:
| n | Path | Account root | Identity key |
|---|---|---|---|
| 0 | m/0'/0' | 4303939e04ef918fa573d990df9897c7cf59e12820ec23b9892ce2c482d8aea0 | 03bc1b7e076b075a9c159c0b2a66ad7589585ade3a097c802f343076914c3b0586 |
| 1 | m/0'/1' | 2f7c7332643e37919578e6dde41ac248cbeaf25729e61b5c03c877d19893be6b | 03c9e3d39da57af25878ddd2e434f7cbbaa14cbed08aa208ef5ae029a1c6a632d9 |
| 2 | m/0'/2' | 1ed4d12cbe20a087ac5a96a654f46f78dc72553bde92259d1251419f36051cfc | 02bda138d95a4ac8dba67198700b3eeb0cf87710a792425d5e5f3a4e414d32a8d1 |
Account 0 of the 12-word phrase in BRC-157's worked example is BRC-157's own vector, 27e442c8015fc055789d6628f3b30461e8b2598aff74dc87ceef00dd8e670e55.
self-child derivation
Root key, the BRC-75 example key:
root key 1ad0895dd317163f0e83499c30bc593dbcc54cad96a5f57b065ce9f700513250
identity key 021c2361fa1c39e21422b1374c2a08106f99b5425ada71f46f55b8e8e9d4a932db
| n | Invoice number | Account root | Identity key |
|---|---|---|---|
| 0 | — | 1ad0895dd317163f0e83499c30bc593dbcc54cad96a5f57b065ce9f700513250 | 021c2361fa1c39e21422b1374c2a08106f99b5425ada71f46f55b8e8e9d4a932db |
| 1 | 2-account-account-1 | ba883e48fc890f89b6fee2c7f7f2ff727468c8bc525f364511ecbd5d90d619e5 | 0269b9208f87d9aefe02f7a49a5d6bd10621165d3c768755a8e2da7989fe0404fc |
| 2 | 2-account-account-2 | 3b4cd297156d6973c51cef7913da558d675936c2d5b5483072712dd7a3f7d55d | 0329e91b2bf9109a829e8e8217df43e6e793fa32ab45a13cf6387e85ba05e4ae4d |
The same 32 bytes read as BRC-157 entropy open different accounts, which is why a restore has to tell the readings apart.
Security considerations
- The vault master controls every account. Anyone who holds it holds them all. That is the purpose of one backup and one unlock, and wallets SHOULD say so where accounts are created.
- How independent an account key is depends on the derivation.
profile: account roots are hardened BIP-32 children. One account key, together with anything the wallet reveals, gives nothing about the master or any other account. A wallet MAY export one account's key, for example to hand that account to someone else, without exposing the rest.self-child: account rootnis the root key plus a BRC-42 offset, and only the root key can compute that offset. One exported account key plus that offset reveals the root key, and with it every account. The reserved-request rules exist to keep the offset unreachable. A wallet that exports such an account key MUST NOT describe it as isolated from the vault.- Either way, the vault master can always re-derive an account it has handed over. Exporting an account key never revokes the vault's copy; a real transfer of ownership moves the account's value and identities to keys the recipient controls.
- Rotation.
- Account roots cannot be rotated in place. An account whose key is exposed is retired: its funds move to a new index, and the old index stays allocated (discovery still finds it).
- Rotating the vault master replaces every account. A wallet that does so must move each account's value and identities to the new master's accounts; nothing carries over automatically.
- Keys under an account root rotate independently of this standard, for example a BAP signing key moving from
identity-Ntoidentity-N+1. But rotation does not change a retired key's offset: an old exported key plus its offset still gives up the account root. That is why rule 2 refuses linkage outright rather than per key.
- Signing. Account roots sign with ECDSA. Nonces MUST be deterministic per RFC 6979, keyed by the signing key and message, as the BSV SDKs already do; BRC-100 offers no caller-supplied nonce. Account roots under one master are related keys, but each draws its nonces from its own key, so no two of them share one.
- One account, one device. A restored vault can be open on several devices, and two installs that open the same account are two spenders of one key: each builds transactions from its own copy of the account's state, and they double-spend each other. Outputs that only one install's state records stay invisible to the other. Sequential allocation also races: two devices that each create an account at once pick the same index, which is the same key. A wallet that lets several devices use one vault SHOULD keep each account on one device at a time and SHOULD reserve a new index with a conditional create at a store all of its devices share before using it. BRC-249 defines one way to do both. The vault master can still spend every account from anywhere, so this coordinates the owner's own devices and protects nothing against a stolen backup.
- Discovery is visible to the hosts it queries. A host asked about several identity keys from one client in a short time can infer that they share a vault, and restore probing does the same for each reading of a backup. Wallets SHOULD probe only hosts the user already relies on, and only during recovery.
- Accounts are unlinkable to outsiders. Profile keys and BRC-42 children of
selflook like unrelated public keys. No application can obtain the linkage, and the wallet MUST NOT disclose which accounts share a vault to an application.
Rationale
- Why BRC-157 profiles for new wallets? Hardened derivation keeps accounts apart by construction rather than by refusing requests, the master never acts as a wallet, and any wallet that implements BRC-157 derives the same keys from the same backup.
- Why keep
self-childat all? Root-key wallets have no BIP-32 master node, and their root key is already account 0 with coins and identities on it. Deriving from the root key adds accounts to those wallets without moving anything. - Why a BRC-42 child of
selfand not a new KDF for root keys? Every BRC-100 wallet already implements the derivation, it needs no new primitive, and the invoice number cannot collide with any other protocol. BRC-43 names contain no hyphens, so2-account-account-narises only from this protocol. - Why
accountand not a name under BRC-44'sadminprefix? Theself-childderivation was deployed before this specification. Changing the name would move every deployed account root and strand its coins. Reserving the name explicitly gives the same protection. - Why record the derivation instead of always probing? Probing needs the network and reveals the vault's keys to the hosts asked. A recorded derivation makes every unlock after the first local and certain.
Non-goals
- Moving value between accounts. A transfer between accounts is an ordinary payment.
- Migrating a vault from one derivation to the other. That is a move of every coin and identity to a new vault.
- A second identity hierarchy. Each account's identity is its account root, and BAP, certificates and handles attach to it exactly as they would to any BRC-100 root.
- Same-identity device synchronisation. Each account syncs and backs up like any other wallet.
Implementations
- HandCash Desktop and Mobile create BRC-157 vaults (24 words) and use the
profilederivation for them. Vaults created earlier keep theself-childderivation; account 0 keeps its historical wallet database, and accountn ≥ 1uses a database with an-a<n>suffix. Both derivations share one account layer, and the BRC-100 bridge refuses the reserved requests. - After unlock, HandCash discovers accounts the device does not list. It checks each candidate for a history backup and for waiting messagebox mail, signing as that account. An account that opens empty restores from its own history backup before reconciling with the chain.
- HandCash keeps each account on one install. A new account's index is reserved with a create-only, account-signed record beside its history backup, and an account moves to the device that acts on it: the holder releases after uploading its history, or, if it does not answer, is displaced and hands over what it recorded (BRC-249).
- On restore, HandCash reads a phrase as BRC-157, BRC-75 and the older HD scheme, and a key as BRC-157 entropy and as a root key. It checks account 0 of each reading for a history backup, mail, coins at its address and a bound handle, and stops if a host cannot answer. Sealed recovery copies for paired devices carry the derivation.
References
- BRC-42: BSV Key Derivation Scheme (BKDS)
- BRC-43: Security Levels, Protocol IDs, Key IDs and Counterparties
- BRC-44: Admin-reserved and Prohibited Key Derivation Protocols
- BRC-75: Mnemonic For Master Private Key
- BRC-100: Wallet-to-Application Interface
- BRC-140: Threshold Key Sharing and Backup via Shamir's Secret Sharing Scheme
- BRC-157: Entropy-Rooted Backup and Recovery with Mnemonics and Backup Shares
- BRC-249: Vault Account Holding Across Devices (draft)