status.md
Peach — Project Status
Generated: 2026-04-19
Version: 1.0.4
Type: Chrome Extension (Manifest V3)
Build Status
| Check | Status |
|---|---|
| TypeScript | ✅ Passing (tsc --noEmit) |
| Unit Tests | ✅ 6/6 passing (vitest) |
| Vite Build | ✅ Built (dist/) |
| Extension ID | peach-browser |
Architecture
src/
├── background/ Service worker (vault + message handling)
│ ├── entry.ts Entry point
│ ├── aes.ts Background-only AES helpers to avoid popup bundle leakage
│ ├── vault.ts Vault CRUD + crypto ops (in-memory key)
│ ├── messages.ts Message handler bridge
│ ├── clipboard.ts Auto-clear clipboard after 30s
│ └── alarms.ts Auto-lock after 15 min idle
├── content/ Injected into all pages
│ ├── index.ts Detects forms, injects 🍑 icon
│ ├── detect.ts Field detection (username/password)
│ ├── fill.ts Field filling via setNativeValue
│ ├── inject.ts Autofill selector UI (shadow DOM)
│ └── save.ts Detects form submits, prompts to save
├── popup/ Extension popup UI (Solid.js)
│ ├── App.tsx Root component, view router
│ ├── views/ Lock, Vault, Entry, Add, Settings, Import, Export, Generator
│ └── components/ TOTP, Ring, Pip, Generator
├── crypto/ Zero-knowledge crypto
│ ├── argon2.ts Key derivation (Argon2id via WASM)
│ ├── aes.ts AES-256-GCM encrypt/decrypt
│ ├── totp.ts TOTP generation (otpauth)
│ └── crypto.test.ts
└── shared/ Shared types, domain extraction, import parsers
Feature Inventory
Vault Core
- [x] Create vault with master password (min 8 chars)
- [x] Unlock vault (Argon2id → AES-GCM)
- [x] Lock vault (zero key from memory)
- [x] Auto-lock on idle / alarm
- [x] Popup/background message timeout path for service worker unavailability
- [x] Reopen popup shows unlock or active vault instead of falling back to first-run create state
- [x] Detail-first PeachV2 popup shell with slide-in overlays for list, settings, new entry, and generator
Entry Management
- [x] Add entry (title, username, password, URL, notes, TOTP)
- [x] Edit entry
- [x] Soft delete entries (
deletedAt) with filtered active views - [x] Search/filter vault entries
- [x] Copy username / password (with visual/audio feedback)
- [x] Clipboard auto-clear after 30s
- [x] Stable
createdAt/modifiedAttimestamps with legacy migration
Autofill (Content Script)
- [x] Inject 🍑 icon on detected password fields
- [x] Domain-filtered entry matching (eTLD+1)
- [x] Shadow DOM selector popup on icon click
- [x] Fill username + password on selection
- [x] Field visibility check before fill
Save Detection (Content Script)
- [x] Intercept form submits
- [x] Detect username + password fields
- [x] Show save/update banner (shadow DOM)
- [x] Create new entry or update existing
Import / Export
- [x] CSV import (PapaParse)
- [x] 1Password JSON import
- [x] Encrypted JSON export
Password Generator
- [x] Generate random passwords
- [x] Use in new entry flow
TOTP
- [x] Parse otpauth:// URIs
- [x] Display 30s countdown timer in entry view
UI / Visual System
- [x] Self-hosted popup fonts (
Cormorant Garamond,IBM Plex Mono,Inter) inpublic/fonts/ - [x] Static pre-rendered lock screen HTML in popup entrypoint before Solid mounts
- [x] Archive-style chrome, ghost title, mono field rows, bottom search bar, and overlay list
Crypto Implementation
| Concern | Implementation |
|---|---|
| Key derivation | Argon2id (hash-wasm) — 3 iterations, 64MB memory |
| Encryption | AES-256-GCM — fresh nonce per write |
| Auth tag | 128-bit GCM tag, stored alongside ciphertext |
| AAD | Version byte 0x01 prevents downgrade |
| Key zeroing | Uint8Array.fill(0) on lock |
Test Status
TEST_CHECKLIST.md has 22 manual tests defined, 0 marked complete.
Recommended manual tests before daily driving:
- Vault create/unlock/lock cycle
- Entry CRUD + persistence across locks Includes popup close/reopen while the background may still be alive or already suspended
- Autofill on github.com (domain filter, manual trigger, visible fill)
- TOTP display and countdown
- Password generator
- Import CSV / Export encrypted backup
- Clipboard auto-clear (30s wait)
- Auto-lock on idle
- Save credential detection on form submit
- Wrong password → shake animation, clean error
Release blockers to verify before daily driving:
- [ ] No wrong domain filled
- [ ] No silent autofill (icon click required)
- [ ] No fill into hidden fields
- [ ] Vault locks cleanly (key zeroed)
- [ ] Import/export round-trips correctly
- [ ] No console.log in crypto paths
Known Gaps
| Gap | Status |
|---|---|
| HIBP breach check | Defined in types (breach field) but not implemented |
| Backup restore test | Checked as release blocker, not automated |
| Unit tests for content script | None |
| Unit tests for vault CRUD | Basic create + soft-delete coverage added; broader CRUD coverage still missing |
| E2E / Playwright tests | Manual checklist still primary, but test-extension.mjs now exercises extension loading, vault creation, entry save, and popup reopen persistence |
Dependencies
| Package | Purpose |
|---|---|
hash-wasm |
Argon2id WASM implementation |
otpauth |
TOTP code generation |
papaparse |
CSV import |
tldts |
eTLD+1 domain extraction |
webextension-polyfill |
Cross-browser extension API |
solid-js |
Popup UI framework |
vite-plugin-solid |
Solid.js Vite integration |
@crxjs/vite-plugin |
CRX packaging for Vite |
vitest |
Unit testing |
Security Considerations
- Vault is stored encrypted in
chrome.storage.local - Master password never stored — derived key held in memory only while unlocked
- Service worker restart = vault locks (expected MV3 behavior)
- Background bundle is isolated from popup-only chunks so MV3 service worker startup no longer crashes on
documentaccess - Popup boot now waits for
HAS_VAULT/IS_UNLOCKEDbefore choosing create vs unlock, preventing false first-run screens after reopen - Popup fonts are now self-hosted instead of loaded from Google Fonts at runtime
- Content script runs on all URLs but autofill requires explicit user click
- Shadow DOM used for selector/banner to avoid page CSS conflicts
Build Instructions
npm run dev # Dev server with hot reload
npm run build # Production build → dist/
npm run typecheck # TypeScript validation
npm test # Unit tests (vitest)
To load in Chrome: chrome://extensions → Developer mode → Load unpacked → dist/