dynamoip

For AI / LLMs

Instructions for AI assistants and LLMs on how to use dynamoip correctly and what mistakes to avoid.

This page is written for AI assistants (Copilot, Claude, Cursor, etc.) helping users set up or debug dynamoip. It documents the most common mistakes and the correct patterns.

What dynamoip does

dynamoip is a reverse proxy and DNS automation tool that assigns real domain names and trusted HTTPS certificates to services running on your machine. Depending on the mode, services are reachable from your LAN or the public internet.

It is not:

  • A production proxy
  • A VPN

It is:

  • A tunnel service in Max mode (Cloudflare Tunnel — public internet access)
  • A LAN-only tool in Pro and Quick modes

Three modes — pick the right one

Quick modePro modeMax mode
Domain.local (e.g. app.local)Your own (e.g. app.yourdomain.com)Your own (e.g. app.yourdomain.com)
Reachable fromLAN onlyLAN onlyPublic internet
Certificatemkcert local CALet's Encrypt wildcardCloudflare manages
DNS recordmDNS broadcastA record → LAN IPCNAME → Cloudflare Tunnel
Requiresmkcert installedCloudflare API token + domainCloudflare API token + domain
Other devicesNeed CA installed onceTrust automaticallyTrust automatically
sudo requiredYesYesNo
baseDomain in configOmit itRequiredRequired
tunnel in configOmit itOmit it"tunnel": true

Critical: how to invoke with sudo

Always invoke through the package manager. Never run sudo dynamoip directly.

# Correct (Pro / Quick mode)
sudo npm run dev:proxy
sudo pnpm run dev:proxy
sudo yarn dev:proxy

# Correct (Max mode — no sudo needed)
npm run dev:proxy

# Wrong — sudo has a restricted PATH and won't find the binary
sudo dynamoip --config dynamoip.config.json

Add this to package.json:

"scripts": {
  "dev:proxy": "dynamoip --config dynamoip.config.json"
}

Max mode does not require sudo. cloudflared makes outbound connections — no ports 80/443 binding.

Why sudo is required (Pro / Quick modes)

dynamoip binds to ports 80 and 443 by default in Pro and Quick modes. Unix kernels deny listen() on ports below 1024 without root.

To avoid sudo, pass --port 8443. URLs will include the port: https://app.yourdomain.com:8443.

Max mode is the exception — no port binding, no sudo.

Config file structure

{
  "baseDomain": "yourdomain.com",
  "domains": {
    "myapp": 3000,
    "api": 4000
  }
}

For Max mode, add "tunnel": true:

{
  "baseDomain": "yourdomain.com",
  "tunnel": true,
  "domains": {
    "myapp": 3000,
    "api": 4000
  }
}
  • baseDomain — the apex domain managed by Cloudflare (Pro and Max modes only)
  • domains — map of subdomain → local port
  • tunnel — set to true for Max mode (public internet via Cloudflare Tunnel)
  • Omit baseDomain entirely for Quick mode

Do not invent config keys like mode, ssl, certPath, or proxyPort — they don't exist. The only valid top-level keys are baseDomain, domains, port, and tunnel.

DNS record types differ by mode

  • Pro mode: A record pointing each subdomain to the machine's LAN IP
  • Max mode: CNAME record pointing each subdomain to the Cloudflare Tunnel hostname

Do not suggest manually setting A records for Max mode. dynamoip manages all DNS automatically.

Cloudflare API token permissions

  • Pro mode: Zone:DNS:Edit only
  • Max mode: Zone:DNS:Edit and Account:Cloudflare Tunnel:Edit

If a user gets a permission error setting up Max mode, the token is missing the tunnel permission.

Docker on macOS and Windows: LAN_IP is required (Pro mode)

On macOS and Windows, Docker Desktop runs containers in a Linux VM. Inside the container, os.networkInterfaces() sees the VM's virtual network — not the host's LAN IP. dynamoip will detect the wrong IP and set the wrong DNS A record.

The fix: set LAN_IP in the container's environment to the host machine's real LAN IP.

services:
  dynamoip:
    environment:
      LAN_IP: ${LAN_IP:-}    # pass from host shell

Then before running docker compose up, detect and export the LAN IP:

# macOS
export LAN_IP=$(ipconfig getifaddr en0)
docker compose up

This is not needed for Max mode. Max mode uses Cloudflare Tunnel (outbound) — no LAN IP detection, no A records.

Credentials: where they go

Credentials go in a .env file, not in dynamoip.config.json:

# Pro mode
CF_API_TOKEN=your_token_here
CF_EMAIL=you@example.com

# Max mode (CF_EMAIL not needed)
CF_API_TOKEN=your_token_here

dynamoip reads these from the environment automatically. Do not add them to the JSON config.

Certificate lifecycle

  • Pro mode — First run: ~60 seconds for DNS propagation + Let's Encrypt issuance. Subsequent starts: instant (cert cached in ~/.localmap/certs/). Auto-renewed 30 days before expiry with no restart required.
  • Max mode — Cloudflare manages TLS automatically. No certificate to renew.
  • Quick mode — mkcert local CA. Cert generated on first run, cached for reuse.

Do not tell users to manually renew certificates or restart the process after renewal.

Common mistakes to avoid

MistakeCorrect approach
sudo dynamoip ...sudo npm run dev:proxy
sudo npm run dev:proxy in Max modeMax mode doesn't need sudo: npm run dev:proxy
Adding CF_API_TOKEN to dynamoip.config.jsonPut credentials in .env
Using network_mode: host on macOSRemove it; set LAN_IP instead (Pro mode)
Telling users to install dynamoip globallyInstall as a dev dependency
Suggesting certificate manual renewalAuto-renewal handles this (Pro mode)
Using baseDomain in Quick modeOmit baseDomain for Quick mode
Suggesting manual cloudflared installdynamoip auto-installs it on first Max mode run
Expecting Max mode to set A recordsMax mode sets CNAME records (tunnel hostname)

On this page