AI-382: keep sandbox credentials out of workflow history - #1745
Conversation
e7ac375 to
dcc4035
Compare
A sandbox manifest's environment values are serialized into workflow history, which is durable, replayed, and visible in the Web UI. Because the manifest rides inside the session state passed to and returned from every sandbox activity, a literal credential is recorded repeatedly for the life of the session and survives rotation. Add SecretRef, an EnvValue subclass carrying a lookup key rather than a value. Upstream resolves it worker-side at the point the environment is needed and never rewrites the manifest, so only the reference persists. Two related fixes ride along. Host-path bindings in extra_path_grants were written to history in plaintext; they are now refused at the point the manifest crosses into a Temporal payload, which is the only place that sees grants added by a capability. And run_config accepts a dict upstream, which this plugin read attributes off directly, so a dict raised AttributeError before reaching sandbox validation. Requires openai-agents >= 0.19.2 for the EnvValue discriminator, capped below 0.20 where nine tests currently fail.
| # the WorkflowExecutionFailed event. | ||
| bound = [g.path for g in manifest.extra_path_grants if g.host_path is not None] | ||
| if bound: | ||
| raise AgentsWorkflowError( |
There was a problem hiding this comment.
Note that this is a breaking change that could cause a workflow to start failing if dependencies are bumped automatically
There was a problem hiding this comment.
yes, added to changelog
There was a problem hiding this comment.
Pull request overview
Adds durable sandbox secret references while blocking other sensitive manifest data from workflow history.
Changes:
- Adds
SecretRefwith worker-side environment resolution. - Rejects host-path grants and live sandbox sessions.
- Normalizes dictionary run configurations and expands tests/documentation.
Reviewed changes
Copilot reviewed 8 out of 10 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
.gitignore |
Ignores mypy cache files. |
pyproject.toml |
Updates OpenAI Agents dependency bounds. |
temporalio/contrib/openai_agents/__init__.py |
Exports SecretRef. |
temporalio/contrib/openai_agents/_openai_runner.py |
Normalizes run configuration and rejects live sessions. |
temporalio/contrib/openai_agents/README.md |
Documents secret handling and unsupported inputs. |
temporalio/contrib/openai_agents/sandbox/_secret_ref.py |
Implements worker-resolved secret references. |
temporalio/contrib/openai_agents/sandbox/_temporal_sandbox_client.py |
Rejects host-bound path grants. |
tests/contrib/openai_agents/test_openai_sandbox.py |
Adds workflow and activity boundary coverage. |
tests/contrib/openai_agents/test_openai_sandbox_secrets.py |
Tests serialization and secret resolution behavior. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
dcc4035 to
80fd8cf
Compare
| manifest = Manifest( | ||
| environment=Environment( | ||
| value={ | ||
| "OPENAI_API_KEY": SecretRef(key="OPENAI_API_KEY"), |
There was a problem hiding this comment.
Is SecretRef only used in the Workflow? Maybe it belongs in .workflow
There was a problem hiding this comment.
Also, is there any use case for someone using this for a non-secret environment variable?
There was a problem hiding this comment.
How does rotation work, and might you want to rotate a non-secret?
There was a problem hiding this comment.
Is `SecretRef only used in the Workflow?
It's also used by the worker because the worker needs it to deserialize properly.
Is there any use case for someone using this for a non-secret environment variable?
It's pretty generic, so any user could use it for whatever env var they don't want to show up in history
How does rotation work
I think this should be agnostic to all credential rotations? the cert is abstracted at the environment level, and whatever is currently on the worker will be resolved and sent to sandbox. The workflow isn't saving any secret-related state, so we shouldn't really care if/when people want to rotate.
Covers the new SecretRef API and the host-path grant rejection, which is breaking for workflows already running against openai-agents 0.19.2 or later -- the release where SandboxPathGrant.host_path was added.
Environment variables given to a sandbox are written into workflow history, which is durable and visible in the Web UI. A literal credential is recorded again on every sandbox activity, and stays there after you rotate it.
SecretRefrecords the variable's name instead of its value. The worker reads the value from its own environment when the sandbox needs it, so it has to be set on every worker that runs sandbox activities.Note: two related leaks are fixed alongside. Host paths bound through
extra_path_grantswere also written to history in plaintext and are now refused, at the point the manifest crosses into an activity payload so that grants added by a capability are covered too. Separately,run_configaccepts a dict upstream while this plugin read attributes off it directly, so passing one raisedAttributeErrorbefore reaching sandbox validation.Requires
openai-agents >= 0.19.2for the discriminator that lets the reference survive serialization, capped below0.20where nine tests currently fail.