tenmin.app is deliberately small enough to reason about end to end. The backend consists of an edge SMTP ingress gateway, an isolated MIME ingestion service, a strongly consistent SQLite lease store, a read-only HTTP API, and a pre-rendered static web client. Every component is stateless or strictly bounded by a 600-second time-to-live (TTL).
This guide walks through what each layer does, why we designed it this way, and the security and consistency trade-offs behind a zero-retention disposable email service.
System topology
Sender's mail server (MTA)
│
▼
Edge SMTP Ingress (TLS catch-all *@tenmin.app)
│
▼
Ingestion Service (stateless isolate)
• validates local-part against /^[0-9a-f]{6}$/
• checks active lease in SQLite (expires_at > now)
• rejects unallocated mail with SMTP 550
• parses RFC 5322 MIME stream & stores bounded body
• reaps expired rows asynchronously off the critical path
│
▼
Ephemeral Lease Store (serverless SQLite)
table: inboxes (inbox_id PK, messages_json, expires_at)
ttl: 600-second rolling lease, strongly consistent
▲
│
Read API Service (stateless HTTP JSON endpoint)
• GET /api/inbox/:id -> provisions/reads lease
• GET /api/inbox/:id?extend=1 -> extends expires_at
• strict Origin allowlist (no wildcard CORS)
▲
│
Web Client (pre-rendered HTML + browser SPA)
• polls /api/inbox/:id every 3 seconds
• sanitizes & mounts HTML inside sandbox="" iframe
• syncs visual countdown with server-returned ttlThere is no external message queue, no persistent disk archive, and no background cron daemon. Lease expiration is enforced logically on every SQLite query (expires_at > now) and physically cleaned up asynchronously whenever new mail arrives.
Core architectural layers
1. Edge SMTP ingress
Instead of routing inbound mail into a traditional POSIX maildir server (like Postfix or Dovecot on a long-lived VM), our MX records terminate SMTP over TLS at the network edge and pipe the raw RFC 5322 byte stream directly into an event-driven ingestion handler. That handler verifies whether the target inbox has an active 600-second lease in storage and either parses the message or rejects it immediately during the SMTP transaction with a 550 status code.
2. Isolated stateless compute
We separate inbound SMTP processing from outbound HTTP read traffic into two independent services. Both run inside lightweight isolates with no shared mutable memory and communicate exclusively through the ephemeral lease database. Separating ingestion from HTTP reads keeps each service small, auditable, and independently rate-limited.
3. Strongly consistent SQLite leases (vs. eventually consistent KV)
Early iterations of ephemeral inbox architectures often rely on distributed key-value caches with built-in expirationTtl settings. While KV stores automate key deletion, their eventual consistency model causes edge nodes to cache empty lookup misses when browsers poll every three seconds right before a verification email arrives. KV caches also lack partial column updates for extending timers without rewriting full message payloads.
Using serverless SQLite provides strong read-after-write consistency, exact server-computed remaining TTLs, single-column expires_at extensions, and immediate SMTP-level rejection for unallocated addresses. We cover those trade-offs in Migrating ephemeral TTL state from eventually consistent KV caches to serverless SQLite.
4. Build-time static pre-rendering
During every build, a server-side pre-render pass compiles all application routes, reference guides, and structured metadata into static HTML documents so search crawlers and browsers receive complete markup on the initial HTTP response without waiting for client script hydration.
The inbox ID design
The local-part of the address (the substring before @tenmin.app) serves as the inbox primary key. The browser generates six random lowercase hexadecimal characters ([0-9a-f]{6}) using crypto.getRandomValues, yielding 16,777,216 possible combinations. Two security properties follow from this design:
First, inbox IDs are not private credentials. Sixteen million combinations prevent accidental collisions during normal usage, and SMTP deliveries are only accepted while an inbox lease is active, but anyone who knows or guesses an active six-character hex ID can query that inbox during its ten-minute window. Disposable inboxes are built for low-stakes verification codes and QA tests; they should never be used for confidential correspondence or permanent account recovery.
Second, once the 600-second window closes, the row is logically expired immediately and physically purged on the next cleanup pass, ensuring prior messages never carry over if the same hex ID is generated weeks later.
Input validation across three layers
We enforce the exact same local-part regular expression in three separate places: /^[0-9a-f]{6}$/. The SMTP ingestion service rejects any inbound message whose recipient fails this check. The HTTP read API returns HTTP 400 for any non-matching identifier and enforces a strict Origin allowlist. And the client generator emits only compliant six-character hex identifiers.
Inside the ingestion service, text and HTML bodies are bounded with strict byte caps and each inbox retains at most 25 messages so an automated sender cannot exhaust row storage limits.
HTML rendering and XSS isolation
Inbound email contains arbitrary, untrusted HTML. Rendering that markup directly inside the application DOM would allow cross-site scripting against the user's browser session. We isolate message bodies using two independent layers:
- Pre-render HTML sanitization: Before passing markup to the viewer, the client strips executable elements (
<script>,<iframe>,<object>,<embed>), inline event handlers (on*attributes), and dangerous URI schemes (javascript:,vbscript:,data:text/html). - Opaque-origin sandboxed iframe: The sanitized HTML is mounted inside an
<iframe sandbox="">with an empty sandbox token list. The frame runs in a unique opaque origin with JavaScript execution disabled, no parent DOM access, and no ability to trigger top-level navigation.
We allow standard https:// images inside the sandboxed frame because blocking remote images breaks brand logos and layout assets in legitimate verification emails. Users who want to avoid firing sender open-tracking pixels can inspect the plain-text MIME part. For a deeper look at our sanitization pipeline, see Preventing XSS when rendering untrusted HTML emails in a browser.
The client polling lifecycle
The browser polls /api/inbox/:id every three seconds while an inbox is active and syncs its visual countdown with the server-returned ttl value. Each poll attaches an AbortController tied to the active inbox ID so clicking Refresh immediately aborts any in-flight HTTP request from the previous address and prevents stale responses from populating the new view.
A separate one-second interval drives the visual countdown timer between network polls. To prevent the one-second tick from restarting the three-second poller, the polling loop reads remaining timer state through a mutable reference rather than re-subscribing on every tick.
Further reading
- Migrating ephemeral TTL state from eventually consistent KV caches to serverless SQLite examines the consistency, lease-management, and garbage-collection trade-offs behind the storage layer.
- How RFC 5322 MIME parsing works in serverless edge environments covers binary stream parsing and character-set decoding during inbound SMTP ingestion.
- Preventing XSS when rendering untrusted HTML emails in a browser details the DOM sanitization and iframe sandboxing model.
Need an ephemeral test inbox right now?
Generate a fresh @tenmin.app address in one click. Zero registration, sandboxed HTML rendering, automatic 600-second TTL deletion.