# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. **Read [`CONSTRAINTS.md`](./CONSTRAINTS.md) before starting work.** It's the enforced quality/security bar (types, lint, format, IPC/vault boundary, secrets/vuln scanning status, speed budget). Never weaken a rule in it to make a change pass — fix the change, or edit CONSTRAINTS.md in its own commit with a stated reason. ## What This Project Is **Notvex** is a cross-platform Electron desktop app for secure, fully-local encrypted notes. All data lives in a single SQLCipher-encrypted SQLite database — no account, no server, no network. Core crypto stack: Argon2id (KDF), XChaCha20-Poly1305 (note encryption), SQLCipher (AES-256 database), BIP39 recovery key, optional key-file second factor. ## Commands ```bash pnpm dev # Start Electron + Vite dev server pnpm build # Production build (all processes) pnpm build:win # Windows installer pnpm build:mac # macOS installer pnpm build:linux # Linux installer pnpm typecheck # Type-check main + preload + renderer pnpm lint # ESLint (zero warnings allowed) pnpm lint:fix # ESLint with auto-fix pnpm format # Prettier format (TS, CSS, JSON) pnpm format:check # Check formatting without writing pnpm validate # typecheck + lint + format:check pnpm depcruise # IPC/vault boundary check (.dependency-cruiser.cjs) pnpm test # Vitest, all tests (test:watch for watch mode) pnpm test: # One folder under tests/: main, vault, db, ipc, app, preload, renderer pnpm test:coverage # Vitest with coverage pnpm doctor # react-doctor scan (doctor:staged for staged files only) pnpm check:fast # typecheck + lint — edit loop pnpm check:task # validate + depcruise + lint:commits + test — run before calling work done pnpm check:full # check:task + coverage — review/CI ``` `pnpm check:task` is the before-done gate. Gate tiers, speed budget and coverage floor are in [`CONSTRAINTS.md`](./CONSTRAINTS.md). ## Architecture The project uses electron-vite with three separate build targets. Directories are described by responsibility — read the tree for their current contents. **`src/main/`** — Electron main process (Node.js backend) - `index.ts` — Entry point: app lifecycle (single-instance lock, `.nvx` file association), delegates window creation to `window.ts` - `window.ts` — Creates the `BrowserWindow` (`contextIsolation`/`nodeIntegration` config lives here), applies `setContentProtection`, triggers vault lock on suspend/lock-screen/blur/minimize - `ipc-handlers.ts` — Defines all IPC handlers; the only communication bridge to the renderer - `vault/` — All cryptographic operations and vault orchestration (KDF, memory locking, BIP39 recovery, key container, backups) - `db/` — SQLite/SQLCipher queries and schema migrations - Remaining top-level modules are single-purpose main-process services (prefs, updater, file opening, clipboard, renderer/URL guards) **`src/preload/index.ts`** — Exposes `window.notvex` API surface to the renderer via `contextBridge`. Any new IPC channel must be declared here. **`src/renderer/src/`** — React 19 frontend - `views/` — Top-level screens (setup, unlock, main app, post-recovery reset) - `components/` — Feature components, grouped into subfolders by feature; `components/ui/` holds shadcn/ui primitives (see UI Rules) - `hooks/` — Custom React hooks - `store/` — Zustand stores (vault state, UI state, preferences) - `lib/ipc.ts` — Typed IPC client (renderer side); the renderer's only route to main - `lib/` — Other renderer utilities, including the CodeMirror 6 markdown editor (`lib/editor/`) **`src/shared/types.ts`** — TypeScript types shared across all three processes. **`tests/`** — Vitest tests, one folder per area mirroring `src/`: `tests/main/{vault,db,ipc,app}/`, `tests/preload/` and `tests/eslint-rules/` run in node, `tests/renderer/` in jsdom. One file per topic, not per module. Never colocate tests in `src/`. Placement and typecheck rules: [`CONSTRAINTS.md` § Test file location](./CONSTRAINTS.md#test-file-location). ### IPC Pattern All renderer↔main communication goes through IPC: 1. Define handler in `src/main/ipc-handlers.ts` 2. Expose channel in `src/preload/index.ts` via `contextBridge` 3. Call from renderer via `src/renderer/src/lib/ipc.ts` ### Vault Files on Disk - `vault.nvx` — SQLCipher-encrypted SQLite database (notes + tags) with metadata, recovery-key-encrypted master key, salt, settings - `userData/vault-backups//` — copies of a vault taken before a header/schema migration; the newest 3 per vault are kept (`vault/backups.ts`) --- ## Code Style - **Prettier:** no semicolons, single quotes, 2-space indent, 100-char line width, `prettier-plugin-tailwindcss` for class ordering - **Path aliases:** `@/*` → `src/renderer/src/*` in the renderer; `@main/*` → `src/main/*` in the main process; `@shared/*` -> `src/shared/*` - **Tailwind CSS 4** — config lives in CSS (`src/renderer/src/assets/main.css`), not `tailwind.config.js` - **No `any` in TypeScript** — use `unknown` with narrowing or proper types - **File naming:** React components in PascalCase (`NoteEditor.tsx`), everything else in kebab-case (`ipc-handlers.ts`, `vault.store.ts`) - **Commits:** Conventional Commits format (`feat:`, `fix:`, `chore:`, etc.), lowercase subject, max 72 chars - **Comments explain *why*, never history.** A comment states a constraint the code can't express, in the present tense, for a reader who has never seen the issue tracker. - Never in a comment: issue/PR numbers (`#36`), task/phase numbers, "found by"/"flagged by ", "verified by hand", line references (`vault.ts:657`), pointers to `tasks/*.md`. That history goes in the **commit body or PR description** — don't copy the PR text into code. - One canonical explanation per concept (e.g. `withVaultLock` reentrancy lives in its docstring); elsewhere a one-line pointer at most. Never paste the same block twice. - If the code already says it, don't comment it. - ✅ `// rekey succeeded but the checkpoint threw: newRawKey must still be zeroed` - ❌ `// Failure point the issue's literal suggested diff did NOT cover (#24)…` - Enforced by `pnpm lint` (`local/no-history-comments`); narrative and duplication aren't machine-checkable, so they're on you. --- ## UI Rules — read before writing any component or style ### 1. Never use raw HTML elements — always check for an existing component first Before writing any `