Skip to content

fix(cloud): give a cloud box's agent its workspace, and stop three AppleDouble leaks - #370

Open
madarco wants to merge 3 commits into
nightlyfrom
agentbox/cloud-workspace-env
Open

fix(cloud): give a cloud box's agent its workspace, and stop three AppleDouble leaks#370
madarco wants to merge 3 commits into
nightlyfrom
agentbox/cloud-workspace-env

Conversation

@madarco

@madarco madarco commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Blockers 1 and 4 of docs/plans/service-boxes-plan.md. Two independent fixes,
one commit each.

A cloud box's agent never saw its workspace

Measured on a Hetzner OpenClaw box: the user's files were in /workspace and
the gateway was pointed at ~/.openclaw/workspace. The headline "your project
dir is the agent's workspace" behaviour, broken on every VPS provider.

spec.boxRunEnv was built and handed to provision correctly, then dropped
twice:

  • hetzner's cloudInitBoxEnv keeps only AGENTBOX_-prefixed keys — rightly,
    since req.env also carries forwarded provider API keys and cloud-init writes
    box.env 0644;
  • and kickCloudBootstrap rewrites box.env with tee anyway, on every create
    and resume, so anything cloud-init wrote is gone a step later.

The bootstrap is the last writer and the shared cloud path, so it is the one
place to fix. The run-env now rides both surfaces it already maintains:

  • env — exported before agentbox-ctl bootstrap; the daemon is spawned
    with env: process.env and hands each task { ...process.env }. This is what
    actually fixes openclaw-onboard.
  • boxEnvFile — because a cloud login shell does not inherit the
    daemon's env, so a hand-run openclaw would otherwise disagree with the
    service unit. Docker needs only one surface because docker run -e serves
    both; a VPS has no equivalent store.

It takes buildCloudBoxRunEnv, not buildForwardedEnv — only what a
registry row declares, never the API keys that would then land in a
world-readable file.

Three more tars leaking AppleDouble stubs

tar on macOS emits ._* resource-fork stubs without COPYFILE_DISABLE, and
they land wherever the archive is unpacked. Three creating call sites still
lacked it: the no-git cloud seed (what an OpenClaw workspace usually is — a
real box came up holding ._agentbox.yaml, ._AGENTS.md, ._skills), and both
--with-env file packers.

This is the fifth time this class has been fixed, because the rule lived only
in the comments of the sites that already had it. A source scan now asserts it:
every tar that CREATES an archive passes COPYFILE_DISABLE; extractions are
exempt. It names the offending file:line when it fails.

Verified on a fresh Hetzner box

--- box.env:            OPENCLAW_WORKSPACE_DIR=/workspace
--- workspace in use:   /workspace
--- AppleDouble count:  0
--- /workspace:         agentbox.yaml  AGENTS.md  SOUL.md  skills  …

and the gateway's own agents.defaults.workspace reports /workspace, with
/workspace/skills/summarise-thread.md sitting where it now looks. Box
destroyed; the Hetzner API shows only the pre-existing box.

Both behavioural fixes are mutation-checked. build, typecheck, test and
lint green.

Found while verifying, not fixed: a brand-new box refuses any openclaw CLI
command that touches exec approvals until openclaw doctor --fix runs
(ExecApprovalsMigrationRequiredError, on a box where nothing legacy can exist).
The gateway itself is unaffected; whether the runtime approvals path is too is
unknown. Recorded in the backlog and added to the plan's blocker list.

https://claude.ai/code/session_01ChpFVKPMobDd3xk3uwG6gj


Note

Medium Risk
Changes cloud bootstrap env wiring and multiple host-side tar paths used for workspace seeding and sync; incorrect quoting or env filtering could break onboard or leak secrets, though only declared agent run-env is written to world-readable box.env.

Overview
Cloud VPS boxes were onboarding OpenClaw against ~/.openclaw/workspace while project files lived in /workspace because declared spec.boxRunEnv never reached ctl tasks or login shells after bootstrap rewrote /etc/agentbox/box.env. buildBootstrapEnv now accepts agentRunEnv from buildCloudBoxRunEnv (non-secret registry vars only) and writes each key to both the exported bootstrap env and boxEnvFile, with cloud-provider passing it on create and every resume kick.

macOS archive creation no longer leaks ._* AppleDouble stubs into boxes: COPYFILE_DISABLE=1 was added to the no-git cloud seed tar, shared env-file packing in sandbox-core, and docker host export packing. A new tar-copyfile-disable.test.ts source scan fails CI if any archive-creating execa('tar', …) omits that guard.

Planning docs mark the workspace-env and AppleDouble blockers done and record a separate follow-up: fresh boxes may need openclaw doctor --fix before exec-approval CLI commands work.

Reviewed by Cursor Bugbot for commit 32c257c. Configure here.

A hetzner OpenClaw box had the user's files in /workspace and the gateway
pointed at ~/.openclaw/workspace — the headline "your project dir is the
agent's workspace" behaviour, broken off docker.

`spec.boxRunEnv` was built and passed to `provision` correctly, then
dropped twice: hetzner's `cloudInitBoxEnv` keeps only `AGENTBOX_`-prefixed
keys (rightly — `req.env` also carries forwarded API keys and box.env is
0644), and `kickCloudBootstrap` rewrites box.env with `tee` anyway on
every create AND resume.

The bootstrap is the last writer and the shared cloud path, so it is the
one place to fix. The run-env now rides both surfaces it already
maintains: `env`, which the daemon inherits and hands to every task —
that is what fixes onboard — and `boxEnvFile`, because a cloud login
shell does NOT inherit the daemon's env, so a hand-run `openclaw` would
otherwise disagree with the service unit. Docker needs only the one
surface because `docker run -e` serves both.

Takes `buildCloudBoxRunEnv`, not `buildForwardedEnv`: only what a
registry row declares, never the provider API keys that would then land
in a world-readable file.

Claude-Session: https://claude.ai/code/session_01ChpFVKPMobDd3xk3uwG6gj
`tar` on macOS emits `._*` resource-fork stubs unless COPYFILE_DISABLE
is set, and they land wherever the archive is unpacked. Three creating
call sites still lacked it:

- the NO-GIT cloud seed, which is what an OpenClaw workspace usually is —
  a real hetzner box came up with ._agentbox.yaml, ._AGENTS.md, ._skills
  in /workspace;
- and both --with-env file packers, docker's and the generic concern's.

This is the fifth time this class has been fixed, because the rule lived
only in the comments of the sites that already had it. A source scan now
asserts it: every tar that CREATES an archive passes COPYFILE_DISABLE.
Extractions are exempt — the variable means nothing when unpacking.

Claude-Session: https://claude.ai/code/session_01ChpFVKPMobDd3xk3uwG6gj
The cloud workspace-env and AppleDouble blockers are verified fixed on a
fresh hetzner box. Testing it surfaced a new one: a brand-new box refuses
any openclaw CLI command touching exec approvals until `doctor --fix`
has run.

Claude-Session: https://claude.ai/code/session_01ChpFVKPMobDd3xk3uwG6gj
@vercel

vercel Bot commented Sep 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
agentbox-web Ready Ready Preview Sep 7, 2026 8:38am UTC

Request Review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 32c257c. Configure here.

webProxyPort: webPort,
// Re-sent on every kick: the kick REWRITES box.env with `tee`, so a value
// it omits is gone for the rest of the box's life, not merely stale.
agentRunEnv: buildCloudBoxRunEnv(box.agents ?? []),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resume drops cloud agent run-env

Medium Severity

Resume builds agentRunEnv from box.agents ?? [], so a missing agents field becomes no run-env. Create uses agentVolumes.agents, which means every agent when the caller omitted a selection, and the kick then rewrites box.env with tee. A later start therefore strips OPENCLAW_WORKSPACE_DIR (and other declared run-env) for generic and pre-selection boxes, undoing the workspace fix this change just applied.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 32c257c. Configure here.

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