popterminal gives your agents a real @popterminal.com address that streams OTPs, magic links, and signups into a CLI, an MCP server, and a local web inbox — all from one npm install. No signup. No DNS. No Cloudflare account.
One install. Three surfaces.
All three ship in the same npm install -g popterminal. They share the same SQLite store and the same anonymous handle. Pick whichever fits the moment.
The original surface. Predictable exit codes, --json on every read, wait blocks until matching mail arrives. Fits the OTP-retrieval pattern in 6 lines of shell.
$ popterminal wait code \ --subject "code" \ --timeout 60s --json {"text":"Code: 482913", …}
Stdio MCP server. Wire into Claude Desktop / Cursor / Claude Code with one config line and the agent gets wait_for_email, list_emails, read_email as native tools.
"mcpServers": { "popterminal": { "command": "popterminal-mcp" } }
Three-pane browser inbox at 127.0.0.1:9090. Live updates via Server-Sent Events, sandboxed iframe for HTML rendering, raw JSON view per message, click-to-copy address.
$ popterminal web popterminal web ready at http://127.0.0.1:9090 # opens your browser…
Why popterminal
A real domain, real MX, real mail — with the rough edges sanded down for shell-driven workflows.
Mail reaches you through Cloudflare's actual mail servers. GitHub, Stripe, your bank — anyone who emails you, gets through.
One command, you have an address. No signup, no DNS, no Cloudflare account. Your handle persists across restarts via a local token.
Predictable exit codes. --json on every read. wait blocks until mail arrives, prints JSON, exits clean.
Plus-addressing splits mail by tag — +code, +signup, +anything. One handle, infinite scoped inboxes.
Buffered server-side for 48 hours. Replayed when you reconnect. Ack-required protocol so nothing is lost on a crash.
Mail lands in SQLite at ~/.popterminal/popterminal.db. Yours, on your laptop, queryable forever.
How it works
popterminal sits between Cloudflare's MX layer and your local CLI. No public IP. No tunnel binary. No DNS.
Dive in
01 — Address
The first time you run popterminal start, the CLI registers an anonymous handle, stores its token in ~/.popterminal/anonymous-token (mode 0600), and opens a persistent WebSocket back to popterminal.com.
Subsequent restarts reuse the same handle. Your address survives reboots, network changes, laptop swaps. Only popterminal handle reset rolls you to a new one.
02 — Tag
One handle gives you infinite sub-inboxes for free. Mail to <handle>+code@popterminal.com lands in inbox code. Mail to +signup lands in signup. No configuration.
Useful for keeping signups, OTPs, and notifications separated in tests, or for scoping a fresh inbox to each agent run.
03 — Wait
popterminal wait is the primitive your agents reach for. It blocks until matching mail arrives, prints clean JSON, exits with code 0. Timeout exits with 1. No matching mail? Code 1. No daemon? Code 3.
Pipe to jq, parse the OTP, continue your script. The whole pattern fits on one line.
In the wild
Three patterns we see most often. Each takes under 10 lines of shell.
Sign up a synthetic user, receive the verification code, complete the flow. The whole loop fits in three commands.
# trigger the signup, then… $ popterminal wait code --json \ | jq -r .text \ | grep -oE '\d{6}' 482913
Receive the magic-link email, extract the URL, hit it programmatically. Agents complete passwordless login without a browser session.
# click the magic link, headlessly $ popterminal wait login --json \ | jq -r .text \ | grep -oE 'https://[^\s]+' \ | xargs curl -L 200 OK — session established
Trigger your app to send an email, capture it, assert the rendered HTML. End-to-end test without spinning up a fake SMTP.
# assert the reset email renders $ trigger-reset-flow $ popterminal wait reset --json \ | jq '.html' \ | grep 'Reset your password' "Reset your password"
Universal deliverability
popterminal is plumbed into Cloudflare's actual email infrastructure. Anyone who can send to a real address — automated, transactional, human — reaches your inbox.
One install. One command. A real address that streams mail into a SQLite-backed CLI.