@spiriyu/port-forwarding-mapper
v0.0.9
Published
[](https://www.npmjs.com/package/@spiriyu/port-forwarding-mapper) [](https://nodejs.org) [
enable <id|name> Enable a mapping
disable <id|name> Disable a mapping
toggle <id|name> Flip enabled ↔ disabled
remove <id|name> Delete a mapping
edit <id|name> Edit a mapping (-s/--source, -t/--target, --name)
Group commands:
group list List groups
group add --name <name> Create a group
group rename <id> --name <n> Rename a group
group enable/disable <id> Enable or disable all mappings in a group
group remove <id> Delete a group
group duplicate <id> Duplicate a group
Streaming:
watch Stream mapping status changes over WS
logs [--follow] [--level] View daemon logs
Daemon:
serve [-p/--port <port>] Start the daemon + web UI (default port: 65432)
doctor Connectivity and version diagnostics
Service management:
service install [--exec <p>] Install as launchd/systemd/Windows service
service uninstall Remove the service
service start Start the service
service stop Stop the service
service status Show service status
Shell completion:
completion <shell> Print completion script (bash|zsh|fish)
Global flags:
-v, --version Output the version number
--url <url> Daemon base URL (default: http://127.0.0.1:65432)
--json Output machine-readable JSONPrivileged ports
Binding ports below 1024 requires elevated privileges. The daemon returns a structured EACCES_PRIVILEGED_PORT error if it cannot bind. On Linux:
sudo pfs service install
sudo pfs service startOr grant the Node binary CAP_NET_BIND_SERVICE instead of running as root.
Config file
All mappings are persisted to JSON at the OS-standard user config location. Do not edit the file while the daemon is running — all writes go through the daemon API.
| Platform | Path |
|----------|------|
| macOS | ~/Library/Application Support/pfs/config.json |
| Linux | $XDG_CONFIG_HOME/pfs/config.json (fallback: ~/.config/pfs/config.json) |
| Windows | %APPDATA%\pfs\config.json |
Config structure
{
"schemaVersion": 2,
"daemon": {
"port": 65432,
"logRetention": { "maxFiles": 5, "maxFileBytes": 1048576 }
},
"groups": [
{
"id": "01J...", // ULID — generated automatically
"name": "My Services",
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z"
}
],
"mappings": [
{
"id": "01J...", // ULID — generated automatically
"name": "dev-api",
"sourceHost": "127.0.0.1",
"sourcePort": 8080,
"targetHost": "localhost",
"targetPort": 3000,
"enabled": true,
"drainTimeoutMs": 5000,
"groupId": "01J...", // must match a group id above
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z"
}
]
}Groups
Groups are a way to organise related mappings so you can enable/disable them all at once. Every mapping belongs to exactly one group. The daemon creates a Default group on first run if none exist.
To create a group and add mappings to it via the CLI:
pfs group add --name "My Services"
pfs add 8080 localhost:3000 --name dev-api --group "My Services"
pfs add 5432 localhost:5432 --name dev-db --group "My Services"
# Enable or disable the whole group at once
pfs group enable "My Services"
pfs group disable "My Services"The groupId field in each mapping entry is the id of the group it belongs to. IDs are ULIDs generated automatically — use the CLI or web UI rather than writing them by hand.
Development
git clone https://github.com/spiriyu/port-forwarding-switcher.git
cd port-forwarding-switcher
npm install
npx nx run-many -t lint typecheck test # verify everythingRun the full stack locally:
npx nx run cli:build && node dist/apps/cli/main.cjs serve # daemon + web UI
npx nx run web:serve # Vite dev server (proxies /api to :65432)See CLAUDE.md for architecture details and conventions.
License
MIT — see LICENSE.
