How I stopped babysitting terminals and let one small server drive every coding project on my home box.
The problem
I run a lot of coding-agent sessions across many projects on a home server
(<home-server>) and a few other machines. Every one of them assumes you’re
sitting in front of a terminal. I wanted to reach them from a phone — while
commuting, without a laptop, without SSH-ing into five boxes.
opencode has a headless mode: opencode serve. Before building anything, I
wanted to know one thing: can a server control a project it never started?
The probe
I started a server on a throwaway port and hit the API.
curl http://127.0.0.1:4096/global/health
# {"healthy":true,"version":"1.18.34"}
Then I listed “projects” — and this is where the mental model broke, in a good way. The fresh server reported 10 projects it had never launched. It knew about them because they were in the shared database, but it hadn’t started any.
Next test: create a session in a directory the server had never seen.
curl -X POST \
-H "x-opencode-directory: /tmp/opencode-probe" \
-H 'content-type: application/json' \
-d '{"title":"probe"}' \
http://127.0.0.1:4096/session
It worked. That header is the whole trick: it scopes a session to a directory.
The thing that clicked
A “project” in opencode is a directory, not a running process.
That single sentence changed the design. It means:
- One long-lived server can drive every project on the machine. No per-project daemon.
- The server never needs to “start” a project — it just needs its filesystem to see the path.
- “Managing many projects” collapses into “routing a request to a directory.”
I sent a real prompt through the same path and got a reply back. End to end:
curl -X POST -H "x-opencode-directory: /tmp/opencode-probe" \
-H 'content-type: application/json' \
-d '{"parts":[{"type":"text","text":"Reply with exactly: REMOTE-OK"}]}' \
http://127.0.0.1:4096/session/<id>/message
# ... "REMOTE-OK"
Things worth knowing early
- Auth is off by default. Set
OPENCODE_SERVER_PASSWORDthe moment the server is reachable beyond loopback. It’s HTTP basic auth, useropencode. - The header is a trust boundary.
x-opencode-directorylets any caller act as that project, with your user’s rights. There is no sandbox between projects. Any bridge you build must pass only paths from a fixed allowlist. - Server and TUI coexist. A TUI,
opencode run, andopencode serveare all client+server, and they share one config and one database. I ran a standalone server while three TUIs were open and it listed all their sessions. - The API you want is v1. There’s also a newer surface (more on that in
another post) — but the complete, global one is
/project,/session,/event.
Takeaway
The expensive-sounding feature — “remotely manage dozens of projects” — turned out to be a thin routing problem over a very capable server. The mental model was the hard part; the code is trivial. Once you internalize directory, not process, the rest is plumbing.