Skip to Content
FeaturesOutput redaction

Output redaction

When an agent prints a secret, nobody else reads it. Marshal finds credentials in everything a run sends out of its space (its output, its error, its live events and the copies posted to Slack) and replaces each one with a marker before the text is stored or shown. The run page tells you a secret was hidden, your admins get an alert, and the audit log records what kind it was. The value itself never leaves the space.

What you can do with it

  • Share run results freely. A deputy that ran env while debugging still posts a clean result to its Slack channel. Everyone in the channel reads the report, and nobody reads the token.
  • Know when to rotate. A red badge on the run page means the agent printed a credential that Marshal itself put in the space. You know which run did it and what kind of value it was, so you rotate the right key the same day.
  • Answer an auditor. Every hidden secret writes a run.secrets_redacted event with the run, the place it was found, the kind and the count. The log proves the value was caught without ever holding it.

How it works

Marshal checks the text twice, in two layers:

  • Known values. Inside the space, Marshal holds every secret it placed there: your credentials, your HTTP secrets, the run’s own Marshal tokens, and the values its proxy signs in with. It finds each one in every form an agent might print it: as is, base64, URL-encoded, JSON-escaped or inside a JSON document. A match here is certain, so it is the higher severity.
  • Known shapes. Credentials that Marshal did not inject are found by their format, such as a GitHub token, an AWS access key or a private key block. The full list is in What is detected.

The first check runs inside the space, on every event before it is streamed and on the result and error before the run reports back. The Marshal API checks the shapes again as it stores the output and serves the events, so every run is covered on its way to every reader. Where both layers match the same text, the known value wins.

Each secret becomes a marker that names its kind:

Using token [REDACTED:github_token] for the API call. Authorization: Bearer [REDACTED:bearer] DD_API_KEY=[REDACTED:datadog_key]

When a value is found by the name of the variable that holds it, such as DD_API_KEY= or a "private_key" field, the name stays and only the value is replaced. A value Marshal injected becomes [REDACTED:marshal_secret]. Output with nothing to hide is stored exactly as the agent wrote it.

Where it applies

Where the text goesWhat is cleaned
The run’s outputEvery string in the result, including values inside structured output. Marshal cleans it before storing it, so Show raw and the copy button give you the cleaned output too.
The run’s errorThe error message and the failure log that comes with it.
Live eventsThe transcript on the run page, the events API and Download log.
SlackThe result reply a deputy posts, its questions, and agent requests. The text is cleaned first and shortened after, so a long message never shows the start of a key.
MCP clientsruns_logs, runs_wait and runs_attention. Output there also arrives wrapped as untrusted content. See Connect an AI agent (MCP).

What stays as it is

Redaction cleans the text that leaves a space. The files in your workspace are the agent’s own work and are never changed. Commit SHAs, UUIDs, image digests, hashes, certificates and public keys are not secrets, and they pass through untouched.

Inside a space, the tools that sign in for the agent clean their own output separately: a credential value a tool prints reaches the agent as [redacted-by-marshal]. See A tool prints a marker where a token should be.

Redaction catches secrets an agent prints by accident. It works together with the controls that keep secrets away from agents in the first place: placeholders for HTTP secrets, credentials that go to the tool and not to the agent, network policies and the audit trail. See Let agents use secrets they never see.

See what was hidden

On the run page

A run that hid something shows a badge next to its status, for example 3 secrets were hidden. It never shows a value.

Badge colorMeaningTooltip
RedThe run printed a value Marshal injected.This run printed a credential Marshal injected. It was hidden from the output. Rotate it. followed by the kinds
AmberThe run printed text that looks like a credential.This run printed text that looks like a credential. It was hidden from the output. followed by the kinds

The same flag is on the run in the API, as secrets_hidden with the count, the severity (high or medium) and one entry per hit with its path, layer, kind and count.

In the audit log

Each hidden secret writes a run.secrets_redacted event, one for each place, kind and layer in the run. The event carries the run_id, the path (event, output, error, slack or mcp), the kind, the layer (known_value or shape) and the count. It never carries the value, a part of it or a hash of it.

LayerAudit severity
known_valueSecurity, shown with a shield
shapeWarning

Marshal writes these events itself, so no member is named as the actor. Owners and admins find them with Organization on the Audit log page. Text that is read again, such as an MCP client polling the same logs, is recorded once per run, not on every read.

As an alert

Your org’s owners and admins get the A run printed a secret and it was hidden (admins) alert, once for each run that hid a secret. The row reads, for example, “3 secrets hidden in a run: Nightly dependency check”, adds “(a Marshal-injected value: rotate it)” when the run printed an injected value, and links to the run with Open the run. The alert stays open for 7 days.

Turn on Email me when it is raised on the Settings tab of the Alerts page to get an email such as “A run in Acme printed 3 secrets”, with the run and a reminder to rotate any credential it names. The kind also has a section, Secrets hidden in run output, in the weekly digest. See Alerts.

What is detected

KindWhat it finds
marshal_secretAny value Marshal injected into the space, in any printed form
marshal_key, marshal_mcp_token, marshal_orch_tokenMarshal API keys, MCP tokens and orchestration tokens
github_tokenGitHub tokens (ghp_, gho_, ghu_, ghs_, ghr_, github_pat_)
gitlab_tokenGitLab personal access tokens (glpat-)
anthropic_keyAnthropic API keys
openai_keyOpenAI API keys
aws_access_keyAWS access key IDs
google_api_key, google_oauth_token, gcp_private_keyGoogle API keys, OAuth access tokens and the private key in a service account file
azure_client_secret, azure_storage_keyMicrosoft Entra client secrets and storage account keys in a connection string
slack_token, slack_app_tokenSlack bot, user and app tokens
stripe_key, stripe_webhookStripe API keys and webhook signing secrets
atlassian_tokenAtlassian API tokens
linear_tokenLinear API and OAuth tokens
sentry_tokenSentry tokens
tailscale_keyTailscale auth, API and client keys
twilio_keyTwilio API keys
sendgrid_keySendGrid API keys
datadog_key, cloudflare_token, vercel_tokenDatadog, Cloudflare and Vercel keys, found by the variable that names them, such as DD_API_KEY, CLOUDFLARE_API_TOKEN or VERCEL_TOKEN
npm_token, pypi_token, docker_tokennpm, PyPI and Docker Hub tokens
huggingface_token, grafana_token, digitalocean_token, shopify_tokenHugging Face, Grafana Cloud, DigitalOcean and Shopify tokens
age_keyage secret keys
jwtJSON Web Tokens
pemPrivate key blocks (-----BEGIN ... PRIVATE KEY-----)
bearerAny other bearer token after Bearer

Each shape has a minimum length, so ordinary words and identifiers that share a prefix are left alone.

Configure it

Redaction is always on, for every run, with nothing to set up. Each person chooses how they hear about hidden secrets on the Settings tab of the Alerts page: Show this alert and Email me when it is raised apply to this alert, and the Weekly digest switch adds it to the weekly email of all open alerts.