Skip to content

[FEATURE]: opencode join — hub-managed node fleet with centralized session discovery #44760

Closed as not planned

Description

@zhangyx1998

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

I run OpenCode servers on a mixed fleet (Windows + WSL, macOS laptop, several Linux hosts, a cloud VPS) and want one web UI that shows and controls sessions across all of them.

Today's multi-server support is client-side federation: every browser maintains its own server list and connects to every server independently. Server identity is tied to a URL, and there is no enrollment, no presence, and no session index shared between clients. A mesh VPN doesn't close the gap — configuration stays O(N x M) (every client device joins the VPN and configures all N servers), and it still provides no shared index, no presence, and no stable application-level identity.

Related: #43727, #43030, #40843, #41152; #19949 / #15752 asked for multi-server-in-one-UI (closed "not planned").

Proposal

An optional hub-and-node control plane on top of standalone servers — centralized discovery, decentralized execution. Standalone opencode serve is unaffected.

flowchart LR
    USER("Client Browser"):::NODE

    subgraph VPS["Cloud Server"]
        HUB(("☁️ Hub Server")):::NODE
        GATE(["🔒 Auth"]):::NODE
        HUB <--> GATE
    end

    subgraph LANA["Home"]
        NA(["🖥️ PC"]):::NODE
        NB(["💻 WSL"]):::NODE
    end

    subgraph LANB["Office"]
        NC(["🗄️ Server"]):::NODE
    end

    subgraph ROAM["Hotspot"]
        ND(["💻 Laptop"]):::NODE
    end

    NA -->|WebSocket| HUB
    NB -->|WebSocket| HUB
    NC -->|WebSocket| HUB
    ND -->|WebSocket| HUB
    GATE <--> USER

    style VPS fill:#0001,stroke:gray,stroke-width:1px,stroke-dasharray:6 4,opacity:0.6;
    style LANA fill:#0001,stroke:#FA08,stroke-width:1px,stroke-dasharray:6 4;
    style LANB fill:#0001,stroke:#AF08,stroke-width:1px,stroke-dasharray:6 4;
    style ROAM fill:#0001,stroke:#0AF8,stroke-width:1px,stroke-dasharray:6 4;

    classDef NODE fill:#0008,stroke:white,color:white;
Loading
  • Enrollment: opencode join <hub-url> (interactive browser approval, or --token for headless). The node gets a permanent ID independent of hostname/IP/URL and authenticates with a node-generated keypair thereafter.
  • Presence: each node keeps one outbound persistent connection (NAT/firewall-friendly); nodes remain visible while offline.
  • Session index: nodes advertise lightweight session metadata via sequenced events with snapshot fallback — same shape as the existing /sync/history + /global/event reconciliation. The hub holds an index, never the authoritative session data.
  • Access: the hub relays authenticated client traffic to nodes over multiplexed streams (nodes themselves authenticate by keypair, not through the client auth layer). Direct client-node transport is a possible later optimization.

Nodes stay authoritative for projects, tools, execution, and session contents. Configuration concentrates to O(N + M): a node enrolls once, a client only needs the hub URL.

Non-goals for now: no session DB migration to the hub, no filesystem sync, no distributed execution, no P2P requirement, no multi-user ACLs committed (the design stays open to them).

Relationship to the experimental control-plane (WorkspaceAdapter)

There is real overlap here, and the indexing/relay parts of this proposal would conflict with it if built independently: control-plane/workspace.ts already proxies requests to remote OpenCode servers (runInWorkspace over a Target.remote { url, headers }), reconciles session events by posting per-session sequence state to /sync/history and following /global/event via SSE with reconnect backoff, and tracks per-workspace connection status.

The scopes differ in ways that look complementary rather than competing. Workspaces are project-scoped — adapters register per projectID and every workspace carries one — while a node is machine-scoped and owns many projects. A workspace's identity is minted by the controlling instance and its remote is addressed by URL, which requires inbound reachability; a node's identity is minted at enrollment, URL-independent, and connects outbound only.

That suggests a concrete compatibility path: the hub becomes the connectivity substrate underneath the existing machinery. An enrolled node surfaces as an ordinary Target.remote — its target() resolving to a hub relay URL plus auth headers — so the existing proxying and /sync/history + /global/event reconciliation are reused rather than duplicated. I'd value maintainer input on whether that layering matches the intended direction of the control-plane work.

Planned first step

I plan to start with node identity + enrollment only: the opencode join flow, a permanent node ID, and keypair authentication — opt-in, with no change to standalone opencode serve or the control-plane code. Presence, session indexing, and relayed access would follow as separate increments built on the sync machinery above.

I have fuller design notes (enrollment token format, re-enrollment/revocation, epoch-based reconciliation, relay multiplexing, trust model, deployment behind an SSO proxy) and will post them if there's interest. Also open to prototyping out of tree first if working code is preferred before an in-tree abstraction.


This proposal and its architecture originate from me. Claude Fable 5 (Anthropic) and ChatGPT Sol 5.6 (OpenAI) assisted with drafting, research, and design review; I have reviewed and take responsibility for all content and claims.

Activity

  1. AlexanderWagnerDev commented on Nov 9, 2026

    @AlexanderWagnerDev

    I would really like to see this feature reconsidered, as it would solve a real limitation for people running OpenCode across multiple machines.

    My current setup includes Windows and Linux machines, a home lab behind NAT/firewalls without incoming port access, and a remote server where I would like to host a centralized web interface using Docker.

    OpenCode's existing remote pairing and OpenTunnel functionality already solves the connectivity problem. I can access individual OpenCode instances through their *.opentunnel.xyz URLs without SSH, VPNs, or opening inbound ports.

    The missing piece is centralized, server-side management of these instances.

    Currently, adding remote servers to one OpenCode client does not make those connections available in another browser or through the remote web interface.

    For my use case, even a much smaller implementation than the originally proposed hub architecture would be extremely useful:

    • A self-hosted central OpenCode web interface, ideally deployable with Docker.
    • The ability to register multiple existing OpenCode servers using their HTTPS/OpenTunnel URLs.
    • Persistent, server-side storage of registered servers and their authentication details, with appropriate encryption and access controls.
    • One shared server list across browsers and devices.
    • The ability to browse and resume projects and sessions on any connected machine.
    • Connection status indicators for online/offline instances.
    • No SSH requirement, inbound port forwarding, or filesystem synchronization.

    Each OpenCode instance should continue running and executing commands on its own machine. The central server would only handle discovery, authentication, and API routing.

    This would also make the feature usable on native Windows installations, where SSH-based orchestration is not necessarily desirable.

    I understand that the original proposal involves a more comprehensive hub-and-node architecture. However, could a smaller, optional implementation based on registering existing remote OpenCode endpoints be considered instead?

    It could potentially reuse the existing OpenCode server API and OpenTunnel connectivity without introducing a completely new node enrollment protocol.

    For users managing several machines, having one persistent, browser-accessible dashboard would be a substantial improvement over maintaining separate client-side server lists.

    I would love to see this revisited, even if the initial implementation only supports manually registered remote servers.

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