Commit Graph
31 Commits
Author SHA1 Message Date
Claude 32240e18f9 Fix ensure_coturn_user leaving callers in the wrong directory
install_coturn (services/coturn.sh) cd's into $DOCKER_DIR/coturn and never
restores the caller's original working directory. A consumer that chain-
installs coturn mid-flow (e.g. asterisk.sh, already cd'd into its own
install directory) returned from ensure_coturn_user still sitting in
coturn's directory, then went on to write its own docker-compose.yml/.env
there instead of its own directory — clobbering coturn's compose file and
leaving the consumer's directory without one. The consumer's later
`docker compose up --build` then failed with "Dockerfile: no such file or
directory", since the Dockerfile was correctly in the consumer's directory
but the misplaced compose file (and the build) were not.

ensure_coturn_user now saves/restores the caller's cwd around the
install_coturn call, fixing this for every consumer (asterisk, mattermost).

Also fold cloud-init.sh's contents into a collapsible README section so
it's copy-pasteable straight from the repo instead of requiring a separate
file download.
2026-08-10 03:10:26 +00:00
Claude 9e06ed4b83 Bake cross-service port collision avoidance into every service script
With 70+ services sharing a handful of common default ports (emby and
jellyfin both default to 8096, changedetection and frigate both default
to 5000, arm and nextcloud both default to 8080...), nothing previously
checked whether a service's default port was actually free on the host.
Whichever service installed second would silently write a compose file
claiming an already-held port, only failing at `docker compose up` time.

Adds two shared helpers to lib/common.sh:
- port_in_use PORT [PROTO] — true if something's already listening
- find_free_port VARNAME START [PROTO] — scans upward, writes back the
  first free port

Every service that publishes a fixed host port now scans before writing
docker-compose.yml, on every install (not just when adding an explicit
additional instance). On a normal single-install host this is a silent
no-op; it only changes behavior when something else already holds the
port.

- The 19 services already given multi-instance support this session had
  their port scan moved out of the "add instance" branch to run
  unconditionally, since the same collision risk exists on a plain first
  install.
- 20 more services with previously-hardcoded ports gained scanning for
  the first time: archivebox, arm, calibre-web, changedetection,
  drum-rhythm-game, gatus, n8n, nextcloud, onlyoffice, stirling-pdf,
  uptimekuma, portainer, iopaint (both GPU/CPU compose branches), koha
  (paired), syncthing (paired), wg-easy (paired, plus WG_PORT env so
  generated peer configs keep the right Endpoint), homeassistant
  (bridge-mode only — host mode can only warn), frigate and
  frigate-audio (multi-port stacks, moved together).
- caddy.sh is the deliberate exception: 80/443 stay fixed and only warn
  on collision, since silently moving Caddy itself would leave nothing
  listening where any client actually looks.
- authelia.sh needs no change — it has no published host port at all.
- Every service's standalone bootstrap fallback (sudo bash services/x.sh
  with no sibling files) got the same two helpers duplicated into its
  stub block, matching how every other shared helper is already handled
  there.

Documents the full pattern in CLAUDE.md's new "Port collision avoidance"
section, including the quoted-heredoc/backtick-escaping gotcha and the
network_mode:host limitation (can only scan ports the app takes as a
configurable env var).

Verified via bash -n on every changed file, plus functional runs seeding
occupied ports for each collision shape used here (single, paired,
multi-port stacks) and confirming the scan/shift and generated
compose/README output are correct — including the emby/jellyfin,
nextcloud/arm, and frigate/changedetection collision scenarios that
originally motivated this.
2026-08-09 23:55:49 +00:00
Claude ad38b96cfe Make swapfile a default for every install, not just Asterisk droplets
Extracts the swapfile logic out of services/asterisk.sh (previously
DigitalOcean-droplet-gated) into lib/common.sh's ensure_swapfile() —
provider detection was never really the point, the actual condition
that matters is "modest RAM, no swap yet," which applies just as much
to a non-DO VPS running several Docker services at once as it did to a
single-purpose droplet.

- lib/common.sh: new ensure_swapfile(), same fallocate/mkswap/fstab/
  swappiness logic as before, threshold raised from 2048MB to 4096MB
  (a 4GB box running a full service stack is exactly the case that
  motivated this change — the old threshold would have skipped it).
- services/base.sh: calls it unconditionally so every install gets the
  same check regardless of which other services get chosen.
- services/asterisk.sh: swapfile call is no longer gated behind
  IS_DO — calls the shared helper directly. Kept a standalone-mode
  stub (same pattern as this file's other stubbed helpers) so
  `sudo bash asterisk.sh` with no base.sh in the picture still gets
  it. Idempotent either way: a box that already has swap, or already
  got it from base.sh earlier in the same run, no-ops immediately.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TBtExJcqxnokyZZKmphdug
2026-08-09 18:13:37 +00:00
Outis 6151edeeac Merge pull request #265 from outis1one/claude/droplet-capacity-assessment-s2voix
Claude/droplet capacity assessment s2voix
2026-08-08 17:17:43 -04:00
Claude cf3d4bf6de Extract coturn into a shared service; add Mattermost multi-instance support
Asterisk and Mattermost each used to embed their own dedicated coturn
container (network_mode: host), and their default relay port ranges
overlapped by ~100 UDP ports — running both on one box meant a
coin-flip over which service's active call lost its media relay.

- services/coturn.sh: new shared TURN/STUN relay, one instance for
  every consumer instead of one each. Runs --lt-cred-mech with a
  SQLite user database (not --use-auth-secret — coturn doesn't
  support both auth mechanisms on one instance at once, confirmed via
  coturn's own upstream docs/issues) so each consumer gets its own
  dedicated username/password without stepping on any other's.

- lib/common.sh: ensure_coturn_user() — chain-installs coturn.sh on
  first need (same declare -F guard pattern as the existing
  asterisk -> security-dashboard chaining) and registers/reuses a
  per-consumer credential, mirroring configure_caddy_for_service's
  out-param convention.

- services/asterisk.sh: _asterisk_write_compose gains a
  USE_EMBEDDED_COTURN flag. New installs use the shared service;
  existing installs keep their dedicated coturn exactly as-is on
  every "update" (detected from the existing compose file before
  regenerating it, so a rebuild can never silently drop the container
  its own .env TURN_PASSWORD still points at) and only switch on an
  explicit "fresh" reinstall, with a warning first.

- services/mattermost.sh: same embedded/shared coturn handling, plus
  genuine multi-instance support (separate dir/containers/DB/ports per
  instance, auto-scanned free ports for extras) for real isolation
  between groups, as opposed to Team Edition's built-in Teams feature.
  Calls plugin TURN config switched from the HMAC "TURN Static Auth
  Secret" field to the verified "ICE Servers Configurations" JSON
  field, which accepts the same fixed username/credential shared
  coturn issues. Also fixes a latent bug found while adding proper
  update-mode detection: DB_PASS/MM_SECRET were regenerated on every
  single rerun with no existing-install check at all, silently
  breaking Postgres auth on any reinstall.

- CLAUDE.md: documents the ensure_coturn_user pattern (including the
  auth-mechanism constraint and the embedded-coturn migration-safety
  rule) for any future service that needs TURN.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NQkdAn3iG5A4WoqU9FHMaN
2026-08-04 16:32:08 +00:00
Claude 52b4606ab5 Add optional per-service companion doc files to write_readme
Any services/<name>.md next to services/<name>.sh gets appended to the
generated ~/docker/<name>/README.md automatically, with no changes needed
to the calling install_<name>() function. Keeps install-time-invariant
walkthroughs (third-party UI linking steps, multi-account setup) out of
the compose/README heredocs, which should stay focused on values chosen
during install.

Add services/traccar.md as the reference example: documents the
per-user Connections-tab linking needed for ntfy notifications to reach
non-admin accounts, and why TEST CHANNELS can return success without
actually sending anything (Traccar's SMS notificator silently no-ops
when the logged-in user's Phone field is empty).
2026-08-04 12:14:32 +00:00
Claude 526cb4a623 Fix prompt_yn silently discarding its default on empty input
Confirmed live: pressing Enter on any "(y/n): y"-style prompt set the
variable to an empty string instead of the stated default, since prompt_yn
had no ${response:-$default} fallback (prompt_text already had one). Every
downstream [[ "$VAR" =~ ^[Yy]$ ]] check treated "just press Enter" as no.
Also shows the default value in the prompt text itself for both helpers,
since neither displayed it before.
2026-07-23 16:14:19 +00:00
Claude 861b4478a8 Fix Authelia protection being dead code — reverse_proxy ran before the auth check
Both Caddy site block generators (the shared configure_caddy_for_service
helper, and asterisk-digital-ocean.sh's own inline template) wrote
reverse_proxy before the forward_auth/import authelia block. Caddy
doesn't reorder repeats of the same directive within a block — forward_auth
and reverse_proxy are the same directive family internally, so they run in
the order written. With reverse_proxy first, it handled and terminated
every request immediately; the auth check written after it never ran at
all. Full bypass on every domain using either generator with Authelia
protection, regardless of how correct the Authelia access_control rules
themselves were — confirmed live against a config that was otherwise
completely correct (default_policy: deny, explicit wildcard rule covering
the affected domain).

Affects every service that's ever passed `import authelia` or a
forward_auth block through configure_caddy_for_service (asterisk.sh,
wolf-pair.sh, and any future caller), plus asterisk-digital-ocean.sh's
own site block.

Moved the auth block before reverse_proxy in both generators.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015X1jRGHwrvovz2qkhKfDZi
2026-07-20 19:29:26 +00:00
Claude 9f2a3ddfd9 Fix: closing the web admin port to the internet also blocked Caddy
Confirmed live: a bare `ufw delete allow <port>` closes it on every
interface, including the caddy_net bridge — Caddy's own request to
host.docker.internal:PORT is ordinary INPUT-chain traffic as far as
UFW is concerned, not something that bypasses it just because the
source is a local container. Closing the port outright silently took
Caddy's reverse-proxy path down with it.

Added ufw_allow_from_caddy_net() to scope the port to caddy_net's own
subnet instead of leaving it fully closed — reachable from Caddy,
still closed to the public internet. Wired into both
asterisk-digital-ocean.sh and asterisk.sh in place of the plain
delete.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015X1jRGHwrvovz2qkhKfDZi
2026-07-20 18:24:02 +00:00
Claude 2330b47dd9 Add shared helpers: Caddy-fronting signal, safe UFW enable
configure_caddy_for_service() previously gave callers no way to know
whether Caddy actually ended up fronting the service, or whether that
was local (reachable only over host.docker.internal) vs remote
(needs network access to this host). Services that also open a host
firewall port for the same thing had no way to correctly skip that
when Caddy is the only intended way in. Now sets
CADDY_SERVICE_CONFIGURED/CADDY_SERVICE_MODE out-params after each
exit point.

Added ensure_ufw_enabled(): flips UFW from inactive to active (no
service in this repo has ever done this — ufw allow rules just sat
unenforced). Always allows SSH first, reading the real port from
sshd_config in case it's non-default, so this can't lock out the
session running the installer. No-ops if UFW is already active.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015X1jRGHwrvovz2qkhKfDZi
2026-07-20 18:20:23 +00:00
Claude b6046b3ca1 Ensure caddy_net exists via require_docker instead of per-service
43 services declare caddy_net as "external: true" in their compose
file, meaning they require it to already exist — but only Caddy's own
compose file actually creates it (authelia.sh was the sole exception,
with its own inline check-and-create). Installing any of the other 42
before Caddy fails outright with "network caddy_net declared as
external, but could not be found."

Adds ensure_caddy_network to lib/common.sh, called from require_docker
(which every install_* function already calls first), so the network
exists regardless of install order without touching each service file.
Removes authelia.sh's now-redundant duplicate of the same check.

Also documents in CLAUDE.md that network_mode: host services (asterisk/
asterisk-do) need host.docker.internal, not localhost, when Caddy
reverse-proxies to them — the fix from the previous commit.
2026-07-20 04:14:56 +00:00
Claude 6c48bdc707 Fix Caddy reverse-proxy target for host-network services
Caddy runs in its own container on the caddy_net bridge network, so
"localhost" in a Caddyfile site block resolves to Caddy's own
container — never the host, and never a sibling container. That
broke every reverse proxy pointed at a network_mode: host service
(confirmed live with asterisk-do's web admin): once nothing else
(like a forward_auth redirect) intercepted the request first, Caddy
couldn't actually reach the upstream.

- services/caddy.sh: add extra_hosts so host.docker.internal resolves
  inside the Caddy container (Linux Docker needs this explicitly —
  it's automatic only on Docker Desktop).
- lib/common.sh's configure_caddy_for_service: bare-port upstreams
  (its documented "host-network service" case) now target
  host.docker.internal instead of localhost.
- services/asterisk-do.sh: its self-contained Caddy block (doesn't go
  through configure_caddy_for_service) gets the same fix for local
  Caddy, and now correctly targets the droplet's public IP instead of
  localhost for the remote-Caddy snippet case, which had the same bug.

services/asterisk.sh needs no direct change — it already goes through
configure_caddy_for_service, so it inherits the fix.
2026-07-20 04:11:24 +00:00
Claude 8ee35e1995 Add prompt_reinstall_mode helper and document the update/fresh/cancel convention
Replaces the y/n "update in place?" prompt in asterisk.sh/asterisk-do.sh
with an explicit r/f/c choice — (r)einstall in place, (f)ull install,
(c)ancel — defaulting to cancel on a bare Enter (or Ctrl-D) instead of
falling through to a destructive full reinstall.

Adds prompt_reinstall_mode to lib/common.sh (plus matching standalone
stubs in both asterisk scripts for when they run without the full repo)
and documents the convention in CLAUDE.md: any service with a persistent
install directory should offer this choice on rerun instead of re-asking
every prompt just to pick up a script fix.
2026-07-20 01:23:22 +00:00
Claude cb8ff3f4f0 Fall back to a Caddy restart when reload fails (admin API is off)
Confirmed on a real deployment: the template Caddyfile ships with
"admin off" (deliberate — no local API attack surface), which means
`caddy reload` can never work, since it depends on that same admin
endpoint. Every Caddyfile-editing code path was silently failing to
apply changes as a result — `docker logs caddy` showed
"admin endpoint disabled" and the reload command errored, but the
Caddyfile edit itself (which doesn't need the admin API) had already
succeeded, leaving the running config stale until something else
happened to restart the container.

Fixed in the two places that actually matter here: lib/common.sh's
configure_caddy_for_service (used by asterisk.sh and most other
Caddy-fronted services in the full repo) and asterisk-do.sh's own
self-contained Caddy block (both the standalone-bootstrap stub and the
main path). Each now tries the lightweight reload first — harmless,
and still works if a box ever has the admin API enabled — then falls
back to `docker restart caddy` if that fails, rather than leaving an
edited-but-unapplied Caddyfile.

Not fixed: the same duplicated pattern in ~35 other service files that
carry their own standalone-bootstrap copy of this logic. Those only
matter for the rare single-file standalone execution path for each of
those specific services and are unrelated to tonight's actual issue —
out of scope here.

Verified: full regression run on both asterisk.sh and asterisk-do.sh
still completes cleanly end to end.
2026-07-19 17:43:52 +00:00
Claude d374f4983a Add SSH Host alias management (base wizard, standalone service, docs)
Lets 'ssh <alias>' connect directly to user@host instead of retyping it —
especially useful once machines are reachable over NetBird/VPN and have
IPs that aren't worth memorizing.

- lib/common.sh: ssh_config_path/add_ssh_host_alias/list_ssh_host_aliases/
  remove_ssh_host_alias helpers, operating on the invoking user's own
  ~/.ssh/config (not root's) with correct 700/600 permissions and ownership
- base.sh: after SSH key import, optionally add one or more Host aliases
  interactively as part of the base install
- services/ssh-config.sh: new standalone service (sudo ./setup.sh ssh-config)
  to list/add/remove aliases any time, independent of base install; follows
  the existing non-Docker standalone-bootstrap pattern (see crowdsec.sh)
- setup.sh: ssh-config never shows [installed] since it's a repeatable
  management tool, not a one-time install
- README: new 'SSH Host aliases' section, base row and wizard-flow step 1
  updated, ssh-config added to the extras group and copiable service list

Verified end-to-end with a test harness: add with defaults, add with a
custom user/port, list (correct numbering), and remove-by-name preserving
the other entry and file permissions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQJBvqzXeyuhhAcAA3Q5Wq
2026-07-02 16:20:22 +00:00
Claude 83293f61cd common.sh: install Docker via direct apt steps instead of get.docker.com
The get.docker.com convenience script internally wraps every step in
'sudo -E sh -c ...'. On minimal/cloud Ubuntu images that never installed
the sudo package (common when operating purely as root), those internal
sudo calls silently fail while the outer script still exits 0 — apt never
actually runs, but no error surfaces. require_docker already runs as root,
so there's no need for sudo at all.

Replaced it with Docker's documented apt-repo steps run directly: add the
keyring, add the repo (with architecture/codename detected via dpkg and
os-release), apt-get install docker-ce + compose plugin, enable the
service. Real apt/curl/systemctl failures now propagate and print to the
terminal instead of being masked by the wrapper script.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQJBvqzXeyuhhAcAA3Q5Wq
2026-07-01 16:26:16 +00:00
Claude 01979c52f0 fix: SITE_DOMAIN not pre-filling FQDN prompts after wizard
Three fixes:
1. configure_caddy_for_service: remove the '!= example.com' filter that
   silently dropped any valid domain matching that string; now any non-empty
   SITE_DOMAIN is used as the default subdomain suggestion
2. load_site_config: trim leading/trailing whitespace from key and val so
   hand-edited .config files with extra spaces still parse correctly
3. setup.sh: call load_site_config after the site wizard saves so the
   in-memory values are guaranteed fresh for all subsequent service installs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQJBvqzXeyuhhAcAA3Q5Wq
2026-06-28 15:07:07 +00:00
Claude e167f98370 common.sh: fall back to /usr/bin/docker when PATH is restricted under sudo
command -v may miss the binary if sudo stripped PATH; check the canonical
apt install location directly as a fallback before reporting failure, and
use the same fallback when printing the installed version.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQJBvqzXeyuhhAcAA3Q5Wq
2026-06-28 14:37:03 +00:00
Claude 64776731fb common.sh: fix Docker install failing silently in piped execution
Two bugs in require_docker:
1. apt post-install hooks (needrestart etc.) block on stdin which is
   at EOF when running via pipe; DEBIAN_FRONTEND=noninteractive skips them
2. bash's command hash table doesn't pick up a newly installed binary;
   hash -r flushes it so command -v docker finds /usr/bin/docker
Also moved usermod and success log after the binary check so [OK] only
prints when docker is actually reachable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQJBvqzXeyuhhAcAA3Q5Wq
2026-06-28 12:18:10 +00:00
Claude 5b340552c3 Replace CADDY_REMOTE_HOST with explicit CADDY_MODE in site config
The old CADDY_REMOTE_HOST variable was confusingly named — it sounded like
the Caddy server's address but actually stored this machine's IP (so Caddy
knew how to reach services here). Services don't need to know where Caddy
is; they only need to know whether to write a Caddyfile or create a snippet.

Changes in lib/common.sh:
- Add CADDY_MODE=local|remote|none as the authoritative setting
- load_site_config: parse CADDY_MODE; if old CADDY_REMOTE_HOST present and
  CADDY_MODE unset, infer CADDY_MODE=remote (backward compat)
- save_site_config: write CADDY_MODE, drop CADDY_REMOTE_HOST output
- configure_caddy_for_service: use CADDY_MODE for mode detection; for remote
  snippets auto-detect this machine's primary IP via hostname -I instead of
  requiring a stored value (still falls back to CADDY_REMOTE_HOST if present
  in an old .config)

Changes in setup.sh (run_site_configure wizard):
- Replace free-text "Caddy remote host" prompt with a 3-choice menu:
  [1] This machine  [2] Remote machine  [3] None/skip
- Existing installs with CADDY_REMOTE_HOST pre-select option 2 automatically

https://claude.ai/code/session_01S7UecmQRG6CKTYPoBqbVLj
2026-06-10 00:49:41 +00:00
Claude ec3f9bfd3f Add remote Caddy support — generate snippet files when Caddy is on another host
New site config key: CADDY_REMOTE_HOST (set via 'sudo ./setup.sh configure').
When set, configure_caddy_for_service operates in "remote" mode instead of
writing to a local Caddyfile:
- Upstream uses CADDY_REMOTE_HOST:PORT (host IP, not container name)
- Snippet saved to ~/docker/caddy-snippets/<subdomain>.caddy
- User is shown scp/rsync commands to copy it to the Caddy machine

Three modes in configure_caddy_for_service (lib/common.sh and inline stubs):
  local:  ~/docker/caddy/ exists → write Caddyfile + reload (existing behavior)
  remote: CADDY_REMOTE_HOST set → save snippet, print copy instructions
  none:   neither configured → silent return (unchanged)

All 31 service standalone bootstrap stubs updated with the new logic.
CADDY_REMOTE_HOST global added to all 42 standalone bootstrap sections.
setup.sh configure now prompts for CADDY_REMOTE_HOST with a clear explanation.
wolf.sh: add missing stubs (configure_caddy_for_service, write_readme,
  prompt_yn, ensure_docker_dir_ownership) and the Authelia/Caddy/start calls
  that were missing from the install function.

https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
2026-06-09 00:28:30 +00:00
Claude e9f05cc127 Use stored SITE_DOMAIN as default in Caddy domain prompt
lib/common.sh: configure_caddy_for_service now pre-fills the domain prompt
with $DEFAULT_SUBDOMAIN.$SITE_DOMAIN when a site domain has been configured
(setup.sh configure / ~/docker/.config). No more typing the full domain for
every service — just press Enter to accept the default.

services/mattermost.sh: remove redundant custom Caddy/domain block added in
the previous commit. MATTERMOST_SITE_URL is already computed from SITE_DOMAIN
before configure_caddy_for_service is called, so the simple call is sufficient.

https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
2026-06-08 18:41:46 +00:00
Claude 9c52ac22e1 Fix Caddy routing: use container:port via caddy_net (DoTheEvo pattern)
Undo the host.docker.internal approach from the previous commit — proper
Docker networking routes Caddy to services by container name on the shared
caddy_net, not via the host gateway.

- lib/common.sh: configure_caddy_for_service now accepts either a plain
  port number (localhost:PORT fallback) or container:port (preferred).
  The Caddyfile entry uses the container name for direct Docker DNS routing.
- services/caddy.sh: remove extra_hosts hack; update Caddyfile template
  comments to show container_name:port format
- All service files: update configure_caddy_for_service calls to pass
  container_name:internal_port (e.g. "filebrowser:80", "mealie:9000").
  Services using network_mode:host keep plain port numbers.
- tools/manage_users.sh: new FileBrowser user-management script (deployed
  to ~/docker/filebrowser/ during installation). Manages users via the
  FileBrowser REST API: list, add, delete, passwd, scope, info commands.
  Documents username format (letters/numbers/hyphens/underscores),
  password rules (min 8 chars, letter + number required), and scope path
  convention relative to /srv (= FB_PATH on the host).

https://claude.ai/code/session_01UZus2Q9gNTfUdqSMrhuX29
2026-06-07 22:12:01 +00:00
Claude 56bec03c9b Fix Caddy routing and add caddy_net to all Docker services
- lib/common.sh: fix broken reverse_proxy target — localhost inside Caddy's
  container is the container's own loopback, not the host; change to
  host.docker.internal so proxied ports are actually reachable
- services/caddy.sh: add extra_hosts host.docker.internal:host-gateway so
  the above resolves correctly; create caddy_net bridge network in Caddy's
  own compose so other services can reference it as external; update all
  Caddyfile template comments and README examples to match
- services/filebrowser.sh: update image tag from deprecated :s6 to :latest;
  remove non-functional PUID/PGID env vars (filebrowser/filebrowser does not
  honour them); add configure_caddy_for_service call; add caddy_net
- services/ntfy.sh: add configure_caddy_for_service call; add caddy_net
- services/portainer.sh: add configure_caddy_for_service call; add caddy_net
- services/frigate-notify.sh, watchtower.sh: add caddy_net for
  container-to-container comms (frigate, ntfy) without a Caddy call
- All remaining web-facing Docker services: add caddy_net network block to
  docker-compose and CADDY_NET to .env where applicable; services using
  network_mode: host (wolf-pair, lyrion) have the top-level block only

https://claude.ai/code/session_01UZus2Q9gNTfUdqSMrhuX29
2026-06-07 22:02:26 +00:00
Claude 8aa1d4552d Auto-install Docker, add USB drive docs, improve whitelist UI
- lib/common.sh: require_docker now installs Docker CE + Compose plugin
  via get.docker.com instead of erroring out if Docker is missing.
  Also adds the calling user to the docker group automatically.

- README.md: fix 'tells you how to install Docker' → 'installs Docker
  automatically'; add full USB drive usage section (mount, fstab,
  DOCKER_DIR config, moving existing data, tips).

- services/minecraft.sh: replace single-source whitelist import with the
  multi-source UI from the updated setup-minecraft.sh — collects players
  from the current instance, saved backup files, and other servers' backups;
  assigns letters to each source so you can import by letter (all from that
  source) or by number (specific player).

https://claude.ai/code/session_017WJtGcE5jjerAQCUBWUE3H
2026-06-04 01:37:58 +00:00
Claude 92e8866f13 pip_user_install: capability probe instead of version check
Probe for --break-system-packages support once (pip --help, cached in
_PIP_HAS_BSP) rather than comparing Ubuntu version numbers. Works on any
pip >= 22.3 regardless of distro; older pip (Ubuntu 22.04, pip 22.0)
falls back to --user only, which is correct there since PEP 668 isn't
enforced on 22.04 anyway.

The flag name is scary but harmless with --user: installs go to ~/.local/
which apt never manages regardless.

https://claude.ai/code/session_01Y4dMKtkqkpvmgDKoRdzhTG
2026-06-04 00:05:36 +00:00
Claude e8667f02cf pip_user_install: add --break-system-packages on Ubuntu 24.04+
pip3 install --user alone does not reliably bypass PEP 668 in all 24.04
environments. --break-system-packages (pip 22.3+) is the correct override.
Flag is only added when ubuntu_version_ge "24.04" so it does not run on
Ubuntu 22.04 where pip 22.0 ships and the flag is not yet supported.

https://claude.ai/code/session_01Y4dMKtkqkpvmgDKoRdzhTG
2026-06-04 00:01:57 +00:00
Claude ef08fef540 Add OS detection; surface version in header; centralise pip installs
lib/common.sh:
  - detect_os(): reads /etc/os-release into OS_DISTRO, OS_VERSION,
    OS_CODENAME globals (exported, auto-called on source)
  - ubuntu_version_ge(): numeric version comparison helper
  - pip_user_install(): central wrapper for pip3 install --user so any
    future version-specific flags are in one place

setup.sh:
  - Both header banners now show detected OS line (e.g., "Ubuntu 24.04 (noble)")
  - First-run path warns if not Ubuntu or < 24.04

services/sky-cam.sh, services/sync-cc.sh:
  - Replace inline pip3 invocations with pip_user_install helper

https://claude.ai/code/session_01Y4dMKtkqkpvmgDKoRdzhTG
2026-06-03 23:49:51 +00:00
Claude 56d2f9e85b Add site-wide defaults: timezone, domain, Caddy network
Introduces a one-time configuration wizard (sudo ./setup.sh configure)
that stores SITE_TZ, SITE_DOMAIN, and SITE_CADDY_NET in ~/docker/.config.
Every service now uses these as prompt defaults so the user types common
values once instead of re-answering the same questions for each service.

- lib/common.sh: load_site_config / save_site_config; auto-loads on source;
  backward-compat BASE_DOMAIN alias kept for old .config files
- setup.sh: run_site_configure wizard; first-run offer after base install;
  `sudo ./setup.sh configure` command to update defaults at any time
- 14 services: TZ_VAL now honours SITE_TZ, falling back to /etc/timezone
- 3 inline-heredoc services (filebrowser, homeassistant, ntfy): same fix
- authelia: SITE_TZ/SITE_DOMAIN as prompt defaults; SITE_CADDY_NET replaces
  hardcoded caddy_net throughout (env, compose patch, network creation)
- minecraft, frigate-audio: simplify BASE_DOMAIN read to use SITE_DOMAIN
- sky-cam: SITE_TZ as default for timezone prompt

https://claude.ai/code/session_01Y4dMKtkqkpvmgDKoRdzhTG
2026-06-03 22:13:59 +00:00
Claude 4a37b3622d v0.9.6: README generation + migrate authelia + 5 services
- lib/common.sh: add write_readme helper. Every module now writes a README.md
  into its ~/docker/<service>/ folder (self-documenting service folders).
- services/authelia.sh: SSO + 2FA portal, ported from the authelia-setup repo +
  the monolith's working block (secrets + Argon2 hash generation, caddy_net,
  Caddyfile forward-auth snippet + portal block, README). Guards against
  clobbering an existing install.
- services/{filebrowser,ntfy,uptimekuma,portainer,watchtower}.sh: mechanical
  migrations from the monolith, each with a README. Ports 8085/8090/3001/9443/—.

All pass bash -n; ./setup.sh --list shows them under homelab; dry-run run-one
exits 0 for each with real commands guarded.

https://claude.ai/code/session_017eA2qqq9jfF2tNtpUYL8vK
2026-06-03 17:01:14 +00:00
Claude d7b9f935c2 Add modular setup framework (lib + services + dispatcher) and glow
Introduce the modular post-install structure chosen for reconciling 'one
source of truth' with 'run just the service I want':

- lib/common.sh: shared helpers (logging, prompts, ownership, Caddy wiring) and
  a service registry. Single implementation of each helper.
- setup.sh: dispatcher — interactive menu, run-one (./setup.sh <name>), --list,
  --dry-run, --unattended. Sources lib + services/*.sh (self-registering).
- services/base.sh: essential CLI packages incl. glow (Charm apt repo).
- services/homeassistant.sh: first migrated service (bridge/host networking,
  trusted_proxies, Caddy integration).
- MODULAR.md: architecture, how to add a module, migration status.
- Groups: base/homelab/gaming/backup. Gaming group makes this a base for
  homelab OR gaming boxes.

Also add glow as a default app to the live -crowdsec scripts' essential
packages so it's installed today regardless of entry point.

Verified: bash -n on all new files; ./setup.sh --list groups services;
dry-run run-one routes correctly.

https://claude.ai/code/session_017eA2qqq9jfF2tNtpUYL8vK
2026-06-03 12:57:12 +00:00