Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
7.1 KiB
7.1 KiB
IMAP Inbound Email Scratchpad
Last updated: 2026-01-11 Owner: Email Platform
Use this scratchpad to capture key findings, decisions, TODOs, and file references while implementing the IMAP inbound email service.
Scope Notes
- IMAP provider must feed existing
INBOUND_EMAIL_RECEIVEDworkflow event pipeline (same as Gmail/Microsoft). - IMAP v1 must include OAuth2 (XOAUTH2) support.
- IMAP service should be resilient: IDLE, reconnect, backoff, dedupe, and per-folder monitoring.
auto_process_emailsis not exposed in the UI (processing is gated by provider Active flag).
Current Event Flow (Research)
- Gmail webhook:
server/src/app/api/email/webhooks/google/route.ts- Decodes Pub/Sub notification, loads provider config, fetches message details via
GmailAdapter, publishesINBOUND_EMAIL_RECEIVEDviashared/events/publisher.ts.
- Decodes Pub/Sub notification, loads provider config, fetches message details via
- Microsoft webhook:
server/src/app/api/email/webhooks/microsoft/route.ts- Validates Graph notification, fetches message details via
MicrosoftGraphAdapter, publishesINBOUND_EMAIL_RECEIVED(fallback minimal payload on fetch failure).
- Validates Graph notification, fetches message details via
- Workflow worker:
services/workflow-worker/src/WorkflowWorker.ts- Consumes workflow events (Redis stream) and starts
shared/workflow/workflows/system-email-processing-workflow.ts.
- Consumes workflow events (Redis stream) and starts
- Event schema:
shared/workflow/streams/eventBusSchema.tsINBOUND_EMAIL_RECEIVEDpayload expects{ providerId, emailData{...} }.
Important Files / Entry Points
- Provider types + interfaces
shared/interfaces/inbound-email.interfaces.tsserver/src/interfaces/email.interfaces.ts
- Email provider CRUD + orchestration
server/src/services/email/EmailProviderService.tsserver/src/lib/actions/email-actions/emailProviderActions.ts
- Gmail integration (baseline)
server/src/services/email/providers/GmailAdapter.tsserver/src/services/email/GmailWebhookService.ts
- Microsoft integration (baseline)
shared/services/email/providers/MicrosoftGraphAdapter.ts
- Event publishing
shared/events/publisher.ts
- Inbound email docs
docs/inbound-email/README.mddocs/inbound-email/architecture/workflow.mddocs/inbound-email/development/adapters.md
Data Model Notes
- Existing tables:
email_providers(provider_type: google|microsoft)google_email_provider_configmicrosoft_email_provider_config
- IMAP will need
imap_email_provider_config(new migration + Citus distribution).- Implemented migration:
server/migrations/20251226121000_create_imap_email_provider_config.cjs - Includes host/port/TLS/auth_type/username/folder_filters + OAuth fields + state tracking.
- Added folder state jsonb for per-folder UID tracking:
server/migrations/20251226124500_add_imap_folder_state.cjs. - Added runtime metadata columns (leases, capabilities, timeouts):
server/migrations/20251226140000_add_imap_runtime_columns.cjs.
- Implemented migration:
UI/UX Touchpoints
- Provider selection:
server/src/components/EmailProviderSelector.tsx - Provider configuration shell:
server/src/components/EmailProviderConfiguration.tsx - Provider forms:
server/src/components/GmailProviderForm.tsx,server/src/components/MicrosoftProviderForm.tsx - Provider bundle entry:
packages/product-email-providers/{oss,ee}/entry.tsx- Added IMAP forms:
server/src/components/ImapProviderForm.tsxandee/server/src/components/ImapProviderForm.tsx
- Added IMAP forms:
OAuth2 for IMAP (v1)
- Need IMAP OAuth2 flow (likely XOAUTH2, provider-specific endpoints).
- Determine if we must support generic OAuth2 endpoints per provider config (authorize/token URLs).
- Decide how to store IMAP OAuth client secrets + refresh tokens (tenant secrets provider preferred).
- Secret keys used by actions:
imap_password_<providerId>,imap_oauth_client_secret_<providerId>,imap_refresh_token_<providerId> - OAuth endpoints:
POST /api/email/oauth/imap/initiateGET /api/email/oauth/imap/callback
- Secret keys used by actions:
Implementation Notes (Initial)
- Service location proposal:
services/email-service(standalone worker likeservices/workflow-worker). - IMAP service should publish
INBOUND_EMAIL_RECEIVEDviashared/events/publisher.ts. - Read-only IMAP fetch should use
BODY.PEEK[]to avoid marking as read.- IMAP service uses
imapflow+mailparserto fetch/parse messages and publish events. - Per-folder listeners are created for each configured folder (fallback to INBOX).
- Folder state (UIDVALIDITY/last UID) persisted in
imap_email_provider_config.folder_state. - IMAP service acquires DB leases per provider (
lease_owner,lease_expires_at) to avoid double-processing. - IMAP OAuth re-connect flow is available in the IMAP provider form + card action.
- Local test IMAP server: GreenMail (
greenmail/standalone:latest) added to dev compose.- SMTP: host port
${EXPOSE_IMAP_TEST_SMTP_PORT:-3025}→ container 3025 - IMAP: host port
${EXPOSE_IMAP_TEST_IMAP_PORT:-3143}→ container 3143 - IMAPS: host port
${EXPOSE_IMAP_TEST_IMAPS_PORT:-3993}→ container 3993 - For SMTP injection, use recipient local-part
imap_user(GreenMail storesTo: imap_userin headers).
- SMTP: host port
- IMAP runtime tuning is intentionally not exposed in the provider UI:
IMAP_CONNECTION_TIMEOUT_MS(default:10000)IMAP_MAX_EMAILS_PER_SYNC(default:5)IMAP_SOCKET_KEEPALIVE(default:true, set tofalseto disable)
- To avoid thundering herd across instances/providers, IMAP service uses jittered timers:
IMAP_TIMER_JITTER_PCT(default:0.1) applies to provider refresh, heartbeat, poll sleep, and IDLE NOOP interval.IMAP_STARTUP_STAGGER_MS(default:2000) staggers folder listener startup per provider/folder.IMAP_RECONNECT_JITTER_PCT(default:0.5) randomizes reconnect delay within[baseDelay*(1-jitterPct), baseDelay].
- Initial connect and manual resync behavior:
- When cursor state is empty, IMAP listener starts from the most recent
IMAP_MAX_EMAILS_PER_SYNCwindow (based on mailboxuidNext) instead of replaying the whole mailbox from UID 1. last_uidadvances to the highest UID observed even when a message is skipped/deduped, to avoid repeatedly re-scanning the same window.
- When cursor state is empty, IMAP listener starts from the most recent
- Kubernetes deployment assets:
- Helm chart:
ee/helm/email-service(includes liveness/readiness probes via/health). - Argo CI/CD (nm-kube-config):
argo-workflow/alga-psa-dev/templates/build/email-service-ci-cd-sebastian.yaml. - Helm values (nm-kube-config):
email-service/{hosted,sebastian,prod}.values.yaml.
- Helm chart:
- IMAP service uses
Open Questions
- Which IMAP client library should we standardize on (imapflow vs node-imap)?
- OAuth2 endpoints: per-provider config vs pre-baked defaults?
- UID tracking per folder: store as map in JSONB or separate table?
- Do we want a lease/lock (Redis/DB) to prevent multi-instance double processing?
TODO Log
- Add IMAP provider type to shared/server interfaces and UI enums.
- Create
imap_email_provider_configmigration. - Add IMAP provider form (OSS + EE).
- Implement IMAP OAuth2 flow (init + callback + token refresh).
- Stand up
services/email-servicewith IDLE + reconnect + publish.
Decisions
- 2025-12-26: IMAP v1 must include OAuth2 support (XOAUTH2).