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

126 lines
5.6 KiB
Markdown

# PRD — Inbound Email In-App Artifact Persistence (Remaining Work)
- Slug: `2026-02-27-inbound-email-inapp-artifact-persistence-remaining-work`
- Date: `2026-02-27`
- Status: Draft
## Summary
Close the remaining gaps so inbound email artifact handling is fully supported by **in-app callback-hook processing** (direct or app-local async worker), without requiring workflow-worker/event-bus execution for core artifact persistence.
Artifacts in scope:
1. Standard email attachments become ticket documents with persisted file bytes.
2. Referenced embedded images (`data:image/...` and HTML-referenced `cid:` images only) become ticket documents.
3. Original inbound email source is persisted as a deterministic `.eml` ticket document.
## Problem
Current in-app inbound processing creates tickets/comments, but artifact persistence behavior is incomplete/inconsistent versus target behavior:
- Attachment processing in in-app path is metadata-centric and does not reliably persist file bytes as full ticket documents.
- Embedded image extraction/persistence is not executed in the in-app path.
- Original email `.eml` persistence is not executed in the in-app path.
- IMAP webhook path is still event-bus handoff oriented and not aligned with in-app callback processing parity.
## Goals
- Ensure in-app inbound processing (new ticket and reply paths) persists all in-scope artifacts.
- Keep artifact persistence best-effort and non-blocking for ticket/comment creation.
- Maintain deterministic idempotency for retries/duplicates.
- Support IMAP callback execution in the in-app model with bounded processing and payload caps.
- Keep CPU/memory usage bounded during base64 decode and artifact conversion to avoid request-thread starvation.
## Non-goals
- Rebuilding document UI.
- Rewriting comment HTML to hosted image URLs.
- Persisting unreferenced inline CID parts.
- Introducing external workflow-worker or event-bus dependency for primary artifact persistence.
- Full distributed queue platform work (external brokers, multi-service orchestration).
## Decisions (Locked)
- Persist only CID inline images that are actually referenced by HTML (`cid:` links).
- Use deterministic source-email filename format: `original-email-<sanitized-message-id>.eml`.
- App-local in-process async worker mode is allowed.
## Users and Primary Flows
### Flow A — New inbound email (Google/Microsoft/IMAP)
1. In-app callback receives/fetches normalized email payload.
2. Ticket is created.
3. Attachment, embedded image, and `.eml` artifacts are processed and attached as ticket documents.
4. Any artifact failure is logged/recorded without rolling back ticket/comment creation.
### Flow B — Reply to existing ticket
1. In-app callback resolves thread/reply token and creates comment.
2. Artifact pipeline persists attachments, referenced embedded images, and `.eml` to the matched ticket.
3. Duplicate delivery remains idempotent.
### Flow C — Duplicate/retry ingress
1. Same message arrives again.
2. Ticket/comment dedupe remains intact.
3. Artifact persistence dedupes on deterministic keys (no duplicate document rows for same source artifact).
## Requirements
### Functional
- Extend in-app processing to run a unified artifact pipeline after ticket/comment creation for both new and reply flows.
- Accept attachment bytes in callback payload when available (`attachments[].content` base64) and persist actual files/documents.
- Reuse/expose embedded image extraction for in-app path:
- `data:image/*;base64,...` extraction,
- referenced `cid:` mapping to inline MIME parts,
- skip unreferenced CID inline parts.
- Persist one `.eml` document per inbound message in in-app path using available MIME source fields (`rawMimeBase64`/`sourceMimeBase64`/`rawSourceBase64`) or deterministic fallback assembly when required.
- Apply idempotency to each artifact class:
- provider attachment,
- extracted embedded image,
- original email `.eml`.
- Wire IMAP webhook path to in-app processing mode with event-bus fallback only when explicitly configured.
### Safety / Performance
- Enforce ingress caps for IMAP payload processing:
- per-attachment max bytes,
- total attachment bytes,
- attachment count,
- max MIME bytes for `.eml` persistence.
- Keep byte-heavy work off the request critical path when async mode is enabled (app-local worker queue).
- Bound per-message artifact processing concurrency to prevent memory/CPU spikes.
### Data / Contract
- Normalize/validate inbound payload fields needed by in-app artifact pipeline:
- `attachments[].id|name|contentType|size|contentId|isInline|content`,
- `rawMimeBase64`/`sourceMimeBase64`/`rawSourceBase64`,
- `ingressSkipReasons`.
## Acceptance Criteria
- In-app processing path (without workflow-worker) can ingest inbound email and produce ticket documents for:
- regular attachments,
- referenced embedded images,
- original `.eml`.
- Reprocessing the same message does not duplicate artifact documents.
- Oversize/over-limit artifacts are skipped with structured reasons while ticket/comment ingestion still succeeds.
- IMAP callback path supports in-app processing mode and honors caps.
## Risks
- Large base64 payloads may exceed request/body limits if not capped early.
- Byte decoding in request thread can degrade latency under burst conditions.
- Mixed provider payload shapes can cause subtle artifact loss if contract normalization is incomplete.
## Rollout
- Gate IMAP in-app artifact processing behind env/feature flags.
- Start with controlled tenants/providers, then expand.
## References
- Existing plan baseline (superseded for remaining-scope tracking):
- `ee/docs/plans/2026-02-27-inbound-email-embedded-images-and-original-eml/`