npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ratacat/cloudmail

v0.2.0

Published

Robot-mode CLI for disposable email identities on any Cloudflare account.

Readme

cloudmail

Give your AI agent its own email address on a domain you own.

Agents hit "check your email for a verification code" all the time. cloudmail gives an agent a real address, a place where mail lands, and small commands to read, send, and reply. No inbox UI, no IMAP, no server of your own to run.

incoming mail  → Email Routing → Worker + D1 → cloudmail latest / code / wait
cloudmail send / reply → Worker + D1 → Email Service → outgoing mail

Why cloudmail

This is one of the smallest and cheapest ways to set up agent mail on a custom domain.

  • Small. One Worker, one database, one CLI. You can read the whole stack in a sitting.
  • Cheap. Receiving fits Cloudflare's free tier. The $5/month Workers plan covers more agent mail than you will likely ever send, with no per-inbox or per-message fees. Add a domain for about $10 a year and that is the whole bill.
  • Yours. The address lives on your domain, in your Cloudflare account. Password resets and magic links land in a store you own, not in someone else's inbox product.
  • Built for agents. Stable JSON, fixed exit codes, dense output, a self-describing schema, and an MCP server. An agent never scrapes human text.

Install

Needs Bun; the CLI runs on it.

npm i -g @ratacat/cloudmail      # installs the `cloudmail` command
# or run without installing:
bunx --package @ratacat/cloudmail cloudmail help

No npm? The clone-based installer works too (needs git):

curl -fsSL https://raw.githubusercontent.com/ratacat/cloudmail/main/install.sh | bash

Set up an inbox

You need one Cloudflare account with a domain on it.

cloudmail init --domain yourdomain.tld        # prints the ordered plan + a generated API key
cloudmail init --domain yourdomain.tld --run  # or: run it (detects wrangler; rerun-safe)

The plan creates a D1 database, deploys the worker in worker/, enables Email Routing (receive) and Email Sending (send), and ends by saving a profile:

cloudmail config set me \
  --worker-url https://mail.yourdomain.tld \
  --api-key <the-generated-key> \
  --active

One manual step remains in the dashboard: route your address (or the catch-all) to the cloudmail Worker under Email Routing → Routing rules.

Wrangler prefers a CLOUDFLARE_API_TOKEN environment variable over wrangler login. If one is set for a different account (or with too few permissions), init fails with Authentication error [code: 10000] — unset it for the run: env -u CLOUDFLARE_API_TOKEN -u CLOUDFLARE_ACCOUNT_ID cloudmail init ...

Upgrading an existing worker: cloudmail doctor --deep shows what is stale and cloudmail doctor --fix applies the pending schema migrations, then redeploy. Or run them by hand from worker/migrations/.

Try it

cloudmail                 # help (run with no args anytime)
cloudmail latest          # show the newest email that arrived
cloudmail code --wait 60  # wait up to 60s for a verification code/link
cloudmail list            # recent emails
cloudmail doctor          # is everything healthy?

Uninstall

npm rm -g @ratacat/cloudmail    # if installed via npm
curl -fsSL https://raw.githubusercontent.com/ratacat/cloudmail/main/install.sh | bash -s -- --uninstall
rm -rf ~/.cloudmail             # profiles and keys

To tear down the cloud side as well: cd worker && wrangler delete removes the Worker, wrangler d1 delete cloudmail removes the mail store, and Email Routing switches off in the dashboard.

Where this fits Cloudflare's email rules

Cloudflare splits email in two, and cloudmail uses both halves as they are meant:

  • Email Routing (receiving) is free. cloudmail points your address at a Worker instead of forwarding it to another inbox.
  • Email Service (sending) is for transactional mail only: messages one person or system expects, triggered by an action. Cloudflare's terms rule out bulk and marketing mail.

Agent mail is transactional by nature. An agent signs itself up for a service, reads back the code, answers a thread, sends a status note. Low volume, one recipient, expected. That is the shape Email Service allows, so cloudmail fits the rules without bending them.

Need newsletters or campaigns? Wrong tool. Use a bulk sender (SES, Resend, Mailgun, Postmark) on its own subdomain and keep your agent's address clean.

Alternatives

| Tool | Shape | Good fit when | |------|-------|---------------| | AgentMail | Hosted inbox API built for agents | You want zero setup and don't mind renting the identity | | Gmail + app password | Free inbox, heavy API | One throwaway identity; risk of lockouts and rate limits | | MailSlurp / testmail | QA inbox APIs | Test suites; per-message pricing at volume | | SES + Lambda + S3 | Build-it-yourself receiving | High volume and you enjoy plumbing | | Resend / Postmark / Mailgun | Sending platforms with inbound webhooks | Bulk or marketing mail; you still host the store | | Self-hosted MTA (Postfix) | Full control | You own a clean IP and like deliverability work |

cloudmail's bet: for one agent, or a few, on a domain you own, the smallest stack wins.


For agents

Everything below is what an AI agent needs. Stable JSON, fixed exit codes, and a self-describing schema; no scraping human text.

Robot-mode guarantees

| Feature | Behavior | |---------|----------| | JSON output | --json on every command; envelope { ok, data, error, meta }. Auto-JSON when piped. | | Tiny help | bare cloudmail ≈150 tokens; cloudmail robot-docs --json = full machine schema | | Structured errors | { code, message, suggestions[] } | | Exit codes | 0 ok · 1 not-found · 2 bad-args · 3 auth · 4 config · 5 cf-api · 6 network · 124 timeout | | Token-thrifty | email bodies truncated unless --full; everything else dense |

Discover the whole surface in one call:

cloudmail robot-docs --json

Commands

latest     [--to A] [--since ISO]                                      newest email (one object)
list       [--limit N] [-n N] [--unread] [--sent]                     recent emails, newest first
get        <id>                                                        one email by id; marks it read
code       [--to A] [--since ISO] [--wait S]                           extract verification code/link (long-poll w/ --wait)
wait       [--to A] [--timeout S]                                      block until a new email arrives
send       --to A --subject S (--text T | --html H) [--from F] [--from-name N] [--reply-to R]  send email
reply      <id> (--text T | --html H) [--subject S]                    reply to an inbound email (targets its Reply-To, else From)
init       [--domain D] [--api-key K] [--run]                          guided Cloudflare setup; --run converges (rerun-safe)
config     get|set|list|use                                            manage profiles (multi-account)
status                                                                  resolved profile + worker target
doctor     [--deep] [--fix]                                            diagnose; --deep checks provisioning, --fix repairs it
robot-docs                                                              machine schema of commands + codes
mcp                                                                     run as a stdio MCP server

Typical agent flow: sign up somewhere, then

cloudmail code --wait 90 --json   # poll for the code the signup just triggered

MCP mode

cloudmail mcp   # stdio MCP server exposing: cloudmail_latest, cloudmail_list, cloudmail_get, cloudmail_code, cloudmail_wait, cloudmail_send, cloudmail_reply

Multi-account / config

Profiles live in ~/.cloudmail/profiles.json (mode 0600). Switch with --profile/-p, or set CLOUDMAIL_WORKER_URL + CLOUDMAIL_API_KEY for an ephemeral env profile (wins over saved).

cloudmail config set work --worker-url https://mail.acme.dev --api-key $KEY
cloudmail -p work latest

Architecture

Layout and conventions live in AGENTS.md; the frozen per-module contracts in INTERFACES.md.

Develop

bun test               # unit suite (never touches the network)
bun run test:live      # opt-in e2e against your real account
bun run typecheck
bun run cli -- latest --json

Security

This address is an identity root: password resets and magic links arrive here. Keep the worker API_KEY secret and require human approval before an agent creates accounts, sends mail, or clicks sensitive links. Sending is enabled per worker with a Cloudflare Email Service send_email binding and SEND_FROM variable. A worker deployed without the binding stays receive-only; POST /send returns 501 (SEND_DISABLED). To deploy receive-only on purpose, delete the send_email entry from worker/wrangler.jsonc first.

License

MIT