# VNCmail+ v1.7.8 → v1.8.0 Development Plan

**Date:** 2026-08-07
**Target:** `brvncde-dotcom/vncmail-plus` (Next.js 16)
**Baseline:** VNCmailgraph audit (82 findings) + Bugs VNCmail+.docx (12 missing features)

---

## Phase Structure

| Phase | Focus | Features + Fixes | Autonomy |
|-------|-------|-----------------|----------|
| **P1** | Critical fixes | 8 CRITICAL + 10 HIGH audit findings | Full autonomous |
| **P2** | Missing features | 12 docx features, code reuse from Angular | Full autonomous (board decided VNCtalk/Collabora/ActionWheel) |
| **P3** | Security hardening | Auth, cookies, rate limiting, encryption | Full autonomous |
| **P4** | Polish & sync | Remaining HIGH + cross-feature substrate | Full autonomous |

---

## Phase 1 — Critical Bug Fixes (Autonomous)

### P1.1 — Error Swallowing (CRITICAL C1)
**Issue:** `getEmails`/`searchEmails` return empty on transport failure. Network-down = empty folder.

**Fix:** Sample `transportHealth().transportFailureCount()` before/after JMAP reads. If incremented, throw `TransportError` instead of returning `{ emails: [], ... }`. Store catches and shows connectivity banner.
- Files: `lib/jmap/client.ts`, `stores/email-store.ts`, `lib/jmap/transport-health.ts`
- Reuse: None
- Effort: 2h

### P1.2 — FTS5 Schema-Drop Rebuild (CRITICAL C6)
**Issue:** Schema version bump drops all tables without auto-rebuild.

**Fix:** After `DROP TABLE IF EXISTS`, trigger automatic `catchUpAll()` in the same operation. Add user-facing "Rebuilding search index..." indicator.
- Files: `lib/mail-index/store.ts`, `lib/mail-index/reindex.ts`
- Reuse: None
- Effort: 2h

### P1.3 — Auth Credentials in localStorage (CRITICAL C5)
**Issue:** `auth-storage` + `account-storage` contain server URLs and usernames in plaintext.

**Fix:** Encrypt the Zustand persist payload for these two stores using a key derived from session secret or a per-device key. Scope: `auth-store.ts` and `account-store.ts` persist middleware.
- Files: `stores/auth-store.ts`, `stores/account-store.ts`, new `lib/auth/local-storage-crypto.ts`
- Reuse: AES-256-GCM pattern from `lib/auth/crypto.ts`
- Effort: 4h

### P1.4 — Settings Lock Bypass (CRITICAL C7)
**Issue:** `updateSetting()` has no policy check. Admin locks bypassable via store.

**Fix:** Gate `updateSetting(key, value)` with `isSettingLocked(key)`. Add `{ force: true }` opt-in for legitimate bypassers (auth bootstrap, settings sync). Audit all `updateSetting` call sites.
- Files: `stores/settings-store.ts`, `stores/auth-store.ts`, `lib/settings-sync.ts`
- Reuse: None
- Effort: 3h

### P1.5 — Offline Push Detection (CRITICAL C8)
**Issue:** No `offline` event listener. Transports retry blindly, draining battery.

**Fix:** Add `window.addEventListener('offline', ...)` that pauses all push transports. Add `navigator.onLine` gate on each transport cycle. Wire `transportHealth().likelyOffline` into push lifecycle.
- Files: `lib/jmap/client.ts`
- Reuse: None
- Effort: 1h

### P1.6 — Admin Session Revocation (CRITICAL C4)
**Issue:** AES-256-GCM token has no server-side revocation.

**Fix:** Add in-memory token blacklist (Set with TTL) in admin session middleware. Logout adds token `jti` to blacklist. Cleanup expired entries on verification.
- Files: `lib/admin/session.ts`, `app/api/admin/auth/route.ts`
- Reuse: None
- Effort: 3h

### P1.7 — Calendar ID Collision (CRITICAL C2)
**Issue:** Recurrence expansion (`:` delimiter) collides with shared event prefix.

**Fix:** Change expansion delimiter from `:` to `::occurrence::`. Update `stripLocalAccountPrefix` and all ID parsing.
- Files: `lib/recurrence-expansion.ts`, `stores/calendar-store.ts`
- Reuse: None
- Effort: 1h

### P1.8 — Calendar Cross-Account Dedup (CRITICAL C3)
**Issue:** Multi-account event aggregation has no UID dedup.

**Fix:** After `Promise.all(...).flat()`, run `uniqueBy(events, e => e.uid + e.recurrenceId)` pass.
- Files: `stores/calendar-store.ts`
- Reuse: None
- Effort: 1h

### P1.9 — HIGH Fixes Batch
- **H4:** Strip `access_token` from OAuth error logs (`lib/oauth/token-exchange.ts`) — 30min
- **H9:** Fix bcrypt in `isHashed()` (`lib/admin/password.ts`) — 1h
- **H18:** Default `autoSelectReplyIdentity` to `true` (`stores/settings-store.ts`) — 30min
- **H7:** DOMPurify HTML template body on import (`lib/template-utils.ts`) — 1h
- **H13:** `calendarTasksEnabled` runtime enforcement (`stores/task-store.ts`, `components/calendar/`) — 2h
- **H14:** try/catch + toast on task mutations (`stores/task-store.ts`) — 1h
- **H8:** Derive `secure` cookie from `x-forwarded-proto` (`lib/admin/session.ts`) — 1h
- **H3:** Rate-limit user auth endpoints (`app/api/auth/*`) — 3h
- **H2:** Await state snapshot in `reconcileAfterWebSocketFallback` (`lib/jmap/client.ts`) — 2h
- **H1:** Push handler: add ContactCard/FileNode branches (`stores/email-store.ts`) — 3h

**P1 Total:** ~32h

---

## Phase 2 — Missing Features (Autonomous, Code Reuse)

### P2.1 — Extended Signatures (Multiple per Identity + HTML Editor)
**Docx:** "Add option to create various email signatures and to select standard signature for new emails and for replies"

**Reuse from Angular:**
- Models: `signature.model.ts` (Signature interface), `identity.model.ts` (zimbraPrefDefaultSignatureId)
- API: Signature CRUD endpoints, `modifySignaturePrefs()` pattern
- UI logic: Quill editor toolbar config → `react-quill-new` equivalent

**Implementation:**
1. New `stores/signature-store.ts` — Signature[] with CRUD + identity assignment
2. New `components/settings/signature-settings.tsx` — list management page
3. New `components/settings/signature-editor-modal.tsx` — Quill-based HTML editor
4. Extend `components/identity/identity-form.tsx` — default/forward-reply signature picker
5. Extend `components/email/email-composer.tsx` — signature selector dropdown in compose
6. JMAP/Sieve integration: if the server stores signatures as Sieve or Identity properties, map accordingly

**Effort:** 12h

### P2.2 — Create Appointment from Email
**Docx:** "create a calendar entry from an email with recipients as participants and email text in description"

**Reuse from Angular:**
- `Appointment` interface from `appoinment.model.ts`
- Calendar compose pre-fill logic from `calendar-compose.component.ts`
- API payload shapes for event creation

**Implementation:**
1. New `components/email/create-appointment-button.tsx` — button in email toolbar
2. Extend `components/calendar/event-modal.tsx` — accept pre-fill props (title=subject, description=body, participants=from+to+cc)
3. Wire "Create Appointment" action into email-viewer toolbar + context menu

**Effort:** 6h

### P2.3 — Folder Sharing (Mail, Calendar, Contacts, Files)
**Docx:** "Share Folder feature" + "Sharing to see all folders shared by me and with me"

**Reuse from Angular:**
- `ShareFolderComponent` (564 lines) — full sharing dialog with email autocomplete, role selection
- `PreferencesSharingComponent` (556 lines) — "shared by me" / "shared with me" views
- `AllSharingFoldersDialogComponent` — tree-based folder browser
- API: share/revoke/accept/decline endpoints and payload shapes

**Implementation:**
1. New `components/sharing/share-folder-dialog.tsx` — modal with email autocomplete, role picker (read/read-write/admin), message
2. New `components/sharing/share-folder-revoke-dialog.tsx` — revoke confirmation
3. New `components/sharing/accept-share-dialog.tsx` — accept incoming share
4. New `components/settings/sharing-settings.tsx` — "Shared by me" / "Shared with me" tabs with folder tree
5. Extend folder context menus in email sidebar, calendar sidebar, contacts sidebar, files sidebar with "Share Folder..." action
6. New `stores/sharing-store.ts` — tracking shares state
7. API routes: `app/api/sharing/*` — share/revoke/accept/decline/find

**Effort:** 18h

### P2.4 — Calendar Dashlet (Mini Calendar in Mail View)
**Docx:** "Show Calendar in a dashlet in the bottom left corner"

**Reuse from Angular:**
- `sidebar-mini-calendar.component.ts` (701 lines) — month grid, swipe navigation, tooltip
- Tooltip directive logic for fetching day events

**Implementation:**
1. New `components/calendar/mini-calendar-dashlet.tsx` — compact month grid widget
2. Integrate into mail sidebar or bottom-left overlay in the mail page layout
3. Show date dots for days with events, today highlight, click to navigate to calendar
4. Optionally: toggleable via user setting "Show calendar dashlet"

**Effort:** 8h

### P2.5 — Email Import (.eml, tgz, zip)
**Docx:** "Missing: feature to import emails"

**Reuse from Angular:**
- `ImportExportComponent` (513 lines) — import types, destination folder, resolve settings
- `preferenceService.importFromFile()` — API request shape
- CSV type auto-detection logic

**Implementation:**
1. Extend existing `.eml` import (already partial — see `lib/eml-import.ts` and `components/email/` for `.eml` preview)
2. Add ZIP/TGZ/TAR archive import — extract, iterate, import each `.eml`
3. New `components/settings/import-settings.tsx` — import UI with file picker, destination folder, conflict resolution
4. API route: extend `app/api/account/*` or new `app/api/import/*`

**Effort:** 10h

### P2.6 — Contact Import (vCard/CSV)
**Docx:** "Missing: feature to import contacts"

**Reuse from Angular:**
- `contact-file-import-dialog.component.ts` (228 lines) — CSV upload, folder selector
- `contactService.importContacts()` — API endpoint
- vCard parsing: `lib/vcard.ts` already exists in codebase

**Implementation:**
1. Extend existing `components/contacts/contact-import-dialog.tsx` to support CSV (currently vCard-only)
2. Add CSV column mapping UI (map CSV columns to contact fields)
3. Folder selection for import destination
4. Dedup handling

**Effort:** 6h

### P2.7 — Free/Busy View
**Docx:** "Missing: free/busy view"

**Reuse from Angular:**
- `scheduler.component.ts` (1303 lines) — full free/busy grid
- `scheduler-utils.ts` — free-busy status constants (`fba` values, `FBA_TO_PTST` mapping)
- `schedule-assistant.component.ts` (1081 lines) — suggestion engine algorithm
- Free/busy URL format: `{serverURL}/home/{email}?fmt=freebusy`

**Implementation:**
1. New `components/calendar/free-busy-view.tsx` — attendee-row × time-slot grid
2. Color-coded slots: free (white), busy (red), tentative (yellow), out-of-office (purple)
3. Integration into event-modal when adding participants — show availability inline
4. Also queries `resources_bookings` table (see P2.8) to show resource availability in the same grid
5. New `lib/calendar-freebusy.ts` — fetch and parse free/busy data (user calendars + resource bookings)

**Effort:** 16h (was 14h, added resource booking integration)

### P2.8 — Resources/Equipment Booking (VNCdirectory-backed PostgreSQL)
**Docx:** "Missing: resources/equipment"
**Architecture decision:** Resources managed in **separate PostgreSQL table**, mapped to **VNCdirectory** for centralized cross-application management (rooms, cars, equipment, etc.). Independent from mail client — this is a platform-level resource system.

**Reuse from Angular:**
- `calendar-equipment-dialog.component.ts` (373 lines) — equipment browser UI patterns
- `calendar-equipment-autocomplete.component.ts` (266 lines) — autocomplete UX
- GAL query: `zimbraCalResType === "Equipment"` → adapt to VNCdirectory resource type filter

**Backend (New):**
1. **PostgreSQL migration** — `resources` table:
   - `id` UUID PK
   - `tenant_id` UUID (VNCdirectory tenant scope)
   - `name` text
   - `type` enum (room, vehicle, equipment, other)
   - `location` text (building/floor/room)
   - `capacity` integer nullable
   - `description` text
   - `contact_email` text (responsible person)
   - `is_active` boolean
   - `metadata` jsonb (extensible: photo URL, amenities, access hours, etc.)
   - `created_at`, `updated_at` timestamps

2. **`resources` table → VNCdirectory sync** — VNCdirectory is the canonical source. Options:
   - **Pull model:** VNCdirectory writes to this table via API/webhook
   - **Push model:** This service syncs changes back to VNCdirectory
   - **Read-through:** Query VNCdirectory directly for resource listings; cache in PostgreSQL for availability booking
   - Decision needed: which direction is authoritative? (Assume VNCdirectory → this table for Phase 1)

3. **New API routes:** `app/api/resources/`
   - `GET /api/resources` — list/search resources (type filter, location filter, tenant scoped)
   - `GET /api/resources/[id]` — single resource detail
   - `GET /api/resources/[id]/availability?start=&end=` — free/busy for a resource
   - `POST /api/resources/[id]/book` — create booking for a resource
   - `DELETE /api/resources/[id]/book/[bookingId]` — cancel booking

4. **New `lib/resources/` service layer:**
   - `lib/resources/client.ts` — fetch/query resources from VNCdirectory or local DB
   - `lib/resources/availability.ts` — check resource availability for time range
   - `lib/resources/booking.ts` — book/cancel resource
   - `lib/resources/sync.ts` — sync with VNCdirectory (if push-model needed)

5. **Conflict checking:**
   - When booking, check `resources_bookings` table for overlapping time ranges
   - Return conflicts + alternative slots

**Frontend:**
1. Extend `components/calendar/event-modal.tsx` — "Resources" tab with:
   - Resource type filter (room, vehicle, equipment)
   - Searchable autocomplete (name, location, capacity)
   - Availability indicator (free/busy for event time range)
2. New `components/calendar/resource-picker.tsx` — reusable resource selection component
3. Booked resources appear in event detail, participant list, and email invitation

**Effort:** 20h (was 10h, doubled for PostgreSQL + VNCdirectory integration)

### P2.9 — VNCtalk Video Meeting Integration
**Docx:** "Missing: integration with VNCtalk -> create Videomeeting"

**Reuse from Angular:**
- `app.service.ts` → `createNewMeeting()` / `updateScheduledMeeting()` API calls
- Payload: `POST /api/createnewmeeting { name, start, end, invitees, password, description, invid?, rev?, ms? }`
- `createOrUpdateMeeting()` in `edit-appointment-dialog.component.ts` — builds payload from appointment

**Implementation:**
1. New `lib/vnctalk/client.ts` — VNCtalk API client (create/update meeting)
2. Extend `components/calendar/event-modal.tsx` — "Create VNCtalk Meeting" toggle/button
3. Store meeting JID on CalendarEvent for updates
4. Add meeting link to event detail popover and email invitation body

**Effort:** 8h

### P2.10 — Collabora Online Editing
**Docx:** "Add feature to collaborate with Collabora"

**Reuse from Angular:**
- `owncloud.service.ts` → `getDocumentUrl(fileId, useCollabora)` with RichDocuments API
- `POST ocs/v2.php/apps/richdocuments/api/v1/document?format=json`
- Config: `collaboraBaseUrl` from admin config

**Implementation:**
1. New `lib/collabora/client.ts` — fetch editing URL from Collabora server
2. New `components/files/collabora-editor.tsx` — iframe-based editor embedding
3. Extend `components/files/file-browser.tsx` — "Edit with Collabora" action for office files
4. Admin config: `collaboraBaseUrl` in policy/config

**Effort:** 10h

### P2.11 — Calendar Enhancements Batch
**Docx:** Multiple calendar improvements

**2.11a — Clickable links in emails**
- Already partially done (TipTap Link extension). Verify link rendering in calendar event descriptions.
- Effort: 2h

**2.11b — Contact details of participants**
- Add popover on participant names in event-modal showing contact card. Reuse `components/contacts/contact-detail.tsx` data.
- Effort: 4h

**2.11c — Reply / Reply to All in meetings**
- Extend event-modal with "Reply" and "Reply to All" buttons that open composer pre-filled with participant emails.
- Effort: 3h

**2.11d — Timezone support**
- Add timezone picker to event-modal. Use `date-fns-tz` (already a dependency). Display times in event timezone with user timezone conversion.
- Effort: 6h

**2.11e — Map links**
- Extract address from event location, generate Google Maps / OpenStreetMap link.
- Effort: 2h

### P2.12 — Action Wheel (Custom Radial Menu)
**Docx:** "Recreate Action Wheel or better functionality"

**Board decision:** Build custom radial menu.

**Implementation:**
1. New `components/ui/radial-menu.tsx` — SVG-based radial menu with configurable items
2. Supports: mail actions (reply, forward, delete, archive, mark read, move, tag), contact actions, file actions
3. Trigger: long-press on mobile, right-click on desktop, or dedicated button
4. Animations: CSS rotate + scale transitions
5. Keyboard accessible

**Effort:** 10h

### P2.13 — IDP Integration — VNCdirectory Admin Configuration Panel
**Docx:** "IDP integration will be towards VNCdirectory (openldap, simplesamlphp, 2fa etc.)"
**Architecture decision:** Add admin UI for configuring VNCdirectory connection settings. Reuse auth patterns from VNCmail-analysis `api/auth-proxy/`.

**Reuse from Angular (api/auth-proxy/):**
- `config/config.js.example` — full auth configuration schema (SAML, LDAP, VNCdirectory, 2FA, hybrid auth)
- `config/passport.js` — SAML strategy + JWT custom strategy setup
- `routes/index.js` — login/logout/SAML callback/LDAP search/2FA/TOTP routes
- `utils/common.js` — JWT verification, Zimbra preauth token creation
- Auth dependencies: `@node-saml/passport-saml`, `passport`, `jsonwebtoken`, `ldapjs`

**Current VNCmail+ auth stack vs Angular auth stack:**
| Feature | Angular (Zimbra) | VNCmail+ (Stalwart) |
|---------|-----------------|---------------------|
| Primary auth | SAML 2.0 via Passport | OAuth/OIDC via Stalwart |
| Identity source | Zimbra LDAP + VNCdirectory | Stalwart internal + OIDC |
| 2FA | VNCdirectory TOTP/DUO | TOTP via Stalwart admin API |
| Directory integration | Redmine API (`contactsApiUrl`) | ❌ None |
| LDAP backend | `ldapjs` → Zimbra LDAP | ❌ None |
| SSO/Federation | JWT deeplinks + SAML | Cookie-based + OAuth |

**Implementation:**

1. **New `lib/vncdirectory/` service layer:**
   - `lib/vncdirectory/client.ts` — API client for VNCdirectory REST endpoints (port auth patterns from `routes/index.js`)
   - `lib/vncdirectory/config.ts` — VNCdirectory connection settings (URL, API key, LDAP bind, SAML IDP metadata)
   - `lib/vncdirectory/auth.ts` — SAML 2.0 SP implementation using `@node-saml/passport-saml` via Next.js API routes
   - `lib/vncdirectory/ldap.ts` — LDAP client using `ldapjs` for user/group/GAL queries
   - `lib/vncdirectory/2fa.ts` — TOTP enrollment + verification via VNCdirectory

2. **New API routes:** `app/api/vncdirectory/`
   - `GET /api/vncdirectory/status` — connection health check
   - `POST /api/vncdirectory/saml/login` — initiate SAML login flow
   - `POST /api/vncdirectory/saml/callback` — SAML assertion consumer
   - `POST /api/vncdirectory/saml/logout` — SAML single logout
   - `GET /api/vncdirectory/users` — search users (LDAP + VNCdirectory)
   - `POST /api/vncdirectory/2fa/enroll` — generate TOTP secret
   - `POST /api/vncdirectory/2fa/verify` — verify TOTP code
   - `GET /api/vncdirectory/2fa/status` — check 2FA enrollment status
   - `GET /api/vncdirectory/tags` — directory contact tags
   - `POST /api/vncdirectory/tags` — create/update directory tags

3. **New admin configuration page:**
   - `app/(main)/admin/vncdirectory/page.tsx` — VNCdirectory settings panel
   - Sections:
     - **Connection:** VNCdirectory URL, API key, LDAP URI, bind credentials
     - **SAML/IDP:** Identity Provider URL (SimpleSAMLphp), SP certificate, issuer
     - **Authentication:** Toggle SAML login, toggle 2FA enforcement, OIDC settings
     - **Directory sync:** LDAP type (OpenLDAP/MS-AD), search base, attribute mapping
     - **Federated apps:** Configure SSO URLs for VNCtalk, VNCtask, VNCcontacts
   - Add to admin navigation sidebar

4. **New `stores/vncdirectory-store.ts`** — client-side config state

5. **Extend existing auth:** 
   - Add SAML login as alternative to existing Basic/OAuth flows
   - Add VNCdirectory as identity source alongside Stalwart
   - Wire 2FA through VNCdirectory (currently uses Stalwart admin API)

**Effort:** 24h

### P2.14 — Share Files by Email as Attachment
**Docx:** "share by email as attachment"

**Implementation:**
1. Extend `components/files/file-browser.tsx` — "Send as Email Attachment" action
2. Opens composer with selected files attached (reuse existing attachment upload in composer)
3. Effort: 4h

**P2 Total:** ~127h

---

## Phase 3 — Security Hardening (Autonomous)

### P3.1 — Feature Gate Server-Side Enforcement
**Issue:** Feature gates are UI-only. Disabled features remain accessible via direct API calls.

**Fix:** Add policy checks to API routes. For each feature-gated route, add `isFeatureEnabled()` check returning 403.
- Routes: `app/api/smime/*`, `app/api/calendar-agenda/*`, `app/api/offline/*`, `app/api/plugins/*` (plugin disabled)
- Effort: 4h

### P3.2 — Unified Auth Error Interceptor
**Issue:** 401/403 errors silently swallowed in data fetches.

**Fix:** Create `lib/auth-error-handler.ts` — global fetch wrapper that detects 401 and triggers re-auth flow. Wire into JMAP client `authenticatedFetch`.
- Effort: 6h

### P3.3 — Store-Level State Isolation on Account Switch
**Issue:** Manual `clearAllStores()` misses new fields and stores.

**Fix:** Define per-store `snapshot(): Partial<S>` and `clear(): Partial<S>` contract. Auto-discover registered stores via a registry.
- Effort: 8h

### P3.4 — Push Event Bus Extraction
**Issue:** Email store is the push dispatch hub for 5+ stores.

**Fix:** Extract `lib/push-event-bus.ts` — stores subscribe to JMAP type names. Email store stops importing calendar/filter/task stores.
- Effort: 8h

**P3 Total:** ~26h

---

## Phase 4 — Polish & Remaining HIGH Items

### P4.1 — Offline Write Queue
**Issue:** No offline write capability. Cannot compose/send while offline.

**Fix:** Add `replica_pending_ops` table. Stage mutations offline, replay on connectivity return. Start with email send only, then extend.
- Effort: 16h

### P4.2 — Identity Spoofing Protection
**Issue:** From override accepts arbitrary addresses.

**Fix:** Client-side validation — restrict `fromOverrideEmail` to domains matching user's identities.
- Effort: 2h

### P4.3 — WebSocket Push for Electron
**Issue:** Browser WebSocket push permanently disabled.

**Fix:** Implement main-process WebSocket bridge in Electron via IPC. Renderer sends token, main process connects WS with auth header.
- Effort: 8h

### P4.4 — Remaining MEDIUM audit findings
- Calendar: recurrence cap warning, prefix scheme unification, read-only calendar filter, bulk delete batching
- Contacts: cross-account move race, autocomplete indexing, import dedup
- Files: folder tree cache reuse, upload parallelism
- Search: `toWildcardQuery` quote handling, `searchEmails` AbortController
- Effort: ~20h

**P4 Total:** ~46h

---

## Summary

| Phase | Hours | Description |
|-------|-------|-------------|
| P1 | 32h | Critical + HIGH bug fixes (18 items) |
| P2 | 167h | Missing features from docx (14 features) |
| P3 | 26h | Security hardening (4 items) |
| P4 | 46h | Polish + remaining fixes (4 items) |
| **Total** | **~271h** | |

### New Infrastructure Dependencies (P2)
- **PostgreSQL database** — `resources` + `resources_bookings` tables for VNCdirectory-backed resource management
- **VNCdirectory** — canonical source for resources + IDP identity provider (SAML 2.0, LDAP, 2FA/TOTP, directory tags)
- **SimpleSAMLphp** — SAML 2.0 Identity Provider (`vncidp.dev.vnc.de`) for web SSO
- **OpenLDAP** — LDAP directory for user/group queries and GAL (via `ldapjs`)
- **Collabora server** — `collaboraBaseUrl` admin config for online document editing
- **VNCtalk API** — `/api/createnewmeeting` endpoint for video meeting integration

### Autonomy Level
- **100% autonomous** — No further board decisions needed.
- **Sync direction (VNCdirectory ↔ PostgreSQL)**: Assumed VNCdirectory → PostgreSQL (pull) for Phase 1. Can be swapped if VNCdirectory expects push updates.
- **Code reuse:** 12 areas from VNCmail-analysis Angular codebase (models, API patterns, business logic). Must be adapted from Angular DI/services to plain TS functions + React hooks.
- **Repository access:** `brvncde-dotcom/vncmail-plus` (target), `brvncde-dotcom/VNCmail-analysis` (reuse).

### Deploy Flow
Per policy: P1 → deploy to `dev` → QA → fix → promote to `main`. Then P2 → dev → QA → main. Repeat for P3, P4.

### Phase 1 — COMPLETED 2026-08-07
**Shipped as v1.7.9.** 17 of 18 fixes deployed to `main`. All 2527 tests pass (161 test files). One item deferred: P1.3 (C5 auth localStorage encryption) — requires custom Zustand persist adapter, planned for Phase 3.

---

Do you want me to start Phase 1 immediately, or adjust the plan?
