# Probe transcript — mail.tm (e2e) | | | |---|---| | Entry | `entries/mail-tm.yaml` | | Target | https://mail.tm · API `https://api.mail.tm` · docs `https://docs.mail.tm` | | Mode | `e2e` (gate + e2e in one run; gate reads preceded every state-changing request) | | Probe id | `mail-tm-e2e` | | Rubric | `rubric/v0.2.json` sha256 `cd605a44799f49951bf078fda63c5b21ad3c07db494f41d38ad19ce729c5551a` | | Date | 2026-09-05 | | Sandbox up | 2026-09-05T13:07:25Z | | Sandbox down | 2026-09-05T13:09:51Z | | Wall clock | 146 s (epoch 1788613645 → 1788613791; measured from the shell invocation that brought the sandbox up to completion of teardown) | | Real money spent | $0.00 — no payment instrument presented, none requested | | Human interventions | 0 | | Verdict | `completed` | | Secrets carried in | none. `PROBE_WALLET_KEY` unset, `PROBE_INBOX` unset, `PROBE_SPEND_CAP_USD=0`. Secrets created *inside* the run: one random mailbox password and one JWT, both burner, both redacted below; the account was deleted at the end of the run (§8) | Every request below was issued from inside the probe sandbox (`.claude/skills/probe/probe-sandbox.sh`), on an `--internal` Docker network whose only egress is an allowlisting tinyproxy sidecar. No request to the target was made from the host shell. Gate-mode reads of `docs.mail.tm`, `mail.tm/en/tos/`, `/en/tos`, `/en/terms/`, `/en/privacy/`, `/en/`, `api.mail.tm/docs.json` and `api.mail.tm/robots.txt` were additionally made with the host `WebFetch` tool (rendered-page reads only) before the sandbox came up; every quote in the entry that matters was re-retrieved as raw bytes inside the sandbox. ```sh ./.claude/skills/probe/probe-sandbox.sh up --id mail-tm-e2e \ --target api.mail.tm --allow docs.mail.tm --allow mail.tm ``` Allowlist: `api.mail.tm`, `docs.mail.tm`, `mail.tm`. Nothing else. Rate-limit discipline: the docs state "8 queries per second (QPS) per IP"; the run slept 1 s between every request (≤ 1 QPS) and issued 21 requests in total. --- ## 0. Reference task > With no credentials, GET `https://api.mail.tm/domains` and take the first > active domain; POST `/accounts` with a random local-part on that domain and a > random password; POST `/token` with the same pair; then GET `/messages` with > the returned bearer token and pass if it is HTTP 200 with an empty member list > and `totalItems` 0. Pass/fail is one jq expression. The task deliberately stops at "an empty inbox exists": sending real mail into it would need an outbound mail service outside the allowlist. What it proves is the thing the orchestrator asked about — that an agent can provision a programmable inbox from nothing. --- ## 1. Permission gate (read before any state was created) ### 1.1 The terms instrument No terms page exists on the consumer site: `https://mail.tm/en/tos/`, `/en/tos` and `/en/terms/` all render the SPA's "404 Error — Message not found!" page (WebFetch, 2026-09-05). The privacy policy at `/en/privacy/` mentions "the site editor's policy, terms of service, or other usage guidelines" without linking any. The only terms instrument on the property is the **"Terms of Use"** block on the API documentation home, `https://docs.mail.tm/`. Raw source, from `https://docs.mail.tm/llms-full.txt` lines 78–116 (HTTP 200, 19 471 bytes, retrieved in-sandbox): ```untrusted-content ::u-page-section --- features: - title: No API key required description: Just call the endpoints. No signup, no tokens to start. - title: 8 QPS rate limit description: The general quota limit is 8 queries per second (QPS) per IP address. description: Completely free. No API key, no signup, no paid tiers. headline: Pricing title: Access to the API --- ::u-page-section --- features: - title: No illegal activity description: Usage of our API for illegal activity is strictly prohibited. - title: No reselling description: Don't build a paid product that just wraps our API. - title: No proxy services description: Don't mirror or proxy our API under a different domain. - title: Attribution required description: If you use our API, link back to mail.tm somewhere visible. headline: Policy title: Terms of Use --- ``` Four conditions. None mentions bots, scripts, scrapers, automation, or agents. The instrument affirmatively invites unauthenticated programmatic use. ### 1.2 robots.txt, llms.txt, security.txt (raw bytes, in-sandbox) | URL | Status | Body | |---|---|---| | `https://mail.tm/robots.txt` | 200 `text/plain` | see below | | `https://api.mail.tm/robots.txt` | 404 `text/html` | Symfony "An Error Occurred: Not Found" page — no robots file on the API host | | `https://docs.mail.tm/robots.txt` | 200 `text/plain` | `User-agent: *` / `Disallow: ` (empty) / `Sitemap: https://docs.mail.tm/sitemap.xml` | | `https://mail.tm/llms.txt` | 200 `text/plain` | site index; links `llms-full.txt`, sitemap, robots | | `https://docs.mail.tm/llms.txt` | 200 `text/plain` | see below | | `https://mail.tm/.well-known/security.txt` | 404 `application/json` | `{"statusCode":404,"statusMessage":"Page not found: /.well-known/security.txt"}` | | `https://api.mail.tm/.well-known/security.txt` | 404 `text/html` | Symfony 404 | ```untrusted-content # START nuxt-robots (indexable) User-agent: * Disallow: Content-Signal: ai-train=yes, search=yes, ai-input=yes Sitemap: https://mail.tm/sitemap_index.xml # END nuxt-robots ``` `Disallow:` with an empty value is "allow everything"; the `Content-Signal` line explicitly opts in to AI input use. Note for anyone reading a summarised copy: the WebFetch summariser rendered this line as `Disallow: /` while also saying "no disallowed paths" — the raw bytes above are authoritative and show the value is empty. `https://docs.mail.tm/llms.txt`, first lines: ```untrusted-content # Mail.tm API > Free temporary email API. Create accounts, receive emails, and test signups. No API key required. ## Documentation Sets - [Mail.tm API Documentation](https://docs.mail.tm/llms-full.txt): API documentation for Mail.tm. Create temporary email accounts, receive emails via REST API, listen for new messages with SSE, and manage attachments. Free, no API key required. ``` **Gate decision: `granted`.** Published API terms permit programmatic use with four conditions; robots allows all with an explicit AI-input signal; llms.txt present on the docs host. No signal on the property points the other way. The run proceeded. --- ## 2. Step 1 — `GET /domains` — HTTP 200 ``` $ curl -sS -D - "https://api.mail.tm/domains" http=200 time=0.443209s size=347 ``` Response headers (verbatim, proxy preamble stripped): ``` HTTP/2 200 cache-control: no-cache, private content-type: application/ld+json; charset=utf-8 date: Sat, 05 Sep 2026 13:08:40 GMT etag: "576ccccc8def9cf1c094480d3870003d" link: ; rel="http://www.w3.org/ns/hydra/core#apiDocumentation" ratelimit-policy: 30; w=60 server: Caddy vary: Accept vary: Content-Type vary: Authorization vary: Origin x-content-type-options: nosniff x-frame-options: deny content-length: 347 ``` Body: ```json {"@context":"/contexts/Domain","@id":"/domains","@type":"hydra:Collection","hydra:totalItems":1,"hydra:member":[{"@id":"/domains/6a99c5515f0c88ec1469ff3a","@type":"Domain","id":"6a99c5515f0c88ec1469ff3a","domain":"uberip.com","isActive":true,"isPrivate":false,"createdAt":"2026-09-03T00:00:00+00:00","updatedAt":"2026-09-03T00:00:00+00:00"}]} ``` Exactly one public domain on offer, created two days before the probe. Selected `uberip.com`. --- ## 3. Step 2 — `POST /accounts` — HTTP 201 Request (password redacted; it was `openssl rand -hex 16`): ``` POST https://api.mail.tm/accounts Content-Type: application/json {"address":"ag3ntsearch-b7387e3e9f54e2e7@uberip.com","password":"[REDACTED 32 hex]"} ``` ``` http=201 time=0.555652s size=324 HTTP/2 201 content-location: /accounts/6a9c145a0192664dd3061da3 content-type: application/ld+json; charset=utf-8 location: /accounts/6a9c145a0192664dd3061da3 ratelimit-policy: 1; w=60 status: 201 Created ``` ```json {"@context":"/contexts/Account","@id":"/accounts/6a9c145a0192664dd3061da3","@type":"Account","id":"6a9c145a0192664dd3061da3","address":"ag3ntsearch-b7387e3e9f54e2e7@uberip.com","quota":40000000,"used":0,"isDisabled":false,"isDeleted":false,"createdAt":"2026-09-05T13:08:42+00:00","updatedAt":"2026-09-05T13:08:42+00:00"} ``` One unauthenticated POST, 0.56 s, and the account exists with a 40 MB quota. No email verification is possible or requested — the account *is* the inbox. Note `ratelimit-policy: 1; w=60` on this endpoint specifically: **one account creation per 60 s per client**. See §9. --- ## 4. Step 3 — `POST /token` — HTTP 200 Same JSON pair to `https://api.mail.tm/token`: ``` http=200 time=0.556316s size=463 HTTP/2 200 content-type: application/json ``` ```json {"token":"eyJ0eXAiOiJK…[REDACTED, 374 chars]","id":"6a9c145a0192664dd3061da3"} ``` A JWT bearer. No refresh token, no scope selection, no expiry advertised in the response (the JWT's own `exp` claim was not decoded — not needed for the task). The token is the only credential the rest of the run carries. --- ## 5. Step 4 — `GET /messages` with the bearer — HTTP 200 — PASS ``` $ curl -sS -H "Authorization: Bearer [REDACTED]" "https://api.mail.tm/messages" http=200 time=0.423526s size=119 HTTP/2 200 content-type: application/ld+json; charset=utf-8 link: ; rel="http://www.w3.org/ns/hydra/core#apiDocumentation" link: ; rel="mercure" ratelimit-policy: 30; w=60 ``` ```json {"@context":"/contexts/Message","@id":"/messages","@type":"hydra:Collection","hydra:totalItems":0,"hydra:member":[]} ``` Machine check: ``` $ jq -e '((.["hydra:member"] // .member) == []) and ((.["hydra:totalItems"] // .totalItems) == 0)' true REFERENCE_TASK_PASS ``` Four requests, four first-try successes, 13:08:40Z → 13:08:45Z. The four curl examples on the docs home were used essentially verbatim. No out-of-band knowledge was needed at any step. --- ## 6. Corroboration and error shape (not part of the task) | Request | Result | |---|---| | `GET /me` (bearer) | 200, the same Account resource as §3 with `@id: /me`. 0.496 s | | `GET /messages` (no header) | **401**, `www-authenticate: Bearer`, body `{"code":401,"message":"JWT Token not found"}`. 0.493 s. Actionable: names the missing thing | | `GET /docs.json` | **404** `application/problem+json`, `{"title":"An error occurred","detail":"Format \"json\" is not supported","status":404,"type":"/errors/404"}`. RFC 7807 shape | --- ## 7. Machine-readable surface `https://api.mail.tm/docs.jsonld` → HTTP 200, 26 796 bytes, `application/ld+json`: ```json {"@context":{"@vocab":"http://api.mail.tm/docs.jsonld#","hydra":"http://www.w3.org/ns/hydra/core#", …}, "@id":"/docs.jsonld","@type":"hydra:ApiDocumentation","hydra:title":"api.mail.tm", …} ``` `hydra:supportedClass` titles: `Error, ConstraintViolationList, Account, Domain, Message, Source, The API entrypoint, A constraint violation, A constraint violation list`. Every API response links to it (`link: ; rel="…hydra/core#apiDocumentation"`), so a cold-start agent that only knows the base URL is handed the schema by the first response. `https://api.mail.tm/docs?format=json` returns the same document. What is **not** there: the docs say "The API follows the OpenAPI v3 spec. Download it or explore interactively", and the "OpenAPI Spec" button links to `https://api.mail.tm` — an interactive HTML page. `https://api.mail.tm/docs.json` → 404 (`Format "json" is not supported`), `https://docs.mail.tm/openapi.json` → 404. No literal OpenAPI document was found at any advertised URL; the Hydra document is the machine-readable schema. That is the "or equivalent" rung (interface 18), and it is also a docs inaccuracy (§9). `https://mail.tm/llms-full.txt` (the consumer site, not the docs) → HTTP 200 but the body is a build error, reproduced as data: ```untrusted-content # Mail.tm Canonical Origin: https://mail.tm/ ## LLM Resources - [Full Content](https://mail.tm/llms-full.txt): Complete page content in markdown format. Database error: require is not defined Runtime indexing may not be configured correctly for this environment. ``` Harmless for the task (the docs host's file is intact), recorded as staleness. --- ## 8. Cleanup — `DELETE /accounts/{id}` — HTTP 204 ``` $ curl -sS -X DELETE -H "Authorization: Bearer [REDACTED]" "https://api.mail.tm/accounts/6a9c145a0192664dd3061da3" http=204 time=0.460057s HTTP/2 204 content-location: /accounts/6a9c145a0192664dd3061da3 status: 204 No Content ``` The burner inbox was destroyed by the probe. The credential is therefore scoped (one account) and revocable by its holder, which is one of the two `security- posture` conditions; the bonus is still withheld because there is no `security.txt` on any of the three hosts. --- ## 9. Docs discrepancies (new evidence, gate mode could not see it) 1. **Rate limit.** Documented: "8 queries per second (QPS) per IP address." Observed on every response: `ratelimit-policy: 30; w=60` (reads) and `ratelimit-policy: 1; w=60` (`POST /accounts`). The live contract is 30 requests/minute and **one account per minute** per client — an order of magnitude tighter than "8 QPS", and it is the account-creation limit that an agent doing "register disposable mailboxes in bulk" (the homepage's own phrase) will hit first. The header is the better contract; the docs number looks stale. Not tested by exceeding it (observe, never exploit). 2. **OpenAPI.** Advertised as downloadable; the only machine-readable document is Hydra JSON-LD (§7). 3. **Consumer-site `llms-full.txt`** is a build error (§7). Against that: `llms.txt` present; every docs example runs as written; the `401`/`404` bodies are actionable; responses carry an ETag and a schema link. "Good docs, most of the above" — `docs: 7`. --- ## 10. Egress log Collected via `probe-sandbox.sh collect` to `transcripts/tmp-mail-tm-e2e/egress.log` (gitignored, so reproduced here in full). **21 CONNECT requests, every one to an allowlisted host. Blocked: 0.** ``` DEBUG Sep 05 13:07:26.697 [11]: sending stored logs NOTICE Sep 05 13:07:26.700 [11]: Initializing tinyproxy ... NOTICE Sep 05 13:07:26.702 [11]: Reloading config file (/etc/tinyproxy/tinyproxy.conf) WARNING Sep 05 13:07:26.703 [11]: You SHOULD set a UserName in the config file. Using current user instead. NOTICE Sep 05 13:07:26.703 [11]: Reloading config file finished DEBUG Sep 05 13:07:26.704 [11]: done sending stored logs CONNECT Sep 05 13:07:26.969 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:07:26.970 [11]: Request (file descriptor 5): CONNECT mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:07:26.981 [11]: Established connection to host "mail.tm" using file descriptor 6. CONNECT Sep 05 13:07:28.103 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:07:28.106 [11]: Request (file descriptor 5): CONNECT api.mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:07:28.240 [11]: Established connection to host "api.mail.tm" using file descriptor 6. CONNECT Sep 05 13:07:29.561 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:07:29.565 [11]: Request (file descriptor 5): CONNECT docs.mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:07:29.596 [11]: Established connection to host "docs.mail.tm" using file descriptor 6. CONNECT Sep 05 13:07:31.368 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:07:31.369 [11]: Request (file descriptor 5): CONNECT mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:07:31.385 [11]: Established connection to host "mail.tm" using file descriptor 6. CONNECT Sep 05 13:07:32.608 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:07:32.611 [11]: Request (file descriptor 5): CONNECT api.mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:07:32.744 [11]: Established connection to host "api.mail.tm" using file descriptor 6. CONNECT Sep 05 13:07:34.029 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:07:34.030 [11]: Request (file descriptor 5): CONNECT mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:07:34.046 [11]: Established connection to host "mail.tm" using file descriptor 6. CONNECT Sep 05 13:07:35.527 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:07:35.528 [11]: Request (file descriptor 5): CONNECT docs.mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:07:35.542 [11]: Established connection to host "docs.mail.tm" using file descriptor 6. CONNECT Sep 05 13:07:36.663 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:07:36.665 [11]: Request (file descriptor 5): CONNECT docs.mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:07:36.676 [11]: Established connection to host "docs.mail.tm" using file descriptor 6. CONNECT Sep 05 13:08:40.458 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:08:40.461 [11]: Request (file descriptor 5): CONNECT api.mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:08:40.591 [11]: Established connection to host "api.mail.tm" using file descriptor 6. CONNECT Sep 05 13:08:41.915 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:08:41.917 [11]: Request (file descriptor 5): CONNECT api.mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:08:42.064 [11]: Established connection to host "api.mail.tm" using file descriptor 6. CONNECT Sep 05 13:08:43.487 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:08:43.488 [11]: Request (file descriptor 5): CONNECT api.mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:08:43.639 [11]: Established connection to host "api.mail.tm" using file descriptor 6. CONNECT Sep 05 13:08:45.060 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:08:45.060 [11]: Request (file descriptor 5): CONNECT api.mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:08:45.211 [11]: Established connection to host "api.mail.tm" using file descriptor 6. CONNECT Sep 05 13:08:46.493 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:08:46.494 [11]: Request (file descriptor 5): CONNECT api.mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:08:46.663 [11]: Established connection to host "api.mail.tm" using file descriptor 6. CONNECT Sep 05 13:08:48.003 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:08:48.004 [11]: Request (file descriptor 5): CONNECT api.mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:08:48.142 [11]: Established connection to host "api.mail.tm" using file descriptor 6. CONNECT Sep 05 13:08:49.505 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:08:49.508 [11]: Request (file descriptor 5): CONNECT api.mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:08:49.637 [11]: Established connection to host "api.mail.tm" using file descriptor 6. CONNECT Sep 05 13:08:51.097 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:08:51.100 [11]: Request (file descriptor 5): CONNECT api.mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:08:51.231 [11]: Established connection to host "api.mail.tm" using file descriptor 6. CONNECT Sep 05 13:08:52.531 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:08:52.533 [11]: Request (file descriptor 5): CONNECT api.mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:08:52.665 [11]: Established connection to host "api.mail.tm" using file descriptor 6. CONNECT Sep 05 13:08:54.103 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:08:54.104 [11]: Request (file descriptor 5): CONNECT docs.mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:08:54.127 [11]: Established connection to host "docs.mail.tm" using file descriptor 6. CONNECT Sep 05 13:08:55.596 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:08:55.597 [11]: Request (file descriptor 5): CONNECT docs.mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:08:55.621 [11]: Established connection to host "docs.mail.tm" using file descriptor 6. CONNECT Sep 05 13:08:56.746 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:08:56.748 [11]: Request (file descriptor 5): CONNECT mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:08:56.775 [11]: Established connection to host "mail.tm" using file descriptor 6. CONNECT Sep 05 13:08:57.899 [11]: Connect (file descriptor 5): 172.28.0.3 CONNECT Sep 05 13:08:57.901 [11]: Request (file descriptor 5): CONNECT api.mail.tm:443 HTTP/1.1 CONNECT Sep 05 13:08:58.030 [11]: Established connection to host "api.mail.tm" using file descriptor 6. ``` Neither host tried to redirect or pull the client off-property. The only off-allowlist URL anywhere in target output is the Mercure hub advertised in a `link:` header (`https://mercure.mail.tm/.well-known/mercure`), which was never followed. **Target-caused blocked egress attempts: 0.** --- ## 11. Safety observations | Check | Result | |---|---| | Prompt injection / hidden text | None. `grep -oiE "ignore (all )?previous\|you are now\|as an ai\|system prompt\|disregard\|display:\s*none\|visibility:\s*hidden"` over the docs index HTML, both `llms-full.txt` files, and every API body → zero matches. | | Key / seed-phrase request | None. | | Unbounded approval | N/A — no payment or signing surface. | | Env probe | None. | | Overcharge | N/A — $0. | | Malicious payload | None. All bodies `application/ld+json`, `application/json`, `application/problem+json`, or `text/plain`. | | TLS | HTTP/2 over TLS on all three hosts via CONNECT; no downgrade. `x-content-type-options: nosniff`, `x-frame-options: deny` on every API response. | | Secrets in URLs | None accepted or required. Password travels only in a JSON body; token only in an `Authorization` header. | | PII requested | None. The address is a random string on a shared disposable domain; the password is random. Nothing else was asked for. | | Credential scope | One JWT per account; account deletable by the holder (§8). | | `security.txt` | 404 on `mail.tm`, `api.mail.tm`, `docs.mail.tm`. `security-posture` bonus not awarded. | | `robots.txt` | `mail.tm`: allow all + `Content-Signal: ai-train=yes, search=yes, ai-input=yes`. `docs.mail.tm`: allow all. `api.mail.tm`: no file. | **Findings: none.** `safety.penalty_total: 0`. --- ## 12. Scoring rationale | Dimension | Rung | Why | |---|---|---| | `access` | **25** | `POST /accounts` returns a working, immediately usable account; the agent needs nothing beyond HTTP. Not 18: there is no email verification, because the product *is* the inbox. | | `payment` | **25** | "Completely free. No API key, no signup, no paid tiers." Confirmed: $0, no instrument requested. | | `interface` | **18** | Documented REST with a machine-readable Hydra `ApiDocumentation` linked from every response — "or equivalent machine-readable schema". Not 20: no MCP. Not 12: the schema exists and is discoverable from the API itself. | | `completion` | **20** | Reference task completed on the first attempt at each of the four steps, 0 human interventions, 0 out-of-band knowledge — the docs' own curl examples were sufficient. | | `docs` | **7** | `llms.txt` present; examples run as written; errors actionable; but the published rate limit (8 QPS) does not match the live `ratelimit-policy` headers (30/min reads, 1/min account creation), the advertised OpenAPI download does not exist, and the consumer-site `llms-full.txt` is a build error. "Most of the above." | | `permissive-tos` | **awarded** | Published API "Terms of Use" that invite keyless programmatic use; `llms.txt` on the docs host; robots `Content-Signal: ai-input=yes`. | | `security-posture` | **not awarded** | No `security.txt`. | | `permission.status` | **`granted`** | See §1. The one obligation an operator inherits is attribution ("link back to mail.tm somewhere visible"). | Arithmetic is `tools/score.ts`'s job. Dimension vector emitted: `access 25, payment 25, interface 18, completion 20, docs 7`; bonus `permissive-tos`; penalties none. **As the user:** this is what "provision an inbox from nothing" should feel like. Four requests, every one described by a runnable example on a page that is also served as `llms.txt`, the schema handed back in a `link:` header, a JWT that does exactly one thing, and a `DELETE` that cleans up after you. The two things that would make me hesitate before building on it: the single public domain (an address here is a burner in every sense), and the one-account-per-minute limit that the docs do not mention — an agent that needs five inboxes at once will be throttled by a header it was never told about. --- ## 13. Sandbox feedback 1. `/out` is now writable by the box user (the chown at `up` time works); `collect` returned `gate/` and `task/` intact. The frankfurter-era bug is fixed. 2. Busybox `grep -E` rejects `{0,N}` for N > 255 with `Invalid contents of {}`; `awk 'match($0,/…/){print substr($0,RSTART,N)}'` is the in-box idiom for "N characters of context". 3. `egress` still prints the whole log then re-greps it; harmless. 4. The allowlist saw nothing to block this run, so the fail-closed invariant was not exercised here; it was proven on 2026-08-16 (frankfurter transcript §7) and the proxy config is unchanged.