{"uuid": "95bee3a5-edb3-45d6-aad9-be2eba32584d", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "cve-2025-32711", "type": "seen", "source": "https://gist.github.com/DavidIfebueme/e48db44884b59184be41c2228b5fd0db", "content": "# Garden Agentic Mail \u2014 Architecture Design\n\nstatus: draft for review. no code. extends konan's `feat/garden-mail` poc, transplanted onto current dev as `feat/agentic-inbox` (ci green).\ndate: 2026-09-21.\n\n## 1. The one line rule\n\ngarden owns mail. providers only move it.\n\ndomains, addresses, mailboxes, access, conversations, messages, drafts, attachments, delivery history, read state, assignment, authorship, approvals, audit. all garden data. cloudflare carries bytes in and out today. a self hosted transport replaces cloudflare later without migrating data, changing ui, or changing permissions.\n\n## 2. Context: what exists today\n\n- issue 127 shipped the thin human slice (merged as #129): composer writes real gmail drafts and sends through the member's own connection, draft and sent tabs read live gmail. no ledger, no agents, gmail stays source of truth.\n- konan's `feat/garden-mail` poc (107 commits, unmerged) built the agentic direction: gmail import into garden, per conversation agent collaboration, strict approval chain, cloudflare hosted garden addresses, composer with attachments. full handoff doc at `docs/architecture/garden-mail-handoff.md` on his branch.\n- this branch transplants his modules onto current dev. domain, services, schema plus migration, agent scope, import plugin, approval tracker, settings and mail ui, routes. ci green.\n- honestly incomplete: his mail views sit on disk unmounted (inbox route still renders the legacy page), toolkit scoped sessions need a newer executor sdk than this repo pins, delivery workflow env wiring still to prove in deploys.\n\n## 3. Goals and non goals\n\ngoals:\n- a member reads, triages, and answers mail without leaving garden.\n- an agent drafts and researches, never sends alone.\n- a team shares mailboxes with assignment, collision safety, and full attribution.\n- company addresses on garden domains that outlive any provider.\n\nnon goals for v1:\n- no imap/pop compatibility (only if demand proves it).\n- no auto send without approval except an explicit graduated policy later.\n- no replacing gmail overnight. gmail import is the bridge.\n\n## 4. Landscape in one paragraph\n\neveryone drafts, nobody sends alone. superhuman, shortwave, fyxer, missive, gemini, copilot: drafts land for review, sends need a click. missive leads team mailboxes with assign plus collision handling but hosts nothing. copilot and gemini are single vendor locked. notion mail shuts down 2026-09-22, which strands gmail only users. nobody ships hosting plus agents in one product, nobody does graduated auto send with audit, nobody is provider neutral. that empty middle is garden's wedge: domain onboard to working agent inbox on day one.\n\n## 5. Personas and core stories\n\n- executive plus assistant agent. private mailbox shared with one agent. agent triages and drafts, human sends.\n- shared deals mailbox. three members, two agents. queue first, one owner second, talk on the thread.\n- human edits, another human sends. all authorship visible.\n- agent removed mid work. leases expire, turns fail closed.\n- provider bounces after accept. ledger records it separately from the authored message.\n\n## 6. Architecture\n\n### 6.1 context\n\n```mermaid\nflowchart LR\n    Internet((internet mail)) &lt;--&gt; CF[cloudflare email service]\n    Gmail[(member gmail)] &lt;--&gt; GImp[gmail import workflow]\n    CF --&gt; Ingest[garden ingest]\n    GImp --&gt; Ledger[(garden mail store)]\n    Ingest --&gt; Ledger\n    Ledger --&gt; Inbox[inbox surface]\n    Ledger --&gt; Agents[garden agents]\n    Agents --&gt;|drafts, never sends| Ledger\n    Humans --&gt;|approve, send| Ledger\n    Ledger --&gt; CF\n```\n\n### 6.2 containers\n\n```mermaid\nflowchart TB\n    subgraph Worker[garden worker]\n        UI[inbox + settings ui]\n        API[mail api: server functions]\n        Repo[mail repository + services]\n        Orch[agent orchestration + approval]\n        W1[gmail import workflow]\n        W2[delivery workflow]\n    end\n    PG[(postgres: mail tables)]\n    R2[(r2: raw mime + attachments)]\n    D1[(executor d1: connections)]\n    API --&gt; Repo --&gt; PG\n    Repo --&gt; R2\n    W1 --&gt; PG\n    W2 --&gt; PG\n    UI --&gt; API\n```\n\n### 6.3 inbound flow\n\n```mermaid\nsequenceDiagram\n    participant CF as cloudflare\n    participant W as worker email()\n    participant N as normalize + parse\n    participant R as repository ingest\n    participant Q as inbox projections\n    participant A as agents\n    CF-&gt;&gt;W: raw message\n    W-&gt;&gt;N: buffer once, parse mime\n    N-&gt;&gt;R: resolve address, idempotent insert\n    R-&gt;&gt;R: store raw + attachments in r2\n    R-&gt;&gt;Q: update conversation + viewer state\n    Q-&gt;&gt;A: notify triage/draft workers\n    Note over A: agents draft only, writes pause for approval\n```\n\n### 6.4 draft approval send flow\n\n```mermaid\nsequenceDiagram\n    participant H as human\n    participant U as composer ui\n    participant S as server\n    participant A as approval record\n    participant T as transport\n    H-&gt;&gt;U: review draft, press send\n    U-&gt;&gt;S: send with draft id + revision\n    S-&gt;&gt;S: recheck mailbox access + policy\n    S-&gt;&gt;A: mint single use approval, hash recipients subject body attachments\n    alt approval required\n        A-&gt;&gt;H: approve or decline card\n        H-&gt;&gt;A: approve\n    end\n    S-&gt;&gt;S: rederive hash from live state, fail closed on drift\n    S-&gt;&gt;T: send once, atomic consume\n    T-&gt;&gt;S: queued, delivered, bounced, failed\n    S-&gt;&gt;S: ledger outcome separate from authored message\n```\n\n### 6.5 agent turn scoping\n\n```mermaid\nflowchart LR\n    Turn[agent turn starts] --&gt; Tok[opaque context token]\n    Tok --&gt; Scope[resolve member agent mailbox intersection]\n    Scope --&gt; Tools[visible tools: compose_mail + scoped executor only]\n    Tools --&gt; Draft[draft handoff to composer]\n    Draft --&gt; Human[human reviews, edits, sends]\n```\n\nmail turns run on the normal garden agent with a mail only leash: context token, least privilege mailbox intersection, executor catalog narrowed to the authorized gmail connections, compose as client side handoff. the agent has no send tool.\n\n## 7. Security, the non negotiables\n\n1. agent drafts only. no send tool in agent hands. send lives outside the agent and needs human action. zero click mail to exfiltration is proven in production (cve-2025-32711), so containment cannot depend on model obedience.\n2. hash bound single use approval with expiry. canonical sha-256 over recipients subject body attachments, nonce, 60s to 15min lease, atomic consume, server side recheck at send, fail closed.\n3. per agent identity with mailbox scoped acls plus egress allowlist. unique credential, narrow scope, rate limits, revoke alone.\n4. untrusted data pipeline. rendered text only, concealment detection, provenance separation, html sanitize, no auto click or fetch, tracking pixels stripped.\n5. full actor attributed audit. every draft edit approval send logged with who, which agent and model, which approval, idempotency key. append only, 12 to 18 months.\n6. pii minimization and provider governance. redact before third party model calls, tiered retention with deletion evidence, dpas plus zero retention, subprocessor disclosure.\n\n## 8. UX spine\n\nsort into at most 7 labeled lanes with counts, bundles for bulk sweep, drafts by default with insert to keep, 2 to 3 variants per thread with sources shown, coaching on tone with apply all, auto send only per narrow playbook default off with hold for review, one owner per shared thread with live presence and blocked second send, internal notes visually distinct from customer replies, expiry on all automation, log what fired on every ai action. one correction from research: no vendor exposes numeric confidence sliders, so gate by explicit scope per playbook, not by score.\n\n## 9. Infrastructure path\n\ncloudflare email service first: managed ips, auto spf dkim dmarc, no warmup at low volume, 3000 outbound free then usage pricing. caps driving design: 5 mib outbound wire (~3.5 mib usable files, link fallback above it), 50 recipients, 30 domains per zone, account scoped suppressions. multi tenant onboarding automates dns verification, spf merge, dmarc ramp, subdomain isolation, warmup metering, suppression layering. self hosted later (stalwart for light jmap, mailcow for full groupware, postal for outbound tracking) behind the transport seam, no data model rewrite.\n\n## 10. Build order from here\n\n1. mount mail views into the inbox page beside all unread draft sent.\n2. settings tab for domains mailboxes actors.\n3. first live gmail import against a real mailbox, watch the 20k segment path.\n4. supervised agent send end to end on a test mailbox.\n5. executor sdk upgrade for toolkit scoped sessions, then enable scoped catalog.\n6. delivery workflow env wiring in deploys plus bounce webhook route.\n7. graduated auto send policy behind scope gates plus audit.\n8. self hosted transport adapter.\n\n## 11. Open decisions\n\n- one composer or two. recommend one composer with human and agent modes.\n- one draft truth. reconcile gmail drafts with observed agent draft snapshots.\n- do we promise imap ever. default no.\n- retention tiers and eu residency. needed before customer mail.\n- pricing packaging for sending volume plus agent turns.\n\n## 12. Test strategy\n\nunit for schemas, state machines, approval exactness. postgres integration for repository, sync ledger, delivery prepare submit complete. workflow step retry and continuation tests. route tests for sync settings content. mounted ui tests once views land. load test the 500 page enumerate path. red team prompt injection suite before any autonomy increase.\n", "creation_timestamp": "2026-09-21T23:56:14.533292Z"}