- Cloned
microsoft/vscodeinto./vscode - Cloned
microsoft/dev-tunnelsinto./dev-tunnels
- VS Code desktop UI collects an auth session from a configured provider (
githubormicrosoft). - The shared-process
RemoteTunnelServicespawns the bundledcode-tunnelCLI. - The CLI logs in, resolves or creates a dev tunnel, tags it, and hosts reserved ports over the tunnel.
- Remote clients discover those tunnels by label and connect through the Dev Tunnels relay.
- UI / account selection:
vscode/src/vs/workbench/contrib/remoteTunnel/electron-browser/remoteTunnel.contribution.ts
- Shared-process tunnel lifecycle:
vscode/src/vs/platform/remoteTunnel/node/remoteTunnelService.ts
- CLI tunnel orchestration:
vscode/cli/src/commands/tunnels.rs
- CLI auth flow and token storage:
vscode/cli/src/auth.rs
- CLI tunnel management client and tunnel tagging:
vscode/cli/src/tunnels/dev_tunnels.rs
- Agent-host tunnel discovery / connection:
vscode/src/vs/platform/agentHost/node/tunnelAgentHostService.ts
- VS Code UI only offers providers configured in
product.jsonand registered with the authentication service. - The shared process passes the selected access token into the CLI via
VSCODE_CLI_ACCESS_TOKEN. - The CLI supports both:
- interactive device-code login
- direct token injection (
--access-token,--refresh-token, env-backed)
- Device-code endpoints in the CLI:
- Microsoft device code:
https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode - Microsoft token:
https://login.microsoftonline.com/organizations/oauth2/v2.0/token - GitHub device code:
https://github.com/login/device/code - GitHub token:
https://github.com/login/oauth/access_token
- Microsoft device code:
- Credential persistence:
- preferred: OS keyring
- fallback:
token.jsonin the CLI state root
- VS Code launcher tunnels are identified with the label
vscode-server-launcher. - Tunnel labels also carry:
- the human-readable tunnel name
- a protocol tag like
protocolvN - platform flags like
_flag...
- Name parsing on the agent-host side treats the first non-system label as the display name.
- Reserved ports:
31545: control port31546: agent-host port
code tunnel statusreturns JSON with:- whether a service is installed
- whether a tunnel process is already running
- When starting a tunnel, VS Code:
- optionally runs
code tunnel user login - optionally installs the tunnel as a service
- runs
code tunnel --accept-server-license-terms ...
- optionally runs
- The CLI reuses persisted tunnel metadata from:
code_tunnel.jsonport_forwarding_tunnel.json
- If a persisted tunnel is missing or forbidden, the CLI creates a new one.
- When creating or looking up tunnels, the CLI requests scoped access tokens from the service, especially
hostandconnect.
- The standalone SDK surface needed for a custom tool already exists in
microsoft/dev-tunnels. - Relevant TypeScript APIs:
TunnelManagementHttpClient.listTunnels()TunnelManagementHttpClient.getTunnel()TunnelManagementHttpClient.createTunnel()TunnelManagementHttpClient.updateTunnel()TunnelManagementHttpClient.deleteTunnel()
- Request filtering and enrichment are driven by
TunnelRequestOptions:labelsrequireAllLabelsincludePortsincludeAccessControltokenScopes
- The SDK distinguishes:
- user auth headers from the
userTokenCallback - tunnel-scoped tokens from
TunnelRequestOptions.accessTokenortunnel.accessTokens
- user auth headers from the
- Discovery path:
- list tunnels with label
vscode-server-launcher - require protocol version >= 5
- request
connecttoken scope
- list tunnels with label
- Connection path:
- resolve the tunnel by
{ tunnelId, clusterId } - connect through
TunnelRelayTunnelClient - wait for forwarded port
31546 - open a WebSocket over that stream
- resolve the tunnel by
- The connection token used by the agent-host path is derived from
sha256(tunnelId)and base64url-encoded.
- Use the Dev Tunnels SDK directly, not the VS Code CLI, for list/create/update/delete operations.
- Reuse existing GitHub / Microsoft user tokens and pass them as the user auth header to the management client.
- Use
labelsplustokenScopesto discover VS Code-created tunnels and request the right per-tunnel access tokens. - If you need to attach to the VS Code agent host, replicate the port
31546+ WebSocket relay flow.
- Exact auth-header conventions for GitHub tokens across the Rust CLI and TypeScript SDK wrappers.
- Which tunnel labels are stable API contract vs. VS Code-specific convention.
- Whether existing tunnel-host tokens can be safely recovered from local state without reauthenticating.
- Which service endpoints or SDK calls are enough to fully manage tunnels without invoking the VS Code CLI at all.
- UI picks a session token from the selected provider:
vscode/src/vs/workbench/contrib/remoteTunnel/electron-browser/remoteTunnel.contribution.ts
- Shared process passes the token into the Rust CLI through
VSCODE_CLI_ACCESS_TOKEN:vscode/src/vs/platform/remoteTunnel/node/remoteTunnelService.ts
- The CLI uses the Rust
dev-tunnelscrate to create/update the relay endpoint and obtain host access:vscode/cli/src/tunnels/dev_tunnels.rsdev-tunnels/rs/src/connections/relay_tunnel_host.rs
- The agent-host client path uses the TS SDK to resolve the tunnel and request
connectscope:vscode/src/vs/platform/agentHost/node/tunnelAgentHostService.ts
- Host side opens a websocket with:
Sec-WebSocket-Protocol: tunnel-relay-hostAuthorization: tunnel <host_token>dev-tunnels/rs/src/connections/relay_tunnel_host.rs
- Official client side uses:
tunnel-relay-clienttunnel-relay-client-v2-devdev-tunnels/ts/src/connections/tunnelRelayTunnelClient.ts
- Important non-standard behavior:
- the outer SSH session prefers
kex=none,key=none,cipher=none - the Rust source explicitly says this is non-standard and breaks many off-the-shelf SSH stacks
dev-tunnels/rs/src/connections/relay_tunnel_host.rsdev-tunnels/ts/src/connections/tunnelRelayTunnelClient.ts
- the outer SSH session prefers
- After the outer relay SSH session is up, the service opens a channel of type:
client-ssh-session-stream
- Inside that channel is another SSH session that carries port forwarding.
- Port forwarding uses normal SSH requests/channels:
tcpip-forwardforwarded-tcpip
- References:
dev-tunnels/rs/src/connections/relay_tunnel_host.rsdev-tunnels/ts/src/connections/tunnelRelayTunnelHost.ts
- Fixed ports:
31545control port31546agent-host port
- Protocol version tag is currently
protocolv5. - References:
vscode/cli/src/constants.rsvscode/cli/src/tunnels/control_server.rs
- The CLI accepts raw forwarded connections on
31545. - Frames are msgpack objects, not HTTP and not plain JSON.
- RPC includes:
challenge_issuechallenge_verifyserveservermsgcallserverhttpforward/unforwardspawn/spawn_cli- file and socket helpers such as
fs_read,fs_write,fs_connect,net_connect
- References:
vscode/cli/src/tunnels/control_server.rsvscode/cli/src/msgpack_rpc.rsvscode/cli/src/tunnels/protocol.rs
- The relay websocket auth only gets you onto the tunnel.
- The control RPC can still require a challenge/response.
- Non-
vsdabuilds use:- 16-char random challenge
- response is
base64url(sha256(challenge))
- Reference:
vscode/cli/src/tunnels/challenge.rs
- The control RPC method
serveresolves the right server build, starts it if needed, and attaches a bridge to its local socket. - That bridge turns the control channel into
servermsgproxy traffic. - References:
vscode/cli/src/tunnels/control_server.rsvscode/cli/src/tunnels/code_server.rs
- Once the client is talking to the VS Code server, there is another handshake:
- client sends
auth - server replies
sign - client sends
connectionType
- client sends
- Supported connection types include:
ManagementExtensionHostTunnel
- References:
vscode/src/vs/platform/remote/common/remoteAgentConnection.tsvscode/src/vs/server/node/remoteExtensionHostAgentServer.ts
- After the VS Code server connection is up, terminal traffic goes through the remote IPC channel
remoteterminal. - The remote server delegates terminal creation/data to
PtyHostService. - The pty host launches
TerminalProcess, which usesnode-pty.spawn(...). - References:
vscode/src/vs/server/node/serverServices.tsvscode/src/vs/server/node/remoteTerminalChannel.tsvscode/src/vs/platform/terminal/node/ptyService.tsvscode/src/vs/platform/terminal/node/terminalProcess.ts
- “Connect to the dev tunnel” and “interact with a shell like VS Code” are not one protocol.
- They are a stack:
- Dev Tunnels relay websocket
- Outer relay SSH session with non-standard
nonenegotiation - Forwarded private tunnel ports
- CLI msgpack control RPC on
31545 - VS Code server bridge
- VS Code websocket handshake
- Remote terminal IPC
node-ptyshell process
The most likely incompatibility point for third-party implementations is Layer 2, followed by the private control protocol on Layer 5.