feat: add experimental IMAP SMTP BYOK workspace and mobile scaffold - #2086
Closed
Tippye wants to merge 1 commit into
Closed
feat: add experimental IMAP SMTP BYOK workspace and mobile scaffold#2086Tippye wants to merge 1 commit into
Tippye wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
There are confirmed issues that will break Docker builds (.dockerignore excludes package-lock.json) and additional safety/maintainability concerns that should be addressed before merging.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR introduces an experimental self-hosting IMAP/SMTP bridge (Node + Docker), a BYOK (bring-your-own-key) AI configuration path routed through the authenticated Zero backend, and an initial Capacitor-based mobile packaging scaffold for the mail frontend.
Changes:
- Add
integrations/imap-bridge: encrypted credential vault, IMAP/SMTP operations, BYOK AI proxying, Docker/Compose packaging, and CI workflow. - Add new server-side tRPC router + client helper to call the bridge (
imap.*routes +imapBridge()), and add a new/imapmail workspace page in the React app. - Add
native/mobileCapacitor scaffold plus a “prepare web assets” script + tests and supporting docs.
File summaries
| File | Description |
|---|---|
| SELF_HOSTING_IMAP.zh-CN.md | Deployment + limitations guide for the experimental IMAP/BYOK/mobile scaffold. |
| native/mobile/scripts/prepare-web.test.mjs | Tests for the Capacitor web-asset preparation script. |
| native/mobile/scripts/prepare-web.mjs | Script to copy built SPA assets into the Capacitor www directory. |
| native/mobile/README.md | Notes and scope/limitations for the mobile scaffold. |
| native/mobile/package.json | Capacitor scaffold package + scripts. |
| native/mobile/capacitor.config.json | Capacitor configuration for the scaffold. |
| native/mobile/.gitignore | Ignores generated mobile platform projects, assets, and signing material. |
| integrations/imap-bridge/test/server.test.mjs | Unit tests for bridge RPC/auth, BYOK settings, and redaction behaviors. |
| integrations/imap-bridge/test/mail.test.mjs | Unit tests for IMAP listing/get/modify/move and SMTP send constraints. |
| integrations/imap-bridge/test/dependencies.mjs | Tests verifying real installed dependency APIs (ImapFlow/Nodemailer/mailparser/sanitize-html). |
| integrations/imap-bridge/test/core.test.mjs | Unit tests for crypto/vault, validation, IDs, pagination, idempotency. |
| integrations/imap-bridge/src/server.mjs | HTTP RPC server + bridge operations (accounts/mail/ai). |
| integrations/imap-bridge/src/mail.mjs | IMAP/SMTP implementation (ImapFlow + Nodemailer) with sanitization and limits. |
| integrations/imap-bridge/src/core.mjs | Shared primitives: validation, AES-GCM vault, message IDs, AI endpoint allowlist, idempotent send. |
| integrations/imap-bridge/scripts/init-env.mjs | Generates .env with bridge secret + encryption key. |
| integrations/imap-bridge/README.md | Minimal bridge usage docs pointing to the full self-hosting guide. |
| integrations/imap-bridge/package.json | Bridge package definition and scripts. |
| integrations/imap-bridge/package-lock.json | Lockfile for the standalone bridge install. |
| integrations/imap-bridge/Dockerfile | Container build for the bridge service. |
| integrations/imap-bridge/compose.yaml | Compose deployment with restrictive runtime settings. |
| integrations/imap-bridge/Caddyfile.example | Example HTTPS reverse proxy config for exposing the bridge to Workers. |
| integrations/imap-bridge/.gitignore | Ignores env/data/logs for the standalone bridge module. |
| integrations/imap-bridge/.env.example | Example env file for bridge configuration. |
| integrations/imap-bridge/.dockerignore | Docker build context filters for bridge builds. |
| apps/server/src/trpc/routes/imap.ts | New tRPC router exposing IMAP bridge operations to authenticated users. |
| apps/server/src/trpc/index.ts | Registers the new imap router in the app router. |
| apps/server/src/lib/imap-bridge.ts | Server-side helper to call the bridge over HTTP with validation and error mapping. |
| apps/mail/app/routes.ts | Adds /imap route to the mail app. |
| apps/mail/app/imap/page.tsx | IMAP workspace UI: account mgmt, folder/list/thread view, send, and BYOK AI actions. |
| .github/workflows/imap-bridge.yml | CI workflow to test bridge unit/dependency suites and basic container startup. |
Review details
Files not reviewed (1)
- integrations/imap-bridge/package-lock.json: Generated file
- Files reviewed: 29/30 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+1
to
+5
| ** | ||
| !package.json | ||
| !src/ | ||
| !src/** | ||
| !Dockerfile |
Comment on lines
+1
to
+13
| import { access, cp, mkdir, readFile, rm } from 'node:fs/promises'; | ||
| import { resolve, dirname } from 'node:path'; | ||
| import { fileURLToPath, pathToFileURL } from 'node:url'; | ||
|
|
||
| export async function prepareWeb(source, target, config) { | ||
| if (config.server?.url || config.server?.allowNavigation?.length) throw Error('Remote production WebView URLs/navigation allowlists are not allowed by this build scaffold.'); | ||
| const index = resolve(source, 'index.html'); | ||
| try { await access(index); } catch { throw Error('Web assets are missing. Build @zero/mail before preparing the native shell.'); } | ||
| const html = await readFile(index, 'utf8'); | ||
| if (!/<html[\s>]/i.test(html)) throw Error('Invalid web build: index.html is not HTML.'); | ||
| const destination = resolve(target), origin = resolve(source); | ||
| if (destination === origin || origin.startsWith(destination + '/') || destination.startsWith(origin + '/')) throw Error('Unsafe web asset output directory.'); | ||
| await rm(destination, { recursive: true, force: true }); |
Comment on lines
+10
to
+16
| // Deliberately do not use the upstream payload-logging middleware: inputs contain secrets and mail. | ||
| // The context and transformer match the parent router. Identity comes only from server authentication. | ||
| const t = initTRPC.context<{ c: Context<HonoContext> } & HonoVariables>().create({ transformer: superjson }); | ||
| const owned = t.procedure.use(({ ctx, next }) => { | ||
| if (!ctx.sessionUser) throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Sign in to Zero first.' }); | ||
| return next({ ctx: { ...ctx, sessionUser: ctx.sessionUser } }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
READ CAREFULLY THEN REMOVE
Remove bullet points that are not relevant.
PLEASE REFRAIN FROM USING AI TO WRITE YOUR CODE AND PR DESCRIPTION. IF YOU DO USE AI TO WRITE YOUR CODE PLEASE PROVIDE A DESCRIPTION AND REVIEW IT CAREFULLY. MAKE SURE YOU UNDERSTAND THE CODE YOU ARE SUBMITTING USING AI.
Description
Please provide a clear description of your changes.
Type of Change
Please delete options that are not relevant.
Areas Affected
Please check all that apply:
Testing Done
Describe the tests you've done:
Security Considerations
For changes involving data or authentication:
Checklist
Additional Notes
Add any other context about the pull request here.
Screenshots/Recordings
Add screenshots or recordings here if applicable.
By submitting this pull request, I confirm that my contribution is made under the terms of the project's license.