morth
The port manager that doesn't need a port.

The Problem
Every developer has hit "port 3000 is already in use" at the worst possible moment. Across a handful of projects, port assignments drift, collide, and break docker-compose stacks, microservice meshes, and CI pipelines. There is no standard way to claim a port for a project and guarantee it stays unique across your entire machine.
Install
Go install (requires Go 1.22+):
go install github.com/pedeceul/morth/cmd/morth@latest
Shell script:
curl -sSfL https://raw.githubusercontent.com/pedeceul/morth/main/install.sh | sh
30-Second Quickstart
# Initialize morth in your project
morth init
# Add services with deterministic port assignment
morth add api
morth add web
morth add db
# See what you got
morth list
# SERVICE PORT
# api 18291
# web 23402
# db 31987
# Export as environment variables
eval $(morth env)
echo $API_PORT # 18291
echo $WEB_PORT # 23402
# Or write a .env file for docker-compose
morth env --format dotenv > .env
How It Works
morth init creates a .morth.json file in your project root that declares the project name and its services.
- Each project registers its port claims in a global registry at
~/.morth/registry.json, ensuring uniqueness across all projects on the machine.
- Ports are assigned deterministically using an FNV-1a hash of
projectID:serviceName, mapped into the range 10000–49151. If a collision occurs, morth probes linearly until a free slot is found.
- The registry is protected by file locking (
syscall.Flock) so concurrent morth invocations never corrupt it.
Commands
| Command |
Description |
Example |
init |
Initialize a new morth project in the current directory |
morth init |
add |
Add a service and assign it a port |
morth add api |
remove |
Remove a service and release its port |
morth remove api |
list |
List all services and their assigned ports |
morth list |
get |
Get the port for a specific service |
morth get api |
env |
Output port assignments as environment variables |
morth env --format dotenv |
release |
Release all ports and remove project from registry |
morth release |
version |
Print the morth version |
morth version |
Docker Compose Integration
Use morth to generate a .env file that docker-compose reads automatically:
# docker-compose.yml
services:
api:
build: .
ports:
- "${API_PORT}:8080"
postgres:
image: postgres:16
ports:
- "${DB_PORT}:5432"
# Generate .env before running compose
morth env --format dotenv > .env
docker compose up
The variable naming convention is {SERVICE}_PORT, uppercased with hyphens replaced by underscores.
Configuration
.morth.json
Created by morth init in your project root:
{
"project_id": "myapp",
"services": {
"api": { "port": 0, "assigned": 18291 },
"web": { "assigned": 23402 },
"db": { "assigned": 31987 }
}
}
| Field |
Type |
Description |
project_id |
string |
Unique project name (derived from directory name) |
services |
map[string]object |
Service name to port configuration |
services.*.port |
int |
Preferred port (0 or omitted = auto-assign) |
services.*.assigned |
int |
Actually assigned port |
~/.morth/registry.json
Global registry tracking all port assignments across projects:
{
"entries": [
{ "project_id": "myapp", "service": "api", "port": 18291 },
{ "project_id": "myapp", "service": "web", "port": 23402 }
]
}
AI Agents
This project includes an AGENTS.md file with complete architecture, conventions, and development guidelines for AI-assisted development. All AI agent instruction files (CLAUDE.md, .cursorrules, .windsurfrules, .clinerules, .github/copilot-instructions.md) point to AGENTS.md as the single source of truth.
FAQ
Q: What port range does morth use?
A: 10000–49151 (39,152 ports in the unprivileged range).
Q: What happens if my deterministic port is already taken by another project?
A: Morth probes linearly from the hash-derived port until it finds an unclaimed slot in the registry.
Q: What if a port is claimed in the registry but actually free on the OS?
A: Registry claims are authoritative. Use morth release in the stale project to free its ports, or manually edit ~/.morth/registry.json.
Q: Does morth check if a port is actually in use by a running process?
A: morth add verifies the port is not currently bound using net.Listen. If the port is in use at the OS level, morth skips it and probes for the next available port.
Q: Does morth require root/sudo?
A: No. It only assigns ports in the unprivileged range (above 1024 by default).
Q: Can I use morth in CI?
A: Yes. Run morth init and morth add in your CI script, or commit .morth.json and run morth env to export ports.
Security
morth is a local-only CLI tool with no network access, no daemon, and no secrets storage.
Its attack surface is limited to the local filesystem under your user account.
Port assignments are not sensitive data.
For the full threat model, see docs/framework/security.md.
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/my-feature)
- Ensure tests pass (
make test)
- Ensure linting passes (
make lint)
- Commit your changes
- Push to the branch and open a Pull Request
Please read AGENTS.md for code conventions and architecture details.
License
MIT - Copyright 2026 pedeceul