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
[FEATURE]: add OPENCODE_PASSWORD_FILE for more password setting options #52156
I have verified this feature I'm about to request hasn't been suggested before.
Describe the enhancement you want to request
Please can we add OPENCODE_PASSWORD_FILE alongside the existing OPENCODE_PASSWORD support for setting a password?
Among other use cases, this would allow for nice systemd credential integration for folks like myself who enjoy running opencode serve as a systemd service, rather than having opencode manage the children (nice cgroups wins here!). Containers and CI also commonly enjoy this pattern too. Not polluting env vars with secrets can also be safer.
Proposed resolution order:
current OPENCODE_PASSWORD / config password
current OPENCODE_SERVER_PASSWORD (legacy name)
new password read from OPENCODE_PASSWORD_FILE. Fail loudly if not present.
trim whitespace to enhance compatibility with systemd, which often has a newline
current randomly generate fallback
We should also add opencode service set password_file too, for parity with current password config too. (One note, this currently does not affect opencode serve, which was a source of surprise to me. I'm tempted to make a new ticket for that. It would be good to at least be able to opt in to config.)
Implementation suggestions: everything is in packages/cli/. src/env.ts has existing Env.password, and we resolve the password file into it when called for. src/server-process.ts consumes. src/services/service-config gets the new password_file handling.
systemd bonus
For even nicer systemd integration, we could also consider supporting --credential-name <name> too, and loading from $CREDENTIALS_DIRECTORY/$name. This bakes in some systemd assumptions, but means LoadCredential= etc just work, without having to set any special env vars.
A security reason for this, beyond systemd and containers: a process's startup environment stays readable by every same-user process for its whole lifetime (ps eww -p <pid> on macOS, /proc/<pid>/environ on Linux), and deleting the variable after startup does not change that.
So any child opencode serve starts (a shell tool command, an MCP server, an LSP server) can run ps eww -p $PPID, read OPENCODE_SERVER_PASSWORD, and call the server API with full access, including answering permission requests. Repro on 1.18.32, macOS: start OPENCODE_SERVER_PASSWORD=secret opencode serve, run ps eww -p $PPID from a session shell command, and the password is in the output.
A file input fixes this as long as the secret itself never goes into the environment or argv. A --password-fd <n> option would also help apps that embed the server and do not want a secret file on disk.
(Opened separately as #52447; closing that one as a duplicate of this.)
+1, and a security angle: with the password in the environment it also leaks into every tool shell the server spawns.
Version: opencode 2.0.22 (linux-x64).
What happens
opencode serve takes its password from OPENCODE_PASSWORD (or OPENCODE_SERVER_PASSWORD) when not in --service mode. The server's
own child processes inherit that environment. So every shell command an
agent runs through the server's shell tool can read the password with env, and so can anything such a command starts.
Minimal repro (deterministic, with a scripted model)
A scratch HOME, XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_STATE_HOME, XDG_CACHE_HOME and TMPDIR. A provider using @ai-sdk/openai-compatible that points at a local OpenAI-compatible
server, which answers the first tool-enabled request with one shell
call: env | grep -c ^OPENCODE_PASSWORD= > count.txt.
opencode run --server http://127.0.0.1:<p> --format json -m mock/scripted "count".
Result (measured 2026-10-15): the client exits 0, and count.txt
contains 1. The tool's shell has the server's password.
Today's workaround:serve --service reads the password from <XDG_CONFIG_HOME>/opencode/service.json, so it never has to be in the
environment. The binary's own logic is roughly mode === "service" ? config.password || random : env || random. But --service brings
service-mode behaviour (registration file, chdir to HOME, autostart by
clients; see issue 2) that a supervisor may not want.
Ask
--password-file <path> for serve, and for the client's --server
connection, so neither needs the password in its environment or argv.
Independently: strip OPENCODE_PASSWORD / OPENCODE_SERVER_PASSWORD
from the environment of tool processes the server spawns. The client
path already strips the env a client sends; the server's own tools do
not.
A data point for serve --stdio at v2.0.32, plus one requirement for the design.
serve --stdio reads OPENCODE_PASSWORD, then deletes it from process.env (packages/cli/src/server-process.ts:80-85). Shells, command templates and tools then do not inherit it. On Linux the initial environment block still holds it for the server's whole life:
Configure a command whose template is !`tr '\0' '\n' < /proc/$PPID/environ | grep '^OPENCODE_PASSWORD=' | cut -d= -f2-` .
Start OPENCODE_PASSWORD=<synthetic> opencode serve --stdio --hostname 127.0.0.1 --port 0, create a session and run the command.
The stored user message contains the password. The same template with printenv OPENCODE_PASSWORD prints nothing.
A config value {file:/proc/self/environ} also returns it.
For stdio mode, a launcher needs an input that leaves no copy in the environment block, so OPENCODE_PASSWORD_FILE fits if the server reads the file once at startup. The launcher can then delete the file after the server answers. --password-fd <n>, read once and closed, works the same way. Either keeps the stdin lease of stdio mode unchanged.
This does not stop a same-user process that can read the server's memory, for example where kernel.yama.ptrace_scope is 0. It removes the copy that a plain file read reaches.
Activity
rektide commentedon Sep 30, 2026
I am ready to go on the PR for this by the way. It's pretty contained! Trying to follow contributor guidelines and get design review. 🙇
phetzy commentedon Oct 1, 2026
A security reason for this, beyond systemd and containers: a process's startup environment stays readable by every same-user process for its whole lifetime (
ps eww -p <pid>on macOS,/proc/<pid>/environon Linux), and deleting the variable after startup does not change that.So any child
opencode servestarts (a shell tool command, an MCP server, an LSP server) can runps eww -p $PPID, readOPENCODE_SERVER_PASSWORD, and call the server API with full access, including answering permission requests. Repro on 1.18.32, macOS: startOPENCODE_SERVER_PASSWORD=secret opencode serve, runps eww -p $PPIDfrom a session shell command, and the password is in the output.A file input fixes this as long as the secret itself never goes into the environment or argv. A
--password-fd <n>option would also help apps that embed the server and do not want a secret file on disk.(Opened separately as #52447; closing that one as a duplicate of this.)
askaramirov commentedon Oct 16, 2026
+1, and a security angle: with the password in the environment it also leaks into every tool shell the server spawns.
Version: opencode 2.0.22 (linux-x64).
What happens
opencode servetakes its password fromOPENCODE_PASSWORD(orOPENCODE_SERVER_PASSWORD) when not in--servicemode. The server'sown child processes inherit that environment. So every shell command an
agent runs through the server's
shelltool can read the password withenv, and so can anything such a command starts.Minimal repro (deterministic, with a scripted model)
HOME,XDG_CONFIG_HOME,XDG_DATA_HOME,XDG_STATE_HOME,XDG_CACHE_HOMEandTMPDIR. A provider using@ai-sdk/openai-compatiblethat points at a local OpenAI-compatibleserver, which answers the first tool-enabled request with one
shellcall:
env | grep -c ^OPENCODE_PASSWORD= > count.txt.OPENCODE_PASSWORD=<random> opencode serve --hostname 127.0.0.1 --port <p>.opencode run --server http://127.0.0.1:<p> --format json -m mock/scripted "count".Result (measured 2026-10-15): the client exits 0, and
count.txtcontains
1. The tool's shell has the server's password.Today's workaround:
serve --servicereads the password from<XDG_CONFIG_HOME>/opencode/service.json, so it never has to be in theenvironment. The binary's own logic is roughly
mode === "service" ? config.password || random : env || random. But--servicebringsservice-mode behaviour (registration file, chdir to HOME, autostart by
clients; see issue 2) that a supervisor may not want.
Ask
--password-file <path>forserve, and for the client's--serverconnection, so neither needs the password in its environment or argv.
OPENCODE_PASSWORD/OPENCODE_SERVER_PASSWORDfrom the environment of tool processes the server spawns. The client
path already strips the env a client sends; the server's own tools do
not.
Skeptomenos commentedon Oct 19, 2026
A data point for
serve --stdioat v2.0.32, plus one requirement for the design.serve --stdioreadsOPENCODE_PASSWORD, then deletes it fromprocess.env(packages/cli/src/server-process.ts:80-85). Shells, command templates and tools then do not inherit it. On Linux the initial environment block still holds it for the server's whole life:!`tr '\0' '\n' < /proc/$PPID/environ | grep '^OPENCODE_PASSWORD=' | cut -d= -f2-`.OPENCODE_PASSWORD=<synthetic> opencode serve --stdio --hostname 127.0.0.1 --port 0, create a session and run the command.printenv OPENCODE_PASSWORDprints nothing.A config value
{file:/proc/self/environ}also returns it.For stdio mode, a launcher needs an input that leaves no copy in the environment block, so
OPENCODE_PASSWORD_FILEfits if the server reads the file once at startup. The launcher can then delete the file after the server answers.--password-fd <n>, read once and closed, works the same way. Either keeps the stdin lease of stdio mode unchanged.This does not stop a same-user process that can read the server's memory, for example where
kernel.yama.ptrace_scopeis 0. It removes the copy that a plain file read reaches.