Security and privacy
What each secret can do, what Relay keeps, and how the person and you stay in control.
Three secrets#
| Secret | Held by | Can |
|---|---|---|
Publishable key pk_… | Your app, in public | Create links for the project. Nothing else: it cannot list, pause or revoke links, or read usage. |
Link token lt_… | The app on one device | Connect as that link's app: register tools and answer its calls. |
URL token ut_…, inside the MCP URL | The person, and the host they paste it into | Call the tools of that link's app while it runs. |
- Relay stores each secret only as a SHA-256 hash and cannot show it again. The publishable key and the link token are shown once.
- A link has two secrets on purpose. The URL is handed to OpenAI or Anthropic and can appear in logs, so it can be rotated without touching the app's credential. Knowing a link id or the publishable key never lets anyone answer calls in the app's place.
- The app sends the link token in its first frame, never in the socket URL, so it stays out of access logs.
- The person never needs an account of their own. Their URL is their credential.
The publishable key is public#
Anything shipped inside an app can be extracted, so the publishable key is meant to be public, like a Firebase or Stripe publishable key. What someone can do with it is bounded:
- They can create links, at most 60 a minute from one IP address and 10,000 per project.
- With a link of their own they can connect their own socket and call their own tools, which uses your allowance. A spend cap bounds what that can cost, and every link has its own calls-per-minute limit.
- They can never reach a link they did not create, or any person's app.
- Allowed origins stop other websites from using the key in a browser.
If the key ends up somewhere you did not intend, rotate it in the project's settings. Apps shipped with the old key can no longer create links; links that exist keep working.
MCP URLs#
- Anyone holding a person's MCP URL can call that app's tools while it runs. Treat it like a password, and tell the person so next to the copy button.
- Rotate a URL from the dashboard, or from the app with `link.rotate`. The old URL answers
404at once. - Unknown, rotated and revoked URLs all answer
404, so a guessed URL learns nothing.
What Relay keeps#
- Relay never stores arguments or results. They exist in memory only while the call is in flight.
- For each call it keeps the tool name, the outcome, the argument and result sizes and the duration, for 30 days, so you can follow usage and settle disputes.
- The tool list from
hellois stored on the link, so hosts can list tools while the app is closed. Keep secrets out of tool descriptions and instructions. - Relay never fetches a URL named in arguments or results.
Clear text in transit#
Both legs are encrypted with TLS: host to Relay over HTTPS, Relay to app over wss://. Inside Relay, arguments and results are in clear text while they are forwarded, because Relay checks the arguments against the tool's schema. The host reads every argument and result anyway, so the person extends Relay the trust they already give the host. There is no end-to-end encryption between the host and the app.
Pause and revoke#
- The person controls their link from your app: pause, resume, rotate the URL or revoke it, with the link commands. Put those in your app's settings.
- You control every link from the dashboard: pause, resume, rotate the URL or revoke. A rotate there sends
link.rotatedwith the new URL to a connected app, and a pause or resume sendslink.state, so the app never holds a dead URL or a stale paused flag. - Every call reaches the app before anything runs, so the app can show what is about to happen and ask the person before a destructive tool.
- A revoked link closes its socket with
4410, and its link token and URL stop working for good.