Skip to content

agent env: /tmp/opencode advertised as writable scratch but owned by another user #44696

Open

Description

@jc0d35

Summary

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 ...

(Verified 2026-08-24.)

Environment

  • opencode version: opencode2 v0.0.0-beta-18050 (beta)
  • OS: Linux 6.8.0-111-generic x86_64
  • Shell: /bin/bash; Terminal: tmux

Expected Behavior

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.

Activity

  1. github-actions commented on Aug 24, 2026

    @github-actions
    Contributor

    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).
  2. Shra1V32 commented on Oct 13, 2026

    @Shra1V32

    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:

    1. 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.
    2. 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.
  3. lyckantropen commented on Oct 25, 2026

    @lyckantropen

    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.

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions