Worker trust boundaries and secrets
This page is for platform operators. It explains which servers Arcade trusts with which secrets, the settings that control that, and what the does when a hands a secret back.
Two kinds of servers
The Arcade Engine calls two kinds of servers, and trusts them differently.
| Kind | How it gets into the deployment | Who wrote its code | Secrets it receives |
|---|---|---|---|
| Configured server | Declared in the Engine configuration, for example a workers[] entry in the Helm values. Includes the bundled Arcade Worker. | You, the operator, or Arcade | Every secret its tools require, at every level, always |
| API-registered server | Registered in the dashboard or API, deployed with arcade deploy, or added as a remote MCP server | The people using your deployment | Only the levels you share, see Choose which secrets reach API-registered servers |
A server can do anything with a secret it receives, including send it somewhere else. Nothing the Engine does after the call can undo that, so the only boundary that stops a server from carrying a secret away is not sending it the secret in the first place.
Secret levels
The Engine stores a secret at one of three levels. When a tool requires a secret, the Engine uses the most specific level that holds that key: , then organization, then static.
| Level | Who sets it | Who can see it |
|---|---|---|
| Project | Project members, in the dashboard or CLI, or with arcade deploy | That project |
| Organization | The organization’s members | Every project in the organization |
| Static | The platform operator, with the arcade-engine static-secret command | Every project in the deployment |
Static secrets are the deployment’s own credentials, for example a model provider key that Arcade’s toolkits use.
The people who can register or deploy a server are already administrators of their organization or , and Arcade trusts them with that organization’s or project’s secrets. Sending those secrets to their servers gives them nothing they can’t already read. Static secrets are different: they belong to the deployment, not to any organization, so a server written by one organization’s administrator shouldn’t receive them.
Choose which secrets reach API-registered servers
Set engine.workerSecretSharing in your Helm values to choose, level by level, which secrets the Engine sends to API-registered servers:
engine:
workerSecretSharing:
static: false # keep the deployment's own secrets from servers your users wrote
organization: true
project: trueWithout the chart, set the same keys under tools.worker_secret_sharing in engine.yaml.
Every level defaults to true, so upgrading the Engine changes nothing until you set a value. If your can register or deploy servers and you store static secrets, set static: false.
These settings never affect configured servers. To give a server you trust a static secret, declare it in the Engine configuration instead of registering it through the API.
What a caller sees when a secret is withheld
A call to a that requires a withheld secret fails with secret not found: <KEY>, the same error as a secret that doesn’t exist. The error doesn’t reveal that the secret exists at a withheld level. The Engine refuses the call before it contacts the server, so the server never runs.
The Engine logs the real reason at info level, so you can tell a withheld secret from a missing one in the Engine logs.
Things to check before you withhold a level
- that depend on a withheld level stop working on API-registered servers. A tool that requires a key stored only at a level you withhold fails with
secret not found. A copy of the key at a level you still share takes precedence, so the tool keeps working. Before settingstatic: false, find servers whose tools require a key you store only as a static secret, warn their owners, and ask them to store their own value in their or organization. Before settingorganization: false, do the same for keys stored only for the organization, and ask owners to store them in each project. - name collisions fail closed. If a registers its own server with a tool of the same name as a tool on a configured server, calls to that tool in that project are treated as API-registered and fail when they need a withheld secret.
- Readiness checks don’t for withheld secrets. The catalog can still report a ’s secrets as present when one is withheld, and the call then fails with
secret not found. - Plugin hooks aren’t filtered. A secret that a contextual access pre-execution hook you configured adds to a call has no level, and the Engine always passes it through.
Arcade Cloud
Arcade Cloud withholds static secrets from API-registered servers and shares organization and secrets. A server you register or deploy on Arcade Cloud receives the secrets you store in your project and organization, never Arcade’s own keys. To use a key with your own server, store it as a project or organization secret.
Redaction of tool output
Separately from the boundary above, the Engine redacts secrets that come back from any server, configured or API-registered. This prevents accidents, such as a that echoes a credential in an error message or a debug field. It doesn’t stop a server that’s trying to leak a secret, because the server can encode it or send it elsewhere.
Before anything else reads a ’s response, the Engine replaces every exact copy of a credential that the call carried with [REDACTED]. That covers the , organization, and static secrets sent with the call, and the ’s OAuth token. Redaction applies to:
- the result returned to the agent, on every calling path: the REST API, gateways, and Arcade’s built-in
- every response field: the output value, error messages, logs, and structured content
- post-execution hooks
- tool execution history, so operators who can’t read secrets don’t see them there
- the Engine’s own traces and logs for the call
Redaction is always on and has no settings. still receive and use their secrets as before.
Redaction of deployment logs
When you read the logs of a server deployed with arcade deploy, on either the time-window or the streaming route, the Engine replaces exact copies of these values with [REDACTED] in each line:
- the secrets configured on the deployment
- the deployment’s worker signing key
- every , organization, and static secret visible to the project
If the Engine can’t read those secrets, it refuses the logs request rather than returning unredacted lines.
What redaction doesn’t catch
- Values shorter than 10 characters. Short values are usually configuration, such as a region code, and matching them would corrupt unrelated text.
- Anything other than an exact copy. An encoded, split, reversed, or partial secret passes through.
- Secrets that come back as JSON numbers. An all-digit secret returned as a number, not a string, passes through.
- Long configuration values. A secret value of 10 or more characters that also appears in normal output, such as a server URL stored as a secret, is redacted there too.
- For deployment logs:
- only the current values are matched, so a secret that was rotated or deleted after it was logged stays visible
- a stream that’s already open doesn’t pick up secrets created or rotated after it opened
- a secret split across lines isn’t matched
- OAuth tokens aren’t matched, because they belong to individual
- the deployment’s status message isn’t redacted
Next steps
- Arcade Worker for the settings of the bundled configured server
- Create a tool with secrets to see how declare the secrets they require
- Tool executions to review what returned