Skip to content

BlankParticle/portable

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

portable

Run local apps behind the portable proxy, auto allocate ports, and expose friendly local hostnames.

Install

Install latest release:

curl -fsSL https://raw.githubusercontent.com/BlankParticle/portable/main/install.sh | bash

Install a specific release:

curl -fsSL https://raw.githubusercontent.com/BlankParticle/portable/main/install.sh | bash -s -- -v 0.0.1

Quick Start

Install the system service and trust the local root certificate:

portable service install
portable proxy trust

Start the proxy:

portable run myapp -- pnpm dev

Visit https://port.local to view the Web UI.

On macOS, *.port.local hosts are supported without dnsmasq; portable advertises tracked *.port.local app hosts over mDNS while they are running:

portable run web -H web.port.local -- pnpm dev

Use @PORT when your app expects a CLI flag instead of environment variables:

portable run dev-server -- vite dev --port @PORT

By default, portable uses a local development hostname when available, such as <name>.port.local, and falls back to <name>.localhost elsewhere.

On Linux, portable uses systemd-resolved and a local DNS server on 127.0.0.1:5354 for *.port.local hosts.

The macOS mDNS responder and Linux DNS server both run alongside the portable proxy.

On Linux, portable configures systemd-resolved to route *.port.local to the local DNS server automatically while the proxy is running.

portable doctor reports whether systemd-resolved is active on Linux and whether *.port.local routing is currently configured.

While the proxy is running, https://port.local serves the built-in control UI for the portable proxy. If DNS is unavailable, the same UI is also served from http://localhost:2019.

Store extra UI hostnames in the portable state file if you want the same UI served on additional names, such as a Tailscale DNS hostname:

portable config ui-hosts add port.local.tailnet.ts.net
portable config ui-hosts add proxy.example.internal
portable config ui-hosts ls

For local UI development with Vite, run pnpm dev in ui; the dev server proxies /portable/* to http://localhost:2019, so browser requests stay same-origin on http://localhost:5173.

portable also sets PORT=<allocated port>, PORTABLE_APP=<name>, and PORTABLE_HOST=<https URL> for the child process.

Proxy

portable requires the portable proxy to own Caddy.

Start the proxy:

portable proxy start

The proxy can load your existing base Caddyfile so portable can replace a standalone Caddy install while keeping your custom sites. It looks in this order and uses the first file that exists:

  • $PORTABLE_CADDYFILE
  • /etc/Caddyfile
  • ~/.config/caddy/Caddyfile

The embedded build also includes the Cloudflare DNS module, so directives like acme_dns cloudflare ... work in the loaded Caddyfile.

If a .env file exists next to the selected Caddyfile, portable loads it before adapting the file, so you can move provider tokens and other secrets out of it.

Privileged start (bind 80/443 if available):

portable proxy start -p

Common Commands

portable run myapp -- pnpm dev
portable ls
portable rm <name>
portable proxy status
portable proxy reload
portable proxy trust
portable proxy stop
portable dns status
portable dns revert
portable doctor

All commands support --json for scriptable output.

Examples:

portable proxy status --json
portable ls --json
portable --json run api -- uvicorn app:app --port @PORT

Public Tunnels

For a self-hosted public relay, point your wildcard DNS at a machine running portable proxy start, then enable public tunnel serving there:

portable proxy start -p
portable tunnel server enable
portable tunnel server suffixes add public.com

Pair your local machine with that relay over SSH:

portable tunnel pair portable@relay.public.com
portable tunnel status

Expose an app publicly while keeping the local dev hostname:

portable run web --tunnel web.public.com -- pnpm dev

portable registers the public host on the relay, opens an SSH reverse tunnel back to the local app port, and removes the route when the app exits.

Trust

portable proxy trust fetches the local CA root from the Caddy admin API and installs trust using the same truststore approach used by Caddy.

For Node.js clients (fetch, undici, axios over HTTPS), you may also need to enable CA trust in Node:

  • Newer Node versions: set NODE_USE_SYSTEM_CA=1 so Node uses system trust.
  • Older Node versions (or as a fallback): set NODE_EXTRA_CA_CERTS to Caddy's local root cert path.

The Caddy local root cert path is resolved from the Caddy data directory in this order:

  • $PORTABLE_CADDY_DATA_DIR (if set)
  • else $CADDY_DATA_DIR (if set)
  • else the platform default Caddy data directory (macOS: ~/Library/Application Support/Caddy, Linux: ~/.local/share/caddy)

Append pki/authorities/local/root.crt to the selected data directory.

Example:

# Newer Node
export NODE_USE_SYSTEM_CA=1

# Older Node / fallback (macOS)
export NODE_EXTRA_CA_CERTS="$HOME/Library/Application Support/Caddy/pki/authorities/local/root.crt"

# Older Node / fallback (Linux)
export NODE_EXTRA_CA_CERTS="$HOME/.local/share/caddy/pki/authorities/local/root.crt"

Runtime Files

State is stored in:

  • $XDG_STATE_HOME/portable
  • fallback: ~/.local/state/portable

When the proxy is started with portable proxy start -p, portable keeps using the invoking user's state directory rather than a root-owned runtime directory.

Files:

  • state.json
  • state.lock
  • daemon.pid
  • daemon.log

Service Management

Use portable service to install and manage a system service on the current machine:

portable service install
portable service status
portable service restart
portable service stop
portable service uninstall

For supervised installs, set PORTABLE_RUNTIME_DIR and PORTABLE_CADDY_DATA_DIR explicitly so the proxy keeps using the intended user's state and Caddy storage directories.

On Linux, you can run portable as a normal user and grant only CAP_NET_BIND_SERVICE if you want it to bind 80/443 without running the entire service as root.

Development

Build and install from local source:

make install

Source code lives in cmd/portable.

Common root-level tasks:

make install
make openapi
make build
make test
make dev-ui
make fmt

If you prefer, ./install-dev.sh still works directly.

About

Run local apps behind the portable proxy, auto allocate ports, and expose friendly local hostnames

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages