Localghost User Flows

Drop-In Install

As a developer, I want Localghost to be a drop-in dev dependency so I can add friendly local hostnames without rebuilding the same Caddy and hosts setup in every repo.

npm install -D @hamedb89/localghost
pnpm add -D @hamedb89/localghost
yarn add -D @hamedb89/localghost
bun add -d @hamedb89/localghost

Choose the install command for the repository's package manager. Then initialize the project contract:

localghost init --write-scripts

For a repository without Localghost installed, use the package-manager launcher first:

npm exec --yes --package=@hamedb89/localghost -- localghost init --write-scripts
pnpm dlx @hamedb89/localghost init --write-scripts
yarn dlx @hamedb89/localghost init --write-scripts
bunx --package @hamedb89/localghost localghost init --write-scripts

Project Contract

As a developer, I want one small file that explains the local domain map for this repo.

# .localghost
app.localhost 5173
www.app.localhost 5173
api.app.localhost 8787

.localghost is the default config file. Repos can opt into another file name with --config, an ordered list of names, or a filename regex.

Machine Readiness

As a developer, I want to know whether my laptop is ready before Localghost changes system files.

localghost doctor

Localghost checks for Caddy and prints the exact install command when it is missing. It does not run Homebrew automatically.

Update Awareness

As a developer, I want to know when Localghost itself is stale without paying for a network check on every run.

localghost update
LOCALGHOST_NO_UPDATE_CHECK=1 localghost doctor

Localghost checks npm after successful commands, caches the result for 24 hours, and ignores check failures. LOCALGHOST_NO_UPDATE_CHECK=1 and --no-update-check disable the automatic check.

To install or update the package in the current repository, run localghost upgrade. If no local CLI exists yet, invoke it through the package manager, for example npm exec --yes --package=@hamedb89/localghost -- localghost upgrade.

One-Time Setup

As a developer, I want one explicit setup command that updates only the managed Localghost block in /etc/hosts and validates Caddy.

localghost setup

Daily Dev

As a developer, I want a daily command that starts the local HTTP proxy from the same config file, with local HTTPS available only when I ask for it.

localghost status --ready
localghost dev

Most repos will run this next to their app server, for example Vite on 127.0.0.1:5173.

When a repo really needs local certificates:

localghost dev --https

Config Discovery

As a developer, I want Localghost to fit repos that already have naming conventions without hidden file searches.

localghost print --config .localghost.preview
localghost print --config .localghost.private --config .localghost
localghost print --config-pattern '^\.localghost\.(private|preview)$'

Localghost uses the first existing configured file. Regex discovery scans filenames in the project root.

Domain Routing

As a developer, I want to see the local domain layer as a simple domain -> upstream map.

localghost routes
localghost routes
  http://app.localhost/ -> http://127.0.0.1:5173
  http://api.app.localhost/ -> http://127.0.0.1:8787

setup and dev print this same map before Caddy is validated or run.

Vite Integration

As a Vite user, I want Localghost to set the dev host to my configured domain, keep strict allowedHosts, print browser-facing URLs, and never run in production/build mode.

import { localGhostPlugin } from "@hamedb89/localghost/vite";

export default {
  plugins: [localGhostPlugin({ port: 5173 })]
};

The plugin defaults to HTTP. Pass https: true only when Vite is expected to sit behind a Caddy HTTPS proxy. Localghost prints URLs but does not open browser tabs.

If .localghost is missing, an interactive development run asks whether to create it, asks for the primary .localhost domain, allows extra domains, explains the /etc/hosts password prompt, and runs setup when confirmed. Non-interactive runs fail with the exact setup command instead of guessing.

Ghost Tunnel (Experimental)

Ghost Tunnel is an experimental opt-in feature. Its configuration, transports, CLI behavior, and public APIs may change between releases and should not be used for production-critical access.

As a production app, I want one opt-in flag that makes <route>-<project>-<owner>.ghost.<domain> a known product entrypoint on top of the deployed Vite app, without running local Caddy or requiring the local .localghost file.

export default defineLocalghostConfig({
  ghostTunnel: {
    domains: "moonlit-otter.example",
    mode: "manual"
  }
});

The production flow is DNS wildcard -> same-project ingress handler -> constructGhostTunnelUrl -> deployed app auth -> assertSecureGhostTunnelRequest -> exact .ghosttunnel lookup -> transport decision. By default the helper constructs and parses route, project, and owner from the wildcard label before ghost.<domain>, requires HTTPS, and requires the app to pass authenticated: true. transport: "none" stops at ingress plus lookup, transport: "ip" accepts a signed direct-address token and redirects to that address plus the exact .ghosttunnel port, and transport: "tunnel" queues the request through Redis for a local localghost tunnel agent. See Ghost Tunnel.

Relay registration is local-agent-only: signed exact-host claims, explicit local targets, private access by default, no arbitrary URL proxy endpoint, and safe offline behavior when the agent disconnects.

When ghostTunnel is configured, route and Vite startup logs print the production URL shape. Manual mode can fill local defaults for route, project, and owner; public mode leaves those slots as <route>, <project>, and <owner> unless ghostTunnel.preview pins a concrete preview URL. Add ghostTunnel.domains to fill one or more production base domains; omit it to show the wildcard domain as *. In an interactive Vite terminal, press g to show Ghost Tunnel configuration and open a numbered concrete URL when one is configured.

Reset For Testing

As a developer, I want to retest setup without deleting my project config.

localghost reset
localghost setup

reset removes only the managed hosts block, generated Caddyfile, and setup state. It leaves .localghost in place.

Teardown

As a developer, I want to cleanly remove Localghost from a project when the repo is archived or no longer needs friendly hostnames.

localghost teardown
localghost teardown --remove-caddyfile

teardown removes only the Localghost managed /etc/hosts block. The generated Caddyfile is kept unless --remove-caddyfile is passed.

State Tracking

As a developer or agent, I want to see what Localghost changed without reading system files directly.

localghost status
localghost status --json

Localghost records setup and teardown in ops/local/localghost-state.json. That file is project-local state, not OS temp state.

Agent-Friendly Tools

As a Codex or agent user, I want commands that are inspectable and scriptable without opening a browser.

localghost print
localghost doctor
localghost update

The CLI reference lives in localghost(1). Future flows can add MCP helpers and repo templates, but the base package should remain a small, predictable CLI.