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
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
endWhy 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:
- JSON Native: Message payloads, headers, threading, and mailbox metadata are returned as structured JSON objects.
- Stateless HTTPS: Requests execute over standard TLS-encrypted HTTPS endpoints (
/jmap/session), eliminating socket state issues across container restarts. - Pipelining & Batching: Multiple calls (e.g. fetch mailbox list + list newest messages) execute in a single round-trip HTTP request.
- 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:
{
"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.