Overview
When you use OpenHands, an AI agent executes code in a sandboxed environment. A natural security concern is: can the agent access and steal the LLM API key used to power it? The short answer is no. OpenHands implements multiple layers of protection to ensure that LLM API keys are never exposed to the agent’s execution environment.The LiteLLM Proxy Layer
Before diving into SDK-level protections, it’s important to understand the first layer of defense: the LiteLLM proxy.How It Works
OpenHands routes all LLM API calls through a LiteLLM proxy server. This proxy holds the actual provider API keys (OpenAI, Anthropic, etc.) and issues virtual keys to users:| Component | Who Configures It | What It Holds |
|---|---|---|
| LiteLLM Proxy | OpenHands (SaaS) or Customer (Enterprise) | Master API keys for all LLM providers |
| Virtual Key | Generated per Organization/Personal Workspace | Reference to master key, with budget/usage tracking |
| Agent | N/A | Receives only the virtual key (if at all) |
Master Key Configuration
- OpenHands SaaS: OpenHands configures master API keys in the LiteLLM proxy. Users never see or handle provider API keys directly.
- OpenHands Enterprise: Customers configure master API keys in their Helm values file or VM Installer. These keys are stored in the LiteLLM proxy, not in the application layer.
Virtual Keys Per Organization
When a user or organization is created, OpenHands generates a virtual key in LiteLLM:- Cannot be used directly with provider APIs (OpenAI, Anthropic, etc.)
- Only works with the LiteLLM proxy that issued it
- Has budget limits enforced by the proxy
- Can be revoked without affecting other users
What About BYOK (Bring Your Own Key)?
When users provide their own API keys through the OpenHands settings UI, the behavior depends on the configuration:| BYOK Scenario | Goes Through LiteLLM? | Key Exposure |
|---|---|---|
Custom base_url pointing to own LiteLLM | Yes (user’s proxy) | User’s proxy holds master key |
Custom base_url pointing directly to provider | No | Key goes directly to provider |
Only custom api_key (no custom base_url) | Yes (OpenHands proxy) | Key is passed to OpenHands LiteLLM proxy |
Agent.llm.api_key and protected by the SDK-level mechanisms described below.
Architecture
OpenHands uses a split architecture where:- The Agent Server (Python process) holds sensitive credentials and makes LLM API calls
- The Sandbox (isolated container or process) executes agent-requested commands without access to credentials
Protection Mechanisms
1. LLM API Key Isolation
The LLM’sapi_key is stored in the Agent.llm.api_key field as a Pydantic
SecretStr. This key is:
- Used only within the SDK’s Python process when making API calls via LiteLLM
- Never exported as an environment variable to the shell
- Never accessible via bash commands like
echo $LLM_API_KEY
2. SESSION_API_KEY Stripping
TheSESSION_API_KEY is a credential that grants access to user secrets via
the OpenHands API. If an agent could read this, it could potentially access
other sensitive data.
OpenHands explicitly strips this variable before any subprocess execution:
sanitized_env() function is called in:
bash_service.py— before executing any bash commanddesktop_service.py— before starting desktop processesvscode_service.py— before launching VS Codeskills_service.py— before running skill-related processes
3. Registered Secrets: On-Demand Injection with Masking
For secrets that are meant to be used by the agent (likeGITHUB_TOKEN for
git operations), OpenHands uses a controlled injection mechanism:
- On-demand injection: Secrets are only added to the environment when the command text explicitly references them
- Output masking: Any secret values that appear in command output are
automatically replaced with
<secret-hidden>
Understanding “Controlled”: LLM vs Agent Access
Registered secrets are accessible to the agent but hidden from the LLM:| Layer | Access to Secret Values |
|---|---|
| LLM (language model) | ❌ Never sees actual values—masked as <secret-hidden> in conversation history |
| Agent (sandbox execution) | ✅ Full access—can read, write to files, transmit over network |
<secret-hidden> before being added to the conversation history. This prevents
the secret from appearing in prompts sent to the LLM. However, the agent executing
in the sandbox has full access to use the secret as needed.
Expected behavior: The agent will use registered secrets for legitimate tasks—writing
to .git-credentials, including tokens in API headers, configuring services, etc.
This is by design. Output masking keeps secrets out of conversation logs and the UI,
but does not restrict how the agent uses them during execution.
Implementation Details
4. LookupSecret for Dynamic Tokens
For OAuth tokens and other credentials that may be refreshed, OpenHands usesLookupSecret which fetches tokens via authenticated HTTP requests at runtime:
Security Testing
The SDK includes explicit security tests to verify these protections work:What About BYOK (Bring Your Own Key)?
When users provide their own API keys through the OpenHands settings UI:| Secret Type | Exposed to LLM? | Exposed to Agent? | Notes |
|---|---|---|---|
| LLM API Key | ❌ No | ❌ No | Stored in Agent.llm.api_key, used only by SDK |
| LiteLLM Virtual Key | ❌ No | ❌ No | Same protection as direct API keys |
| GitHub/GitLab Tokens | ❌ No | ✅ Yes | Agent can use for git operations, write to files, etc. |
| Custom Secrets | ❌ No | ✅ Yes | Agent can use as needed for tasks |
Potential Attack Vectors (and Mitigations)
Could an agent write a program to read env vars?
The agent could write Python code like:'not found'.
Could an agent read the agent-server’s memory?
In theory, a malicious program could try to read/proc/<pid>/environ of the
parent process.
Mitigation: The sandbox runs in an isolated container (Docker) with no
access to the host’s process space. The agent-server process is outside the
container.
Could an agent intercept LLM API calls?
The agent doesn’t make LLM calls—the agent server does. The agent only receives the LLM’s text responses, not the API request/response details.Could secrets leak through error messages?
FastAPI validation errors could potentially echo back request bodies containing secrets. Mitigation: OpenHands sanitizes all validation error responses:Summary
OpenHands protects LLM API keys through defense-in-depth:- Architectural separation: Keys live in the agent server, not the sandbox
- Environment stripping: Sensitive vars are removed before subprocess exec
- On-demand injection: Only explicitly-needed secrets are injected
- Output masking: Secret values are redacted from all output
- Container isolation: Sandbox cannot access host process memory

