PSA/ee/docs/plans/imap-inbound-email-scratchpad.md
Hermes 284313f908
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
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

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_RECEIVED workflow 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_emails is 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, publishes INBOUND_EMAIL_RECEIVED via shared/events/publisher.ts.
  • Microsoft webhook: server/src/app/api/email/webhooks/microsoft/route.ts
    • Validates Graph notification, fetches message details via MicrosoftGraphAdapter, publishes INBOUND_EMAIL_RECEIVED (fallback minimal payload on fetch failure).
  • Workflow worker: services/workflow-worker/src/WorkflowWorker.ts
    • Consumes workflow events (Redis stream) and starts shared/workflow/workflows/system-email-processing-workflow.ts.
  • Event schema: shared/workflow/streams/eventBusSchema.ts
    • INBOUND_EMAIL_RECEIVED payload expects { providerId, emailData{...} }.

Important Files / Entry Points

  • Provider types + interfaces
    • shared/interfaces/inbound-email.interfaces.ts
    • server/src/interfaces/email.interfaces.ts
  • Email provider CRUD + orchestration
    • server/src/services/email/EmailProviderService.ts
    • server/src/lib/actions/email-actions/emailProviderActions.ts
  • Gmail integration (baseline)
    • server/src/services/email/providers/GmailAdapter.ts
    • server/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.md
    • docs/inbound-email/architecture/workflow.md
    • docs/inbound-email/development/adapters.md

Data Model Notes

  • Existing tables:
    • email_providers (provider_type: google|microsoft)
    • google_email_provider_config
    • microsoft_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.

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.tsx and ee/server/src/components/ImapProviderForm.tsx

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/initiate
      • GET /api/email/oauth/imap/callback

Implementation Notes (Initial)

  • Service location proposal: services/email-service (standalone worker like services/workflow-worker).
  • IMAP service should publish INBOUND_EMAIL_RECEIVED via shared/events/publisher.ts.
  • Read-only IMAP fetch should use BODY.PEEK[] to avoid marking as read.
    • IMAP service uses imapflow + mailparser to 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 stores To: imap_user in headers).
    • 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 to false to 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_SYNC window (based on mailbox uidNext) instead of replaying the whole mailbox from UID 1.
      • last_uid advances to the highest UID observed even when a message is skipped/deduped, to avoid repeatedly re-scanning the same window.
    • 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.

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_config migration.
  • Add IMAP provider form (OSS + EE).
  • Implement IMAP OAuth2 flow (init + callback + token refresh).
  • Stand up services/email-service with IDLE + reconnect + publish.

Decisions

  • 2025-12-26: IMAP v1 must include OAuth2 support (XOAUTH2).