npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@spiriyu/port-forwarding-mapper

v0.0.9

Published

[![npm](https://img.shields.io/npm/v/@spiriyu/port-forwarding-mapper)](https://www.npmjs.com/package/@spiriyu/port-forwarding-mapper) [![node](https://img.shields.io/node/v/@spiriyu/port-forwarding-mapper)](https://nodejs.org) [![license](https://img.shie

Readme

pfs — port-forwarding manager

npm node license Release

A host-local TCP port-forwarding manager. One long-running daemon owns all the listeners; a web UI, desktop app, and CLI drive it interactively — all staying in sync in real time.

Think kubectl port-forward, but persistent, named, and toggled with a single click or command.

Features

  • Named, persistent mappings — forward source port → target host:port, survive daemon restarts
  • One-click toggle — enable or disable any mapping without restarting the daemon or losing others
  • Live sync — the web UI, Electron desktop app, and CLI all reflect changes instantly via WebSocket events
  • Service install — install as a launchd/systemd/Windows Service so it starts at login
  • Cross-platform — macOS, Linux, and Windows first-class

Install

npm install -g @spiriyu/port-forwarding-mapper

This puts the pfs binary on your PATH.

Or run without installing:

npx @spiriyu/port-forwarding-mapper serve                 # default port 65432
npx @spiriyu/port-forwarding-mapper serve --port 8888     # custom port
npx @spiriyu/port-forwarding-mapper add 8080 localhost:3000 --name dev-api
npx @spiriyu/port-forwarding-mapper list

If you prefer a shorter alias without a global install, add this to your shell profile:

alias pfs='npx @spiriyu/port-forwarding-mapper'

Quickstart

1. Start the daemon

pfs serve                  # default port 65432
pfs serve --port 8888      # custom port
pfs serve -p 8888          # shorthand

Or install it as a persistent system service:

pfs service install
pfs service start

2. Open the web UI

Navigate to http://127.0.0.1:65432/ui in your browser, or launch the Electron desktop app.

3. Add a mapping

# Forward local port 8080 → localhost:3000
pfs add 8080 localhost:3000

# Give it a name
pfs add 8080 localhost:3000 --name dev-api

# Bind on all interfaces
pfs add 0.0.0.0:8080 localhost:3000 --name dev-api

4. List and manage mappings

pfs list
  NAME      SOURCE             TARGET            STATUS
  dev-api   127.0.0.1:8080  →  localhost:3000    listening
pfs toggle dev-api      # flip enabled ↔ disabled
pfs disable dev-api
pfs enable dev-api
pfs remove dev-api

5. Watch events in real time

pfs watch          # stream mapping status changes
pfs logs --follow  # stream daemon log entries

CLI reference

pfs [--url <daemon-url>] [--json] <command> [args]

Mapping commands:
  list                           List all port mappings
  add <source> <target>          Create a mapping  (--name, --enabled, --group)
  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 JSON

Privileged 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 start

Or 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 everything

Run 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.