Skip to content

[FEATURE]: Export actual server URL as environment variable #9099

Closed as not planned

Description

@alvinunreal

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

Problem

When OpenCode starts its HTTP server, the actual port used is not easily discoverable by child processes. This creates issues for integrations that need to spawn additional OpenCode instances (e.g., tmux panes, background tasks, CI/CD scripts).

Current Behavior

  1. Server URL discovery relies on ctx.serverUrl, which returns a fallback when no server is running:

    // From server/server.ts
    export function url(): URL {
      return _url ?? new URL("http://localhost:4096")  // Always returns fallback
    }
  2. Port auto-fallback is silent:

    // From server/server.ts:2883
    const server = opts.port === 0 
      ? (tryServe(4096) ?? tryServe(0))  // Tries 4096, then random port
      : tryServe(opts.port)
    • If port 4096 is taken, OpenCode silently uses a random OS-assigned port
    • Child processes have no way to discover the actual port used
  3. Multi-instance conflicts:

    • User runs OpenCode in Project A → binds to port 4096
    • User runs OpenCode in Project B → falls back to random port (e.g., 49152)
    • Child processes from Project B can't discover port 49152

Real-World Use Case: Tmux Integration

Our plugin (oh-my-opencode-slim) spawns tmux panes that run opencode attach <url> --session <id> to show sub-agent work in real-time.

Current workaround:

  • Require users to configure a fixed port: { "server": { "port": 4096 } }
  • This breaks when multiple OpenCode instances run simultaneously
  • Port conflicts cause startup failures

What we need:

  • Discover the actual server URL dynamically
  • Support multiple concurrent OpenCode instances on different ports

Proposed Solution

Export the actual server URL as an environment variable when the server starts:

// After server starts successfully (server.ts:2886)
const server = opts.port === 0 ? (tryServe(4096) ?? tryServe(0)) : tryServe(opts.port)
if (!server) throw new Error(`Failed to start server on port ${opts.port}`)

_url = server.url

// NEW: Export for child processes
process.env.OPENCODE_SERVER_URL = server.url.toString()

Environment Variables to Export

Variable Example Value Description
OPENCODE_SERVER_URL http://localhost:4096 Full URL of running server
OPENCODE_SERVER_PORT 4096 Port number (optional, for convenience)

Benefits

  1. Child processes can discover server:

    # In tmux pane or background task
    opencode attach $OPENCODE_SERVER_URL --session abc123
  2. Multi-instance support:

    • Each OpenCode instance exports its own URL
    • Child processes automatically connect to the correct parent
  3. No breaking changes:

    • Existing code continues to work
    • Environment variable is purely additive
  4. Better than file-based coordination:

    • Environment variables inherit automatically (Unix convention)
    • No cleanup required
    • No file permission issues

Alternative Considered

File-based approach: Write server URL to .opencode/.server-url

  • Pros: Works across all platforms
  • Cons: Requires cleanup, file watching, permission handling, race conditions

Environment variables are cleaner and more Unix-idiomatic.


Implementation Notes

Where to Add This

In packages/opencode/src/server/server.ts after line 2886:

export function listen(opts: { port: number; hostname: string; mdns?: boolean; cors?: string[] }) {
  // ... existing code ...
  
  const server = opts.port === 0 ? (tryServe(4096) ?? tryServe(0)) : tryServe(opts.port)
  if (!server) throw new Error(`Failed to start server on port ${opts.port}`)

  _url = server.url
  
  // Export for child processes
  process.env.OPENCODE_SERVER_URL = server.url.toString()
  process.env.OPENCODE_SERVER_PORT = server.port.toString()

  // ... rest of existing code ...
}

Cleanup on Server Stop

server.stop = async (closeActiveConnections?: boolean) => {
  if (shouldPublishMDNS) MDNS.unpublish()
  
  // Clean up environment variables
  delete process.env.OPENCODE_SERVER_URL
  delete process.env.OPENCODE_SERVER_PORT
  
  return originalStop(closeActiveConnections)
}

Example Usage

Plugin Code (Tmux Integration)

// Before (hardcoded or unreliable fallback)
const serverUrl = ctx.serverUrl?.toString() ?? "http://localhost:4096"

// After (dynamic discovery)
const serverUrl = process.env.OPENCODE_SERVER_URL ?? ctx.serverUrl?.toString()

Shell Scripts

#!/bin/bash
# Spawn a background OpenCode task connected to the parent server
if [ -n "$OPENCODE_SERVER_URL" ]; then
  opencode attach "$OPENCODE_SERVER_URL" --session "$SESSION_ID"
else
  echo "No OpenCode server found"
  exit 1
fi

Related

  • Similar pattern used by tools like DOCKER_HOST, DISPLAY, SSH_AUTH_SOCK
  • Follows Unix convention of using environment variables for service discovery
  • Enables better ecosystem integrations (plugins, scripts, CI/CD)

Would you be open to this enhancement? Happy to submit a PR if this approach makes sense!

Activity

  1. added
    discussionUsed for feature requests, proposals, ideas, etc. Open discussion
    on Jan 17, 2026
  2. github-actions commented on Jan 17, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    Feel free to ignore if this doesn't address your specific case.

  3. alvinunreal commented on Jan 17, 2026

    @alvinunreal
    Author

    this will allow to write tmux plugin for agents integration

  4. zedd3v commented on Jan 29, 2026

    @zedd3v
  5. alvinunreal commented on Feb 24, 2026

    @alvinunreal
    Author
  6. gustavocaiano commented on Apr 2, 2026

    @gustavocaiano

    Hello @alvinunreal im having an issue where everytime Orchestrator delegates to a sub agent, a new pane is created (cmux) that always shows this :

    Image

    is this anyhow linked to this issue ?
    it is automatic and closes when the sub agent finishes (if i dont close it first)

  7. fzacek commented on Apr 9, 2026

    @fzacek

    @gustavocaiano : are you using opencode-cmux? because this issue was addressed in version 0.2.2
    Also be careful about the plugin management when you update because the doc page is apparently out of date.

    Another thing somewhat related to the port thing is that the CLI help, for the --port option states it defaults to 0.

    Image

    Which is simply not true. Without the option, there is no server. To actually have opencode pick a random port, you must explicitly run it with --port 0.

    This leads me to my last point and that is that it probably would be nice if "starting a server" was available through config or at least env variable. Because aliasing just to force a parameter is not ideal.

  8. gustavocaiano commented on Apr 9, 2026

    @gustavocaiano

    @fzacek thank you so much ! yea i figured it out yesterday and now have opencode-cmux updated.

    i had to alias :

    oc_openai() { OH_MY_OPENCODE_SLIM_PRESET=openai command opencode --port 0 "$@"; }
    oc_pdmfc()  { OH_MY_OPENCODE_SLIM_PRESET=pdmfc  command opencode --port 0 "$@"; }
    
    alias pdmfc='oc_pdmfc'
    
    opencode() {
      if (( $# == 0 )) || [[ "$1" == -* ]]; then
        oc_openai "$@"
      else
        command opencode "$@"
      fi
    }

    this way opencode is set to run with --port 0 but opencode attach doesnt run with --port 0 when delegating a sub agent.

    let me know if you found a better solution !

  9. removed
    discussionUsed for feature requests, proposals, ideas, etc. Open discussion
    on May 3, 2026
  10. Zireael commented on Jun 21, 2026

    @Zireael

    Seems straightforward improvement. Why is it open for 6 months?

  11. alvinunreal commented on Jun 21, 2026

    @alvinunreal
    Author

    I got married meanwhile

  12. github-actions commented on Aug 21, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

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

Metadata

Metadata

Assignees

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