CodeTap bridges VS Code on the host with containers, VMs, and remote machines. It downloads and runs VS Code Server inside the target environment, exposes the session over Unix sockets in /dev/shm/codetap/, and pairs with a companion VS Code extension that discovers sessions automatically and connects with one click.
The problem it solves: Getting a full VS Code remote development session into a container typically requires the Dev Containers extension, SSH access, or baking VS Code Server into images. CodeTap eliminates all of that — drop a single static binary into any Linux environment and get a working VS Code connection in seconds.
Key properties:
- Single static binary, zero dependencies (no CGO, no libc, stdlib-only Go)
- Two connection modes: shared
/dev/shm(simplest) or stdio relay (works anywhere) - Auto-downloads and caches VS Code Server — containers need nothing pre-installed
- Works with Docker, Podman, SSH, kubectl, or any bidirectional stdio transport
- Cross-compiled for Linux amd64 and arm64
┌─────────────────────┐ /dev/shm/codetap/
│ VS Code (host) │◄────── session.ctl.sock (CTAP1 control protocol)
│ + CodeTap ext │ session.sock (VS Code Server data)
└─────────────────────┘ ▲
│ (ipc=host or stdio relay)
┌──────┴───────┐
│ Container │
│ codetap run │
└──────────────┘
The socket directory contains only .ctl.sock and .sock files, created exclusively by codetap. The extension reads nothing from disk — all metadata and authentication is served over the CTAP1 control protocol.
Download the static binary for your architecture from Releases:
# amd64
curl -Lo /usr/local/bin/codetap https://github.com/cli-tools/codetap/releases/latest/download/codetap-linux-amd64
chmod +x /usr/local/bin/codetap
# arm64
curl -Lo /usr/local/bin/codetap https://github.com/cli-tools/codetap/releases/latest/download/codetap-linux-arm64
chmod +x /usr/local/bin/codetapInstall the VS Code extension from the VS Code Marketplace, or from the .vsix file in Releases.
The simplest mode. The container shares /dev/shm with the host, so VS Code can see the sockets directly.
# Start a container with shared IPC
docker run --ipc=host -v /usr/local/bin/codetap:/usr/local/bin/codetap:ro \
-it myimage bash
# Inside the container (auto-resolves latest VS Code Server)
codetap run --name myproject --folder /workspaceThe VS Code extension discovers the session via its control socket in /dev/shm/codetap/ and offers to connect.
For containers that can't share IPC, the stdio relay multiplexes VS Code Server traffic over stdin/stdout. No shared memory required.
The VS Code extension connects via the CTAP1 control socket protocol to negotiate the VS Code Server version. The relay reads the commit from the CONNECT handshake and negotiates with the remote side via an init frame — no --commit flag needed.
# On the host — codetap creates the /dev/shm socket and spawns the remote command
codetap relay --name myproject -- \
docker exec -i mycontainer \
codetap run --stdioThis also works over SSH:
codetap relay --name remote-dev -- \
ssh user@host \
codetap run --stdioOr with any transport that provides bidirectional stdin/stdout:
codetap relay --name k8s-pod -- \
kubectl exec -i mypod -- \
codetap run --stdiocodetap list
# NAME COMMIT FOLDER PID STATUS STARTED
# myproject abc123def456 /workspace 1234 alive 2024-01-15 10:30:00codetap cleanRemoves socket files for sessions whose control sockets are no longer alive (e.g., after a container exit without graceful shutdown).
| Command | Description |
|---|---|
codetap |
Print help (also: codetap help, codetap --help) |
codetap run |
Start VS Code Server on a socket in /dev/shm/codetap/ |
codetap run --stdio |
Start VS Code Server and relay over stdin/stdout |
codetap list |
List all discovered sessions |
codetap clean |
Remove stale (dead) session entries |
codetap relay |
Host-side relay: creates /dev/shm socket and spawns remote command |
Running with no subcommand prints help. Passing flags without a subcommand defaults to run (e.g. codetap --commit abc123).
| Flag | Env var | Default | Description |
|---|---|---|---|
--name |
hostname | Session name | |
--commit |
CODETAP_COMMIT |
auto-resolved | VS Code Server version, commit hash, or latest |
--folder |
cwd | Workspace folder path | |
--socket-dir |
CODETAP_SOCKET_DIR |
/dev/shm/codetap |
Socket directory |
--stdio |
false | Use stdin/stdout relay mode |
| Flag | Default | Description |
|---|---|---|
--name |
hostname | Session name |
--folder |
cwd | Workspace folder for metadata |
--socket-dir |
/dev/shm/codetap |
Socket directory |
CodeTap sessions expose a text-based, line-oriented control protocol on <name>.ctl.sock. The VS Code extension uses it for session discovery, authentication, and version negotiation.
Extension → codetap: CTAP1 INFO\n
codetap → Extension: {"name":"myproject","commit":"072586...","arch":"x64","folder":"/workspace","pid":197,"started_at":"2024-01-15T10:30:00Z"}\n
The connection is closed after the response. Used by codetap list and session discovery.
Extension → codetap: CTAP1 CONNECT <commit> <client_id>\n
codetap → Extension: OK <token>\n
or: ERR <message>\n
client_id is typically the VS Code process PID. After OK, the control connection stays open as a lease — codetap tracks connected clients via open lease connections. When the connection closes (VS Code exits, window closes), the lease is released.
Version negotiation:
- If the requested commit matches the running server:
OK <token>immediately. - If different and no other clients are connected: codetap restarts code-server with the new version, then responds
OK <token>. - If different but other clients are connected with the current version:
ERR version mismatch: <current> running, <N> client(s) connected.
CodeTap automatically determines which VS Code Server version to download. The resolution order for direct mode (codetap run) is:
--commitflag (hash likeabc123..., version like1.109.5, orlatest)CODETAP_COMMITenvironment variable~/.codetap/.commitfile- Local
code --versionoutput (if the VS Code CLI is on PATH) - Latest stable release from the Microsoft Update API
Running bare codetap run with network access downloads the latest stable server. To run offline, provide a commit via any of the first three methods.
In stdio relay mode (codetap relay ... -- codetap run --stdio), the commit is negotiated automatically: the VS Code extension sends the client's commit hash via the CTAP1 CONNECT handshake, and the relay forwards it to the remote side via a FrameInit frame before any connections are accepted. This ensures the remote always provisions the exact VS Code Server version that the client needs.
| Path | Purpose |
|---|---|
~/.codetap/cache/ |
Downloaded VS Code Server tarballs |
~/.codetap/repository/ |
Extracted VS Code Server binaries |
~/.codetap/.commit |
Default commit hash |
/dev/shm/codetap/ |
Runtime socket files (.ctl.sock and .sock only) |
The companion TypeScript extension (extension/) turns VS Code into a CodeTap client. It registers a codetap remote authority, polls the socket directory for .ctl.sock control sockets, queries each via the CTAP1 INFO command, and presents discovered sessions in a sidebar tree view with live/dead status indicators. Connecting performs a CTAP1 CONNECT handshake to negotiate the version and obtain the connection token, then opens the remote folder over the data socket using VS Code's managed message-passing protocol — no port forwarding or SSH required.
Note:
codetapuses the VS Code resolver API proposal. One-time setup on stable VS Code:
- Run
Preferences: Configure Runtime Arguments.- Add this to
argv.json:{ "enable-proposed-api": ["codetap.codetap"] }- Restart VS Code. Flatpak
argv.jsonpath:~/.var/app/com.visualstudio.code/config/Code/argv.json
Commands: codetap.connect (open session), codetap.refresh (re-scan).
| Setting | Default | Description |
|---|---|---|
codetap.socketDir |
/dev/shm/codetap |
Directory to scan for sessions |
codetap.pollInterval |
3000 |
Polling interval in milliseconds |
Build: cd extension && npm ci && npm run compile && npm run package
Publish to the Marketplace: npm run publish (requires Microsoft account with cli-tools publisher access, uses --azure-credential)
VS Code Dev Containers couple your container lifecycle to the IDE. Docker Compose + CodeTap gives you the same remote-development experience with plain Docker tooling — no Dev Containers extension, no devcontainer.json, no IDE lock-in.
compose.yamlruns CodeTap as the container entrypoint with stdin open and no TTY:
services:
myservice:
image: myimage:latest
container_name: myservice
stdin_open: true # keep stdin pipe open (-i)
tty: false # no pseudo-TTY (critical for stdio relay)
command: ~/.local/bin/codetap run --stdio
# ... volumes, environment, etc.-
Start the container with
docker compose up -d. The container launches CodeTap in stdio mode and waits for a relay connection on stdin. -
Attach from the host using
codetap relay+docker attach:
codetap relay --name myservice -- docker attach myservicedocker attach connects to the main process's stdin/stdout — exactly the stdio pipe that codetap run --stdio expects. The relay creates the /dev/shm/codetap/ sockets and the VS Code extension discovers the session via the control socket.
| Docker Compose key | Effect |
|---|---|
stdin_open: true |
Keeps the container's stdin file descriptor open (equivalent to docker run -i) |
tty: false |
No PTY allocation — raw byte stream, which is what the stdio multiplexer needs |
The combination gives CodeTap a clean bidirectional pipe. docker attach hooks into that same pipe without spawning a new process (unlike docker exec).
| Dev Containers | Compose + CodeTap | |
|---|---|---|
| IDE dependency | VS Code only | Any editor (VS Code via extension, others via socket) |
| Config files | devcontainer.json + Dockerfile |
compose.yaml (you already have one) |
| Container lifecycle | Managed by IDE | Managed by docker compose |
| Multi-service | Awkward | Native — compose was built for this |
| CI reuse | Requires special tooling | Same compose.yaml runs in CI unchanged |
| Attach/detach | Kills session | Relay reconnects; container keeps running |
services:
dev:
image: registry.example.com/myteam/devimage:latest
container_name: dev
user: ${USER}
network_mode: host
ipc: host
volumes:
- /etc/passwd:/etc/passwd:ro
- $HOME:$HOME
- ..:/workspaces
working_dir: /workspaces/myproject
environment:
- LOG_LEVEL=${LOG_LEVEL:-DEBUG}
stdin_open: true
tty: false
command: ~/.local/bin/codetap run --stdio
volumes:
shared-data:
external: truedocker compose up -d
codetap relay --name dev -- docker attach dev
# VS Code discovers the session and connects- Go 1.22+ (at
/opt/goor in PATH) - Node.js 20+ and npm (for the extension)
cd codetap
make build # Build for current platform
make build-all # Cross-compile for amd64 + arm64
make test # Run all testscd codetap/extension
npm ci
npm run compile
npm run package # Creates codetap.vsix
npm run publish # Publish to VS Code Marketplace (requires cli-tools publisher access)BSD Zero Clause (0BSD) — see LICENSE.