Skip to content

feat: configurable env passthrough, extra mounts, and host path trans…#5

Open
Jenriquez5 wants to merge 1 commit into
13rac1:mainfrom
kapitecsoluciones:feat/configurable-mounts-env-passthrough
Open

feat: configurable env passthrough, extra mounts, and host path trans…#5
Jenriquez5 wants to merge 1 commit into
13rac1:mainfrom
kapitecsoluciones:feat/configurable-mounts-env-passthrough

Conversation

@Jenriquez5

Copy link
Copy Markdown

Summary

Adds four non-breaking PodmanConfig / plugin config fields so this plugin can be used in environments that the built-in behavior does not cover today:

  • extraEnvPassthrough — additional env var names to forward to the sandbox via -e, appended to the built-in list (ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC). Typical uses: CLAUDE_CODE_OAUTH_TOKEN, GH_TOKEN, GITHUB_TOKEN.
  • envFallback — fallback value map for passthrough keys when process.env has none. Lets hosts whose own config store is not mirrored into process.env still feed secrets to the sandbox. process.env wins when both are present.
  • extraMounts — extra -v mounts appended to every sandbox container, for SSH keys, GPG keyring, credentials bind mounts, etc. Each entry is { hostPath, containerPath, mode? } with mode defaulting to rw.
  • hostPathTranslations — prefix rewrites applied to every mount source path before being handed to the container runtime. Required for docker-out-of-docker setups: when the plugin runs inside a container with a mounted docker/podman socket, the paths it sees (/home/user/.claude, ~/.openclaw/workspaces/...) are not visible to the host runtime daemon and must be rewritten to the real host paths. Longest matching prefix wins.

Motivation

I'm running this plugin inside an OpenClaw bot container with /var/run/docker.sock bind-mounted. Two things stop working out of the box and both need small config hooks rather than environment-specific monkey-patching of dist/:

  1. Mount paths: hostClaudeDir and workspaceDir are container-local paths that the host daemon can't resolve. hostPathTranslations rewrites them.
  2. OAuth / gh token passthrough: OpenClaw's config store (env.vars) is not injected into its own process.env; it's only injected into skill subprocesses, which the plugin is not. Without envFallback, secrets configured for the bot can't reach the sandbox, even though the user has already put them in a config file on the same machine. extraEnvPassthrough + envFallback solves this without tying the plugin to any particular host application.

All four fields are optional, default to empty, and match the existing behavior when unset. No migration is required for existing installs.

Implementation notes

  • translateHostPath is exported as a pure function so the rewrite logic can be unit-tested in isolation (longest-match, exact match, suffix, non-boundary prefix). It is applied uniformly to hostClaudeDir, workspaceDir, and every extraMounts[i].hostPath — callers cannot forget to translate one of the three.
  • BUILTIN_PASSTHROUGH_KEYS stays intact; user keys are merged via new Set([...builtin, ...extraEnvPassthrough]) so duplicates are harmless.
  • extraMounts entries default to :rw to match the behavior of the built-in claude home / workspace mounts. :ro is available when the caller wants it.

Test plan

  • npx tsc --noEmit clean
  • npx eslint src/ clean
  • npm run build produces dist/claude-code.js (162.5kb)
  • npx vitest run208/208 tests passing, including 9 new tests in src/podman-runner.test.ts:
    • translateHostPath: undefined translations, no-match, exact prefix, prefix + suffix, longest-match wins, non-boundary prefix not rewritten
    • startDetached with hostPathTranslations applied to both hostClaudeDir and workspaceDir
    • startDetached with extraMounts using default (rw) and explicit (ro) modes
    • startDetached with extraEnvPassthrough reading from envFallback when process.env is empty
    • process.env winning over envFallback when both present
  • Manually verified on a real bot: the rewritten mounts resolve correctly on the host, the sandbox picks up CLAUDE_CODE_OAUTH_TOKEN and GH_TOKEN from envFallback, and an extraMounts entry for SSH keys lets ssh -T git@github.com succeed inside a claude_code_start session.

Docs

  • README.md gains one table row per new option and two worked examples:
    • docker-out-of-docker with hostPathTranslations
    • SSH keys via extraMounts + OAuth/GitHub tokens via extraEnvPassthrough + envFallback

Happy to split this into smaller PRs (one per field) if you'd prefer that review shape — they all stack on top of the same PodmanConfig interface extension so I left them together.

…lation

Adds four non-breaking `PodmanConfig` / plugin config fields so users can
run the plugin in environments that the built-in behavior does not cover:

- `extraEnvPassthrough`: additional env var names to forward to the sandbox
  via `-e`, appended to the built-in list (`ANTHROPIC_BASE_URL`,
  `ANTHROPIC_AUTH_TOKEN`, `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`).
  Typical use: `CLAUDE_CODE_OAUTH_TOKEN`, `GH_TOKEN`, `GITHUB_TOKEN`.

- `envFallback`: a fallback value map for passthrough keys when
  `process.env` has none. Lets hosts whose own config store is not
  reflected in `process.env` still feed secrets to the sandbox
  (`process.env` wins when both are present).

- `extraMounts`: extra `-v` mounts appended to every container, for
  things like SSH keys, GPG keyring, or credentials bind mounts.

- `hostPathTranslations`: prefix rewrites applied to every mount source
  path before being handed to the container runtime. Required when the
  plugin runs inside a container with a mounted docker/podman socket
  (docker-out-of-docker): the in-container paths the plugin sees are
  not visible to the host runtime daemon, so they must be rewritten to
  the real host paths. Longest matching prefix wins.

The rewrite is extracted as an exported pure function `translateHostPath`
for unit testing, and is applied uniformly to `hostClaudeDir`,
`workspaceDir`, and `extraMounts[i].hostPath` so callers cannot forget
to translate one of the three.

Tests: 9 new tests in `podman-runner.test.ts` covering
`translateHostPath` (undefined/no-match/exact/suffix/longest/non-boundary),
`hostPathTranslations` applied at `startDetached` time, `extraMounts`
with default and explicit modes, `extraEnvPassthrough` reading from
`envFallback` when `process.env` is empty, and `process.env` winning
over `envFallback` when both are present. README updated with a new
config options table row for each field and two worked examples
(docker-out-of-docker, extra mounts + env passthrough).

Full suite: 208/208 passing, tsc clean, eslint clean, build clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant