Skip to content

Disposable Mail Engine (Stalwart RFC 8620 JMAP) ​

June features an integrated ephemeral email platform powered by Stalwart Mail Server using modern JMAP (JSON Meta Application Protocol, RFC 8620 & RFC 8621). This architecture replaces antiquated IMAP/POP3 polling with efficient, stateless JSON-over-HTTP requests and real-time event streaming.


Architectural Workflow ​

mermaid
sequenceDiagram
    autonumber
    actor Sender as External Mail Sender
    participant MX as MX (temp.4ofl.dev)
    participant Stalwart as Stalwart JMAP Server
    participant June as June Application Core
    participant DB as SQLite DB
    participant SSE as Server-Sent Events Hub
    actor User as Discord / Web User

    User->>June: Create Inbox (/tempmail or Dashboard)
    June->>DB: Store Address Lease & Expiry (Fixed TTL: 60m)
    June-->>User: Return Allocated Address (e.g. 7f6899f...@temp.4ofl.dev)
    
    Sender->>MX: Deliver Email via SMTP
    MX->>Stalwart: Ingest & Parse MIME Message
    
    par Real-Time Push Notification
        Stalwart->>June: Webhook / JMAP Push Notification
        June->>SSE: Broadcast New Message Event
        SSE-->>User: Live Notification on Dashboard
    and On-Demand Query
        User->>June: Fetch Inbox / Read Message
        June->>Stalwart: JMAP Email/get Request (using resolved accountId)
        Stalwart-->>June: Return Parsed JSON (Headers, Text, HTML)
        June-->>User: Render Sanitized Email Viewer
    end

Why JMAP (RFC 8620)? ​

Legacy mail protocols like IMAP require persistent stateful socket connections, expensive bandwidth-heavy synchronization, and complex MIME parsing on the client.

JMAP provides distinct architectural advantages:

  1. JSON Native: Message payloads, headers, threading, and mailbox metadata are returned as structured JSON objects.
  2. Stateless HTTPS: Requests execute over standard TLS-encrypted HTTPS endpoints (/jmap/session), eliminating socket state issues across container restarts.
  3. Pipelining & Batching: Multiple calls (e.g. fetch mailbox list + list newest messages) execute in a single round-trip HTTP request.
  4. Memory Efficient: Payloads are streamed with strict size boundaries (MAX_JMAP_RESPONSE_BYTES = 2MB), preventing memory exhaustion.

Ingestion & Lifecycle Management ​

1. Ephemeral Address Allocation & SQLite Leases ​

When an inbox is created via /tempmail or the web dashboard:

  • A cryptographically randomized address is generated: <hex>@temp.4ofl.dev.
  • A fixed 60-minute Time-To-Live (TTL) (TEMPMAIL_TTL_SECONDS=3600) is recorded in SQLite alongside the owning Discord user ID.
  • Inboxes are strictly isolated: only the owning Discord user (or authenticated dashboard session) can view incoming messages.

2. Message Querying (Email/query and Email/get) ​

To fetch messages for an address, June's JMAP client (src/utils/tempmail.js) first queries the JMAP session endpoint (/jmap/session) to extract the resolved account ID from session.primaryAccounts['urn:ietf:params:jmap:mail'] (e.g. "june"), and then constructs an authenticated JMAP invocation:

json
{
  "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
  "methodCalls": [
    [
      "Email/query",
      {
        "accountId": "<resolved_account_id>",
        "filter": { "to": "7f6899f3a1@temp.4ofl.dev" },
        "sort": [{ "property": "receivedAt", "isAscending": false }],
        "limit": 10
      },
      "call-0"
    ],
    [
      "Email/get",
      {
        "accountId": "<resolved_account_id>",
        "#ids": {
          "resultOf": "call-0",
          "name": "Email/query",
          "path": "/ids"
        },
        "properties": ["id", "blobId", "threadId", "from", "to", "subject", "receivedAt", "preview", "hasAttachment"]
      },
      "call-1"
    ]
  ]
}

The server evaluates the query and returns the matching email headers in a single atomic response.

3. Expiry vs Storage Boundary ​

It is important to understand the boundary between June's SQLite lease and Stalwart's message store:

  • June SQLite Lease Expiry: After 60 minutes, the address lease in SQLite expires. June's periodic purge routine unbinds the address, and the bot/dashboard will no longer query or expose messages for that address.
  • Stalwart Server Storage: The underlying MIME emails and raw message bodies stored on Stalwart Mail Server are subject to Stalwart's server-side retention policies or administrative cleanup tasks. Expiring a lease in June removes access to the address, while Stalwart manages low-level storage lifecycle.

4. HTML Sanitization & Safety ​

When rendering email bodies on Discord or the web dashboard:

  • Zero Raw Execution: Script tags, inline event handlers (onload, onerror), and external CSS tracking pixels are scrubbed before display.
  • Link Defanging: Hyperlinks are analyzed through June's security scanner before clickthrough.
  • Attachment Protection: Attachments exceeding 10MB are blocked from previewing to protect client and server memory.

Obsidian-engineered Discord utility platform & temporary intelligence engine.