Documentation menu

Secrets and Environment Variables

A run needs credentials: a key for its model provider, and tokens for the services the runbook touches. This page describes how Jetty handles those values today: where they are stored, how they get from your collection into the sandbox, which process receives each one, and what is kept out of the recorded run.

The short version:

  • Values are stored on the collection, or passed with a single run. A runbook holds names, never values.
  • The sandbox is created without your variables. Jetty sets them on the agent's process when it starts the agent.
  • The agent receives the model provider's key and the secrets the runbook declares, not the whole collection environment.
  • A secret that Jetty itself uses, for an MCP server or to clone a check repository, is withheld from the agent's shell.
  • The run's environment is held in memory for the run and is not written to the run record. The agent's own output is recorded as produced.
How a secret reaches the sandboxCollection environmentSettings, the API or the MCP serverEvery run in the collectionPer-run secret_paramsThe run request or a scheduleOne run; wins over the collectionmerged at launchThe run's environmentHeld in the worker’s memory for the runNot written to the run record or its filesSANDBOXAgent processProvider keyDeclared secretsagent_env overridesMCP configurationToken of a server thatnames secret:Not in the agent’s shellJetty’s checksCheck-repository cloneSecrets the agent had,plus expose_to_checksAfter the agent exits: credential files deleted, leftover processes stopped,then outputs are collected and Jetty's checks run.

Where values come from

SourceSet inScopeUse it for
Collection environmentSettings → Integrations and Bring Your Own Key, the PATCH /api/v1/collections/{c}/environment endpoint, or the MCP server's set-environment-vars toolEvery run in the collectionProvider keys and integration credentials
secret_paramsThe run request, or a scheduleOne run, or every run of one scheduleA different account or environment for a single run
Jetty trial keysJetty's own secret managerCollections with an active trialTrial model access before you set a provider key
agent_envThe run requestOne runNon-secret knobs of the agent CLI. It is saved with the run's parameters, so don't put a credential in it

Integration credentials and provider keys share one store: a collection has a single set of named variables, and the two settings pages are two views onto it.

How values are stored

  • Collection variables are stored with the collection record in Jetty's database as a set of names and values. Jetty does not add its own per-value encryption to them. An update merges: a new name is added, an existing name is replaced, and a name sent with null is removed.
  • Who can read them. The environment endpoint returns names and values to any caller with read access to the collection, and requires write access to change them. The settings page masks each value until you reveal it. Treat membership of a collection as access to its secrets.
  • Schedule secrets. A schedule's secret_params are stored with the schedule. The API accepts them and never returns them.
  • Runbooks and tasks store no values. The secrets: block is a list of names, and an MCP server definition that carries a credential-looking header or env entry has that entry dropped before it is stored.

From launch to sandbox

  1. Launch. When a run starts, Jetty reads the collection's variables and merges the run's secret_params over them. On a name clash the run wins.
  2. Hand-off. The merged set travels to the worker with the run's launch input. It is not part of the run's parameters and is not saved on the run record.
  3. In the worker. The worker holds the set in memory as the run's environment. That field is excluded when the run is serialized, so it reaches neither the stored run file nor the database row. The worker logs each name as it is set, with the value masked. One run's environment is never placed in the worker's own process environment, so concurrent runs can't see each other's values.
  4. Sandbox creation. The sandbox is created from the runbook's image or snapshot with no user variables in it. Nothing is baked into an image, and a snapshot never contains a secret.
  5. Agent start. Jetty assembles the agent's environment (next section) and applies it to the agent's command when it starts. From there the agent, and every command it runs, reads a variable as it would from a local .env.

What the agent's environment receives

The agent does not get the whole collection environment. Jetty builds its environment in this order, and an earlier entry is not overwritten by a later one, except for the last row:

WhatVariablesCondition
Provider credentialsThe key and settings of the run's model provider, for example OPENROUTER_API_KEY, ANTHROPIC_API_KEY or OPENAI_API_KEY, plus what the agent CLI needs to find themAlways, for the provider the run uses. See Runtime Configuration
Declared secretsEach secrets: entry, under its env: nameSet in the run's environment and not withheld (below)
Media keysREPLICATE_API_TOKEN, GEMINI_API_KEY, OPENAI_API_KEYForwarded when set, without a declaration, so an agent can call image, video and vision APIs
agent_envWhatever the run request namesApplied last; overrides the rows above

Any other collection variable stays out of the sandbox until a runbook declares it. That is why every integration guide has a “declare the secret” step.

Declared secrets, and who receives them

A secrets: entry declares a name and the environment variable the run delivers its value in. Who receives the value follows one rule: a declared secret reaches the agent unless something of Jetty's consumes it.

RUNBOOK.md frontmatter
---
name: nightly-report
secrets:
  # Forwarded to the agent: nothing of Jetty's consumes it.
  SLACK_BOT_TOKEN: { env: SLACK_BOT_TOKEN }

  # Consumed by the MCP server below, so withheld from the agent's shell.
  TRACKER_TOKEN: { env: TRACKER_TOKEN }

  # Consumed by the code-check source below; also given to Jetty's checks.
  CHECKS_REPO_TOKEN: { env: CHECKS_REPO_TOKEN, expose_to_checks: true }
mcp_servers:
  tracker:
    url: https://mcp.tracker.example.com/mcp
    secret: TRACKER_TOKEN
code_checks:
  sources:
    - name: checks
      url: https://github.com/acme/report-checks
      secret: CHECKS_REPO_TOKEN
---
  • An MCP server's secret becomes that server's Authorization: Bearer header in the MCP configuration file the agent CLI reads. It is resolved at run time on a copy of the definition, so the stored run inputs stay token-free.
  • A code-check source's secret authenticates Jetty's clone of that repository. It is passed to git as a host-scoped header in the command's private environment, not on a command line and not in the clone URL.
  • Overrides. expose_to_agent: true also gives a consumed secret to the agent, for a runbook body that needs the same token. expose_to_agent: false withholds a secret nothing consumes. expose_to_checks: true gives a withheld secret to the checks Jetty runs after the agent.
  • Who consumes what is decided per run, from the run's effective mcp_servers and code_checks, since a run can override either. If a run passes a server's token directly in mcp_auth_tokens, that server no longer consumes its secret.
  • A declared secret the run did not set is reported in the run log before the agent starts. The run continues without it.
  • Agent-run checks have the agent's environment and nothing more. A command check the agent runs that reads a withheld secret's variable is refused before the run. See Code Checks.

Important

Withholding is hygiene, not a boundary

The agent runs as root in its sandbox. Keeping a secret out of its shell stops a command from reading it by accident, and keeps it out of the transcript. It is not a defence against a runbook written to find it: the MCP configuration file is readable by the agent while it runs, and a secret marked expose_to_checks: true is used later in a sandbox the agent controlled. Give a run only credentials you would trust the agent with.

After the agent exits

Before anything else reads the sandbox, on every run:

  1. Credential files are deleted: the MCP configuration and agent auth files that Jetty wrote, so no downloaded artifact carries one.
  2. Leftover processes are stopped when Jetty has checks to run, so a process the agent left behind can't read the environment of what runs next.
  3. Jetty's checks run in a clean shell: no login profile, with the PATH and HOME captured before the agent started. Their environment holds the secrets the agent already had plus those marked expose_to_checks: true, and no provider key.
  4. Check output is scrubbed. The value of every declared secret the run set is removed from check output before it is kept.

The sandbox is deleted when the run ends.

What is and isn't recorded

WhereWhat Jetty does
The run's environmentNever serialized. Not in the run file, not in the database row.
secret_paramsMerged into the environment at launch; not stored with the run.
Run setupRecords the declared secret names, never a value.
Run logNames only: which secret was forwarded, which was withheld and why, which was missing.
Step configuration and model-call parametersKeys that look like credentials are saved as [REDACTED]: api_key, authorization, access_token and similar, and any name ending in _API_KEY, _TOKEN, _SECRET_KEY or _ACCESS_KEY.
Jetty-provided trial keysScrubbed from step outputs and error messages, and removed from the run's environment as soon as the step ends.
Run parameters (init_params, vars)Recorded as sent. They are inputs, shown on the run page. Never pass a credential as a parameter.
Agent transcript and output filesRecorded as produced. Jetty does not scan the agent's output for your secrets. If the agent prints a token or writes it to a file under its results, it is in the run.

Warning

The last two rows are where a credential can end up in a run: a value passed as a parameter, or one the agent prints or writes to its results. Jetty records both as they are, and anyone who can open the run can read them.

Precedence

When the same name is set in more than one place, the first match wins:

  1. agent_env on the run request
  2. secret_params on the run request or schedule
  3. The collection environment
  4. A Jetty trial key

Working with it

  • Store credentials on the collection and declare them in the runbook. The Custom Integrations guide walks through it.
  • Use the check-secrets tool to compare what a runbook needs with what a collection has. It returns names only.
  • Split collections by audience: every member can read a collection's variables, and every runbook in it can declare them.
  • Rotate by updating the collection value. The next run picks it up; a run in progress keeps the value it started with.