Encrypted shared secrets store
Status: Runnable from source for startup smoke; Deferred/incomplete integration for sharing and revocation.
examples/password-manager is a Vite, React, and Yjs reference application. Its typechecked production build and strict Chromium startup smoke test pass. It has no acceptance test proving two-user sharing, invitation delivery, restart recovery, or revocation. A vault lasts only for the current browser session; reopening one after a reload is tracked in issue #534.
Packages are unpublished. From a repository checkout prepared with the quick start, run:
Build and smoke-test the application:
yarn buildyarn workspace @peerborne/password-manager buildyarn test:e2e:password-managerTo run the Vite development server separately:
yarn workspace @peerborne/password-manager startThe smoke test proves startup only, not operational sharing.
Required key material
Section titled “Required key material”A deployable application must generate, authenticate, persist, restore, and back up separate keys:
- a stable ECDSA P-384 key pair for identity signing and ACL membership;
- a stable ECDH P-256 KEM key pair whose private key permits
deriveBits, for encrypted BeeKEM Welcomes; - document epoch keys and membership state needed for recovery.
The application, not Peerborne, owns identity binding and recovery. Exchange the recipient’s signing public key and raw 65-byte SEC1-uncompressed KEM public key over an authenticated out-of-band channel. A pasted, unauthenticated key is vulnerable to substitution.
Correct onboarding sequence
Section titled “Correct onboarding sequence”Status: Illustrative core sequence; not an end-to-end application recipe. The direct document API has the necessary shape:
const kemKeyPair = await crypto.subtle.generateKey( { name: 'ECDH', namedCurve: 'P-256' }, true, ['deriveBits'],);
await recipientDocumentRef.setKemKeyPair(kemKeyPair);const recipientKemRaw = recipientDocumentRef.getKemPublicKeyRaw();
await ownerDocumentRef.addReader(recipientIdentityPublicKey, recipientKemRaw);await ownerDocumentRef.addWriter(recipientIdentityPublicKey);Each participant must call setKemKeyPair on the relevant document ref before receiving Welcomes or administering BeeKEM membership. Add the recipient as a reader with addReader(identity, kemRaw) before granting writer status; addReader requires the KEM bytes.
The React wrapper’s controls forward addReader(user, kemRaw) and setKemKeyPair(keyPair). The password-manager example generates a KEM key pair at login, installs it on each secret’s document ref, and shows its public half on the Settings page. Its permissions table requires a new member’s signing public key and raw KEM public key, both base64, before any membership change, so the owner sends a recipient-sealed Welcome. Its Chromium test promotes a reader added with a generated KEM key to editor in one browser. The example has no recipient flow that opens the shared secret afterwards, and its KEM key pair does not survive a reload, so no two-browser test proves that a second identity receives the Welcome and decrypts the document.
Safer implementation checklist
Section titled “Safer implementation checklist”Status: Deferred/incomplete integration. Prefer completing and testing these items over copying a runnable vault snippet:
- Persist and restore signing keys and P-256 KEM keys without logging or exposing private material.
- Bind both public keys to the intended human through an authenticated channel.
- Install KEM state on every opened document before invitation handling.
- Add a reader with signing identity plus raw KEM key; only then add writer rights if needed.
- Confirm the recipient decrypts and edits in a two-browser, two-identity test.
- Test dropped, replayed, delayed, and out-of-order Welcome and PathUpdate messages.
- Test restart before and after invitation, removal, and key rotation.
- Provide explicit backup, export, recovery, and destructive-loss UX.
- Reconcile the per-user index and secret document after partial dual-write failure.
BeeKEM reader mappings, tree state, and cached Welcomes have important memory-only paths. PathUpdate delivery is best effort, and restart, missed-update, multiwriter, and complete revocation behavior remain unresolved. Removing a member cannot erase plaintext or old keys they already copied. See Security and Limitations.