Guides
Security
What we store and what we never store, how keys work, and how to lock down your account, your clients and your agents.
Connecting a service to MCP clients through us means a few things cross a boundary: your tool definitions, the calls clients make, and the results you return. This page says what crosses it, what we keep, how your keys and account are protected, and what you should do on your side. If you only read one section, read If a key leaks.
The trust boundary
Your code runs only in your own process. Your agent opens one outbound HTTPS connection to us. We never connect in to your network, you open no inbound port, and we cannot run code in your service. The only things we can ask your agent to do are the tools, resources and prompts you registered.
What passes through us on each call:
| What | Passes through us | Do we keep it |
|---|---|---|
| Your tool names, descriptions and argument schemas | Yes, when your agent connects | Yes, while the agent is connected |
| The arguments a client sends | Yes, on the way to your agent | No. Only the argument names are stored in the call log |
| The result your agent returns | Yes, on the way back to the client | No, with one exception: if you set a cache time on a tool, we hold successful results in memory for that long (at most 1 hour), and serve them to every client of the project |
| Secrets, environment variables, database contents | No, unless your tool returns them | No |
So the content of a call is visible to us in transit, but it is not written to our logs. Do not return secrets from a tool unless you mean a client to see them, and do not cache tools whose answer depends on who is asking.
Your agent is trusted by us in the sense that it receives every call to its project, including the arguments. Give an agent key only to the services that should handle those calls.
What we store, and what we never store
| Stored | Never stored |
|---|---|
| Your account: email, name, a salted hash of your password | Your password in readable form |
| Projects, and your tools' names, descriptions and schemas while connected | Argument values |
| API keys: a SHA-256 hash, the first 12 characters, label, kind, expiry, last-used date | Results returned by your tools (apart from the optional in-memory cache) |
A call log: time, project, tool, outcome, duration, sizes in bytes, argument names (first 20), the error message of a failed call (first 300 characters), the key's label, the caller's User-Agent, the MCP client's name, version and capabilities as it reported them, the runtime your agent's SDK reported, request and trace ids, timings your agent reported | The IP address of callers on the call log |
| Per-day usage counts per project and tool | Key values: only a hash is kept, so we cannot show a key again |
| An activity log of account changes, with the address each came from | Your OpenTelemetry data: it goes from your process to your backend and never through us |
| Sign-in sessions and the devices they came from |
Error messages are stored, and they are whatever your handler threw. Do not put secrets or personal data in error messages.
How long things are kept is in Plans and limits and Observability. See also the Privacy policy, the Terms and the Acceptable use policy.
Connections and TLS
- Connections to us are outbound only, and always carry your key in the
Authorization: Bearerheader. - Use an
https://address for the gateway in your agent (SPACEFIX_SERVICE_URL) and in your MCP clients, so keys and calls are encrypted in transit. The SDK's default address is HTTPS. Plainhttp://is only appropriate for a gateway running on your own machine. - Webhook URLs must be
https://, and must not point at private addresses. See Alerts and webhooks. - The dashboard's sign-in cookie is
HttpOnly,SameSite=LaxandSecure, and every change made from the browser carries a header another website cannot add, so a malicious page cannot act on your behalf.
API keys
A key is a password for your project.
| Kind | Dashboard label | Use it for |
|---|---|---|
agent | Agent | The SDK in your service: registers tools and answers calls. Cannot call tools |
client | Client | An MCP client: calls tools. Cannot register tools |
full | Full | Both: your service and AI apps. Every new project's Default key is one |
- Only a hash is stored. A key looks like
sf_live_followed by 22 random characters. We store its SHA-256 hash, so a lost key is replaced, never recovered. The recognisable prefix lets secret scanners, including GitHub's, find a key that was committed by accident. - A key only works for its own project. A valid key presented to another project's address is refused with
403. - Use the narrowest kind. Agents get
agentkeys, people and tools that call getclientkeys, andfullkeys are for one-off use. Replace the Default key once you have dedicated ones. - One key per deployment and per client, with a label. The label shows on Agents, Logs and Traces, so you can see which credential did what, and revoke one without disturbing the rest.
- Set an expiry (1 to 365 days) and keep the reminder emails on. They arrive 14 and 3 days before. When a key expires, an agent connected with it is disconnected.
- Add a tool allow-list to client keys (patterns such as
get_*), so a leaked key can reach less. It filters tools only, not resources or prompts. You cannot edit a key's list or expiry afterwards: create a replacement. - Make keys read-only when a client only needs to look things up: choose Read only under Access when you create it. It then sees only the tools that read (the agent sets
readOnlyHint, or you mark the tool Read); unlabelled tools count as writes and are hidden. - Keep keys out of git. Put them in your secrets manager. Be careful with
.cursor/mcp.jsonand.vscode/mcp.jsonin repositories. - Check Activity for
key.createdandkey.revokedyou do not recognise.
Details are in API keys.
Restrict who can call you
Allowed client addresses. In a project's Project settings, you can list up to 20 IP addresses or CIDR ranges (one per line, IPv4 or IPv6). MCP clients calling from anywhere else are refused with 403 and This address is not on the project's client allow-list. It limits clients only. Your agents connect from wherever your service runs and are never held to the list. A client behind a corporate proxy or NAT presents the proxy's address.
Tool allow-lists on client keys, described above, limit what a key can call.
Client access. Set Client access to Read only in Project settings and every client of the project, whatever its key or token, sees only the tools that read. Agents are not affected. Label your read tools with readOnlyHint: true, or mark them Read with Edit under Tools; a tool with neither counts as a write. The label comes from your own code, so a tool marked as reading must really only read.
Disable a tool. On the project page, under Tools, you can switch a tool off for every client without redeploying. A disabled tool is hidden from clients and refused if called.
Validate in your handler. We check each call's arguments against the schema you registered before it reaches your agent, but we do not enforce the pattern or format keywords. We still list them to clients as hints. If a rule matters for safety, enforce it in your handler. Your handler also cannot tell which client or person made a call, so the key is the only place to draw a line between callers.
OAuth for clients
By default MCP clients authenticate with a client key. OAuth is the alternative when you do not want to paste a key into every user's client: each person signs in through an identity provider and the client sends a short-lived token instead.
OAuth is a deployment-wide setting that we turn on, not a per-project switch, and it applies to MCP clients only. Your agents always use keys. When it is on:
/mcp/<project-id>accepts access tokens only. A static client key is no longer accepted there, and per-key tool allow-lists do not apply.- Tokens are checked for issuer, audience, expiry and scope, and the token names the one project it may reach.
- The scope sets what the token may do. By default
mcp:write(or the oldermcp:invoke) reads and writes;mcp:readalone limits it to tools that read. A token with neither is refused with403insufficient_scope. The project's Client access applies to tokens too. - There is no revocation check, so a token is valid until it expires. Keep lifetimes short.
- Your handler still cannot see which user a token belonged to.
If you want this for your deployment, contact us. The client side needs nothing but the URL: Claude Code, Claude Desktop and the MCP Inspector run the sign-in flow themselves.
Account security
Passwords and sign-in
Passwords are 8 to 1,024 characters, stored as salted scrypt hashes. There are no complexity rules and no check against known leaked passwords, so choose a long, unique one. A wrong password and an unknown email give the same answer, Incorrect email or password..
A sign-in starts a session that lasts 30 days from that moment and does not extend as you use it. Sign-ins are recorded in Activity with your address.
Resetting a forgotten password signs you out everywhere. Changing your password on the Security page signs out every other device. Changing your email sends a confirmation link to the new address and a warning to the old one, so a hijacked session cannot quietly move your account to another inbox.
Email verification
After sign-up we email a link that proves you own the address. It works once and expires after 24 hours. Until you confirm, creating projects and keys is held back where we can deliver email.
Two-factor authentication
Two-factor authentication means a password alone is not enough to sign in. It uses a time-based one-time password (six digits, 30-second steps) from any authenticator app.
- Turn it on under Security, Two-factor authentication, Set up. You get a secret key to copy into your app (there is no QR code), then enter a code and press Turn on.
- Save your recovery codes. You are shown 10, once, each in the form
a1b2c-d3e4f. Each works one time if you lose your phone. Only their hashes are stored. - A code cannot be replayed, and guesses are limited to 5 a minute per account.
- Turning it off asks for your password and a current code or recovery code, so a stolen session alone cannot weaken the account.
- If you lose both your phone and your recovery codes, there is no self-service way back in. Contact us.
- Not built: enforcing two-factor for everyone in a team, passkeys, and a QR code.
Signed-in devices
The Security page shows a security overview (email confirmed, two-factor on, three or fewer signed-in devices), and a list of Signed-in devices with the address and time of each. Press Sign out on any device, or Sign out all others.
Guess limits
These slow down people guessing credentials. They are rate limits, not lockouts, and they lift by themselves.
| Protection | Limit |
|---|---|
| Sign-in and credential routes, from one address | 20 a minute |
| Second-factor code guesses | 5 a minute per account |
| Account sign-ups from one address | 10 a day |
| Wrong API keys offered from one address | 30 a minute, then 429 Too many failed authentication attempts. A valid key is never refused by this limit |
The Activity log
/activity (under Observe) is your account's audit trail: what happened, to what, when, and from which address. It is kept for at least 30 days, and longer on plans that keep more history. Secrets are never recorded.
Use the filter chips (Everything, Sign-ins, Keys, Projects, Agents, Passwords, Email, Account, Two-factor, Devices, Tools, Webhooks) and Older activity to page back. It records:
| Area | Actions |
|---|---|
| Account | account.created, login, login.failed_2fa, email.verified, email.change_requested, email.changed, password.changed, password.reset, 2fa.enabled, 2fa.disabled |
| Devices | session.revoked, session.revoked_others |
| Projects | project.created, project.updated, project.deleted |
| Keys | key.created, key.revoked, key.rotated |
| Tools | tool.disabled, tool.updated |
| Agents | agent.disabled, agent.enabled |
| Webhooks | webhook.created, webhook.deleted |
A wrong password at sign-in is not recorded. A wrong second-factor code is.
Export and deletion
Export. The dashboard offers CSV downloads of your call logs (on Logs) and usage (on Usage). See Observability. There is no one-click export of the account itself (profile, projects, keys, activity). If you need one, contact us.
Delete a project. Press Delete project on its page. Its keys, call logs and usage history are deleted, connected agents are disconnected at once, and its keys stop working. This cannot be undone.
Delete your account. Under Settings, Danger zone, Delete account, enter your password and type your email address. Deletion is permanent and immediate: there is no soft delete and no backup to restore from. It removes your projects and their keys, call logs, usage and telemetry history, webhooks, activity log and every signed-in session. Connected agents are dropped as soon as it completes.
If a key leaks
- Revoke it now. On the project page, press Revoke on the key. It stops working on the next request, and an agent connected with it is disconnected.
- Replace it without downtime if it was in use: create a new key of the same kind first, deploy it, check the new key shows a Last used date, then revoke the old one. See API keys.
- Look at what it did. Open Logs and look at the calls whose Credential is that key's label. Open Activity for key and project changes you do not recognise.
- Find out how it leaked and fix that: a committed file, a shared chat, a log line.
- Consider what an attacker could do. A leaked client key can call the tools it was allowed to. A leaked agent key can register tools and receive calls, including their arguments, so treat any data that passed through that project as exposed, and tell your own users if it matters.
- If you cannot tell whether the account is safe, change your password, turn on two-factor, and press Sign out all others on the Security page.
Next steps
- API keys: kinds, allow-lists, expiry and rotation.
- Observability: what the call log holds.
- Alerts and webhooks: get told when keys are created or revoked.
- Troubleshooting: when a key or sign-in does not work.