Skip to content

[Bug]: terminal env_passthrough not honored by Daytona backend — allowlisted env never reaches the sandbox #59286

Description

@chokolademilch7

Bug Description

Bug Description
terminal.env_passthrough (and a skill's required_environment_variables) are
silently ignored when terminal.backend: daytona. Allowlisted env vars that are
present in the Hermes process environment are never forwarded to the remote
sandbox, so commands run there see them as empty.

The same allowlist works correctly on the local / execute_code backends,
which consult is_env_passthrough. The Daytona backend does not — it never
passes env= to the sandbox process.exec() call, and
tools/environments/daytona.py contains zero references to is_env_passthrough.

Confirmed present in 0.17.0, 0.18.0, and main (re-checked 2026-07-06).

Steps to Reproduce

  1. Set terminal.backend: daytona in config.yaml.
  2. Allowlist a var that exists in the Hermes process env, e.g.:
    terminal:
    env_passthrough: [MY_TEST_VAR]
    (export MY_TEST_VAR=hello so it is in Hermes' os.environ)
  3. Ask the agent to run, via the terminal tool:
    echo "val=${MY_TEST_VAR:-MISSING}"
  4. Observe the output.

Steps to Reproduce

  1. Configure the terminal tool to use the Daytona backend:
    terminal:
    backend: daytona

  2. Make a variable available to Hermes and allowlist it for passthrough.
    Either:
    (a) config.yaml:
    terminal:
    env_passthrough: [MY_TEST_VAR]
    or (b) a skill's frontmatter:
    required_environment_variables: [MY_TEST_VAR]
    Ensure MY_TEST_VAR is present in Hermes' own process environment
    (e.g. export MY_TEST_VAR=hello before launching Hermes, or via .env).

  3. Start Hermes and ask the agent to run this via the terminal tool:
    echo "val=${MY_TEST_VAR:-MISSING}"

  4. Observe the command output from the Daytona sandbox.

Expected: val=hello (as happens with terminal.backend: local)
Actual: val=MISSING — the allowlisted var never reaches the sandbox.

Expected Behavior

The allowlisted variable is forwarded into the Daytona sandbox, so the command
prints:

  val=hello

i.e. terminal.env_passthrough and a skill's required_environment_variables
should be honored on the Daytona backend, exactly as they already are on the
local / execute_code backends. The env_passthrough docstring itself scopes
the feature to "sandboxed execution environments (execute_code, terminal)", and
the Daytona backend is a terminal backend.

Actual Behavior

The command prints:

  val=MISSING

No allowlisted variable is forwarded to the Daytona sandbox, and no warning is
emitted. Any skill that relies on required_environment_variables for
credentials is therefore unusable on the Daytona backend.

Affected Component

Tools (terminal, file ops, web, code execution, etc.), Configuration (config.yaml, .env, hermes setup)

Messaging Platform (if gateway-related)

N/A (CLI only)

Debug Report

Code-path bug, backend-agnostic (not environment-specific), so a full debug
  bundle adds little. Verified on the latest published version: clean-installed
  hermes-agent==0.18.0 from PyPI (equivalent to `hermes update`) —
  tools/environments/daytona.py has zero `is_env_passthrough` references and
  `exec_fn` calls `sandbox.process.exec(shell_cmd, timeout=timeout)` with no
  `env=`. Also present on `main` (2026-07-06). terminal.backend: daytona (DinD).
  OS: Ubuntu 24.04, Python 3.13.
  Full `hermes debug share` omitted intentionally: it bundles Discord conversation
  logs (private), and this bug is in the code path, not the environment.

Operating System

Ubuntu 24.04 (kernel 6.17.0-35-generic)

Python Version

3.13

Hermes Version

0.17.0 (also confirmed on 0.18.0 and main)

Additional Logs / Traceback (optional)

N/A — no traceback; the failure is silent (allowlisted var is simply absent in
  the sandbox environment).

Root Cause Analysis (optional)

tools/environments/daytona.py _run_bashexec_fn:

  def exec_fn() -> tuple[str, int]:
      response = sandbox.process.exec(shell_cmd, timeout=timeout)   # no env=
      return (response.result or "", response.exit_code)
  • daytona.py never calls is_env_passthrough; local.py builds a run env from it
    and passes env=. The Daytona SDK supports it:
    process.exec(command, cwd, env, timeout).
  • Subtlety: the allowlist must be resolved in the calling context, not inside
    exec_fn. exec_fn runs in the _ThreadedProcessHandle worker thread
    (threading.Thread, base.py), and ContextVar-based skill declarations
    (required_environment_variables) do not propagate into it. Config-based
    passthrough happens to work only because it is a process-global cache.

Proposed Fix (optional)

Resolve the allowlisted subset of os.environ in _run_bash (honoring
is_env_passthrough, which already refuses Hermes provider credentials per
GHSA-rhgp-j443-p4rf), then pass it as env= to process.exec. Only
explicitly-allowlisted names are forwarded, so the remote sandbox stays isolated
by default.

I have a working, ACP-verified patch on the latest main with regression tests
(_collect_passthrough_env allowlist filtering + _run_bash passing env= to
exec; tools/environments/daytona.py → 29 passed, ruff clean). Happy to open a
PR.

Are you willing to submit a PR for this?

  • I'd like to fix this myself and submit a PR

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existsarea/configConfig system, migrations, profilesbackend/daytonaDaytona cloud workspacebugtool/terminalTerminal execution and process managementtype/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions