Skip to content

Shell command environment differs by mode and is undocumented; OpenCode 2 has no per-call env channel (shell.env is only triggered on OpenCode 1) #639

Closed

Description

@iceteaSA

Summary

The environment a bash/shell command gets depends on how AFT runs, and that is not documented. On OpenCode 2 a plugin also has no way to set variables for one call.

  • Standalone (AFT as a child of the host): the command inherits the host process's whole environment. A variable set only on the OpenCode service was visible in env inside the command.
  • subc (the daemon module): the command inherits the module process's environment, not the host's. Host-side variables never arrive.
  • In both modes AFT removes subc credentials and adds its shims (crates/aft/src/agent_child_env.rs:729, apply_to_command).

Per-call variables: the tool has no env argument. On OpenCode 1, AFT triggers the host's shell.env plugin hook for each call and passes the result through (packages/opencode-plugin/src/tools/bash.ts:689, ctx.plugin?.trigger?.("shell.env", …)). ctx.plugin is set only on the OpenCode 1 entry (src/index.ts:458). The OpenCode 2 runtime builds its tool context without it (src/entry/server-runtime.mjs:116), so on OpenCode 2 that call is skipped and no plugin can add a variable for one command.

Why it matters

  • Secrets in the host service environment reach every command in standalone mode, but not in subc mode, so an allow-list or audit has to know the mode.
  • A plugin that wraps the shell (for example one that sets SUDO_ASKPASS for a single elevated call) must write the variables into the command text instead. That text is stored verbatim in AFT's task database and log.

Proposal

  1. Document the environment each mode gives a command: inherited from what, what AFT removes, what it adds.
  2. On OpenCode 2, give plugins a per-call environment channel: trigger an equivalent hook, or accept an env argument that a plugin can set in tool.execute.before (values never written to the task record or log).

Checked on main (da592c3). #610 asks for config-level env options (fixed variables, hermetic mode); this issue is about the mode difference and the per-call channel.

Activity

  1. aft-alfonso commented on Oct 23, 2026

    @aft-alfonso

    Thanks for the clear write-up.

    We've documented the environment a command gets in each mode in docs/tools.md: where it comes from in standalone and daemon mode, what AFT removes and adds, and what remote runon jobs receive. That lands in the next release.

    We're not going to add a per-call environment channel for plugins on OpenCode 2. OpenCode 2's shell hook is one a plugin subscribes to, not one AFT can trigger, and we'd rather not add a hidden plugin-only argument to the shell tool. A variable for a single command goes in the command text (NAME=value cmd). For values that shouldn't be stored with the command, point the variable at a file or helper rather than passing the value itself, as SUDO_ASKPASS already does.

    I'll close this once the documentation is released.

  2. aft-alfonso commented on Nov 2, 2026

    @aft-alfonso

    The documentation of each mode's command environment is released in v0.63.4, in docs/tools.md, so I'm closing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions