Rework `rsmon-worker install` so one host can run several isolated
workers and so the installer consumes the full worker env-var set.
- main.go now loads .env before dispatching management commands, so
install/deploy read the same environment as the runtime.
- New --name flag installs a co-located worker as rsmon-worker-<name>
with its own binary (/usr/local/bin/rsmon-worker-<name>), config
(/etc/rsmon-worker-<name>), data dir (/var/lib/rsmon-worker-<name>),
and systemd unit. Named instances require an explicit WORKER_PORT.
- Configuration is resolved flags > --env-file > process env/.env
(godotenv) > defaults; the resolved set is written as a stable,
systemd-safe 0600 env file.
- WORKER_LOGIN/WORKER_PASSWORD default to a generated admin password
(printed once) when both are unset; XOR is rejected.
- The generated unit is now hardened (After=docker.service, CAP_NET_RAW,
ProtectSystem=full, ReadWritePaths=data dir) and parameterized by
instance; the Docker unit is namespaced by instance too.
- install creates the data + config directories and prints a summary
(unit, binary, env file, data dir, console URL, generated password).
- New flags: --name, --host, --port, --login, --password/--password-file.
- Tests: resolvePaths, validateInstanceName, resolveInstallEnv
precedence/XOR/port-required, renderEnvFile, validateEnvValue, plus
named-instance unit assertions. End-to-end verified by installing and
removing a throwaway --name instance.
- docs/install.md documents the tool, config sources/precedence,
single- and multi-instance flows, the exact actions performed, the
generated unit, options, and uninstall.
parseBoolFalseDefault returned false only for explicit falsy literals
(including the empty string) and true otherwise, but the ComposeEnabled
call site negated it. The double error cancelled for an unset variable
(empty -> false -> !false -> true) but inverted every explicit value:
WORKER_COMPOSE_ENABLED=true disabled the subsystem while =false enabled
it. Rename the helper to parseBoolTrueDefault, drop the empty string
from the falsy set so unset stays on, and drop the negation. Add a
table-driven regression test pinning unset/true/1/yes -> on and
false/0/no/off (any case, trimmed) -> off.
Add an internal/compose package that discovers Compose projects via
`docker compose ls` + `docker ps` labels (grouped by
com.docker.compose.project/service) and enriches each container with
`docker inspect` ports/mounts and Traefik router labels. Management
runs `docker compose` in each project's working directory for
up/down/stop/restart/pull plus per-service variants and log tails.
Wire it into the webapp: a 60s ComposeRefresher (constructed in New,
started in Start, stopped in Close), a /compose list + detail + logs
HTML surface, and /web/api/compose/* JSON endpoints (list, detail,
logs, project/service lifecycle). Browser lifecycle POSTs are
session+CSRF protected; the /web/api/* variants accept HTTP basic auth.
WORKER_COMPOSE_ENABLED defaults on (false to disable).
Tests cover discovery parsing/grouping/traefik/summary, the action
allowlists, and the full handler surface (list/detail/logs HTML+API,
CSRF enforcement, disabled/unknown-action rejection, audit writes,
success+failure exec paths) via a stub Docker binary.
- reconnect safely after token rotation and retry leased results
- reject malformed tasks and remove production cluster debug mutation
- validate environment files and require immutable container images
BREAKING CHANGE: Docker install, deploy, and Compose now require an
immutable repository@sha256 image reference.