Files
automations/deployments/ergo/.env.example
T
57_WolveandClaude Opus 5 4cbd808eb1 feat(ergo): add Ergo IRC server deployment with ergoctl admin CLI
New deployments/ergo/: the Ergo IRC server behind Caddy for Let's Encrypt TLS
and the IRC-over-WebSocket endpoint. Docker rather than a native OpenRC/systemd
service, because Alpine's apk ergo trails upstream (3.24 ships 2.18.0 against a
2.19.1 security release) and Debian/Alma package it at all -- so native would
mean three install paths plus a per-distro ACME client.

Both containers run with network_mode: host. IRC bans, throttling and cloaking
key on the client's address, and Docker's userland proxy would hide every IPv6
client behind the bridge gateway; host mode also makes the repo's INPUT firewall
genuinely govern 80/443/6697. Caddy reaches Ergo over loopback, which is what
lets Ergo honour X-Forwarded-For (proxy-allowed-from defaults to localhost) and
mark web sessions secure.

- deploy.sh generates ircd.yaml ONCE from the pulled image's own default.yaml
  (version-matched), rewriting the listeners/websockets blocks wholesale rather
  than patching lines, then asserts hard post-conditions and validates with
  `ergo run --smoke` in a throwaway container before anything starts.
- update.sh: pinned vX.Y.Z tags, GHSA + "### Security" release-note policies,
  pre-flight against the new image, user NOTICE + grace, stop-consistent DB
  snapshot, health check (IRC-level, not a bare TCP connect) and rollback that
  restores the DB only when the schema actually moved. Compatibility-break
  releases are held for review. certsync copies Caddy's cert pairwise-atomically
  and verifies the fingerprint served on 6697 after the rehash.
- ergoctl: status/users/logs, validated edit+rehash, oper add/passwd/rm,
  moderation, backup/restore, cert and update passthrough. Talks IRC to the
  loopback listener over bash /dev/tcp and strips control characters from
  replies.
- Ergo runs as a non-root system user, read-only rootfs, all caps dropped;
  Caddy keeps only NET_BIND_SERVICE, with admin API and HTTP/3 off.

Reviewed adversarially across six lenses; 20 confirmed findings fixed, notably
a dead SIGHUP fallback (`rc=$?` after an `if` is always 0), several `set -e`
aborts from non-total pipelines, a release-list cache that only ever populated
in a subshell, and re-runs that used shell defaults instead of the deployed
.env. Verified locally: bash -n, LF endings, the ircd.yaml render against the
real 2.19.1 template in both PLAINTEXT modes, the yaml/oper/version/env helpers,
and the IRC client against a fake server (registration, oper, rehash success and
400-failure, control-character stripping, server-down paths). Not yet exercised
on a Docker host: the containers themselves, ACME issuance and cert sync.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 13:19:31 -05:00

64 lines
3.1 KiB
Bash

# Copy to .env and fill in. docker compose picks .env up automatically.
# deploy.sh seeds this file on first run and never rewrites it afterwards.
#
# NOTE: values are NOT quoted and must not contain a dollar sign (Compose
# interpolates .env). Secrets never live here: the admin oper password is in
# secrets/admin.pass (0600) and only its bcrypt hash is in ircd/ircd.yaml.
# --- Identity -----------------------------------------------------------------
# Public hostname (lower-case). Becomes Ergo's server.name, the Let's Encrypt
# cert subject, the Caddy site and the allowed websocket origin. Immutable after
# the first deploy (Ergo refuses to rehash a changed server name).
ERGO_DOMAIN=irc.example.com
# Email for Let's Encrypt registration / expiry notices.
ACME_EMAIL=admin@example.com
# IRC network name (ISUPPORT NETWORK=, welcome line). Letters, digits, . _ -
# only -- no spaces. deploy.sh defaults it to ERGO_DOMAIN.
NETWORK_NAME=irc.example.com
# --- Image --------------------------------------------------------------------
# ghcr.io tags carry the leading 'v' (v2.19.1). deploy.sh pins the newest
# release on first deploy; update.sh bumps this when it updates. Never pin
# 'stable'/'latest' here -- those are separate builds and defeat rollback.
ERGO_IMAGE=ghcr.io/ergochat/ergo
ERGO_TAG=v2.19.1
# uid:gid Ergo runs as inside the container = the host's `ergo` system user
# (created by deploy.sh; owns ./ircd). Filled in by deploy.sh.
ERGO_UID=
ERGO_GID=
# --- Listeners ----------------------------------------------------------------
# 0 (default): plaintext 6667 on 127.0.0.1 only (healthcheck + ergoctl).
# 1: ALSO serve plaintext IRC on :6667 publicly, with STS advertised so capable
# clients pin TLS. Credentials sent over 6667 cross the internet in clear.
# Chosen at the FIRST deploy: it selects a listener in ircd/ircd.yaml, which
# deploy.sh never rewrites. To change it later, edit that listener with
# 'ergoctl edit', set the value here, 'ergoctl restart', then re-run deploy.sh
# so the host firewall matches.
PLAINTEXT=0
# --- Updates (update.sh) ------------------------------------------------------
# This file is where the scheduled jobs read these four knobs from; edit them
# here. (An explicit environment variable, or a value pinned in
# /etc/ergo-update.conf, overrides what is set here for that run.)
# latest - update to the newest release whenever one exists (default)
# security - update ONLY when a published GitHub security advisory covers the
# running version, or a release in range has a "### Security"
# section in its notes
# off - never change the running version (check/notify only)
UPDATE_POLICY=latest
# Releases whose notes announce "Compatibility breaks" are held for review and
# only notified, unless FORCE_UPDATE=1.
FORCE_UPDATE=0
# Seconds of warning (NOTICE to all users) before an update restarts Ergo.
UPDATE_GRACE=60
# 1 = the daily run also pulls a newer Caddy image and recreates it (drops every
# web-client websocket at that moment). 0 = Caddy updates via `ergoctl update caddy`.
CADDY_AUTOUPDATE=0
# --- Image tags ---------------------------------------------------------------
CADDY_TAG=2-alpine