morth

module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 21, 2026 License: MIT

README

morth

The port manager that doesn't need a port.

Go Report Card Release

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

  1. morth init creates a .morth.json file in your project root that declares the project name and its services.
  2. Each project registers its port claims in a global registry at ~/.morth/registry.json, ensuring uniqueness across all projects on the machine.
  3. 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.
  4. 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

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/my-feature)
  3. Ensure tests pass (make test)
  4. Ensure linting passes (make lint)
  5. Commit your changes
  6. Push to the branch and open a Pull Request

Please read AGENTS.md for code conventions and architecture details.

License

MIT - Copyright 2026 pedeceul

Directories

Path Synopsis
cmd
morth command
Package main is the entry point for the morth CLI tool.
Package main is the entry point for the morth CLI tool.
internal
cli
Package cli implements the morth CLI commands.
Package cli implements the morth CLI commands.
compose
Package compose provides helpers for Docker Compose integration.
Package compose provides helpers for Docker Compose integration.
pkg
envgen
Package envgen formats port assignments as environment variables.
Package envgen formats port assignments as environment variables.
portcheck
Package portcheck provides OS-level port availability checking.
Package portcheck provides OS-level port availability checking.
project
Package project manages the per-project .morth.json configuration file.
Package project manages the per-project .morth.json configuration file.
registry
File locking for concurrent access to the global registry.
File locking for concurrent access to the global registry.
resolver
Package resolver provides deterministic port assignment using FNV-1a hashing.
Package resolver provides deterministic port assignment using FNV-1a hashing.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL