Port Manager
Port Manager is shifting from post-conflict port rerouting to logical development networks. The target workflow is to attach a terminal window/session to a named network, keep app-internal ports unchanged, and explicitly expose selected network ports on the host machine.
Example:
A network: frontend 3004, backend 8004, exposed as localhost:3004
B network: frontend 3004, backend 8004, exposed as localhost:3005
The previous managed-process routing, native hook, and rerun-on-failure implementation remains in the repository as deprecated compatibility code. It is hidden from the default sidebar and command surfaces while the logical network model is implemented.
Target Capabilities
- Discover terminal windows across VS Code and external OS terminals. Initial implementation groups noisy shell processes by VS Code terminal instance, tty, or process group, and uses the terminal window/tab title when available.
- Let the user attach a selected terminal window title to a logical network. With the current proxy runtime this is a logical association; runtime-level isolation requires a stronger adapter.
- Let child processes launched from that terminal inherit the selected network context. This is not supported by the current local proxy adapter.
- Allow multiple networks to reuse the same internal ports when the selected runtime provides isolation.
- Configure explicit host port exposure, such as
localhost:3005 -> B network:3004. Initial implementation provides a real local TCP proxy.
- Detect host exposure conflicts before exposing a port by actually binding the host listener.
- Keep fixed protocol ports such as SSH, MySQL, and PostgreSQL meaningful inside each logical network.
- Implement real network behavior through runtime adapters such as container runtimes, OS-native network namespaces, privileged helpers, or proxy fallbacks.
Implemented Logical Network Mode
- Create and remove logical network records.
- Persist logical networks, terminal attachments, and host exposures in VS Code global state.
- Discover VS Code integrated terminals and OS shell processes as grouped Terminal Windows with visible titles.
- Attach a terminal window to a logical network as a persisted association.
- Attach Docker Compose published ports as logical-network service routes by moving selected services into a hidden network-scoped Compose project, so the original host ports become reusable.
- Copy an attached Compose project into another logical network as a separate hidden Compose project, including stopped services so later
docker compose start or up commands keep using the copied project.
- Discover running Docker/Podman services with published ports, group Compose services by project, and attach them from the Compose / Containers sidebar section.
- Start every primary network command from sidebar action rows, not only Command Palette.
- Expose a host TCP port to a target address and port through
Local TCP Proxy.
- Close proxy listeners when exposures or networks are removed.
- Reopen persisted active exposures when the extension starts.
- Show runtime capabilities so unsupported same-port isolation and terminal attach are explicit.
Current Compatibility Code
- Start a managed process from the command palette or sidebar.
- Detect whether the requested port is busy.
- Keep logical requested ports unoccupied in hashed routing mode.
- Route to a deterministic available port in the virtual range, or to a nearby port in nearest mode.
- Inject the actual port through
PORT, ${port} replacement, or --port.
- Inject
PORT_MANAGER_LOGICAL_PORT, PORT_MANAGER_ACTUAL_PORT, PORT_MANAGER_ROUTES, and PORT_MANAGER_ROUTES_FILE.
- Track requested and actual ports in the sidebar.
- Show daemon status, routing table, managed processes, and OS listeners as separate sidebar accordion sections.
- Start, stop, and inspect the daemon from the sidebar UI as well as the Command Palette.
- Watch all local listening TCP ports through the shared local agent and update the sidebar automatically.
- Show which process owns each visible port when the OS exposes PID/name data.
- Offer daemon-managed routing as soon as a VS Code terminal command explicitly requests a port.
- Inject the native hook into new VS Code terminals so non-fixed protocol bind ports are allocated before bind.
- Install a native shell hook for OS terminals outside VS Code.
- Detect VS Code terminal listen failures and offer to rerun the failed command through Port Manager routing.
- Stop, restart, remove, open, and copy managed process URLs.
- Keep sidebar row clicks as selection only; browser opening is an explicit action.
- Treat routed URLs as live only while the process is running.
- Register an already running process for sidebar management.
These commands and views are no longer the primary product surface. They remain available internally for migration and testing until the logical network runtime adapters replace them.
Quick Start: One Worktree, One Network
Port Manager assumes the normal unit of isolation is a Git worktree. A worktree
can still have more than one logical network.
To create a complete isolated workspace from the repository currently open in
VS Code:
- Start the Docker Compose project you want to use as the source, if it is not
already represented by a Port Manager Compose attachment.
- Select Create Isolated Worktree in the Port Manager view.
- Enter the branch/worktree name and confirm the proposed sibling folder,
dedicated Logical Network, and automatically detected Compose projects.
- Continue in the new VS Code window. Port Manager applies the network and
opens a ready terminal automatically.
That guided action creates or safely reuses the Git worktree, creates one
network, copies Compose projects (including stopped services and isolated
persistent data), rebases repository-owned bind mounts into the new worktree,
seeds standard local Compose .env files without overwriting a target,
verifies DNS/TLS, and hands setup to the new window. Git's normal worktree rule
still applies: uncommitted and untracked changes in the source checkout are not
copied. Partial progress is retained, so running the same action again resumes
instead of creating duplicate worktrees, networks, or Compose attachments.
To apply Port Manager to an already-created worktree, the default path also
needs only one action:
- Open the worktree folder in VS Code.
- Open the Port Manager activity bar view.
- Select Initialize This Worktree and confirm. On macOS, approve the one
operating-system prompt that installs loopback, Local DNS, and development
TLS trust.
- Use the
Port Manager: <network> terminal that opens when verification
succeeds. New VS Code terminals in this window use the same logical network.
Initialization creates or reuses one network named after the worktree, installs
the pm shell integration, applies the network to this VS Code window, completes
browser DNS/TLS setup, and verifies the actual DNS responder and macOS Keychain
trust before reporting success. It also resolves both public aliases through
the macOS system resolver, checks hostname coverage in the signed TLS leaf,
preloads the native hook through the OS loader, and parses/sources the installed
shell integration to prove pm exists. A cancelled or failed attempt is
retryable and reuses the partially created network instead of creating
duplicates.
To add another network inside the same worktree, use Port Manager: Create
Logical Network. The one-network worktree path remains the default; the
advanced attach, Compose, exposure, and per-network controls remain available.
Select the terminal-plus icon on any Logical Network, or expand Connect
actions → Open Network Terminal, to open a new integrated terminal already
attached to that specific network without changing the VS Code window default.
If pm is missing in a separately opened terminal, run Install or Repair pm
Integration under System → Maintenance, then start a new shell. If browser
aliases fail after a reboot or machine migration, inspect System → Browser
access & DNS and run Repair Local DNS; the same section reports Local DNS
and TLS trust as separate, real runtime states.
Legacy Routing
By default, Port Manager uses hashed logical routing: a requested port such as 8000 remains the logical port, while the launched process binds to a deterministic actual port in portManager.virtualPortRangeStart through portManager.virtualPortRangeEnd. Set portManager.routingMode to nearest to use the older nearby-port behavior.
For new VS Code terminals, Port Manager injects the native socket hook while the daemon is running. When a terminal-launched process calls bind() on a port that is not in portManager.fixedProtocolPorts, the hook asks the daemon for an actual port before the OS bind happens, then registers the logical route. The explicit Rerun Routed prompt and listen-failure monitor remain fallback paths for terminals that were already open or are not running with the hook environment.
Worktree initialization installs external-shell support automatically. For a
repair or a manual install, run Port Manager: Install or Repair pm Shell Integration, then open a new shell. Port Manager brackets the user profile with
a builtin-only prelude and a deferred activation step, so runtime managers
initialize without inherited PM preload/PATH shims while prompt commands keep
the same routing behavior. Port Manager: Restore Shell Profiles removes only
PM-owned profile blocks and historical source lines if you want to return to
manual activation.
daphne -b 127.0.0.1 -p 8000 myapp.asgi:application
npm run dev
When a hooked process calls bind(8000), the hook asks the same per-user daemon to allocate an actual port, rewrites the bind call, and registers the logical route. Fixed protocol ports such as SSH, MySQL, and PostgreSQL are preserved by default because the port number itself is part of the protocol contract. When another hooked local process calls connect(...:8000), the hook reads the daemon route table and redirects the connection to the actual port.
Compose published ports can be attached to a logical network from the sidebar. Refresh the Compose / Containers section to discover running Docker/Podman services, then attach a service or grouped Compose project to a network. Port Manager inspects the original containers, pins their actual volume/bind/tmpfs mounts into a generated Compose override, stops only the original services that own selected host-published ports, starts a hidden project whose name is derived from the logical network and original project, and includes already-running internal Compose services such as brokers so the hidden project keeps its private service network intact. For example, an original PostgreSQL publish on host 15432 can become logical 15432 -> 127.0.0.1:57001 inside the attached network while host 15432 is free for another local process or for docker compose up under the original project name. Removing the attachment stops the hidden services before starting the original services again, so data volumes are not mounted by both projects at the same time.
Copy Compose Attachment creates a second hidden Compose project for the destination network when runtime metadata is available. Services with routes are started, and stopped/no-port services are created in the copied project so later lifecycle commands do not fall back to the source project.
Local Agent
Port Manager starts after VS Code startup and connects to a single local agent.
The agent listens on a per-user local socket or named pipe. If no agent is running, the first VS Code window starts one. Additional VS Code windows connect to the same agent and receive the same port snapshot. While a VS Code client is attached, the daemon rescans the OS listening table periodically so stopped external processes disappear without a manual refresh.
The sidebar shows:
- daemon status, PID, listener count, route count, and route table file
- active logical routing table rows
- managed processes launched by Port Manager
- routed requested port -> actual port mappings
- OS-level listening ports detected by the daemon
- best-effort PID, process name, command, and URL information
Stopped processes stay visible for restart or removal, but their routed URL is cleared and the sidebar no longer presents their old port mapping as an active route.
Important limitation: the agent does not transparently create isolated per-process networks. Automatic pre-launch rerouting works only for supported managed or hooked process paths. Running two ordinary host processes that both bind 127.0.0.1:3004 still requires true network isolation, runtime injection, or a proxy/runtime adapter.
Settings
portManager.enabled: enable managed process routing.
portManager.defaultHost: host used to build routed URLs.
portManager.scanRange: nearby port scan range.
portManager.scanDirection: scan direction for routing.
portManager.routingMode: hashed logical routing or nearest conflict-only routing.
portManager.virtualPortRangeStart: first actual port used by hashed routing.
portManager.virtualPortRangeEnd: last actual port used by hashed routing.
portManager.preferredPorts: ports watched in the background and suggested by prompts.
portManager.fixedProtocolPorts: ports the native hook leaves untouched; set to [] to make every bind port eligible for logical routing.
portManager.autoOpenBrowser: open routed URLs after managed process launch.
portManager.showConflictNotification: show a notification when a managed process is routed.
portManager.monitorAllListeningPorts: show all listening TCP ports reported by the agent.
portManager.watchPreferredPorts: watch preferred ports for external listeners.
portManager.watchIntervalMs: polling interval for preferred port watching.
portManager.notifyOnDetectedConflict: show a notification when a preferred port becomes occupied externally.
portManager.detectTerminalListenFailures: detect VS Code terminal bind/listen failures and offer a routed rerun.
portManager.processKillSignal: signal used to stop managed processes.
Commands
Port Manager: Initialize This Worktree
Port Manager: Create Isolated Worktree
Port Manager: Create Logical Network
Port Manager: Open Terminal in Logical Network
Port Manager: Install or Repair pm Shell Integration
Port Manager: Repair Local DNS
Port Manager: Start Daemon
Port Manager: Daemon Status
Port Manager: Start Managed Process
Port Manager: Add Existing Process
Port Manager: Refresh
Port Manager: Stop Process
Port Manager: Restart Process
Port Manager: Stop All Processes
Port Manager: Copy Routed URL
Port Manager: Open Routed URL
Port Manager: Open Settings
Development
npm install
npm run compile
npm test
Build the optional native terminal hook with:
npm run build:hook
Packaging and publishing run this hook build again through vscode:prepublish. On macOS the generated hook library and asdf shim are ad-hoc signed when codesign is available.
Development log endpoint
To trace how a connection is attributed and routed without rebuilding native
binaries, set the portManager.developmentLogPath setting (or the
PORT_MANAGER_DEV_LOG env var) to an absolute path and reload the window. The
native hook, TCP router, and agent — plus the extension host — append their
routing/attribution decisions to that one file; tail -f it. Empty disables it
(zero overhead). Full reference, line format, and how to extend it:
docs/dev-logging.md.
Per-network identity
Running the same repo directory in two logical networks makes hostname-keyed
app identity collide (celery node name @%h, pidfiles/logs, locks, metrics),
because every process still thinks it is on the one machine hostname. The native
hook virtualizes gethostname()/uname() to the network's loopback address
(e.g. 127.93.164.7 — unique per network, hostname-safe, connectable as-is; the
sanitized network name is only a fallback) for a hooked process and all its
children, so any app that keys off its hostname distinguishes itself per network
automatically — no app-specific logic in the extension. Apps
use it by keeping standard hostname templates (celery %h/%n); argv literals
like --hostname=localhost are additionally rewritten to the network loopback
at the exec boundary (host-positioned occurrences only — a standalone
localhost token, e.g. a grep pattern, is never touched). See
docs/per-network-hostname.md.
Per-network values (credential paths, bucket names, flags) come from an
optional .portmanager/env/<network>.env found upward from the process working
directory: the hook applies it before main() in every attached process —
ahead of any runtime's env snapshot and with precedence over the app's own
.env. See docs/per-network-env.md. For whole-file
replacement, a file at .portmanager/files/<network>/<relative-path> is opened
instead of the original (reads and writes; existence is the opt-in). See
docs/per-network-files.md.
For Marketplace release steps, publisher identity, VSIX verification, and native hook packaging checks, see PUBLISHING.MD. The Marketplace publisher ID is newdlops.
Architecture
src/extension: activation and command orchestration
src/core: routing policy and process registry
src/platform: Node and OS adapters for ports and processes
src/ui: sidebar tree provider
src/config: VS Code settings loader
src/shared: framework-neutral contracts and event utilities