Files
SRCmail/runs/2026-08-07-v1.7.8-baseline/inventory.md
T
Bernd Rodler a622e3755b fix: Phase 1 critical+high fixes (17/18 items)
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.
2026-08-07 12:17:41 +02:00

16 KiB

Wave 0 — Feature Inventory

Run: 2026-08-07-v1.7.8-baseline
Skill: VNCmailgraph (adapted for Next.js/Zustand)
Commit: d8bebb531f
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
  • 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-storemessage-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-storemessage-list-tabs-store — bidirectional cycle
  3. email-storecalendar-store — cross-feature coupling (invitation banners, birthday calendar)
  4. settings-storetheme-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)