Skip to content

[FEATURE]: acp: let the internal server use a fixed port, or no TCP port at all #62986

Open

Description

@pauljsymonds

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

Context

We run opencode acp under an OS sandbox (sandbox-runtime:
Seatbelt on macOS, bubblewrap on Linux) so that the agent can write only inside one git worktree
and reach only its model. OpenCode works well this way on Linux. On macOS it only starts if the
sandbox is opened much wider than it should be, and the cause is one detail of how acp starts.

What happens

opencode acp spawns a second OpenCode process as its server and talks to it over HTTP on
127.0.0.1. The port is assigned by the OS, so it is different on every start. Three starts in a
row, unsandboxed, with lsof -nP -iTCP -sTCP:LISTEN:

opencode 127.0.0.1:59774
opencode 127.0.0.1:59783
opencode 127.0.0.1:59793

In the source this is packages/cli/src/services/standalone.ts, which runs the server with
--stdio --port 0. We found no flag on acp, no config key and no environment variable that
changes it. A server.port entry in opencode.json and an OPENCODE_PORT variable had no effect
on this listener.

Why it matters under a sandbox

  • Linux (bubblewrap): no problem. The sandbox has its own network namespace, so the listener
    is private to the sandboxed process tree.

  • macOS (Seatbelt): a sandboxed process may not bind a TCP port unless the sandbox allows it.
    Without that, opencode acp exits before its first protocol message:

    Error: Standalone server exited before reporting readiness
    

    The only setting sandbox-runtime has for this allows binding any local port and connecting to
    localhost on any port
    . With it OpenCode runs, and so does a connect from the agent's shell
    to any other service on the machine's loopback: a local database, a dev server, another agent's
    listener. A per-port allowance is proposed there, but it needs the port in advance, and this one
    cannot be known in advance. We raised that side as sandbox-runtime [bug] Interruption in GPT-4.1 prevents continuation #817.

So today the choice on macOS is "OpenCode does not start" or "OpenCode can reach everything on
localhost". For people who pick a local model because nothing should leave their control, the
second is the wrong default.

Request

Either of these would fix it. The first is the smaller change; the second is the better one.

  1. A way to set the internal server's port. For example opencode acp --server-port <n>, or
    an environment variable that standalone.ts passes through instead of 0. opencode serve
    already takes --port, so the plumbing exists. A supervisor can then pick a free port, allow
    exactly that port in its sandbox policy, and tell OpenCode to use it.
  2. No TCP listener for this hop. The two processes are parent and child, and the child is
    already started with --stdio. Carrying the client's requests over the child's stdio, or over
    a Unix socket in OpenCode's state directory, removes the port altogether.

If neither is wanted, a line in the ACP documentation would still help. It says ACP mode "doesn't
open a network port for ACP", which is true of the protocol side but not of this internal
listener, and it is the internal listener that a sandbox trips over.

How we would verify

  • With option 1: lsof shows the listener on the requested port, and under sandbox-runtime with
    only that port allowed, opencode acp completes initialize, session/new and a prompt turn
    while a connect to a listener on a second loopback port is refused.
  • With option 2: lsof shows no TCP listener for the acp process tree, and the same session
    works under a sandbox that allows no local binding at all.
  • On Linux nothing changes.

We have a harness that runs exactly these checks on macOS and Linux and can test a build.

Environment

  • OpenCode 2.2.6 (Homebrew on macOS arm64; the opencode-linux-x64 release build on Ubuntu 24.04)
  • macOS 27.2; sandbox-runtime 0.0.81
  • ACP client: agent-client-protocol 5.1.0 (Rust), protocol version 1

Activity

  1. opencodejr commented on Nov 9, 2026

    @opencodejr
    Contributor

    Thanks for the detailed write-up and the verification plan, @pauljsymonds.

    To restate the request: opencode acp starts its internal server with an OS-assigned loopback port (serve --stdio --port 0 in packages/cli/src/services/standalone.ts). You'd like a way to pin that port, or better, to drop the TCP listener for this parent-to-child hop altogether (stdio or a Unix socket). Then a macOS Seatbelt sandbox can allow exactly that port, or no local binding at all, without opening all of localhost.

    I checked the code and it matches what you found. The port is hardcoded to 0, and neither the acp command, server.port/OPENCODE_PORT nor any other setting reaches it. A plugin can't work around this, because the child server is spawned before plugins load. You're also right that the ACP docs' "doesn't open a network port for ACP" line doesn't mention this internal listener.

    The maintainers will consider this. Related issues:

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions