Commands
Every ecaddy subcommand and its flags. Commands that touch a project use a site name — a short identifier you supply with --site NAME (or -s NAME). The name determines the fragment filename and is how up, down, edit, and remove target the project.
ecaddy setup
One-time machine bootstrap. Install Caddy, scaffold the global config, trust the local CA, start the brew service.
ecaddy setup
Every step is idempotent — safe to re-run if a prior step failed or you've reinstalled Homebrew. See Getting Started › Step 2 for the per-step breakdown.
ecaddy run
Register a project Caddyfile, block, and unregister on shutdown. The Procfile-friendly variant — pair it with foreman, overmind, or bin/dev.
ecaddy run --config ./Caddyfile --site fishme
ecaddy run -c ./Caddyfile -s fishme
Flags
| Flag | Required | Description |
|---|---|---|
| --config, -c PATH | yes | Path to the project's source Caddyfile |
| --site, -s NAME | yes | Site identifier (fragment filename, registry key) |
Lifecycle
- Validate that
--configexists. - Run conflict detection against the registry — BLOCK on shared domain or port.
- Rewrite relative
output filelog paths to absolute paths (resolved from the directory of--config). - Write the result to
~/.config/caddy/sites/<name>.caddy. - Upsert the registry entry.
- Validate the global config with
caddy validateand reload Caddy. - Block on a signal trap.
- On
SIGTERM/SIGINT: delete the fragment, reload Caddy, exit 0.
/projects/fishme/Caddyfile; Caddy runs from brew services and has no relation to that directory. output file log/caddy.log would write somewhere unpredictable. ecaddy rewrites it to /projects/fishme/log/caddy.log before the fragment is installed.
ecaddy ensure
One-shot variant of run. Copy the Caddyfile, reload Caddy, exit immediately. The site stays registered across reboots until you run ecaddy down or ecaddy remove.
ecaddy ensure --config ./Caddyfile --site fishme
Use this in CI, shell scripts, or any setup where you want Caddy configured but don't need a foreground process tied to a dev loop.
ecaddy list
Show all registered sites in a table.
ecaddy list
ecaddy list --format json
┌─────────┬────────┬────────────────────────────────────────────┬────────────┬──────────────────────────┐
│ Name │ Status │ Domains │ Ports │ Source │
├─────────┼────────┼────────────────────────────────────────────┼────────────┼──────────────────────────┤
│ fishme │ up │ fishme.localhost, vite.fishme.localhost │ 3054, 3104 │ /projects/fishme/Caddyfile │
│ letly │ down │ letly.localhost, vite.letly.localhost │ 3050, 3100 │ /projects/letly/Caddyfile │
└─────────┴────────┴────────────────────────────────────────────┴────────────┴──────────────────────────┘
--format json emits a machine-readable list suitable for piping to jq.
ecaddy up NAME / ecaddy down NAME
Enable or disable a registered site without removing it. The fragment is moved between sites/ and disabled/ via Pathname#rename (atomic), the registry's enabled flag is flipped, and Caddy is reloaded.
ecaddy down fishme # sites/fishme.caddy → disabled/fishme.caddy, reload
ecaddy up fishme # disabled/fishme.caddy → sites/fishme.caddy, reload
Disabled fragments are preserved — their content stays on disk so re-enabling is instant.
ecaddy status
Show global Caddy state and per-site health (whether the upstream app is actually listening).
ecaddy status
Caddy service: running
Config: /Users/you/.config/caddy/Caddyfile
fishme up
fragment: /Users/you/.config/caddy/sites/fishme.caddy
source: /projects/fishme/Caddyfile
letly up (app not running)
fragment: /Users/you/.config/caddy/sites/letly.caddy
source: /projects/letly/Caddyfile
ecaddy doctor
Scan all registered sites cross-wise for port and domain conflicts, plus TCP-probe each upstream port.
ecaddy doctor
Exits 0 if all clear or only INFO findings. Exits 1 on any BLOCK.
| Severity | Meaning |
|---|---|
| BLOCK | Two sites share a port or domain — one will fail to bind |
| WARN | A port is bound by an unexpected process (not the registered upstream) |
| INFO | Upstream not listening (the project's app simply isn't running) |
ecaddy audit
Full system + TLS audit with optional --fix prompts. Where doctor looks at the registry, audit also probes the live Caddy service, brew-service state, TLS handshakes per domain, and the system-keychain trust state.
ecaddy audit # report-only
ecaddy audit --fix # prompt to run each suggested fix
ecaddy audit --site fishme # limit to one site
The --fix mode walks each collected finding, prints the proposed command, asks for confirmation, runs it, and re-verifies. If the primary fix doesn't resolve the finding, audit chains to a next_fix (e.g. caddy trust → sudo caddy trust).
ecaddy edit NAME
Open a site's installed fragment in $EDITOR. Caddy is validated and reloaded after you save and exit the editor.
ecaddy edit fishme
ecaddy edit opens ~/.config/caddy/sites/fishme.caddy — the installed fragment, not your project's Caddyfile. Re-running ecaddy run (or ecaddy ensure) will overwrite your edits with the project source. Use this for quick experiments; for lasting changes edit the project Caddyfile and re-register.
ecaddy logs --site NAME
Tail the project's Caddy log files. ecaddy reads the fragment, extracts every output file PATH directive, and shells out to tail on them.
ecaddy logs --site fishme # tail -F (follow)
ecaddy logs --site fishme --lines 100 # tail -n 100, no follow
If the Caddyfile has no output file directives, ecaddy prints guidance and exits. Works for both enabled and disabled sites.
ecaddy remove NAME
Delete a site's fragment and registry entry entirely.
ecaddy remove fishme
ecaddy remove fishme --force # skip the confirmation prompt
ecaddy reload
Validate the global config with caddy validate and reload Caddy without restarting it. Equivalent to what run / ensure / up / down do internally after each change.
ecaddy reload
ecaddy version
ecaddy version
# ecaddy 0.1.0
A note on the run command
run is a Thor reserved method name. Internally the method is caddy_run, exposed as run via map 'run' => :caddy_run in cli.rb. This is irrelevant from the CLI side — ecaddy run works as expected — but worth knowing if you crack open the source.