An interactive terminal UI plus CLI toolkit (built with Colorama + Rich) that keeps Caddy configuration in SQLite (~/.caddy-tui/config.db), lets you inspect/import data, offers inline block editing, and safely regenerates validated configs before reloading the Caddy service.
caddy-tui/
├── caddy_tui/
│ ├── cli.py # Click-based CLI skeleton & entry points
│ ├── tui_app.py # Rich/Colorama TUI loop
│ ├── db.py # Schema builder + session helpers
│ ├── importer.py # Caddyfile → snapshot pipeline
│ ├── exporter.py # Snapshot → Caddyfile renderer
│ ├── block_editor.py # CRUD helpers for caddy-tui snapshot blocks
│ ├── drift.py, status.py # Diff + reporting utilities
│ └── ... # Additional helpers (config, models, etc.)
├── pyproject.toml # PyPI metadata + console scripts
├── MANIFEST.in # Source distribution manifest
├── README.md # Usage + architecture guide
├── CHANGELOG.md # Release notes
└── LICENSE # MIT terms
The modules are intentionally decoupled so you can script against the importer/exporter hooks or database builder without invoking the CLI.
pip install -e .
caddy-tui init # DB schema builder
sudo caddy-tui import --caddyfile /etc/caddy/Caddyfile
caddy-tui tui # Interactive block editor + drift monitorYou can install the published package directly (recommended inside a virtualenv):
pip install --upgrade caddy-tuiNeed a globally available CLI without touching the system Python? Use pipx:
pipx install caddy-tuiThis installs both caddy-tui and the optional caddy-tui-helper command. If you want the helper to run via sudo without a password, follow the “Privileged helper” section below to add it to /etc/sudoers.d/caddy-tui.
The CLI prints JSON so it can slot straight into scripts, CI jobs, or other automation.
| Command | Purpose |
|---|---|
caddy-tui init |
Create the SQLite schema at ~/.caddy-tui/config.db. |
caddy-tui import --caddyfile PATH |
Parse an existing Caddyfile and load it into the DB. Use sudo when PATH lives under /etc/caddy. |
caddy-tui list-sites / add-site / remove-site |
Manage site definitions directly from the CLI. |
caddy-tui apply |
Regenerate a Caddyfile from the DB, validate it, and reload Caddy via systemctl reload caddy (run with sudo). |
caddy-tui refresh-live |
Force a fresh snapshot of the live Caddyfile via the configured helper (same action as the TUI “Refresh live snapshot” option). |
caddy-tui status [--caddyfile PATH] [--diff] [--refresh-live] |
Compare the DB-rendered config with the specified Caddyfile (defaults to the last imported path). Pass --refresh-live to mirror the live file just before comparing. Exits with code 1 when drift is detected. |
caddy-tui tui |
Launch the interactive scrolling menu for importing files, editing caddy-tui blocks (add/edit/delete), refreshing the live snapshot, reviewing drift, and checking Caddy service health. |
caddy_tui/cli.pydefines a single Click group (main) plus entry points for init/import/apply/status/refresh-live/tui/validate/version. Each command simply marshals arguments then calls the relevant helper module, so you can copy the file as a starter CLI scaffold for other admin scripts.- Every command emits JSON so shell automation stays predictable—check the
statusfield and inspect payload keys for details. - New commands should live in dedicated helper modules (example:
drift.compare_caddyfile) and then be wired into the CLI via a short@main.command()block.
Use caddy-tui status --diff (typically with sudo) to verify the live /etc/caddy/Caddyfile still matches the SQLite data. The tool prints hashes, whether everything is in sync, and a truncated unified diff when requested. This is handy in CI or cron to catch manual edits. When --refresh-live is set the helper mirrors /etc/caddy/Caddyfile, computes the comparisons, and immediately purges the live snapshot from SQLite so sensitive data is not stored after the check completes.
Most distro packages restrict /etc/caddy/Caddyfile to root. Keep the TUI unprivileged and run imports as needed via:
sudo /home/alexander_skystamper_com/projects/caddy-tui/.venv/bin/caddy-tui import --caddyfile /etc/caddy/CaddyfileThe database directory honours SUDO_USER, so the sudo-run import updates the same ~/.caddy-tui/config.db that the TUI uses.
If you prefer to grant finely scoped permissions instead of full sudo caddy-tui, install the accompanying helper entry point:
sudo visudo -f /etc/sudoers.d/caddy-tui
# Allow your user to run the helper without a password
alexander ALL=(ALL) NOPASSWD: /usr/local/bin/caddy-tui-helper
caddy-tui-helper exposes mirror/install/reload plus two extra commands: status (wraps systemctl is-active caddy by default) and restart (wraps systemctl restart caddy). When the TUI hits a permission error it prints the exact helper command (e.g. sudo caddy-tui-helper mirror --source /etc/caddy/Caddyfile ...) so you can re-run it immediately or let sudoers execute it without a prompt.
The helper runner resolves the executable to an absolute path before invoking sudo, so as long as which caddy-tui-helper works in your shell you do not need to export CADDY_TUI_HELPER_BIN. Just copy that which output into the sudoers entry (alexander ALL=(ALL) NOPASSWD: /home/.../.venv/bin/caddy-tui-helper) so sudo can locate the same binary.
Set CADDY_TUI_ADMIN_ENDPOINT (defaults to http://127.0.0.1:2019/config) if your Caddy admin API listens elsewhere. The TUI/CLI will fetch live status from this endpoint on every refresh, report whether the service is up, and (when the endpoint returns text/caddyfile) mirror the running config straight into the short-lived caddy_live snapshot before diffing. The snapshot is purged immediately after comparisons so sensitive live data never lingers in SQLite.
caddy-tui tui prints a repeating block in this order:
- Result of the previous selection (e.g. import success, drift diff panel).
- A Rich table that captures database readiness, stored block count, last import path, drift summary for every snapshot source (caddy-tui, Caddyfile, live helper), and a color-coded line for the Caddy service state (green when live+in-sync, orange when live but drifting, red when down, yellow when unknown).
- A context-aware menu. Options currently include:
f– Write the privileged Caddyfile back intocaddy-tui(helper-assisted import so sudo can mirror/etc/caddy/Caddyfile).t– Write the currentcaddy-tuisnapshot over the system Caddyfile (helper-assisted install when write permissions are missing).r– Refresh the live snapshot (polls the Caddy admin API for content first, falling back to the helper mirror only when the API is unavailable).p– Print the live Caddyfile exactly as Caddy is serving it. The TUI refreshes the live snapshot, renders every block, and shows the full text inside a scrollable Rich panel so you can copy/paste or audit directives without leaving the menu.b– Show the block contents for each snapshot side-by-side (caddy-tui, Caddyfile, and live) in a dedicated table with wrapped text so you can visually compare directives.n/e/x– Add, edit, or delete blocks inside the caddy-tui snapshot. The TUI opens your$EDITOR(or nano/vi fallback), validates the single-block snippet, and persists it back to SQLite socaddy-tui tuiis the one-stop shop for CRUD.c– Reload Caddy through the helper and automatically queue a live snapshot refresh afterwards (shown when the helper reports Caddy is live).s– Restart Caddy through the helper when the status probe reports the service is down.d– Show the unified diff between the DB-rendered config and/etc/caddy/Caddyfile(available once an import path exists). The diff is shown inside a Rich panel and can be copied directly from the terminal scrollback.h– Show an in-app CLI reference table listing everycaddy-tuicommand, its usage string, and a concise description so you can jump into automation or remind yourself of available flags without leaving the interface.u– When GitHub publishes a newer release, this option appears and prints upgrade instructions with bothpip install --upgrade caddy-tuiandpipx upgrade caddy-tuiso you can follow the workflow you originally used.q– Quit the session.
Every action prints the exact helper command when elevated access is required, so you can copy/paste or add it to sudoers immediately.
caddy-tui init (or python -m caddy_tui.db) runs caddy_tui.db.init_db, which:
- Ensures
~/.caddy-tui/exists (or honours--db PATH). - Applies the SQLAlchemy schema declared in
caddy_tui/models.py. - Seeds baseline
Config+ snapshot rows so importer/exporter hooks always have a target.
The command is idempotent, so you can re-run it whenever you ship a new version or want to bootstrap a fresh environment.
caddy_tui.importer.import_caddyfilemirrors an existing Caddyfile into SQLite. Passtarget_snapshot(defaults tocaddyfile) andmirror_toto control where parsed blocks land, or passhelper_interactive=Trueto log the helper command when elevated access is required.caddy_tui.exporter.generate_caddyfilerenders thecaddy-tuisnapshot back to a canonical Caddyfile and callscaddy adaptas needed. The helper cooperates withdrift.compare_caddyfileso diffing against arbitrary files stays consistent.caddy_tui.block_editorexposesload_caddy_tui_blocks,save_caddy_tui_blocks, andparse_single_blockfor fine-grained CRUD that matches what the TUI uses.
Integrate those helpers directly from Python (no Click dependency) whenever you need to script imports/exports outside of the bundled CLI.
- Initialise the DB:
caddy-tui init. - Import the live config:
sudo caddy-tui import --caddyfile /etc/caddy/Caddyfile. - Launch
caddy-tui tui, edit sites, and review status messages (userwhenever you need a fresh live snapshot). - Apply and reload:
sudo caddy-tui apply(the CLI auto-queues a live snapshot refresh afterward, or runcaddy-tui refresh-liveto grab a short-lived snapshot of/etc/caddy/Caddyfilethat is purged once the comparison completes). - Keep an eye on drift:
sudo caddy-tui status --diff --refresh-livein CI or a nightly cron.
python3 -m venv .venv
source .venv/bin/activate
pip install -e .[test]
pytest
caddy-tui tuiRun privileged steps (import/apply/status) with sudo, but keep the interactive menu under your normal user account.
CI now handles almost everything:
- Update docs/changelog and bump
pyproject.toml+caddy_tui/__init__.py. - Commit to
mainand push. Thebuildjob runs pluspublish-to-testpypi, so TestPyPI always mirrorsmain. - Tag the release (
git tag v0.2.2 && git push origin v0.2.2). GitHub pauses thepublish-to-pypijob until thepypienvironment is approved; once approved, PyPI receives the artifacts.
Local uploads are still possible when you need to hotfix outside CI:
python -m build
twine upload dist/*The wheel exposes both CLI entry points (caddy-tui, caddy-tui-helper) and ships all helper modules for downstream automation.
- CLI deep dive for AI assistants – expand the Click command surface (status filters, automation-friendly outputs, helper diagnostics) so Copilot/Codex-style agents can treat
caddy-tuias a powerful scripting target. Document each addition in the changelog to keep the roadmap transparent.