CVE-2026-77938: Server Credentials Exposed to Tool Sandbox Subprocess via Full Environment Inheritance
A flaw in Letta's API takes the caller's tenant identity from a user_id request header without verifying it. Any API caller can impersonate any tenant and read or modify that tenant's agents, memory and configuration, even when authentication is enabled.

Overview
Letta is an open-source platform for building stateful AI agents with long-term memory. Its LocalSandbox, the default for running tool code, executes user-supplied tools as a subprocess of the server and passes it a full copy of the server's environment.
In the shipped compose.yaml that environment holds the database connection URI with credentials, the database password and every configured model-provider API key. Tool code can simply read these values, and with the database credentials an attacker can connect directly to the database and access all tenants' data.
Because any user who can run a tool through Letta's API can read the server's database credentials and every configured model-provider API key, and use them to reach all tenants' data directly, deployments should stop using the local sandbox for untrusted tool code immediately.
Affected Product
Affected Files
- letta/services/tool_sandbox/base.py line 487 (env = os.environ.copy() if is_local else {})
- letta/services/tool_sandbox/local_sandbox.py lines 120-122 (
python_executable = sys.executable), lines 192-194 (create_subprocess_exec)
- compose.yaml lines 34-52 (
LETTA_PG_URI, POSTGRES_PASSWORD, OPENAI_API_KEY, ANTHROPIC_API_KEY, and all configured provider API keys set in the server container environment)
Root Cause
LocalSandbox executes tool source code as a subprocess of the server process (sys.executable, create_subprocess_exec) and passes the full server environment to that subprocess via os.environ.copy().
The default use_venv=False local config applies. The server container's environment, as defined in compose.yaml, contains
LETTA_PG_URI (with embedded database credentials), POSTGRES_PASSWORD, and every configured model-provider API key (OpenAI, Anthropic, Groq, Gemini, Azure, etc.). Tool source code is user-supplied.
PoC
POST /v1/tools/run with source_code:
import ospg_uri = os.environ["LETTA_PG_URI"]
openai_key = os.environ.get("OPENAI_API_KEY", "not set")
anthropic_key = os.environ.get("ANTHROPIC_API_KEY", "not set")
# Return all secrets to the caller via the tool response
Demonstrated follow-on impact - using the recovered LETTA_PG_URI with pg8000:
import pg8000conn = pg8000.connect(...)
conn.run("SELECT/UPDATE across all tenants")
Impact
Any user able to execute a tool via the API can read the full server process environment, including the database connection URI with credentials, the database password, and all configured model-provider API keys.
With the recovered database credentials, the attacker can connect to the database directly (port published to host by default) and read or modify data across all tenants. Exposure of model-provider API keys enables unauthorized use of those services at the operator's expense. The LocalSandbox provides no isolation: tool code runs as a subprocess inside the server process with full access to the server's credential set.
Suggested Remediation
Do not pass the server's full environment to LocalSandbox subprocesses. Scope the child process environment to only what the tool execution requires.
Use the isolation-backed sandbox implementations (E2B, Modal) for any multi-tenant or production deployment, and document that LocalSandbox is not safe for multi-tenant use.
Additional Resources
- CVE-2026-77938 record