CRITICAL fixes: - C1: Error swallowing - throw TransportError on network failure in getEmails/searchEmails - C2: Recurrence expansion ID delimiter changed from ':' to '::occurrence::' - C3: Cross-account calendar event UID dedup after multi-account aggregation - C4: Admin session token revocation via JTI blacklist on logout - C6: FTS5 schema-drop - add warning log for automatic reindex trigger - C7: Settings lock - gate updateSetting() with isSettingLocked() check - C8: Offline push pause - add offline event handler that closes push transports HIGH fixes: - H1: Push handler - add ContactCard and FileNode branches - H2: WS fallback - await state snapshot before reconcileAfterWebSocketFallback - H3: Auth rate limiting - add checkUserAuthRateLimit to session and token routes - H4: OAuth logs - strip access_token from error log context - H7: Template XSS - apply DOMPurify to HTML template body on import - H8: Secure cookie - derive from x-forwarded-proto, not NODE_ENV - H9: bcrypt fix - remove bcrypt prefixes from isHashed() so scrypt-only - H13: calendarTasksEnabled - apply admin gate at runtime in calendar page - H14: Task mutations - add try/catch error handling to update/delete/toggle - H18: autoSelectReplyIdentity default changed from false to true Deferred: P1.3 (C5 auth localStorage encryption) - requires custom Zustand persist adapter.
291 lines
16 KiB
Markdown
291 lines
16 KiB
Markdown
# Wave 0 — Feature Inventory
|
|
|
|
**Run:** 2026-08-07-v1.7.8-baseline
|
|
**Skill:** VNCmailgraph (adapted for Next.js/Zustand)
|
|
**Commit:** d8bebb531f86cab3507aed2113e8d0e6a03c1aa8
|
|
**Version:** vnc-v0.3.0-94-gd8bebb53 (VERSION=1.7.8)
|
|
**Codebase:** ~188K LOC across 745 TS/TSX files (excluding node_modules, .git)
|
|
**Framework:** Next.js 16 (App Router) + React 19 + Zustand 5
|
|
**Backend:** Stalwart Mail Server (JMAP protocol)
|
|
**Targets:** Web (PWA), Electron Desktop, Native (planned via Capacitor/RN)
|
|
|
|
---
|
|
|
|
## Method Adaptation
|
|
|
|
The original VNCmailgraph methodology assumes Angular + NgRx + `isCordova`/`isElectron` gates. This codebase uses:
|
|
- **Next.js 16 App Router** (not Angular) — routes defined via file-system routing
|
|
- **Zustand** (not NgRx) — stores in `stores/*.ts`, accessed via hooks
|
|
- **`isElectronShell()`** (not `isCordova`/`isElectron`) — platform detection via `window.vnc`
|
|
- **No `zimbra-features.ts`** — feature flags are in `lib/admin/types.ts` (`FeatureGates`, `SettingsPolicy`)
|
|
- **JMAP protocol** (not Zimbra SOAP) — `lib/jmap/client.ts` (7446 lines)
|
|
|
|
The audit method translates cleanly: feature domains = page routes + component trees; shared substrate = stores + lib services; coupling = cross-store imports.
|
|
|
|
---
|
|
|
|
## Feature Domains (derived from code)
|
|
|
|
### F1 — Mail / Email (CORE)
|
|
- **Routes:** `/app/(main)/[locale]/page.tsx` (3758 lines — main mail client)
|
|
- **Components:** `components/email/` (24 files): email-list, email-viewer (5102 lines), email-composer (3432 lines), thread-conversation-view, rich-text-editor (TipTap), tag-picker, email-hover-actions, recipient-popover, calendar-invitation-banner, unsubscribe-banner, read-receipt-banner, message-list-tabs
|
|
- **Store:** `stores/email-store.ts` (4087 lines) — largest store, manages emails, mailboxes, threads, search, tags, push connection, cross-account views
|
|
- **Protocol:** `lib/jmap/client.ts` (7446 lines) — the JMAP client powering all mail operations
|
|
- **Sub-features:** Compose/drafts, threading, unified mailbox, cross-account views, search, tags/keywords, attachments, scheduled send, read receipts (MDN), archive modes, virtual scrolling, TNEF extraction, .eml import, print
|
|
|
|
### F2 — Calendar
|
|
- **Routes:** `/app/(main)/[locale]/calendar/page.tsx`
|
|
- **Components:** `components/calendar/` (22 files): month/week/day/agenda views, event-modal (1375 lines), mini-calendar, task-modal, recurrence-editor, ical-import/subscription modals, participant-input
|
|
- **Store:** `stores/calendar-store.ts` (1289 lines) — calendars, events, multi-account aggregation, recurrence expansion, iMIP, CalDAV
|
|
- **Sub-features:** Month/week/day/agenda views, drag-to-reschedule, recurring events, iMIP invitations, .ics import, birthday calendar, virtual locations, tasks, CalDAV shared calendars
|
|
|
|
### F3 — Contacts
|
|
- **Routes:** `/app/(main)/[locale]/contacts/page.tsx`
|
|
- **Components:** `components/contacts/` (17 files): contact-list, contact-detail, contact-form, import-dialog, groups
|
|
- **Store:** `stores/contact-store.ts` (1146 lines) — contact cards, address books, multi-account, vCard import/export
|
|
- **Sub-features:** JMAP sync (RFC 9553/9610), address books, groups, vCard import/export, trusted senders, autocomplete
|
|
|
|
### F4 — Files / Briefcase
|
|
- **Routes:** `/app/(main)/[locale]/files/page.tsx`
|
|
- **Components:** `components/files/` (10 files): file-browser (2022 lines), folder-tree, upload-area, preview-modal
|
|
- **Stores:** `stores/file-store.ts` (1050 lines) + `stores/webdav-store.ts` (480 lines) — dual storage backends
|
|
- **Sub-features:** JMAP FileNode browsing, WebDAV upload with progress, grid/list views, preview, cut/copy/paste, favorites, sharing (RFC 9670)
|
|
|
|
### F5 — Tasks
|
|
- **Store:** `stores/task-store.ts` (95 lines) — VTODO CRUD, filter by pending/completed/overdue
|
|
- **Components:** `components/calendar/task-modal.tsx`, `task-list-view.tsx`, `task-toolbar.tsx`
|
|
- **Feature gate:** `calendarTasksEnabled` in admin policy
|
|
|
|
### F6 — Settings / Preferences
|
|
- **Routes:** `/app/(main)/[locale]/settings/page.tsx`
|
|
- **Components:** `components/settings/` (33 files) — account, appearance, themes, layout, reading, composing, notification, folder, filter, vacation, identity, template, keyword, calendar, contacts, files, downloads, sidebar-apps, plugins, language, protocol-handler, AI, debug, about
|
|
- **Store:** `stores/settings-store.ts` (1221 lines) — all user settings with cross-device encrypted sync
|
|
- **Sub-features:** Theme/density/font, layout preferences, reply/signature behavior, notification preferences, mailto:/webcal: protocol handling, settings sync (AES-256-GCM)
|
|
|
|
### F7 — Filters / Sieve
|
|
- **Components:** `components/filters/` (2 files): filter-rule-modal (534 lines), sieve-editor-modal
|
|
- **Store:** `stores/filter-store.ts` (254 lines) — Sieve script management
|
|
- **Lib:** `lib/sieve/` (parser + generator) — round-trip for external scripts
|
|
- **Sub-features:** Visual rule builder, raw Sieve editor, vacation responder, opaque script preservation
|
|
|
|
### F8 — Templates
|
|
- **Components:** `components/templates/` (4 files): template-picker, template-form, manager-modal, placeholder-fill
|
|
- **Store:** `stores/template-store.ts` (153 lines) — CRUD, favorites, import/export
|
|
- **Feature gate:** `templatesEnabled`
|
|
|
|
### F9 — Identity / Aliases
|
|
- **Components:** `components/identity/` (3 files): identity-manager-modal (407 lines), identity-form, sub-address-helper
|
|
- **Store:** `stores/identity-store.ts` (140 lines) — sender identities, sub-addressing
|
|
- **Sub-features:** Multiple sender identities per account, sub-addressing (`user+tag@domain`)
|
|
|
|
### F10 — AI Assistant
|
|
- **Components:** `components/ai/ai-ask-button.tsx` (264 lines)
|
|
- **Lib:** `lib/ai/` (9 files + retrieval/): local-client, local-discovery, opencode, entitlement, key-store, retrieval/fusion
|
|
- **API:** `app/api/ai/` (server/chat, server/models, retrieve, opencode/chat, opencode/models, policy)
|
|
- **Sub-features:** Local LLM (Ollama), server-hosted AI, BYOK, OpenCode runtime, mail retrieval with RRF fusion, FTS5 search index
|
|
- **Feature gate:** `aiAssistantEnabled` (default=true)
|
|
|
|
### F11 — Admin / Management
|
|
- **Routes:** 14 admin pages (dashboard, settings, branding, auth, password, policy, plugins, themes, marketplace, version, telemetry, logs)
|
|
- **Lib:** `lib/admin/` (18 files): config-manager, session, password, audit, plugin-registry, plugin-approvals, plugin-signing, bundled-plugins, domain-branding, CSP
|
|
- **Store:** `stores/admin-tab-store.ts` (42 lines)
|
|
|
|
### F12 — Plugin System
|
|
- **Components:** `components/plugins/` (7 files): plugin-slot, iframe-slot, dialog-host, consent-dialog, error-boundary
|
|
- **Store:** `stores/plugin-store.ts` (596 lines) — lifecycle management
|
|
- **Lib:** `lib/plugin-sandbox/` (13 files): runtime, host-bridge, host-api, loader, tier, protocol, registry, bundle-integrity, bundle-signing, consent, shortcuts
|
|
- **Sub-features:** Sandboxed iframe execution, postMessage RPC, tiered permissions (untrusted/privileged), admin approval gates, marketplace, dev-mode loading
|
|
|
|
### F13 — Pro Multi-Tab Shell
|
|
- **Routes:** `/app/(main)/[locale]/pro/page.tsx`
|
|
- **Components:** `components/pro/` (4 files): tab-bar, email-tab-body, compose-tab-body, interface-redirect
|
|
- **Store:** `stores/pro-tab-store.ts` (576 lines) — multi-tab management, split pane layout
|
|
|
|
### F14 — Search
|
|
- **Components:** `components/search/` (2 files): search-chips, advanced-search-panel
|
|
- **Backend:** `lib/mail-index/` (8 files) — encrypted SQLite/FTS5 index (Electron-only)
|
|
- **API:** `app/api/offline/search`, `app/api/ai/retrieve`
|
|
- **Coupling:** Heavily coupled with email-store (search state lives there)
|
|
|
|
### F15 — Authentication
|
|
- **Lib:** `lib/auth/` (7 files): crypto, session-cookie, session-secret, verify-jmap-auth, pair-reauth, pairing-store, active-account-slot
|
|
- **Store:** `stores/auth-store.ts` (2033 lines) — THE hub; manages login, logout, token refresh, multi-account sessions
|
|
- **Sub-features:** Basic auth, OAuth/OIDC with PKCE, demo mode, device pairing, TOTP 2FA, impersonation (JWT)
|
|
- **API:** `app/api/auth/` (10 routes)
|
|
|
|
### F16 — Setup Wizard
|
|
- **Routes:** `/app/(main)/setup/*`
|
|
- **Lib:** `lib/setup/` (3 files): session, state, token
|
|
- **API:** `app/api/setup/` (6 routes)
|
|
|
|
---
|
|
|
|
## Shared Substrate
|
|
|
|
### S1 — State Management (Zustand stores)
|
|
- 28 store files in `stores/` — Zustand with `persist` middleware
|
|
- **Central hub:** `auth-store.ts` imports 7 other stores; bootstraps all feature stores after login
|
|
- **Bidirectional cycle:** `email-store` ↔ `message-list-tabs-store`
|
|
- **Anti-cycle pattern:** `client-registry.ts` — utility indirection to avoid auth-store cycles
|
|
- **Clean leaves:** `locale-store`, `policy-store`, `toast-store`, `ui-store` (0 dependencies)
|
|
|
|
### S2 — JMAP Protocol Layer
|
|
- `lib/jmap/client.ts` (7446 lines) — the communication backbone for ALL features
|
|
- `lib/jmap/types.ts` (938 lines) — shared type definitions
|
|
- `lib/jmap/client-interface.ts` (358 lines) — `IJMAPClient` interface (real + demo implementations)
|
|
- `lib/jmap/transport-health.ts` — failure counter for offline fallback gating
|
|
- `lib/jmap/request-limits.ts` — request batching utilities
|
|
- `lib/jmap/search-utils.ts` — search query building
|
|
|
|
### S3 — Auth / Session / Multi-Account
|
|
- `lib/auth/*` — AES-256-GCM cookie encryption, session lifecycle, device pairing
|
|
- `lib/oauth/*` — OAuth2/OIDC discovery, PKCE, token exchange
|
|
- `lib/impersonation/*` — JWT platform auth
|
|
- `lib/stalwart/*` — server-side Stalwart auth context, JMAP passthrough
|
|
- `stores/account-store.ts` — multi-account registry
|
|
- `stores/account-security-store.ts` — TOTP 2FA, app passwords, API keys
|
|
|
|
### S4 — Offline Replica (encrypted delta-sync mail store)
|
|
- `lib/offline-replica/` (13 files): engine, sync (1122 lines), store (997 lines), apply, jmap, states, read, retention, errors, schema, types
|
|
- SQLCipher-encrypted SQLite; delta sync via JMAP `/changes`
|
|
- Two-tier storage (envelope + body); cursor provenance with branded types
|
|
- **Fallback only** — consulted after live JMAP read fails; online session never sees replica data
|
|
|
|
### S5 — Local Search Index (encrypted FTS5)
|
|
- `lib/mail-index/` (8 files): store (534 lines), jmap (388 lines), extract (311 lines), reindex (385 lines), key (203 lines), paths, binding
|
|
- SQLCipher-encrypted SQLite with FTS5, **Electron/desktop only**
|
|
- Event-driven (triggered by JMAP push changes via API routes)
|
|
- Key transport uses inherited fd (pipe), not env var
|
|
|
|
### S6 — Platform Bridge
|
|
- `lib/electron-bridge.ts` (55 lines) — `isElectronShell()`, `showElectronNotification()`
|
|
- `lib/platform-capabilities.ts` (29 lines) — single source of truth for platform feature availability
|
|
- `lib/web-push.ts` — web push for PWA
|
|
- `electron/main.ts` (424 lines) — Electron main process (spawns Next.js server)
|
|
- `electron/preload.ts` (27 lines) — contextBridge, nodeIntegration=false, sandbox=true
|
|
|
|
### S7 — Plugin Sandbox
|
|
- `lib/plugin-sandbox/` (13 files) — iframe isolation, postMessage RPC, tier gating
|
|
- `lib/plugin-types.ts` (1092 lines) — massive type definitions
|
|
- `lib/plugin-hooks.ts` — hook registration for email/calendar/composer/sidebar slots
|
|
- `lib/plugin-loader.ts`, `lib/plugin-validator.ts`, `lib/plugin-storage.ts`
|
|
|
|
### S8 — HTTP Proxy / API Gateway
|
|
- `proxy.ts` (211 lines) — Next.js middleware: CSP, nonce, setup-state gating, intl routing
|
|
- `app/api/*` — 24 API endpoint groups (auth, admin, AI, offline, setup, etc.)
|
|
|
|
### S9 — Security / Sanitization
|
|
- `lib/security/url-guard.ts` (67 lines) — SSRF prevention, loopback/private-IP blocking
|
|
- `lib/email-sanitization.ts` — DOMPurify HTML sanitization
|
|
- `lib/smime-ca/` (5 files) — S/MIME certificate authority (EJBCA + local dev CA)
|
|
|
|
### S10 — Theme System
|
|
- `stores/theme-store.ts` (593 lines) — light/dark/system, custom theme installation
|
|
- `lib/theme-compiler.ts`, `lib/theme-loader.ts`, `lib/theme-logo.ts`
|
|
- `lib/builtin-themes.ts` — 8 built-in themes (2 shipping, 6 hidden)
|
|
- `lib/color-transform.ts` — luminance-based color remapping for dark mode
|
|
|
|
### S11 — Internationalization
|
|
- `stores/locale-store.ts` (19 lines)
|
|
- `i18n/routing.ts` — next-intl configuration
|
|
- `locales/` — 24 language translations
|
|
- `lib/jalali-utils.ts` — Persian calendar support
|
|
|
|
### S12 — Cross-Device Settings Sync
|
|
- `lib/settings-sync.ts` — encrypted settings sync (AES-256-GCM)
|
|
|
|
### S13 — Telemetry
|
|
- `lib/telemetry/` (7 files) — anonymous heartbeat, opt-in, HMAC-hashed logins
|
|
|
|
### S14 — Version Check
|
|
- `lib/version-check/` (5 files) — polls version server, daily jittered schedule
|
|
- `stores/update-store.ts` (118 lines)
|
|
|
|
---
|
|
|
|
## Feature Flag Registry (from lib/admin/types.ts)
|
|
|
|
| Flag | Default | Description |
|
|
|------|---------|-------------|
|
|
| `pluginsEnabled` | false | Plugin system on/off |
|
|
| `pluginsUploadEnabled` | true | Allow manual plugin upload |
|
|
| `requirePluginApproval` | true | Admin must approve plugins |
|
|
| `themesEnabled` | true | Theme system on/off |
|
|
| `sidebarAppsEnabled` | true | Sidebar app visibility/order |
|
|
| `userThemesEnabled` | true | Users can install custom themes |
|
|
| `settingsExportEnabled` | true | Settings export |
|
|
| `customKeywordsEnabled` | true | Custom keyword/tag creation |
|
|
| `templatesEnabled` | true | Email templates |
|
|
| `calendarEnabled` | true | Calendar feature |
|
|
| `calendarTasksEnabled` | true | Calendar tasks |
|
|
| `smimeEnabled` | true | S/MIME |
|
|
| `externalContentEnabled` | true | External content in emails |
|
|
| `debugModeEnabled` | true | Debug tools |
|
|
| `folderIconsEnabled` | true | Custom folder icons |
|
|
| `hoverActionsConfigEnabled` | true | Hover action configuration |
|
|
| `filesEnabled` | true | File browser |
|
|
| `contactsEnabled` | true | Contacts |
|
|
| `crossAllViewEnabled` | false | Cross-account All Mail |
|
|
| `crossUnreadViewEnabled` | false | Cross-account Unread |
|
|
| `crossStarredViewEnabled` | false | Cross-account Starred |
|
|
| `unifiedCrossAccountEnabled` | false | Cross-account unified inbox |
|
|
| `aiAssistantEnabled` | true | AI assistant (visible by default — local ships free) |
|
|
|
|
---
|
|
|
|
## Dependency / Coupling DAG (Store Layer)
|
|
|
|
```
|
|
auth-store (HUB) ──→ identity-store
|
|
├──→ account-store
|
|
├──→ calendar-store
|
|
├──→ contact-store
|
|
├──→ filter-store
|
|
├──→ settings-store
|
|
└──→ vacation-store
|
|
|
|
email-store ──→ calendar-store
|
|
├──→ auth-store
|
|
├──→ account-store
|
|
├──→ settings-store
|
|
└──→ message-list-tabs-store ←── (BIDIRECTIONAL CYCLE)
|
|
|
|
settings-store ──→ theme-store
|
|
└──→ locale-store
|
|
|
|
plugin-store ──→ locale-store
|
|
└──→ policy-store
|
|
|
|
theme-store ──→ policy-store
|
|
|
|
account-security-store ──→ auth-store
|
|
|
|
Standalone (14): file-store, task-store, filter-store, identity-store,
|
|
account-store, pro-tab-store, ui-store, vacation-store, totp-reauth-store,
|
|
calendar-notification-store, managed-account-store, update-store,
|
|
toast-store, admin-tab-store, template-store, webdav-store
|
|
```
|
|
|
|
**Critical coupling paths to audit in Wave 2:**
|
|
1. `auth-store` → bootstraps 7 feature stores on login → any bootstrap failure cascades
|
|
2. `email-store` ↔ `message-list-tabs-store` — bidirectional cycle
|
|
3. `email-store` → `calendar-store` — cross-feature coupling (invitation banners, birthday calendar)
|
|
4. `settings-store` → `theme-store` → theme injection affects ALL components
|
|
5. JMAP client (`lib/jmap/client.ts`) → used by ALL feature stores via `client-registry`
|
|
|
|
---
|
|
|
|
## Platform Parity Surface
|
|
|
|
| Feature | Web (PWA) | Electron Desktop | Native (planned) |
|
|
|---------|-----------|------------------|------------------|
|
|
| Mail | Full | Full | Full (planned) |
|
|
| Calendar | Full | Full | Full (planned) |
|
|
| Contacts | Full | Full | Full (planned) |
|
|
| Files | Full | Full | Full (planned) |
|
|
| Local Search Index | ❌ | SQLCipher FTS5 | ❌ (planned) |
|
|
| Offline Replica | ❌ | SQLCipher SQLite | ❌ (planned) |
|
|
| Native Notifications | Web Push | Electron Notification | Native (planned) |
|
|
| AI Local LLM | Browser CORS | No CORS (loopback) | Native (planned) |
|
|
| S/MIME | Full | Full | Full (planned) |
|