Security & privacy
Two ways to use every tool, with very different data flows. This page says exactly what each one does, what is stored, and how to check it yourself.
Summary
| Tool pages (local) | HTTP API, MCP and A2A | |
|---|---|---|
| Computation | JavaScript in your browser tab | Cloudflare Pages Function at the nearest Cloudflare data centre |
| Your input leaves the device | No; blocked by the browser via CSP | Yes, that is what an API call is |
| Inputs and results stored or logged | No | No, except a tool request, which is stored so it can be read |
| What is stored | Nothing on our side. In your browser: no cookies, and one display preference (see below) | Your client record and one metadata row per call (below) |
| Secrets | Some tools (key generation, HMAC) handle secrets, locally only | Never asked for or created, except your own client secret, stored only as a hash |
| Third parties | None (the credentials page loads Cloudflare Turnstile) | None, apart from Cloudflare as host |
Tool pages: what “local” means, precisely
Every tool page ships its logic as JavaScript from this site. When you type, paste or open a file, the computation happens in your tab (file hashing runs in a Web Worker, also in your tab). Opening a file reads it from disk with the browser File API; it is never uploaded.
This is enforced, not just promised. Tool pages are served with this Content-Security-Policy:
default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self';
connect-src 'none'; worker-src 'self'; manifest-src 'self'; form-action 'none';
frame-ancestors 'none'; base-uri 'none'; object-src 'none' -
connect-src 'none': the page cannot usefetch, XHR, WebSocket, EventSource orsendBeaconto any host, including this one. -
form-action 'none': no form can be submitted anywhere. -
default-src 'none',img-src 'self': no third-party images, frames, media or other resources (a common way to leak data in a URL). -
script-src 'self'with no'unsafe-inline'or'unsafe-eval': only this site's own script files run, so injected markup cannot execute code. -
frame-ancestors 'none': other sites cannot embed these pages to capture input.
Four groups of pages relax exactly one thing, because they need to make a request:
| Pages | Policy change | Why |
|---|---|---|
/reference/*, /tools/tls/ | connect-src 'self' | “Try it” sends your request (and the credentials you type) to this site's API, and nowhere else. |
/tools/time/ | connect-src 'self' |
One HEAD request to the page's own address, to read the server's clock
from the Date header. It carries nothing you type.
|
/credentials/, /tools/request/ | connect-src 'self'; script and frame from https://challenges.cloudflare.com | Cloudflare Turnstile checks you are human, then the form posts to this site. |
CSP does not cover top-level navigation (clicking a link). The tools never build links that
contain your input. Downloads (for example “Download private.pem”) are created in memory as blob: URLs and saved by your browser. Browser storage holds only what you choose,
and nothing in it is sent anywhere except as described here:
- Display preference. If you choose View → Inline config on a tool,
that choice (the word
onoroff) is kept inlocalStorageso the next tool opens the same way. It is never sent. - API credentials, only if you ask. On “Try it” and on the credentials page
you can remember your client ID and secret: Don't (the default, nothing saved), This tab (
sessionStorage, gone when the tab closes) or This device (localStorage, kept until you click Forget). They are only ever sent to this site's API, in theAuthorizationheader. Anyone using the same browser profile can use credentials kept on the device, so owner keys (which have no rate limit) need a second confirmation before they are kept there. Browser storage is shared by every page on this site, including the credentials page, where Cloudflare's Turnstile script also runs.
Verify it yourself
-
Open developer tools → Network, then use a tool page. After the page has loaded (including
the file-hash page's Web Worker script, which loads with the page) there are no further
requests. The Time page shows exactly one more: its
HEADrequest for the server clock. -
Check the page's response headers for
content-security-policy. - Or disconnect from the network after a tool page loads: it keeps working.
The API, MCP server and A2A endpoint
A call sends your input to https://util.caruana.me. Each endpoint's reference states exactly what the server receives.
What is stored (Cloudflare D1)
- Your client record: client ID; SHA-256 hash of your secret; kind (agent, person, owner); name; expiry; the profile you registered with (as given, unverified); the registration method, country and network operator (from Cloudflare's request metadata, not your IP address) and user agent.
- Credential events: creation, renewals (old and new expiry), refused renewals, bearer-token issue, use of expired credentials, revocation.
- Bearer tokens: SHA-256 hashes only, with expiry. Deleted once expired.
- One metadata row per call:
client_id,time,transport,operation,status,error_code,duration_ms,request_bytes,response_bytes,country. Kept 90 days. The table has no column that could hold a parameter, a body or a result, and the single function that writes it takes only those fields. A test enforces both. - Tool requests: the one exception to “inputs are never
stored”. A request for a new tool (
tool_request, or the form at /tools/request/) is stored as sent (title, description, examples, category, and the email you give on the form), with your client ID when it comes through the API, MCP or A2A. It exists to be read. It is never shown to other clients, except that the title of a request the owner has accepted can be listed as related to a similar one. - Discovery counts: how often each discovery document (the agent card, MCP
server card,
llms.txtand so on) is fetched, per day and per broad client family (for example “ClaudeBot” or “curl”). A counter only: no IP address, no full user agent, no content. -
GET /api/auth/whoamireturns your record, events and usage, andGET /api/tools/requestsyour tool requests: everything held about you.
What is never stored or logged
- Request parameters, bodies, uploaded files, or results (other than a tool request, above).
- Your client secret or bearer tokens in plaintext.
- Your IP address. Cloudflare sees it as the host; this service does not record it.
- Logs: the function code contains no logging calls, and a test fails if one is added.
No secrets, server-side
No endpoint, MCP tool or A2A skill asks for passwords, private keys, HMAC keys or other
secrets, and none creates them. Key generation and HMAC exist only on the pages, which run
locally. Inputs that contain a private key are refused without being parsed. The one secret
the service creates is your own client_secret: generated from 256 random bits,
shown once, and stored only as a SHA-256 hash.
Two tools take input that can be sensitive by nature, and say so in their reference: jwt_decode
(an unexpired token is a bearer credential) and url_parse (a URL can embed a
password; it is never echoed back). For live credentials, use those tools' pages, which run
locally.
Outbound requests
- Human registration and the tool request form ask Cloudflare Turnstile to
confirm the human check (
challenges.cloudflare.com/turnstile/v0/siteverify), sending the Turnstile token and your IP address, as Turnstile requires. -
tls_checksends the host and port you name to the TLS checker operated by this service: a container on the operator's own server, reached through an authenticated Cloudflare Tunnel. It connects to that host. It refuses private, loopback and other non-public addresses, both before and after DNS resolution. - Nothing else. No tool fetches URLs, OCSP responders, CRLs, key servers or JWKS. A test fails if an outbound request appears anywhere else.
What Cloudflare sees
As the host, Cloudflare terminates TLS and processes request metadata (IP address, the full URL including the query string, headers) under its own policies, and the account owner can view live request metadata. Request bodies are not part of that metadata, so put inputs in POST bodies: a query string is also kept in shell history, proxy logs and browser history.
Credentials and limits
-
Client IDs and secrets come from the platform CSPRNG (128 and 256 bits) and are prefixed
cu_id_/cu_sk_so leaks are easy to spot. - Secrets are compared in constant time, and unknown client IDs are compared against a dummy hash, so timing reveals nothing.
- Credentials last 30 days, renew for 30 days, and stay renewable for 90 days after expiry. Bearer tokens last at most 1 hour.
-
Each client is limited to 60 requests per minute. A Cloudflare
rate-limiting rule also limits each IP on
/api/*,/mcpand/a2a, including registration.
For agents
The A2A agent card carries these commitments in
machine-readable form (extension data-policy/v1). When a signing key is
configured, the card is signed (JWS, Ed25519, over its RFC 8785 canonical form), with the
public key at /.well-known/jwks.json
. See A2A.
Cryptography used
- Randomness:
crypto.getRandomValuesandcrypto.subtle.generateKey(the browser's or Workers' CSPRNG). NoMath.randomin security paths; a test checks this. - Key generation (page only): Web Crypto. RSA public exponent 65537; EC curves P-256/384/521; Ed25519; X25519. Optional private-key encryption: PBES2 with PBKDF2-HMAC-SHA256 (600,000 iterations by default, 16-byte salt) and AES-256-CBC.
- Hashing:
@noble/hashes(audited, pure JavaScript), identical code in the browser and on the server. - Certificates and CSRs:
@peculiar/x509, with signatures verified through Web Crypto. The TLS checker uses OpenSSL 3 and the Mozilla CA bundle.
What these tools do not do
- The certificate decoder checks structure, dates, host names and the signatures between certificates you supply. It does not decide trust or check revocation. The TLS checker does check trust (Mozilla roots), but not revocation.
- OpenPGP user IDs and registration identity claims are recorded as given, not verified.
- MD5 and SHA-1 are offered only to match existing checksums.