You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
agent env: /tmp/opencode advertised as writable scratch but owned by another user #44696
The agent environment banner advertises /tmp/opencode as "pre-created and approved for external access" and directs agents to prefer it over generic system temp dirs. On this host the directory exists but is owned by a different user than the session (uid 1000), so the agent cannot write to it:
$ printf 'probe' > /tmp/opencode/probe1.txt
/bin/bash: line 1: /tmp/opencode/probe1.txt: Permission denied
$ id
uid=1000(cunabu) ...
$ ls -ld /tmp/opencode
drwxrwxr-x 2 iconoclasta iconoclasta ...
Either /tmp/opencode is owned/mode'd so the session user can write to it, or the banner stops claiming it is approved for access when it is not.
Actual Behavior
A sanctioned scratch location is unusable; agents are steered toward a path that fails, then must fall back to un-advertised locations.
Additional Context
Low priority and mostly host-provisioning, but it is an instruction-vs-reality mismatch in shipped surface text. If /tmp/opencode is created by harness/host tooling rather than by the user, it should be created with the session user as owner; if it is expected to be pre-existing, docs should say so and note the permission requirement.
This issue might be a duplicate of existing issues. Please check:
Agent writes tmp files to /tmp instead of /tmp/opencode #44644: Also concerns agents and the /tmp/opencode scratch directory — that issue reports agents writing to /tmp instead of /tmp/opencode, while this issue reports /tmp/opencode being owned by a different user. They are related and may benefit from a shared fix (e.g. ensuring the directory is created with the correct ownership/permissions by the session user or host tooling).
Reproduced on 2.0.28 (Linux x86_64, multi-user host where /tmp/opencode is drwxrwxr-x 2 ubuntu ubuntu and a second user runs as uid=2571).
Beyond the EACCES failure for the second user, there are a few multi-tenant security implications with how Global.Path.tmp and os.tmpdir() are currently handled on shared POSIX hosts:
Global.Path.tmp (/tmp/opencode) has no 0o700 mode or UID/symlink verification (packages/util/src/global.ts:68-74):
fs.promises.mkdir(service.tmp, { recursive: true }) uses the default umask (0755 / 0775), so any files written into /tmp/opencode by the owning user (agent scratch files, web/desktop attachment uploads at ${info.paths.tmp}/uploads/... in packages/app/src/composer/attachments/destination.ts, etc.) are world-readable (0644) to other local users.
Because mkdir(..., { recursive: true }) succeeds if the directory already exists without checking lstat ownership (uid === process.getuid()) or permissions, another local user can pre-create /tmp/opencode with 0777 (or as a symlink that fs.promises.realpath(service.tmp) resolves and injects into the system prompt via packages/core/src/instructions/builtins.ts:43), allowing cross-user reading/tampering of scratch files.
Note for fix(util): use a per-user tmp directory #52973 (opencode-<uid>): even with opencode-<uid> in os.tmpdir(), acquire() in packages/util/src/global.ts should create service.tmp with mode: 0o700 and verify via lstat that the directory is not a symlink and is owned by process.getuid(), since /tmp/opencode-<uid> is still a predictable name in world-writable /tmp.
Other direct os.tmpdir() writes bypass Global.Path.tmp without 0o600:
packages/tui/src/editor.ts:17-18: writes the user's external $EDITOR prompt buffer to path.join(os.tmpdir(), "${Date.now()}.md") with default umask (0644, world-readable) and a predictable millisecond filename without flag: "wx".
packages/core/src/plugin/vcs/git.ts:562-569: writes ${os.tmpdir()}/opencode-untracked-${randomUUID()}.{list,index} with default umask (0644) in world-listable /tmp.
One more shape of this problem beyond the cross-user permission failure: /tmp/opencode\ is also a single flat namespace shared by all of one user's concurrent sessions. Even single-user, two sessions writing scratch or harness-created directories (the /tmp/opencode/t\ pattern) collide in the same way — there is no per-session namespace under the temp root.
Workarounds that avoid /tmp\ entirely: set \worktree.directory\ (already in the config schema) to a home-relative parent so new worktrees never land in temp, and have agents keep per-session scratch under a home-level directory (e.g. ~/.opencode/tmp/). The multi-user permission failure itself can only be fixed by moving off shared /tmp, which is what this issue tracks.
Activity
github-actions commentedon Aug 24, 2026
This issue might be a duplicate of existing issues. Please check:
/tmp/opencodescratch directory — that issue reports agents writing to/tmpinstead of/tmp/opencode, while this issue reports/tmp/opencodebeing owned by a different user. They are related and may benefit from a shared fix (e.g. ensuring the directory is created with the correct ownership/permissions by the session user or host tooling).Shra1V32 commentedon Oct 13, 2026
Reproduced on
2.0.28(Linux x86_64, multi-user host where/tmp/opencodeisdrwxrwxr-x 2 ubuntu ubuntuand a second user runs asuid=2571).Beyond the
EACCESfailure for the second user, there are a few multi-tenant security implications with howGlobal.Path.tmpandos.tmpdir()are currently handled on shared POSIX hosts:Global.Path.tmp(/tmp/opencode) has no0o700mode or UID/symlink verification (packages/util/src/global.ts:68-74):fs.promises.mkdir(service.tmp, { recursive: true })uses the default umask (0755/0775), so any files written into/tmp/opencodeby the owning user (agent scratch files, web/desktop attachment uploads at${info.paths.tmp}/uploads/...inpackages/app/src/composer/attachments/destination.ts, etc.) are world-readable (0644) to other local users.mkdir(..., { recursive: true })succeeds if the directory already exists without checkinglstatownership (uid === process.getuid()) or permissions, another local user can pre-create/tmp/opencodewith0777(or as a symlink thatfs.promises.realpath(service.tmp)resolves and injects into the system prompt viapackages/core/src/instructions/builtins.ts:43), allowing cross-user reading/tampering of scratch files.opencode-<uid>): even withopencode-<uid>inos.tmpdir(),acquire()inpackages/util/src/global.tsshould createservice.tmpwithmode: 0o700and verify vialstatthat the directory is not a symlink and is owned byprocess.getuid(), since/tmp/opencode-<uid>is still a predictable name in world-writable/tmp.Other direct
os.tmpdir()writes bypassGlobal.Path.tmpwithout0o600:packages/tui/src/editor.ts:17-18: writes the user's external$EDITORprompt buffer topath.join(os.tmpdir(), "${Date.now()}.md")with default umask (0644, world-readable) and a predictable millisecond filename withoutflag: "wx".packages/core/src/plugin/vcs/git.ts:562-569: writes${os.tmpdir()}/opencode-untracked-${randomUUID()}.{list,index}with default umask (0644) in world-listable/tmp.lyckantropen commentedon Oct 25, 2026
One more shape of this problem beyond the cross-user permission failure: /tmp/opencode\ is also a single flat namespace shared by all of one user's concurrent sessions. Even single-user, two sessions writing scratch or harness-created directories (the /tmp/opencode/t\ pattern) collide in the same way — there is no per-session namespace under the temp root.
Workarounds that avoid /tmp\ entirely: set \worktree.directory\ (already in the config schema) to a home-relative parent so new worktrees never land in temp, and have agents keep per-session scratch under a home-level directory (e.g. ~/.opencode/tmp/). The multi-user permission failure itself can only be fixed by moving off shared /tmp, which is what this issue tracks.