Done on 2026-09-23. What was built, and where it differs from this plan, is in the roadmap handoff note.
Branch: web-wallet/vault-lock, created from seedelf-web-wallet at b3e9383 (after PR #248). It ends with a PR back into seedelf-web-wallet.
git checkout web-wallet/vault-lock && git pull. If seedelf-web-wallet has moved since, merge it in.wasm32-unknown-unknownclang and llvm-ar-18wasm-bindgen-cli 0.2.128Build and test commands are in extension/README.md.
When the chunk is done, a user can:
The chunk-4 Wallet core check screen is removed. Balances come in chunk 6.
| Decision | Suggestion | Notes |
|---|---|---|
| Auto-lock default | 15 minutes | Lace defaults to “never”. A settings screen can come later. |
| Password rule | At least 12 characters, no composition rules, with a strength hint | The CLI requires 14 characters with character classes. |
| QR code for the receive address | Later (chunk 6 or 11) | Copy is enough for now. |
In seedelf-crypto::derivation, exposed through seedelf-wasm. The frozen v1 derivation is not touched.
phrase_to_entropy(phrase) -> Vec<u8> and entropy_to_phrase(bytes) -> String.
parse_phrase: 12, 15 or 24 words, checksum, and normalization.wordlist(): the English BIP39 list, for autocomplete (bip39::Language::English.word_list()).phraseToEntropy, entropyToPhrase, bip39Wordlist.seedelf-crypto/tests/vectors/seedelf_key_v1.json.SBV1): extension/src/background/vault/packages/lib/core/src/secret-box/{constants,format,kdf,seal,open}.ts.
vault/LICENSE-APACHE-2.0).emip3.ts).@noble/hashes: argon2idAsync, randomBytes@noble/ciphers: chacha20poly1305"SBV1" ‖ salt(32) ‖ nonce(12) ‖ ciphertext ‖ tag(16).Uint8Array (via TextEncoder) and zeroed after use.chrome.storage.local under seedelf.vault, as { version: 1, blob: <base64>, createdAt }.argon2 crate in WASM behind the same deriveKey interface.argon2-cffi plus cryptography’s ChaCha20Poly1305, in a scratch venv.States: no-wallet → unlocked (after create or restore) ⇄ locked.
Unlock:
SeedelfKey and CardanoAccount (account 0), and keep them in worker memory.chrome.storage.session, so a restarted worker can re-derive the keys without the password. See architecture.md, Staying unlocked across restarts.Worker start-up:
chrome.storage.session holds entropy → unlocked.locked.no-wallet.Lock: free() the WASM objects, clear chrome.storage.session, and zero the buffers.
Auto-lock:
chrome.alarms, because worker timers don’t survive restarts.activity pings while the user interacts.lastActivity in session storage, and an alarm checks it every minute.Unlock back-off:
packages/contract/authentication-prompt/src/store/unlock-backoff.ts.failedAttempts and lastFailureAt in chrome.storage.local, and reject early attempts. The UI only shows the countdown.Lace reference: packages/contract/app-lock/src/store/{state-machine,side-effects}.ts.
Manifest: add "permissions": ["storage", "alarms"], and update tests/manifest.test.ts.
Create flow: the worker generates the phrase and returns it for display. It writes the vault only after the user confirms the phrase.
extension/src/shared/rpc.tsReplace preview with:
| Request | Payload | Result |
|---|---|---|
status |
none | { state, network, networks, version } |
generate-phrase |
none | { phrase } |
create-wallet |
{ phrase, password } |
writes the vault and unlocks |
restore-wallet |
{ phrase, password } |
writes the vault and unlocks |
unlock |
{ password } |
OK, or { retryAfterMs } |
lock, activity |
none | none |
account |
none | { receiveAddress, stakeAddress, seedelfPublicValue }, only when unlocked |
wordlist |
none | the BIP39 list. The UI doesn’t load WASM, so it fetches the list once and caches it. |
reset-wallet |
none | deletes the vault. The UI requires a typed confirmation first. |
extension/src/ui/validatePhrase’s reason when the phrase is wrong.packages/lib/ui-toolkit/src/design-system/organisms/mnemonicTextInput/.packages/lib/ui-toolkit/src/design-tokens/theme/dark.ts). Otherwise it waits for chunk 11. Take the look only, not Lace’s brand assets or fonts.Vitest:
chrome.storage fake and the real WASM:
Playwright:
addr_test1….CI already runs Vitest and Playwright.