Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PerrinPort

One name. One port. Always the same.

Stop guessing which localhost port your service is on. PerrinPort derives a stable, deterministic port (10000–19999) from any project name or URL using SHA-256 — the port is computed, not remembered, not coordinated, not configured.


The problem

You've played this game:

$ curl localhost:3000    # ECONNREFUSED — that's Tuesday's project
$ curl localhost:8080    # nope, that's the old Jenkins
$ curl localhost:4200    # maybe?
$ grep -r "port" .env   # finally

Or the team version: "What port is the auth service on?" — a question that should never need to be asked.

PerrinPort ends it. If you know the name, you know the port.


How it works

canonical = lowercase(scheme + "://" + label)
port      = 10000 + (SHA256(canonical)[0:8] as uint32  mod 10000)

The canonical form is derived from your input — a folder name, a URL, a bare project name — via a simple set of rules that make a project's folder path and its dev URL land on the same port automatically.

Canonicalization

Input Canonical Port
myproject http://myproject 19894
http://localhost/MyProject/ http://myproject 19894
C:\dev\MyProject http://myproject 19894
/home/me/projects/MyProject/ http://myproject 19894
grafana http://grafana 13029
https://myproject.test/ https://myproject.test 15656
https://shop.example.com/admin/products?x=1 https://admin 18043

Rules:

  • Input containing :// is treated as a URL: scheme = URL scheme, label = first path segment, or the host if there is no path.
  • Everything else is a name or path: scheme = http, label = the final path component (basename).
  • Everything is lowercased before hashing.
  • Query strings and fragments are ignored.

Quick start

Pick your platform:

# Bash (Linux / macOS / WSL)
chmod +x implementations/bash/perrinport
./implementations/bash/perrinport myproject   # 19894
# Python 3 — no dependencies
python implementations/python/perrinport.py myproject   # 19894
// Node.js
node implementations/node/perrinport.js myproject   // 19894
# PowerShell (Windows)
.\implementations\powershell\perrinport.ps1 myproject   # 19894
REM Windows batch + PowerShell (original reference)
docs\perrinport.bat myproject   :: 19894

Implementations

Language File Notes
Windows batch docs/perrinport.bat Original reference — PowerShell inline
Bash implementations/bash/perrinport Linux, macOS, WSL
Python 3 implementations/python/perrinport.py stdlib only; importable as a module
Node.js implementations/node/perrinport.js stdlib only; importable as a module
PowerShell implementations/powershell/perrinport.ps1 standalone .ps1
Go implementations/go/ importable package + CLI in cmd/perrinport/

All implementations pass the same 10-vector conformance suite (docs/perrinport-conformance.json).

# Run all conformance tests
python implementations/python/test_conformance.py
node   implementations/node/test_conformance.js
go test ./implementations/go/
bash   implementations/bash/test_conformance.sh
pwsh   implementations/powershell/test_conformance.ps1

Browser extension

The extension shows the PerrinPort for the URL in your active tab — right in the toolbar, without switching to a terminal.

What it does:

  • Toolbar badge shows the port number as you browse.
  • Click the icon for the full breakdown: canonical form, port, and a one-click Open localhost:PORT link.

Install (developer mode)

Chrome / Edge:

  1. Run python generate_icons.py inside extensions/ (once).
  2. Open chrome://extensions, enable Developer mode.
  3. Click Load unpacked and select the extensions/ folder.

Firefox:

  1. Run python generate_icons.py inside extensions/ (once).
  2. Open about:debugging#/runtime/this-firefox.
  3. Click Load Temporary Add-on and select extensions/manifest.json.

Both browsers use the same manifest.json (Chrome ignores the Firefox-specific browser_specific_settings field).


Dashboard

A port 80 landing page that enumerates all your local PerrinPort services, probes each one, and presents a live clickable registry — your personal service directory, generated from names alone.

extras/dashboard/
  perrinport_dashboard.py   # the server (stdlib only, no deps)
  projects.txt              # your services — edit without restarting
  00-startup.bat            # Windows launcher

Start it:

# Windows (run as Administrator for port 80)
extras\dashboard\00-startup.bat

# Any OS, any port
python extras/dashboard/perrinport_dashboard.py --port=8099

# Or via $PORT env var
PORT=8099 python extras/dashboard/perrinport_dashboard.py

Edit projects.txt to add your services — no restart needed, the file is re-read on every poll cycle:

myproject
api-gateway
frontend
grafana
https://shop.example.com/admin/

The dashboard probes all ports in parallel (250 ms timeout), shows UP/DOWN status, and updates every 5 seconds. Click any row to open localhost:PORT.


Conformance

docs/perrinport-conformance.json is the canonical test-vector file. Any implementation must pass all vectors to be considered conformant.

# Python
python implementations/python/test_conformance.py

# Node.js
node implementations/node/test_conformance.js

Open question

Root URLs with no path (e.g. https://site.test/) currently fall back to the host and receive a derived port like any other input. Whether roots should map to 80/443 instead is intentionally unsettled — see the unsettled fields in the conformance JSON. Until resolved, conformant implementations must keep the host-fallback behaviour.


Why 10000–19999?

  • Ports below 1024 require root on Unix.
  • Ports 1024–9999 are crowded with well-known services.
  • The 10,000-wide window (10000–19999) is largely unassigned and gives a roughly 0.5% collision probability for 10 projects — acceptable for individual and small-team use. Expected first collision at ~125 projects.

Contributing

New implementation? Add it to implementations/<language>/, write a conformance test, and make sure all 10 vectors pass before opening a PR.

Changing the spec? The algorithm and canonicalization rules are in docs/perrinport-conformance.json. Any spec change requires updating every conformance vector that is affected.


Licence

Copyright (C) 2026 pp

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. See the GNU AGPL v3 for details.

The AGPL was chosen deliberately: if you embed PerrinPort in something you distribute, or run it as a network service, your software must also be open. The port is a shared standard — keep the ecosystem open.


Spec

perrinport spec version 0.1.0 — algorithm variant pp8 (the default; pp implies pp8)
Algorithm: port = 10000 + (first8hex(SHA256(canonical)) as uint32 mod 10000)
Canonical: lowercase(scheme + "://" + label) per the rules above.

About

Stable, deterministic localhost ports from project names — no config, no conflicts

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages