# 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` / `modifiedAt` timestamps 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`) in `public/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: 1. Vault create/unlock/lock cycle 2. Entry CRUD + persistence across locks Includes popup close/reopen while the background may still be alive or already suspended 3. Autofill on github.com (domain filter, manual trigger, visible fill) 4. TOTP display and countdown 5. Password generator 6. Import CSV / Export encrypted backup 7. Clipboard auto-clear (30s wait) 8. Auto-lock on idle 9. Save credential detection on form submit 10. 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 `document` access - Popup boot now waits for `HAS_VAULT` / `IS_UNLOCKED` before 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 ```bash 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/`