ecaddy Docs Commands

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

FlagRequiredDescription
--config, -c PATHyesPath to the project's source Caddyfile
--site, -s NAMEyesSite identifier (fragment filename, registry key)

Lifecycle

  1. Validate that --config exists.
  2. Run conflict detection against the registry — BLOCK on shared domain or port.
  3. Rewrite relative output file log paths to absolute paths (resolved from the directory of --config).
  4. Write the result to ~/.config/caddy/sites/<name>.caddy.
  5. Upsert the registry entry.
  6. Validate the global config with caddy validate and reload Caddy.
  7. Block on a signal trap.
  8. On SIGTERM / SIGINT: delete the fragment, reload Caddy, exit 0.
Why log paths are rewritten Your Caddyfile lives at /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.

SeverityMeaning
BLOCKTwo sites share a port or domain — one will fail to bind
WARNA port is bound by an unexpected process (not the registered upstream)
INFOUpstream 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 trustsudo caddy trust).

Per-domain TLS checks also validate the leaf certificate's date window: a cert that is expired or not-yet-valid is reported as ERR_CERT_DATE_INVALID (rather than a misleading “browser-trusted”), with a fix that restarts Caddy to reissue it and escalates to ecaddy retrust. For a root-owned, unwritable log file, --fix offers an interactive choice — keep as-is, take ownership (sudo chown), or delete — and points to re-registering the site as the durable fix (the fragment is rewritten with mode 0660).

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
This edits the copy, not your source 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 retrust

Re-trust the local Caddy CA and reissue certificates. Run this when your browser shows net::ERR_CERT_DATE_INVALID or NET::ERR_CERT_AUTHORITY_INVALID on a *.localhost site — the cached leaf certificate has expired, or the local CA is missing from the system keychain.

ecaddy retrust

Runs caddy untrust (removes the old cert) then caddy trust (re-installs it), then restarts the Caddy service so it reissues the short-lived *.localhost leaf certificates. macOS prompts for your password for each keychain operation. Afterwards, fully reload your browser (or quit and reopen it) to drop the stale cached certificate.

ecaddy version

ecaddy version
# ecaddy 0.1.4

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.

ecaddy v0.1.4 · MIT License · GitHub