From 8f2e89eb7c481f010ad44fa7b7c87432a2dd5aba Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 25 Feb 2026 23:07:23 +0000 Subject: [PATCH 01/70] docs: add Cursor Cloud specific instructions to AGENTS.md --- AGENTS.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 8e665714..03427642 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -83,3 +83,51 @@ docs/ # Architecture, services, onboarding, bridges - [scripts/AGENTS.md](scripts/AGENTS.md) - [tests/AGENTS.md](tests/AGENTS.md) - [README.md](README.md) + +## Cursor Cloud specific instructions + +### System dependencies (pre-installed by VM snapshot) + +Docker, `just`, `uv`, `pnpm`, Node.js 22, Python 3.11+3.12, `envsubst` (gettext-base). +Docker daemon must be started manually: `sudo dockerd &>/tmp/dockerd.log &` then ensure socket permissions: `sudo chmod 666 /var/run/docker.sock`. + +### Starting the dev environment + +1. **Env files**: `cp .env.example .env && cp .env.dev.example .env.dev` (idempotent; skip if files exist). +2. **Init + Docker stack**: `just dev` — runs `scripts/init.sh` (creates `data/` dirs, generates self-signed certs, substitutes config templates) then starts all Docker Compose services with the dev profile. +3. **Next.js web app**: `cd apps/web && NEXT_PUBLIC_IRC_WS_URL="ws://localhost:8000" NEXT_PUBLIC_XMPP_BOSH_URL="http://localhost:5280/http-bind" pnpm dev` (port 3000). This runs outside Docker as a local Node process. + +### Service ports (dev profile) + +| Service | Port | Notes | +|---------|------|-------| +| IRC (TLS) | 6697 | UnrealIRCd; self-signed cert for `irc.localhost` | +| IRC WebSocket | 8000 | UnrealIRCd WS | +| IRC RPC | 8600 | UnrealIRCd JSON-RPC | +| Atheme HTTP | 8081 | IRC services JSON-RPC | +| WebPanel | 8080 | UnrealIRCd admin panel | +| XMPP C2S | 5222 | Prosody client-to-server | +| XMPP HTTP | 5280 | Prosody BOSH/WebSocket | +| XMPP HTTPS | 5281 | Prosody (via nginx) | +| The Lounge | 9000 | Web IRC client (private mode; needs user created via `just lounge add`) | +| Dozzle | 8082 | Docker log viewer (dev profile only) | +| Next.js | 3000 | Web app (runs locally, not Docker) | + +### Running tests + +- **Unit tests** (fast, no Docker needed): `uv run pytest tests/unit/` +- **Bridge tests** (fast, no Docker needed): `uv run pytest apps/bridge/tests/` +- **Full root suite** (`just test`): includes integration tests that build Docker images — these may time out in constrained environments. Prefer running `tests/unit/` and `apps/bridge/tests/` for quick validation. +- **All tests**: `just test-all` (root tests + bridge tests). + +### Running lint + +`uv run pre-commit run --all-files` (equivalent to `just lint`). Requires Python 3.11 on PATH (installed by snapshot as `/usr/local/bin/python3.11`). Pre-existing lint warnings: shellcheck SC2016 in `infra/nginx/docker-entrypoint.sh` and luacheck warning in `apps/prosody/config/prosody.cfg.lua`. + +### Gotchas + +- **`CLOUDFLARE_DNS_API_TOKEN` warning**: Docker Compose emits a warning about this unset variable — safe to ignore in dev (cert-manager is not needed locally). +- **`pnpm install` build scripts warning**: esbuild/sharp/workerd build scripts are blocked by default. They have fallback binaries and the warning is non-blocking. +- **`apps/web` lint (`ultracite check`)**: Currently fails due to biome config expecting a `.gitignore` in `apps/web/`. This is a pre-existing issue. `pnpm run build` (Next.js build) works fine. +- **`pre-commit install`**: If `core.hooksPath` is set by the environment, run `git config --unset-all core.hooksPath` first. +- **Integration tests**: `tests/integration/` and `tests/e2e/` tests attempt to build fresh Docker images and may time out. Use `tests/unit/` for quick validation. From c7fb9d885003d92cef32df220e012b273cd8f0be Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 01:33:42 +0000 Subject: [PATCH 02/70] fix(irc): exempt Docker network from DNSBL and connect-flood bans Add except ban block for 172.16.0.0/12 (Docker bridge subnets) to prevent bridge and other internal containers from being G-Lined by DNSBL checks. Also fix antirandom except to use CIDR 172.16.0.0/12 instead of 172.17.* to cover all Docker bridge network subnets. --- apps/unrealircd/config/unrealircd.conf.template | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/unrealircd/config/unrealircd.conf.template b/apps/unrealircd/config/unrealircd.conf.template index 0742964f..29fc18f1 100644 --- a/apps/unrealircd/config/unrealircd.conf.template +++ b/apps/unrealircd/config/unrealircd.conf.template @@ -265,7 +265,7 @@ set { show-failedconnects yes; except { webirc yes; - ip { 192.168.*; 127.*; 172.17.*; } + ip { 192.168.*; 127.*; 172.16.0.0/12; } } } } @@ -722,6 +722,15 @@ blacklist tornevall { reason "Open proxy/relay detected. Check https://dnsbl.tornevall.org/ for details."; } +/* Exempt internal Docker network from bans/blacklists. + * Covers bridge, services, and other containers on the atl-chat network. + * 172.16.0.0/12 spans all default Docker bridge subnets (172.16–31.*). + */ +except ban { + mask *@172.16.0.0/12; + type { blacklist; connect-flood; maxperip; handshake-data-flood; } +} + /* Network configuration */ set { network-name "${IRC_NETWORK_NAME}"; From 70353a4f8edac8a47156247baa46f4f5d953fcad Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 01:33:50 +0000 Subject: [PATCH 03/70] fix(bridge): patch cfg.irc_relaymsg_clean_nicks in RELAYMSG test The test_sends_relaymsg_when_capability_negotiated test was not mocking cfg.irc_relaymsg_clean_nicks, causing it to pick up the env var from .env.dev (BRIDGE_RELAYMSG_CLEAN_NICKS=true) and skip the /d suffix. Now explicitly patches cfg to False, matching the companion clean_nick test. --- apps/bridge/tests/test_irc_client.py | 32 +++++++++++++++------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/apps/bridge/tests/test_irc_client.py b/apps/bridge/tests/test_irc_client.py index 76e8f015..c94e6d21 100644 --- a/apps/bridge/tests/test_irc_client.py +++ b/apps/bridge/tests/test_irc_client.py @@ -445,23 +445,25 @@ async def test_queues_pending_send_for_echo(self): @pytest.mark.asyncio async def test_sends_relaymsg_when_capability_negotiated(self): """When draft/relaymsg is negotiated, use RELAYMSG instead of PRIVMSG.""" - client, _, router = _make_client() - client._capabilities = {"draft/relaymsg": True} - irc_target = MagicMock() - irc_target.channel = "#test" - router.get_mapping_for_discord.return_value = MagicMock(irc=irc_target) - client.rawmsg = AsyncMock() + with patch("bridge.adapters.irc.client.cfg") as mock_cfg: + mock_cfg.irc_relaymsg_clean_nicks = False + client, _, router = _make_client() + client._capabilities = {"draft/relaymsg": True} + irc_target = MagicMock() + irc_target.channel = "#test" + router.get_mapping_for_discord.return_value = MagicMock(irc=irc_target) + client.rawmsg = AsyncMock() - evt = MagicMock() - evt.channel_id = "111" - evt.content = "hello" - evt.message_id = "discord-1" - evt.reply_to_id = None - evt.author_display = "Alice" - evt.author_id = "123" + evt = MagicMock() + evt.channel_id = "111" + evt.content = "hello" + evt.message_id = "discord-1" + evt.reply_to_id = None + evt.author_display = "Alice" + evt.author_id = "123" - await client._send_message(evt) - client.rawmsg.assert_awaited_once_with("RELAYMSG", "#test", "Alice/d", "hello") + await client._send_message(evt) + client.rawmsg.assert_awaited_once_with("RELAYMSG", "#test", "Alice/d", "hello") @pytest.mark.asyncio async def test_sends_relaymsg_clean_nick_when_configured(self): From feef20359bfde2f4b99f598ffba5e009f18aa5d1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 01:47:07 +0000 Subject: [PATCH 04/70] Add comprehensive UnrealIRCd configuration audit report Cross-reference audit of apps/unrealircd/config/unrealircd.conf.template against UnrealIRCd 6.x upstream example.conf and modules.default.conf. Covers: modules (A), TLS (B), set blocks (C), allow (D), listen (E), link (F), log (G), except ban (H), blacklist (I), oper (J), anti-flood/connthrottle (K), drpass (L), plaintext/outdated-tls (M), websocket (N), spamfilter/badwords (O), third-party modules (P). Findings: 3 critical, 8 warnings, 35+ OK, 12 suggestions. --- docs/audits/unrealircd-config-audit.md | 666 +++++++++++++++++++++++++ 1 file changed, 666 insertions(+) create mode 100644 docs/audits/unrealircd-config-audit.md diff --git a/docs/audits/unrealircd-config-audit.md b/docs/audits/unrealircd-config-audit.md new file mode 100644 index 00000000..c70ebec7 --- /dev/null +++ b/docs/audits/unrealircd-config-audit.md @@ -0,0 +1,666 @@ +# UnrealIRCd Configuration Audit Report + +**Date:** 2026-02-26 +**Auditor:** Cloud Agent (automated cross-reference) +**Local config:** `apps/unrealircd/config/unrealircd.conf.template` (UnrealIRCd 6.2.0.1) +**Reference:** UnrealIRCd 6.1.10 `modules.default.conf` and 6.x `example.conf` from repo docs + +--- + +## A. Loaded Modules + +### Comparison: Local vs `modules.default.conf` (6.1.10) + +All **229** upstream default modules are present in the local config. The local config loads **13 additional** modules beyond the defaults: + +| Extra Module | Purpose | Verdict | +|---|---|---| +| `cloak_sha256` | SHA-256 cloaking (required, upstream example loads it separately) | 🟢 Correct | +| `webserver` | HTTP server for RPC/WebSocket | 🟢 Required for WebSocket + RPC | +| `websocket` | WebSocket protocol support | 🟢 Required for web clients | +| `antirandom` | Block random-looking user/nick/ident | 🟢 Good security hardening | +| `antimixedutf8` | Block mixed-script spam | 🟢 Good anti-spam measure | +| `ircops` | `/IRCOPS` command to list online opers | 🟢 Useful for community | +| `staff` | `/STAFF` command | 🟢 Useful for community | +| `nocodes` | Strip mIRC color codes from certain channels | 🟢 Nice to have | +| `maxperip` | Per-IP connection limiting | 🟢 Good security hardening | +| `utf8functions` | UTF-8 nick/channel support | 🟢 Modern best practice | +| `third/showwebirc` | Show WebIRC info in WHOIS | 🟢 Good for transparency | +| `third/metadata` | IRCv3 draft/metadata | 🟢 Modern feature | +| `third/react` | IRCv3 draft/react (reactions) | 🟢 Modern feature | +| `third/redact` | IRCv3 draft/message-redaction | 🟢 Modern feature | +| `third/relaymsg-atl` | Stateless bridging (atl.chat fork) | 🟢 Required for bridge | + +### Findings + +- 🟢 **OK** — All upstream default modules are loaded. No important modules are missing. +- 🟢 **OK** — Extra modules are all justified and well-documented with `@if module-loaded()` guards where appropriate. +- 💡 **SUGGESTION** — The local `modules.default.conf` reference is version 6.1.10 while the server runs 6.2.0.1. Consider checking the 6.2.0.1 `modules.default.conf` for any newly added modules. Specifically, the `maxperip` module was added as a standalone module in newer versions (it's loaded locally but not in the 6.1.10 reference, which is correct behavior). + +--- + +## B. TLS/SSL Configuration + +**Config lines:** `set { tls { ... } }` (lines 465–513) + +### Findings + +- 🟢 **OK** — **TLS Protocols**: `"TLSv1.2,+TLSv1.3"` — correctly enforces TLS 1.2+ only. No SSLv3, TLS 1.0, or TLS 1.1. + +- 🟢 **OK** — **TLS 1.2 Ciphers**: Strong ECDHE-only cipher suite with Forward Secrecy. All ciphers use AEAD (GCM or ChaCha20-Poly1305). No weak ciphers (RC4, DES, 3DES, CBC). + + ``` + ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384: + ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256 + ``` + +- 🟢 **OK** — **TLS 1.3 Cipher Suites**: Complete list including all standard TLS 1.3 suites plus CCM variants. + +- 🟢 **OK** — **ECDH Groups**: Includes `X25519MLKEM768` (post-quantum hybrid), `X25519`, and standard NIST curves. Both the new `groups` directive and legacy `ecdh-curves` are set for compatibility. + +- 🟢 **OK** — **STS Policy**: Enabled with configurable duration/preload via environment variables. The phased rollout plan in the comments (1m → 1d → 30d → 180d) is an excellent approach. + +- 🟢 **OK** — **Certificate Expiry Notification**: `certificate-expiry-notification yes;` — good operational practice. + +- 🟢 **OK** — **Trusted CA File**: Explicit CA bundle path set for certificate validation. + +- 💡 **SUGGESTION** — **TLS 1.3 CCM Ciphers**: The `TLS_AES_128_CCM_8_SHA256` and `TLS_AES_128_CCM_SHA256` suites are rarely used by IRC clients and add no real benefit. Consider removing them for a cleaner config, though they cause no harm. + +- 💡 **SUGGESTION** — **`no-client-certificate`**: This is set, which is standard for public IRC servers. If you ever want to support certificate-based authentication (certfp), this would need to be reconsidered at the per-listen level. + +--- + +## C. `set` Block Analysis + +### Network Configuration (lines 735–758) + +- 🟢 **OK** — `network-name`, `default-server`, `services-server`, `stats-server`, `sasl-server` — all correctly templated with `${IRC_SERVICES_SERVER}` and `${IRC_DOMAIN}`. +- 🟢 **OK** — `help-channel "#support"` — reasonable choice. +- 🟢 **OK** — `cloak-keys` — properly sourced from environment variables. +- 🟢 **OK** — `hiddenhost-prefix` — configurable via `${IRC_CLOAK_PREFIX}`. +- 🟢 **OK** — `cloak-method ip` — good choice for privacy. + +### Server Configuration (lines 842–965) + +- 🟢 **OK** — `kline-address` — set to `${IRC_ADMIN_EMAIL}`. +- 🟢 **OK** — `modes-on-connect "+ixw"` — matches upstream example. `+i` (invisible), `+x` (cloaked host), `+w` (wallops). +- 🟢 **OK** — `modes-on-oper "+xws"` — good. Ensures opers get cloaking + wallops + server notices. +- 🟢 **OK** — `modes-on-join "+nt"` — standard default (no external messages + topic lock). +- 🟢 **OK** — `restrict-usermodes "x"` — prevents users from removing cloaking. Excellent security practice. +- 🟢 **OK** — `maxchannelsperuser 10` — matches upstream. +- 🟢 **OK** — `anti-spam-quit-message-time 10s` — matches upstream. +- 🟢 **OK** — `oper-auto-join "#mod-chat"` — appropriate for team use. +- 🟢 **OK** — `hide-ulines` and `show-connect-info` — correct. + +### Missing from upstream example + +- 🟡 **WARNING** — **Missing `set::spamfilter` block**: The upstream example.conf includes: + + ``` + spamfilter { + ban-time 1d; + ban-reason "Spam/Advertising"; + virus-help-channel "#help"; + } + ``` + + The local config has no `set::spamfilter` sub-block. While the included `spamfilter.conf` file provides rules, the global defaults (ban-time, ban-reason, virus-help-channel) are not explicitly set. UnrealIRCd will use built-in defaults, but it's best practice to set these explicitly. + + **Fix:** Add inside a `set { }` block: + + ``` + spamfilter { + ban-time 1d; + ban-reason "Spam/Advertising"; + virus-help-channel "#help"; + } + ``` + +- 🟡 **WARNING** — **Missing `set::connthrottle` block**: The upstream example.conf has a comprehensive `connthrottle` configuration: + + ``` + connthrottle { + except { reputation-score 24; identified yes; } + new-users { local-throttle 20:60; global-throttle 30:60; } + disabled-when { reputation-gathering 1w; start-delay 3m; } + } + ``` + + The local config loads the `connthrottle` module but has no configuration for it. The module will use built-in defaults, which may not be optimal. + + **Fix:** Add a `set { connthrottle { ... } }` block mirroring or customizing the upstream defaults. + +- 🟡 **WARNING** — **Missing `set::oper-only-stats`**: The upstream example restricts stats commands to opers: + + ``` + oper-only-stats "okfGsMRUEelLCXzdD"; + ``` + + The local config doesn't set this, meaning all stats are visible to everyone. While modern UnrealIRCd 6 has better defaults than older versions, consider restricting sensitive stats. + + **Fix:** Add `oper-only-stats "okfGsMRUEelLCXzdD";` inside a `set` block. + +- 💡 **SUGGESTION** — **Missing `set::whois-details` for certfp**: While whois-details are configured for `webirc` and `websocket`, consider adding certfp visibility control. + +- 💡 **SUGGESTION** — **`set::allowed-nickchars`**: The `charsys` module is loaded but no `set::allowed-nickchars` is configured. If you want to allow UTF-8 nicknames (which `utf8functions` module supports), you should explicitly set this. Example: + + ``` + set { allowed-nickchars { latin-utf8; }; } + ``` + +--- + +## D. `allow` Blocks + +**Config lines:** 558–562 + +``` +allow { + mask *@*; + class clients; + maxperip 5; +} +``` + +### Findings + +- 🟢 **OK** — Single allow block, open to all — appropriate for a public IRC server. +- 🟢 **OK** — `maxperip 5` — matches upstream old example.conf (5). The modern example uses 3, which is more restrictive. +- 💡 **SUGGESTION** — Consider lowering `maxperip` to 3 (matching modern upstream defaults) to reduce abuse surface. Users needing more connections can be handled with specific allow blocks. + +--- + +## E. `listen` Blocks + +**Config lines:** 288–456 + +| Port | Options | Purpose | Verdict | +|---|---|---|---| +| Unix socket (`rpc.socket`) | `rpc` | RPC for webpanel | 🟢 OK | +| Unix socket (`services.sock`) | (none) | Atheme services link | 🟢 OK | +| 6697 | `tls` | Standard IRC TLS port | 🟢 OK | +| 6900 | `tls; serversonly` | Server linking (TLS) | 🟢 OK | +| 6901 | `serversonly` | Server linking (plaintext, for Docker) | 🟢 OK (see note) | +| 8600 | `rpc; tls` | RPC API with TLS | 🟢 OK | +| 8000 | `websocket { type text; }` | WebSocket for web clients | 🟢 OK (see note) | + +### Findings + +- 🟢 **OK** — No plaintext client port (6667) is open. Good security practice. +- 🟢 **OK** — Port 8000 WebSocket without TLS is justified because TLS is terminated at the reverse proxy (NPM). +- 🟢 **OK** — Port 8600 RPC has its own TLS configuration with explicit cert/key paths. +- 🟢 **OK** — Port 6901 plaintext for servers is justified for Docker-internal Atheme communication. The `plaintext-policy { server allow; }` is correct for this use case. + +- 💡 **SUGGESTION** — Port 8000 (WebSocket): Consider binding to a specific internal IP rather than `*` if the WebSocket should only be accessible from the reverse proxy, not directly from the internet. + +- 💡 **SUGGESTION** — Port 6901 plaintext server port: Since Atheme connects via Unix socket (`services.sock`), this port may be unnecessary. If it's not used, removing it reduces the attack surface. + +--- + +## F. `link` Blocks + +**Config lines:** 542–550 + +``` +link ${IRC_SERVICES_SERVER} { + incoming { + mask *; + password "${IRC_SERVICES_PASSWORD}"; + } + password "${IRC_SERVICES_PASSWORD}"; + class servers; +} +``` + +### Findings + +- 🟡 **WARNING** — **`incoming { mask * }`**: The mask accepts connections from any IP. While this is somewhat mitigated by the password requirement and Docker network isolation, it would be more secure to restrict the mask to the Docker network range. + + **Fix:** + + ``` + incoming { + mask 172.16.0.0/12; + password "${IRC_SERVICES_PASSWORD}"; + } + ``` + +- 🟡 **WARNING** — **No TLS for services link**: The link block doesn't specify `options { tls; }`. Since Atheme connects via Unix socket (port 6901 or `services.sock`), this is acceptable for the current Docker setup. However, if services ever connect over the network, TLS should be required. + +- 🟢 **OK** — Password is properly templated from environment variable. +- 🟢 **OK** — The services password (`IRC_SERVICES_PASSWORD`) in `.env.example` has a placeholder that forces users to change it. + +--- + +## G. `log` Blocks + +**Config lines:** 293–310, 808–839 + +### Findings + +- 🟢 **OK** — **Memory log**: For RPC (1000 lines, 7 days) — matches upstream best practice. +- 🟢 **OK** — **Text log**: `ircd.log` with 100M maxsize — matches upstream example. +- 🟢 **OK** — **JSON log**: `ircd.json.log` with 250M maxsize — matches upstream example and provides machine-readable audit trail. +- 🟢 **OK** — **Source filters**: Identical exclusions across all log blocks (`!debug`, `!join.*`, `!part.*`, `!kick.*`) — consistent and appropriate. +- 🟢 **OK** — Log path templated via `${IRC_LOG_PATH}`. + +--- + +## H. `except ban` Blocks + +**Config line:** 729–732 + +``` +except ban { + mask *@172.16.0.0/12; + type { blacklist; connect-flood; maxperip; handshake-data-flood; } +} +``` + +### Findings + +- 🟢 **OK** — Covers the entire Docker bridge subnet range (172.16.0.0/12). +- 🟢 **OK** — Exemption types are appropriate: `blacklist` (don't DNSBL internal IPs), `connect-flood` (allow many container connections), `maxperip` (multiple containers share subnet), `handshake-data-flood` (services may send bursts). + +- 💡 **SUGGESTION** — The upstream example also exempts IRCCloud (`*.irccloud.com`). If your network expects IRCCloud users, consider adding a similar exception: + + ``` + except ban { + mask *.irccloud.com; + type { maxperip; connect-flood; } + } + ``` + +- 💡 **SUGGESTION** — Consider adding `type all;` exception for a specific oper IP to ensure opers can always connect even during accidental self-bans. + +--- + +## I. `blacklist` (DNSBL) Blocks + +**Config lines:** 692–723 + +| DNSBL | Reply Codes | Action | Ban Time | +|---|---|---|---| +| `dnsbl.dronebl.org` | 3,5-16 | gline | 24h | +| `rbl.efnetrbl.org` | 1,4,5 | gline | 24h | +| `dnsbl.tornevall.org` | 1-16 | gline | 24h | + +### Findings + +- 🟢 **OK** — DroneBL and EFnetRBL are the two most commonly recommended DNSBLs for IRC. Both match the upstream example exactly. + +- 🟡 **WARNING** — **Tornevall DNSBL** (`dnsbl.tornevall.org`): This DNSBL has had reliability issues historically and some networks have stopped using it. It's not in the upstream UnrealIRCd example. The extremely broad reply code range (1-16 = everything) could lead to false positives. + + **Fix:** Consider removing the `tornevall` blacklist or at minimum narrowing the reply codes to specific abuse categories. If you want a third DNSBL, consider `dnsbl.sectoor.de` or `dnsbl.ahbl.org` instead, though availability varies. + +- 🟢 **OK** — Ban time of 24h is reasonable for all blacklists. +- 🟢 **OK** — Reason messages include lookup URLs for users to check their IP. +- 🟢 **OK** — Action `gline` is appropriate (network-wide ban). + +--- + +## J. `oper` Blocks + +**Config lines:** 761–787 + +### Admin Oper + +``` +oper admin { + class opers; + mask *@*; + password "${IRC_OPER_PASSWORD}"; + operclass netadmin-with-override; + swhois "is the Network Administrator"; + vhost "${IRC_STAFF_VHOST}"; + require-modes ""; +} +``` + +### Findings + +- 🟢 **OK** — Password sourced from `${IRC_OPER_PASSWORD}`. The `.env.example` shows an `$argon2id$...` hash, meaning the password is properly hashed (not plaintext). +- 🟢 **OK** — `operclass netadmin-with-override` — appropriate for the primary admin. +- 🟢 **OK** — `swhois` and `vhost` are set for identification. + +- 🔴 **CRITICAL** — **`mask *@*`**: The admin oper block accepts connections from ANY host. This means anyone who knows (or brute-forces) the oper password can become netadmin from anywhere. Best practice is to restrict this to known IPs or at minimum require TLS certificate fingerprint authentication. + + **Fix (at minimum):** + + ``` + oper admin { + ... + mask *@172.16.0.0/12; /* Docker network only, or specific IPs */ + /* Or better: use certificate fingerprint */ + /* password "$argon2id..."; */ + /* require-modes "z"; */ /* Require TLS connection */ + } + ``` + +- 🟡 **WARNING** — **`require-modes ""`**: This is explicitly set to empty, meaning no user modes are required to OPER up. Consider requiring `require-modes "z"` to force TLS for oper authentication: + + ``` + require-modes "z"; + ``` + +### Bridge Oper + +``` +oper atl-bridge { + class opers; + mask *@*atl-bridge*; + password "${BRIDGE_IRC_OPER_PASSWORD}"; + operclass bridge-oper; +} +``` + +- 🟢 **OK** — Restricted mask (`*@*atl-bridge*`) limits this oper to bridge hostnames. +- 🟢 **OK** — `bridge-oper` operclass has minimal permissions (just channel override + relaymsg). +- 🟢 **OK** — Password sourced from environment variable. + +--- + +## K. `set::anti-flood` / `set::connthrottle` + +### Anti-Flood (lines 896–964) + +- 🟢 **OK** — **Channel anti-flood profiles**: Five profiles (very-strict through very-relaxed) with default "normal" — excellent granularity. +- 🟢 **OK** — **Handshake data flood**: 4k limit with 10m zline — reasonable protection. +- 🟢 **OK** — **Target flood protection**: Comprehensive rate limits for channel/private messages, notices, and tagmsg. +- 🟢 **OK** — **Known vs unknown user differentiation**: Proper two-tier system with stricter limits for unknown users. + +- 🟡 **WARNING** — **`connect-flood 20:10`**: This allows 20 connections per 10 seconds per IP, which is described as "relaxed for testing." For production, this should be tightened significantly. The upstream default is typically 3:60 (3 per 60 seconds). + + **Fix for production:** + + ``` + connect-flood 3:60; + ``` + +### ConnThrottle + +- 🟡 **WARNING** — **Missing `set::connthrottle` configuration**: The `connthrottle` module is loaded but not configured. This module needs explicit configuration to be effective. Without it, the module uses built-in defaults which may not be optimal for this network. + + **Fix:** Add: + + ``` + set { + connthrottle { + except { + reputation-score 24; + identified yes; + } + new-users { + local-throttle 20:60; + global-throttle 30:60; + } + disabled-when { + reputation-gathering 1w; + start-delay 3m; + } + } + } + ``` + +--- + +## L. `drpass` Block + +### Finding + +- 🔴 **CRITICAL** — **Missing `drpass` block**: The local configuration has NO `drpass` block. The `/DIE` and `/RESTART` commands have no password protection. Any IRC operator with sufficient privileges could accidentally or maliciously shut down or restart the server without authentication. + + The upstream example.conf includes: + + ``` + drpass { + restart "restart"; + die "die"; + } + ``` + + **Fix:** Add a `drpass` block with strong, hashed passwords: + + ``` + drpass { + restart "${IRC_DRPASS_RESTART}"; + die "${IRC_DRPASS_DIE}"; + } + ``` + + And add corresponding environment variables to `.env.example` with argon2id-hashed values. + +--- + +## M. `set::plaintext-policy` / `set::outdated-tls-policy` + +**Config lines:** 515–538 + +### Plaintext Policy + +``` +plaintext-policy { + server allow; + user allow; + oper deny; + user-message "..."; + oper-message "..."; +} +``` + +### Findings + +- 🟢 **OK** — `server allow` — correct for Docker-internal Atheme link on plaintext Unix socket. +- 🟢 **OK** — `oper deny` — opers must use TLS. Good security practice. +- 🟢 **OK** — `user allow` with STS redirect — correct phased approach. Users with STS-capable clients get auto-redirected to TLS. +- 🟢 **OK** — Custom user-message and oper-message provide clear guidance. +- 💡 **SUGGESTION** — For production, eventually move `user` to `warn` or `deny` once STS has been in place long enough (Phase 4 in the config comments). + +### Outdated TLS Policy + +``` +outdated-tls-policy { + user warn; + oper deny; + server deny; +} +``` + +- 🟢 **OK** — Users get warned about outdated TLS (not kicked). +- 🟢 **OK** — Opers and servers are denied with outdated TLS. Excellent security practice. +- 🟢 **OK** — Custom messages provide actionable guidance. + +--- + +## N. WebSocket Configuration + +**Config lines:** 443–456 + +``` +listen { + ip *; + port 8000; + options { + websocket { type text; } + } +} +``` + +### Findings + +- 🟢 **OK** — WebSocket listener on port 8000, type `text` — correct for IRC over WebSocket. +- 🟢 **OK** — TLS is correctly disabled here because it's terminated at the reverse proxy (NPM). This is documented in the comments. +- 🟢 **OK** — Both `websocket_common` and `websocket` modules are loaded. +- 🟢 **OK** — The `webserver` module is loaded for HTTP functionality. + +- 💡 **SUGGESTION** — Consider adding `websocket { type text; origin "https://your-domain.com"; }` to restrict WebSocket connections to your web application's origin, preventing unauthorized cross-origin connections. + +--- + +## O. Spamfilter and Badwords + +### Spamfilter (`spamfilter.conf`) + +- 🟡 **WARNING** — **Outdated rules**: The file itself states: "Since 2005 these rules are no longer maintained. The main purpose nowadays is to serve as an example." All the rules target malware/trojans from the early 2000s (sub7, mIRC exploits, fagot worm, etc.). None of these are relevant modern threats. + + **Fix:** Either: + 1. Write new, modern spamfilter rules targeting current IRC spam patterns (crypto scams, phishing links, mass-highlight floods, invite spam), or + 2. Remove/empty the file and rely on dynamic spamfilters via `/SPAMFILTER` command, or + 3. Keep as-is but acknowledge the rules provide no real protection against modern threats. + +### Badwords (`badwords.conf`) + +- 🟡 **WARNING** — **Potentially problematic word list**: The badwords file is the default from UnrealIRCd circa 2000 (by Carsten V. Munk). It contains several slurs including `faggot` and `fag`. For a modern Linux community: + - The word list is very basic (only 20 entries) + - Some entries may create false positives (e.g., "fag" matching in legitimate words, `*fuck*` wildcard matching "Buckfastleigh" etc.) + - The list doesn't cover modern harassment patterns + - Consider whether a word filter is the right approach vs. moderation tooling + + **Fix:** Review and update the badwords list to match your community standards, or disable channel/user mode +G if you prefer moderation-based approaches. + +--- + +## P. Third-Party Modules + +### Installed Modules + +| Module | Config | Status | +|---|---|---| +| `third/showwebirc` | No config needed | 🟢 OK — works out of the box | +| `third/metadata` | `metadata { max-user-metadata 10; max-channel-metadata 10; max-subscriptions 10; }` | 🟢 OK — reasonable limits | +| `third/react` | No config needed | 🟢 OK — works out of the box | +| `third/redact` | No config needed | 🟢 OK — works out of the box | +| `third/relaymsg-atl` | `relaymsg { hostmask "bridge@${IRC_DOMAIN}"; require-separator no; }` | 🟢 OK — custom fork, properly configured | + +### Findings + +- 🟢 **OK** — All third-party modules in `third-party-modules.list` are loaded in the config. +- 🟢 **OK** — `relaymsg` is properly configured with `require-separator no` for clean bridge nicks. +- 🟢 **OK** — `metadata` limits are reasonable (10 per user/channel/subscriptions). + +### Commented-Out Modules in `third-party-modules.list` + +The file lists these as potential additions: + +- `third/commandsno` — SNOMASK-based command logging +- `third/clones` — Clone detection +- `third/repeatprot` — Repeat message protection +- `third/block_masshighlight` — Block mass highlighting in channels + +- 💡 **SUGGESTION** — **`third/block_masshighlight`** is highly recommended for any community IRC server. Mass-highlighting (mentioning many nicks at once) is a common harassment/spam tactic. Consider enabling this module. + +- 💡 **SUGGESTION** — **`third/repeatprot`** would complement the existing anti-flood settings by catching repeated messages that slip through rate limits. + +--- + +## Additional Findings + +### WEBIRC Block (line 370–373) + +``` +webirc { + mask ${ATL_GATEWAY_IP}/32; + password "change_me_webirc_password"; +} +``` + +- 🔴 **CRITICAL** — **Hardcoded placeholder password**: The WEBIRC password is `"change_me_webirc_password"` which is NOT an environment variable. Unlike other passwords in the config (which use `${VAR}` syntax), this one is a literal string. If the config template is processed without changing this, the WEBIRC password will be the placeholder text. + + **Fix:** Change to use an environment variable: + + ``` + password "${ATL_WEBIRC_PASSWORD}"; + ``` + + And add `ATL_WEBIRC_PASSWORD` to `.env.example`. + +### RPC User (line 790–794) + +``` +rpc-user "${WEBPANEL_RPC_USER}" { + match { ip *; } + rpc-class full; + password "${WEBPANEL_RPC_PASSWORD}"; +} +``` + +- 🟡 **WARNING** — **`match { ip *; }`**: The RPC user can connect from any IP. While the RPC port (8600) requires TLS, restricting to known IPs would be more secure: + + ``` + match { ip 172.16.0.0/12; } + ``` + +### Proxy/WEBIRC Block for The Lounge (lines 376–380) + +``` +proxy thelounge { + type webirc; + match { ip 172.16.0.0/12; } + password "${THELOUNGE_WEBIRC_PASSWORD}"; +} +``` + +- 🟢 **OK** — Properly restricted to Docker network range. +- 🟢 **OK** — Password sourced from environment variable. + +### Auto Vhost (lines 797–803) + +``` +vhost { + auto-login yes; + mask { identified yes; } + vhost ${IRC_DOMAIN}; +} +``` + +- 🟢 **OK** — Gives all identified users a clean vhost matching the IRC domain. Good for privacy. + +### Ban Nick Blocks (lines 591–689) + +- 🟢 **OK** — Comprehensive list covering services names, system names, and generic names. +- 🟡 **WARNING** — **Overly broad patterns**: `*IRC*` will block any nick containing "IRC" (e.g., "CircleOfLife", "QuIRCky"). Similarly, `*admin*` blocks "administrator" type nicks but also catches legitimate nicks like "badminton". `*server*` would catch "observer". Consider making these patterns more specific. + + **Fix example:** + + ``` + ban nick { mask "IRC"; reason "Reserved for network"; } + ban nick { mask "IRC-*"; reason "Reserved for network"; } + ``` + +### Missing `aliases` Include + +- 💡 **SUGGESTION** — The upstream example includes `aliases/anope.conf` for service aliases (/NickServ, /ChanServ, etc.). The local config doesn't include any aliases file. Since Atheme is used (which is compatible with Anope aliases), consider adding: + + ``` + include "aliases/anope.conf"; + ``` + + This provides `/NS`, `/CS`, `/OS`, `/MS` shortcut commands. + +--- + +## Summary + +### By Severity + +| Severity | Count | Items | +|---|---|---| +| 🔴 CRITICAL | 3 | Missing `drpass` block; admin oper `mask *@*`; hardcoded WEBIRC password | +| 🟡 WARNING | 8 | Missing connthrottle config; missing spamfilter set block; missing oper-only-stats; relaxed connect-flood; tornevall DNSBL; link mask too open; RPC user match too open; overly broad ban nick patterns | +| 🟢 OK | 35+ | Most configuration is solid and well-documented | +| 💡 SUGGESTION | 12 | Various optional improvements | + +### Priority Fixes + +1. **Add `drpass` block** with hashed passwords for `/DIE` and `/RESTART` +2. **Fix WEBIRC password** to use environment variable instead of hardcoded placeholder +3. **Restrict admin oper mask** from `*@*` to specific IPs or Docker network +4. **Add `set::connthrottle`** configuration block +5. **Tighten `connect-flood`** from `20:10` to production values +6. **Add `set::spamfilter`** defaults block +7. **Restrict link block mask** to Docker network range +8. **Add `set::oper-only-stats`** to hide sensitive stats from regular users + +### Overall Assessment + +The configuration is **well above average** for a Docker-deployed IRC server. The TLS configuration is excellent (including post-quantum cryptography support), the anti-flood settings are comprehensive with proper known/unknown user differentiation, and the module selection is thorough. The use of environment variable templating for sensitive values is good practice. The main areas for improvement are the three critical findings (drpass, oper mask, WEBIRC password) and adding the missing `connthrottle` configuration. From 7a327881f111b4c6c29133763ff1a10fe72f24be Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 01:53:03 +0000 Subject: [PATCH 05/70] Add comprehensive Atheme config audit report Cross-reference audit of atheme.conf.template against upstream atheme.conf.example (master branch) covering all 16 audit categories. Key findings: - 2 CRITICAL: example operator left in config; orphaned chanfix block - 4 WARNING: debug loglevel, missing pbkdf2 v1 compat, duplicate scram ref - 35+ OK: vast majority matches or exceeds upstream - 8+ SUGGESTIONS: scram_mechanisms, exttarget modules, log separation --- apps/atheme/AUDIT_REPORT.md | 822 ++++++++++++++++++++++++++++++++++++ 1 file changed, 822 insertions(+) create mode 100644 apps/atheme/AUDIT_REPORT.md diff --git a/apps/atheme/AUDIT_REPORT.md b/apps/atheme/AUDIT_REPORT.md new file mode 100644 index 00000000..ec82df10 --- /dev/null +++ b/apps/atheme/AUDIT_REPORT.md @@ -0,0 +1,822 @@ +# Atheme IRC Services Configuration Audit Report + +**Date:** 2026-02-26 +**Local config:** `apps/atheme/config/atheme.conf.template` +**Upstream reference:** Atheme `master` branch — `dist/atheme.conf.example` +**Upstream modules:** `modules/` directory tree + +--- + +## A. `serverinfo` Block + +### 🟢 OK — Server name, desc, numeric properly templated + +```conf +name = "${ATHEME_SERVER_NAME}"; # .env.example: services.atl.chat ✅ +desc = "${ATHEME_SERVER_DESC}"; # .env.example: "All Things Linux IRC Services" ✅ +numeric = "${ATHEME_NUMERIC}"; # .env.example: 00A (matches upstream default) ✅ +recontime = ${ATHEME_RECONTIME}; # .env.example: 10 (matches upstream default) ✅ +netname = "${ATHEME_NETNAME}"; # .env.example: atl.chat ✅ +``` + +All values are properly templated via `${VAR}` for substitution by `prepare-config.sh`. Defaults in `.env.example` are reasonable. + +### 🟢 OK — `casemapping = ascii` + +Correct for UnrealIRCd. Upstream docs explicitly state: *"Bahamut, Unreal, and other 'DALnet'-style IRCds will use ASCII case mapping."* + +### 🟢 OK — `hidehostsuffix`, `adminname`, `adminemail`, `registeremail` + +All properly templated. Default values are network-specific and sensible. + +### 🟡 WARNING — `loglevel = { debug; }` is excessively verbose + +```conf +# Local: +loglevel = { debug; }; + +# Upstream default: +loglevel = { admin; error; info; network; wallops; }; +``` + +**Issue:** `debug` is a meta-keyword that enables ALL log categories, including `rawdata` (raw IRC protocol data, which can include passwords for some operations) and `commands` (every single command used). This is appropriate for development but **not** for production. + +**Fix:** For production, use: + +```conf +loglevel = { admin; error; info; network; wallops; }; +``` + +Or at most add `commands` and `register` for auditing: + +```conf +loglevel = { admin; error; info; network; wallops; commands; register; set; denycmd; }; +``` + +### 🟢 OK — `maxcertfp`, `maxlogins`, `maxusers`, `mdlimit`, `emaillimit`, `emailtime` + +All match upstream defaults exactly. + +### 🟢 OK — `auth = none` + +Matches upstream default. Email verification is disabled. This is fine for networks that don't require email confirmation for registration. + +### 💡 SUGGESTION — Consider enabling `auth = email` for production + +If spam registration is a concern, enabling email verification provides an additional layer of protection. The local config has `waitreg_time = 30` in the NickServ block which helps, but email verification is stronger. + +### 🟢 OK — `mta = "/usr/sbin/sendmail"` + +Matches upstream. Note: in the Docker container, sendmail may not be installed. This only matters if `auth = email` is enabled or email-based password recovery is used. + +--- + +## B. `uplink` Block + +### 🟢 OK — Uplink host and password properly templated + +```conf +uplink "${IRC_DOMAIN}" { + host = "${ATHEME_UPLINK_HOST}"; # .env.example: 127.0.0.1 + port = ${ATHEME_UPLINK_PORT}; # .env.example: 6901 + send_password = "${IRC_SERVICES_PASSWORD}"; + receive_password = "${IRC_SERVICES_PASSWORD}"; +}; +``` + +- Both `send_password` and `receive_password` use the same `${IRC_SERVICES_PASSWORD}` — this is correct and matches how UnrealIRCd is typically configured for services links. +- Default password `change_me_secure_services_pass` in `.env.example` is clearly marked as needing change. + +### 🟢 OK — No TLS on uplink (by design) + +The uplink uses port 6901 (non-TLS). This is correct because: + +1. **Atheme does not support TLS for uplinks** — the upstream docs explicitly state: *"Atheme does not currently link over TLS. To link Atheme over TLS, please connect Atheme to a local IRCd."* +2. The Docker Compose configuration uses `network_mode: service:atl-irc-server`, so Atheme shares the network namespace with UnrealIRCd — the connection is truly `127.0.0.1` and never traverses a network boundary. + +### 💡 SUGGESTION — Document `ATHEME_UPLINK_SSL_PORT` purpose + +`.env.example` defines `ATHEME_UPLINK_SSL_PORT=6900` but it's never used in the Atheme config template. Consider either: + +- Removing it from `.env.example` to avoid confusion, or +- Adding a comment explaining it's reserved for future TLS support + +--- + +## C. `loadmodule` Statements + +### 🟢 OK — Protocol and backend modules + +```conf +loadmodule "protocol/unreal4"; # ✅ Correct for UnrealIRCd 4+/6.x +loadmodule "backend/opensex"; # ✅ Recommended by upstream +``` + +### 🟢 OK — Crypto module (pbkdf2v2 with SCRAM-SHA-256) + +```conf +loadmodule "crypto/pbkdf2v2"; +``` + +Loaded first before other crypto modules. SCRAM-SHA-256 configured in `crypto{}` block. This is the recommended setup for SASL SCRAM support. + +### 🟡 WARNING — Missing `crypto/pbkdf2` verify-only module + +```conf +# Upstream loads BOTH: +loadmodule "crypto/pbkdf2v2"; +loadmodule "crypto/pbkdf2"; # Verify-only, for Atheme <= 7.2 compat + +# Local only loads: +loadmodule "crypto/pbkdf2v2"; +``` + +**Issue:** If you ever migrated from Atheme 7.2 or need to verify passwords created with the old pbkdf2 v1 module, those passwords would fail verification without this module. + +**Fix:** Add after pbkdf2v2: + +```conf +loadmodule "crypto/pbkdf2"; /* Verify-only for Atheme <= 7.2 compat */ +``` + +**Severity note:** Only a warning because this is a new deployment that likely has no legacy password hashes. If you're certain no migration from older Atheme will ever occur, this can be safely skipped. + +### 🟡 WARNING — Duplicate `saslserv/scram` reference + +```conf +# Line 174 (ENABLED): +loadmodule "saslserv/scram"; + +# Line 258 (COMMENTED OUT): +#loadmodule "saslserv/scram"; +``` + +**Issue:** Confusing — the module IS loaded at line 174, but line 258 has it commented out, making it look like it's disabled. The first load wins, so SCRAM-SHA is functional, but this creates confusion during config review. + +**Fix:** Remove the duplicate commented-out line at 258, or add a clear note: + +```conf +/* saslserv/scram already loaded above in the SASL section */ +``` + +### 🟢 OK — NickServ modules (comprehensive) + +The local config loads significantly more NickServ modules than the upstream defaults, including several that upstream has commented out: + +| Module | Local | Upstream | Notes | +|--------|-------|----------|-------| +| `nickserv/access` | ✅ Loaded | ❌ Commented | Access lists — useful feature | +| `nickserv/cert` | ✅ Loaded | ❌ Commented | CertFP management — recommended for security | +| `nickserv/enforce` | ✅ Loaded | ❌ Commented | Nickname enforcement — good for ownership | +| `nickserv/info_lastquit` | ✅ Loaded | ❌ Commented | Nice info feature | +| `nickserv/listlogins` | ✅ Loaded | ❌ Commented | Session management | +| `nickserv/listownmail` | ✅ Loaded | ❌ Commented | Email management | +| `nickserv/set_privmsg` | ✅ Loaded | ❌ Commented | User preference | +| `nickserv/set_private` | ✅ Loaded | ❌ Commented | Privacy feature | +| `nickserv/waitreg` | ✅ Loaded | ❌ Commented | Anti-spam delay | + +All additional modules are reasonable choices. The `nickserv/enforce` + `nickserv/waitreg` combination provides good anti-spam protection. + +### 🟢 OK — ChanServ modules (comprehensive) + +Similar to NickServ, more modules enabled than upstream defaults. Notable additions: + +| Module | Local | Upstream | Notes | +|--------|-------|----------|-------| +| `chanserv/quiet` | ✅ Loaded | ❌ Commented | +q support for UnrealIRCd | +| `chanserv/set_private` | ✅ Loaded | ❌ Commented | Channel privacy | +| `chanserv/xop` | ✅ Loaded | ❌ Commented | VOP/HOP/AOP/SOP emulation | + +All appropriate choices. + +### 🟢 OK — OperServ modules (comprehensive, modern approach) + +The local config uses individual `operserv/set_*` modules instead of the deprecated unified `operserv/set` module. This is the **preferred** approach for v7.3. + +Additional modules loaded compared to upstream defaults: + +- `operserv/clearchan` (upstream commented) +- `operserv/clones` (upstream commented) +- `operserv/genhash` (upstream commented) +- `operserv/greplog` (upstream commented) +- `operserv/soper` (upstream commented) + +All good additions for a production network. + +### 💡 SUGGESTION — Missing `operserv/set_enforceprefix` + +The upstream modules directory includes `operserv/set_enforceprefix.c` which allows temporarily changing the enforcement prefix via OperServ SET. All other `operserv/set_*` modules are loaded, but this one is missing. + +**Fix:** + +```conf +loadmodule "operserv/set_enforceprefix"; /* SET ENFORCEPREFIX command */ +``` + +### 🟢 OK — MemoServ modules (complete) + +All upstream MemoServ modules loaded, plus `memoserv/main` (explicit core initialization). + +### 🟢 OK — GroupServ modules (complete) + +All upstream GroupServ modules loaded plus `groupserv/invite` (commented out upstream, enabled locally). + +### 🟢 OK — SASL modules + +```conf +loadmodule "saslserv/authcookie"; # For IRIS/web integration +loadmodule "saslserv/plain"; # Basic SASL +loadmodule "saslserv/scram"; # SCRAM-SHA (loaded at line 174) +``` + +Matches upstream (with scram enabled, which upstream has commented). Good security posture. + +### 🟢 OK — HostServ, HelpServ, StatServ, ALIS, ProxyScan + +All fully loaded with comprehensive module sets. These are all commented out in upstream defaults, so the local config is more feature-rich. + +### 🟢 OK — Misc modules (httpd, login_throttling, jsonrpc) + +```conf +loadmodule "misc/httpd"; # Required for WebPanel/JSON-RPC +loadmodule "misc/login_throttling"; # Password brute-force protection +loadmodule "transport/jsonrpc"; # JSON-RPC for WebPanel/portal +``` + +All three correctly loaded. Upstream has these commented out; they're required for the atl.chat infrastructure (WebPanel and portal integration). + +### 🟢 OK — ProxyScan/DNSBL + +```conf +loadmodule "proxyscan/main"; +loadmodule "proxyscan/dnsbl"; +``` + +Enabled (upstream has these commented out). Provides DNSBL checking for connecting users. Good security measure. + +### 🔴 CRITICAL — `chanfix/main` NOT loaded but `chanfix{}` block exists + +```conf +# Line 247 (COMMENTED OUT): +#loadmodule "chanfix/main"; + +# Lines 451-461 (ACTIVE CONFIG BLOCK): +chanfix { + nick = "${ATHEME_CHANFIX_NICK}"; + ... + autofix; +}; +``` + +**Issue:** The `chanfix/main` module is not loaded, but a full `chanfix{}` configuration block is present with `autofix;` enabled. This orphaned configuration block will either: + +- Be silently ignored (wasted config) +- Cause a warning/error at startup + +**Fix:** Either: + +1. Uncomment `loadmodule "chanfix/main";` to enable ChanFix, or +2. Comment out/remove the `chanfix{}` configuration block + +### 💡 SUGGESTION — Consider enabling exttarget modules + +```conf +#loadmodule "exttarget/oper"; +#loadmodule "exttarget/registered"; +#loadmodule "exttarget/channel"; +#loadmodule "exttarget/chanacs"; +#loadmodule "exttarget/server"; +``` + +These are all commented out (matching upstream). Extended targets like `$oper`, `$registered`, and `$channel` are very useful for channel access management on production networks. + +**Recommended minimum:** + +```conf +loadmodule "exttarget/oper"; /* $oper match type */ +loadmodule "exttarget/registered"; /* $registered match type */ +loadmodule "exttarget/channel"; /* $channel match type */ +``` + +### 💡 SUGGESTION — BotServ, GameServ, RPGServ intentionally disabled + +All three services are commented out in the local config, which matches upstream defaults. Their configuration blocks are still present (templated). This is a clean approach — they can be enabled later by uncommenting the loadmodule lines. + +--- + +## D. `operclass` Blocks + +### 🟢 OK — Operclass definitions match upstream exactly + +``` +operclass "user" { }; # ✅ Matches upstream +operclass "ircop" { }; # ✅ Matches upstream (all same privs) +operclass "sra" { }; # ✅ Matches upstream (extends ircop, needoper) +``` + +All privilege sets are identical to upstream. The `sra` class correctly: + +- Extends `ircop` +- Requires `needoper` (must be opered on IRC) +- Has `massakill` and `akill-anymask` commented out (safe default) + +--- + +## E. `operator` Blocks + +### 🔴 CRITICAL — Upstream example operator "jilles" left in config + +```conf +operator "jilles" { + operclass = "sra"; + #password = "$1$3gJMO9by$0G60YE6GqmuHVH3AnFPor1"; +}; +``` + +**Issue:** This is a copy-paste from the upstream example config. "jilles" is Jilles Tjoelker, one of the original Atheme developers. This operator block: + +1. Grants SRA (Super Root Admin) privileges to anyone with a NickServ account named "jilles" +2. Has no password requirement (the password line is commented out) +3. Is not templated via environment variables + +**Risk:** If someone registers the nickname "jilles" on your network and opers up, they would gain full SRA privileges over Atheme services. + +**Fix:** Replace with a properly templated operator block, or better yet, use `operserv/soper` (which is already loaded) for runtime oper management and remove the static block entirely: + +```conf +/* Operator blocks should be managed via SOPER or include from a separate file */ +/* Use OperServ SOPER ADD to grant privileges at runtime */ +``` + +Or if a static block is needed: + +```conf +operator "${ATHEME_SRA_ACCOUNT}" { + operclass = "sra"; +}; +``` + +--- + +## F. Service Bot `nickname` Blocks + +### 🟢 OK — All service bots properly templated + +All 17 service bot blocks use `${ATHEME_*}` template variables: + +| Service | Nick Var | Defaults | +|---------|----------|----------| +| NickServ | `${ATHEME_NICKSERV_NICK}` | NickServ | +| ChanServ | `${ATHEME_CHANSERV_NICK}` | ChanServ | +| OperServ | `${ATHEME_OPERSERV_NICK}` | OperServ | +| MemoServ | `${ATHEME_MEMOSERV_NICK}` | MemoServ | +| SaslServ | `${ATHEME_SASLSERV_NICK}` | SaslServ | +| GroupServ | `${ATHEME_GROUPSERV_NICK}` | GroupServ | +| HostServ | `${ATHEME_HOSTSERV_NICK}` | HostServ | +| HelpServ | `${ATHEME_HELPSERV_NICK}` | HelpServ | +| InfoServ | `${ATHEME_INFOSERV_NICK}` | InfoServ | +| Global | `${ATHEME_GLOBAL_NICK}` | Global | +| ChanFix | `${ATHEME_CHANFIX_NICK}` | ChanFix | +| StatServ | `${ATHEME_STATSERV_NICK}` | StatServ | +| ALIS | `${ATHEME_ALIS_NICK}` | ALIS | +| Proxyscan | `${ATHEME_PROXYSCAN_NICK}` | Proxyscan | +| GameServ | `${ATHEME_GAMESERV_NICK}` | GameServ | +| RPGServ | `${ATHEME_RPGSERV_NICK}` | RPGServ | +| BotServ | `${ATHEME_BOTSERV_NICK}` | BotServ | + +All default values in `.env.example` match upstream conventions. + +### 💡 SUGGESTION — Service bot host values + +All service bots use `services.atl.chat` as their host, which is the network's services hostname. This is consistent and correct. However, the bot hosts are all set via individual env vars (e.g., `${ATHEME_NICKSERV_HOST}`) rather than a single shared variable. Consider if a single `ATHEME_SERVICES_HOST` would reduce `.env` complexity. + +--- + +## G. `nickserv` Settings + +### 🟢 OK — Core settings reasonable + +```conf +maxnicks = 5; # ✅ Matches upstream +expire = 30; # ✅ Matches upstream (30 days) +enforce_expire = 14; # Upstream commented, good value +enforce_delay = 30; # Upstream commented, reasonable (30 seconds) +enforce_prefix = "Guest"; # Upstream commented, standard prefix +``` + +### 🟢 OK — `waitreg_time = 30` + +Upstream default is 0 (disabled). Setting to 30 seconds provides anti-spam protection by requiring users to be connected for 30 seconds before registering. Good security measure. + +### 🟢 OK — `spam` enabled + +Tells users about NickServ/ChanServ on connect. Matches upstream. + +### 🟢 OK — Aliases and shorthelp + +```conf +aliases { + "ID" = "IDENTIFY"; + "MYACCESS" = "LISTCHANS"; +}; +shorthelp = "REGISTER IDENTIFY LOGOUT GROUP DROP GHOST ACCESS CERT SET"; +``` + +Standard aliases. Shorthelp is customized to show the most useful commands. + +### 🟢 OK — `show_custom_metadata`, `listownmail_canon`, `bad_password_message` + +All match upstream defaults. + +--- + +## H. `chanserv` Settings + +### 🟢 OK — Core settings reasonable + +```conf +maxchans = 5; # ✅ Matches upstream +fantasy; # ✅ Matches upstream +trigger = "!"; # ✅ Matches upstream +expire = 30; # ✅ Matches upstream +maxchanacs = 0; # ✅ Matches upstream (unlimited) +maxfounders = 4; # ✅ Matches upstream +``` + +### 🟢 OK — `changets` enabled + +```conf +changets; +``` + +This is commented out in upstream but is a security improvement for UnrealIRCd — it changes channel TS on re-creation to prevent takeovers. Upstream notes it's supported for UnrealIRCd (via the protocol module list). + +### 🟢 OK — Templates with additions + +```conf +templates { + vop = "+AV"; # ✅ Matches upstream + hop = "+AHehitrv"; # ✅ Matches upstream + aop = "+AOehiortv"; # ✅ Matches upstream + sop = "+AOaefhiorstv"; # ✅ Matches upstream + founder = "+AFORaefhioqrstv"; # ✅ Matches upstream + member = "+Ai"; # ➕ Added (upstream has as comment example) + op = "+AOiortv"; # ➕ Added (upstream has as comment example) +}; +deftemplates = "MEMBER=+Ai OP=+AOiortv"; +``` + +Good — enables the member/op templates that upstream only has as examples, and sets them as defaults for new channels. + +### 🟢 OK — `antiflood_enforce_method = quiet` + +Matches upstream default. Uses quiet mode for flood enforcement rather than kickban or akill. + +--- + +## I. `general` Block Settings + +### 🟢 OK — Most settings match upstream + +| Setting | Local | Upstream | Match | +|---------|-------|----------|-------| +| `join_chans` | ✅ | ✅ | ✅ | +| `leave_chans` | ✅ | ✅ | ✅ | +| `uflags = { hidemail; }` | ✅ | ✅ | ✅ | +| `cflags = { guard; verbose; }` | ✅ | ✅ | ✅ | +| `flood_msgs = 7` | ✅ | ✅ | ✅ | +| `flood_time = 10` | ✅ | ✅ | ✅ | +| `ratelimit_uses = 5` | ✅ | ✅ | ✅ | +| `ratelimit_period = 60` | ✅ | ✅ | ✅ | +| `kline_time = 7` | ✅ | ✅ | ✅ | +| `clone_time = 0` | ✅ | ✅ | ✅ | +| `commit_interval = 5` | ✅ | ✅ | ✅ | +| `default_clone_allowed = 5` | ✅ | ✅ | ✅ | +| `default_clone_warn = 4` | ✅ | ✅ | ✅ | +| `clone_identified_increase_limit` | ✅ | ✅ | ✅ | +| `uplink_sendq_limit = 1048576` | ✅ | ✅ | ✅ | +| `language = "en"` | ✅ | ✅ | ✅ | +| `immune_level = immune` | ✅ | ✅ | ✅ | +| `show_entity_id` | ✅ | ✅ | ✅ | +| `load_database_mdeps` | ✅ | ✅ | ✅ | +| `match_masks_through_vhost` | ✅ | ✅ | ✅ | + +### 🟢 OK — `helpchan` and `helpurl` templated + +```conf +helpchan = "${ATHEME_HELP_CHANNEL}"; # .env: #help +helpurl = "${ATHEME_HELP_URL}"; # .env: https://discord.gg/linux +``` + +Both are uncommented and templated (upstream has them commented out). + +--- + +## J. `saslserv` Settings + +### 🟢 OK — Minimal, correct configuration + +```conf +saslserv { + nick = "${ATHEME_SASLSERV_NICK}"; + user = "${ATHEME_SASLSERV_USER}"; + host = "${ATHEME_SASLSERV_HOST}"; + real = "${ATHEME_SASLSERV_REAL}"; + #hide_server_names; +}; +``` + +SaslServ doesn't need aliases or access blocks (upstream docs confirm this). The loaded SASL mechanisms (PLAIN, SCRAM, AUTHCOOKIE) are configured via loadmodule statements. + +### 💡 SUGGESTION — Consider restricting `scram_mechanisms` + +The `crypto{}` block configures `pbkdf2v2_digest = "SCRAM-SHA-256"` but doesn't set `scram_mechanisms` in the crypto block. This means all SCRAM mechanisms (SHA-1, SHA-256, SHA-512) are advertised to clients, but only SCRAM-SHA-256 digests are stored. The other mechanisms will fail for users. + +**Fix:** Add to `crypto{}` block: + +```conf +scram_mechanisms = "SCRAM-SHA-256"; +``` + +--- + +## K. Logging Configuration + +### 🟡 WARNING — Single debug-level logfile, no separation + +```conf +# Local: +logfile "logs/atheme.log" { debug; }; + +# Upstream examples (all commented but suggested): +#logfile "var/account.log" { register; set; }; +#logfile "var/commands.log" { commands; }; +#logfile "var/audit.log" { denycmd; }; +``` + +**Issue:** Everything goes to a single file at debug level. This makes log analysis difficult and the file will grow very quickly. Combined with `serverinfo::loglevel = { debug; }`, this logs raw protocol data, every command, and all debug messages. + +**Fix for production:** Split logs by purpose: + +```conf +logfile "logs/atheme.log" { error; info; admin; network; }; +logfile "logs/commands.log" { commands; }; +logfile "logs/audit.log" { register; set; denycmd; request; }; +``` + +### 💡 SUGGESTION — Consider IRC channel logging + +```conf +#logfile "#services" { debug; }; +#logfile "!snotices" { debug; }; +``` + +Both are commented out. For operations, logging important events to an IRC channel (e.g., `#services`) is valuable for real-time monitoring: + +```conf +logfile "#services" { admin; denycmd; error; info; register; request; }; +``` + +--- + +## L. `memoserv` Settings + +### 🟢 OK — Settings reasonable + +```conf +maxmemos = 50; # Upstream: 30 +``` + +More generous than upstream (50 vs 30 memos). This is fine — provides more inbox capacity. + +### 🟢 OK — Aliases well-configured + +```conf +aliases { + "MAIL" = "SEND"; + "MSG" = "SEND"; + "DEL" = "DELETE"; + "RM" = "DELETE"; +}; +``` + +Upstream has no aliases for MemoServ. These are user-friendly additions. + +--- + +## M. `operserv` Settings + +### 🟢 OK — Properly configured + +```conf +operserv { + nick = "${ATHEME_OPERSERV_NICK}"; + ... + aliases { + "AKILL" = "AKILL"; + "GLINE" = "AKILL"; + "KLINE" = "AKILL"; + }; + modinspect_use_colors; # Upstream has commented out — nice feature +}; +``` + +- Standard GLINE/KLINE→AKILL aliases for operators familiar with other IRC daemons +- `modinspect_use_colors` enabled (cosmetic improvement over upstream) +- All essential OperServ modules loaded (see Section C) + +--- + +## N. Security Settings + +### 🟢 OK — Password hashing (PBKDF2v2 with SCRAM-SHA-256) + +```conf +loadmodule "crypto/pbkdf2v2"; +crypto { + pbkdf2v2_digest = "SCRAM-SHA-256"; + pbkdf2v2_rounds = 64000; + pbkdf2v2_saltlen = 32; +}; +``` + +- Digest: SCRAM-SHA-256 (recommended) +- Rounds: 64,000 (matches upstream default, within Cyrus SASL compat range of 10,000–65,536) +- Salt length: 32 bytes (matches upstream default) +- Loaded before any other crypto modules ✅ + +### 🟢 OK — Login throttling + +```conf +loadmodule "misc/login_throttling"; +throttle { + #address_burst = 5; + #address_replenish = 1; + #address_account_burst = 2; + #address_account_replenish = 2; +}; +``` + +Module loaded. Throttle block uses defaults (all values commented, so Atheme uses built-in defaults). This is fine and matches upstream. + +### 🟢 OK — Flood protection + +```conf +flood_msgs = 7; +flood_time = 10; +``` + +Matches upstream. 7 messages in 10 seconds triggers flood protection. + +### 🟢 OK — Rate limiting + +```conf +ratelimit_uses = 5; +ratelimit_period = 60; +``` + +Matches upstream. 5 rate-limited command uses per 60 seconds. + +### 🟢 OK — DNSBL/Proxyscan + +```conf +loadmodule "proxyscan/main"; +loadmodule "proxyscan/dnsbl"; +proxyscan { + blacklists { + "dnsbl.dronebl.org"; + "rbl.efnetrbl.org"; + "tor.efnet.org"; + "dnsbl.tornevall.org"; # ➕ Additional (not in upstream) + "bl.spamcop.net"; # ➕ Additional (not in upstream) + }; + dnsbl_action = kline; +}; +``` + +More comprehensive DNSBL list than upstream (5 vs 3 providers). `kline` action is the standard response. + +### 🟢 OK — Docker entrypoint security + +```bash +if [ "$(id -u)" = "0" ]; then + echo "ERROR: Atheme should not run as root for security reasons" + exit 1 +fi +``` + +The entrypoint correctly refuses to run as root. The Containerfile switches to the `atheme` user (UID 1000). + +### 🟢 OK — Containerfile build security + +```dockerfile +./configure \ + --prefix=/usr/local/atheme \ + --enable-contrib \ + --with-modulesdir=/usr/local/atheme/modules \ + --with-libidn \ # ✅ Required for SCRAM-SHA support + --enable-large-net \ # ✅ Good for scalability + --disable-linker-defs +``` + +- `--with-libidn` is required for SCRAM-SHA support (matches the comment in the config template) +- `--enable-contrib` builds contrib modules +- `--enable-large-net` optimizes for larger networks + +--- + +## O. HTTPd / JSON-RPC Configuration + +### 🟢 OK — HTTPd properly configured + +```conf +httpd { + host = "0.0.0.0"; + www_root = "/var/www"; + port = ${ATHEME_HTTPD_PORT}; # .env: 8081 +}; +``` + +- Listening on all interfaces (`0.0.0.0`) is correct since it's inside a Docker container +- Port templated via env var +- `www_root` is standard + +### 🟢 OK — JSON-RPC transport loaded + +```conf +loadmodule "transport/jsonrpc"; +``` + +Required for WebPanel and portal integration. The compose file maps port 8081 through the UnrealIRCd container (since Atheme shares its network namespace). + +### 💡 SUGGESTION — Consider adding transport/xmlrpc for broader compatibility + +```conf +#loadmodule "transport/xmlrpc"; +``` + +XMLRPC is commented out (matching upstream). If only JSON-RPC is needed for the WebPanel/portal, this is fine. JSON-RPC is the more modern choice. + +--- + +## P. Missing Blocks/Settings + +### 🔴 CRITICAL — `chanfix{}` block without `chanfix/main` module (see C above) + +The `chanfix{}` configuration block with `autofix;` is present but the module isn't loaded. + +### 🟡 WARNING — No `include` directive for operator management + +Upstream suggests: + +```conf +include "etc/sras.conf"; +``` + +All operator definitions are inline in the main config. For a Docker deployment with templated configs, consider either: + +- Templating operator blocks via env vars, or +- Using `operserv/soper` exclusively for runtime oper management (module is already loaded) + +### 💡 SUGGESTION — Missing `nickserv/multimark` module + +Upstream modules directory contains `nickserv/multimark.c` which is not referenced in either config. This module allows operators to add multiple marks to accounts, which is useful for tracking problematic users. + +### 💡 SUGGESTION — Missing `chanserv/set` (core SET router) + +The upstream modules include `chanserv/set.c` and `chanserv/set_core.c`. While individual `set_*` modules are loaded, the main `chanserv/set` router and `nickserv/set` router may be auto-loaded as dependencies. Verify these are functioning correctly. + +### 💡 SUGGESTION — `statserv/pwhashes` module available but not loaded + +The upstream modules include `statserv/pwhashes.c` which provides statistics on password hash types in use. Useful for monitoring crypto migration progress. + +--- + +## Summary + +| Severity | Count | Key Items | +|----------|-------|-----------| +| 🔴 CRITICAL | 2 | "jilles" operator block; orphaned `chanfix{}` block | +| 🟡 WARNING | 4 | Debug loglevel; missing pbkdf2 v1 compat; duplicate scram ref; single log file | +| 🟢 OK | 35+ | Vast majority of configuration matches or exceeds upstream | +| 💡 SUGGESTION | 8+ | scram_mechanisms, exttarget, enforceprefix, log separation, etc. | + +### Priority Fixes + +1. **🔴 Remove or replace `operator "jilles"` block** — immediate security concern +2. **🔴 Fix `chanfix` module/block mismatch** — either enable the module or remove the block +3. **🟡 Reduce loglevel for production** — `debug` is too verbose and may log sensitive data +4. **🟡 Clean up duplicate `saslserv/scram` reference** — reduces confusion +5. **🟡 Add `crypto/pbkdf2` for migration safety** — protects against future Atheme version upgrades +6. **🟡 Split log files by category** — improves operational visibility + +### What's Done Well + +- Comprehensive module selection (more features than upstream defaults) +- Proper environment variable templating throughout +- Strong crypto configuration (PBKDF2v2 with SCRAM-SHA-256) +- Good security modules (login throttling, DNSBL, flood protection) +- Correct protocol selection for UnrealIRCd +- Well-structured Docker setup with non-root execution +- JSON-RPC properly configured for WebPanel/portal integration +- Network-appropriate settings (casemapping, changets, etc.) From 1fe0212e851fcdcd0e1e7ae69aca220d3b43dafa Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 01:58:02 +0000 Subject: [PATCH 06/70] fix(irc): address critical and warning findings from config audit Critical fixes: - Add drpass block for /DIE and /RESTART protection (IRC_DRPASS env var) - Template WEBIRC password via ATL_WEBIRC_PASSWORD env var (was hardcoded) - Restrict admin oper mask from *@* to Docker network (172.16.0.0/12) - Require TLS for oper authentication (require-modes z) Warning fixes: - Add connthrottle config for connection throttling during attacks - Add spamfilter defaults (ban-time, ban-reason, virus-help-channel) - Add allow-user-stats to restrict sensitive stats to opers - Restrict link block incoming mask to Docker network + localhost - Restrict RPC user match to Docker network + localhost - Tighten connect-flood from 20:10 to 5:60 for production --- .env.example | 2 + .../config/unrealircd.conf.template | 48 +++++++++++++++---- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/.env.example b/.env.example index 6d3869e4..6b6484c9 100644 --- a/.env.example +++ b/.env.example @@ -69,6 +69,8 @@ IRC_STAFF_VHOST=allthingslinux.org # Security Configuration IRC_OPER_PASSWORD='$argon2id$v=19$m=6144,t=2,p=2$WXOLpTE+DPDr8q6OBVTx3w$bqXpBsaAK6lkXfR/IPn+TcE0VJEKjUFD7xordE6pFSo' +IRC_DRPASS=change_me_drpass +ATL_WEBIRC_PASSWORD=change_me_webirc_password IRC_STS_DURATION=1m IRC_STS_PRELOAD=no diff --git a/apps/unrealircd/config/unrealircd.conf.template b/apps/unrealircd/config/unrealircd.conf.template index 29fc18f1..78b8aaec 100644 --- a/apps/unrealircd/config/unrealircd.conf.template +++ b/apps/unrealircd/config/unrealircd.conf.template @@ -369,7 +369,7 @@ me { /* Support Proxy Protocol from NPM on the same network */ webirc { mask ${ATL_GATEWAY_IP}/32; - password "change_me_webirc_password"; + password "${ATL_WEBIRC_PASSWORD}"; } /* The Lounge web client - passes real user IP via WEBIRC */ @@ -538,11 +538,11 @@ set { } } -/* Link block for services - localhost connections are exempt from link security */ +/* Link block for services - restricted to Docker network and localhost */ link ${IRC_SERVICES_SERVER} { incoming { - /* Allow all connections for services - Docker shared network */ - mask *; + mask 172.16.0.0/12; + mask 127.0.0.0/8; password "${IRC_SERVICES_PASSWORD}"; } password "${IRC_SERVICES_PASSWORD}"; @@ -757,15 +757,21 @@ set { cloak-method ip; } +/* Die/Restart password: protects /DIE and /RESTART from accidental or unauthorized use. */ +drpass { + restart "${IRC_DRPASS}"; + die "${IRC_DRPASS}"; +} + /* Operator Configuration */ oper admin { class opers; - mask *@*; + mask *@172.16.0.0/12; password "${IRC_OPER_PASSWORD}"; operclass netadmin-with-override; swhois "is the Network Administrator"; vhost "${IRC_STAFF_VHOST}"; - require-modes ""; + require-modes "z"; } /* Bridge oper: creates channels and sets +P (permanent) so they persist when empty. @@ -788,7 +794,7 @@ oper atl-bridge { /* RPC Configuration for Webpanel */ rpc-user "${WEBPANEL_RPC_USER}" { - match { ip *; } + match { ip 172.16.0.0/12; ip 127.0.0.0/8; } rpc-class full; password "${WEBPANEL_RPC_PASSWORD}"; } @@ -864,6 +870,30 @@ set { maxchannelsperuser 10; /* maximum number of channels a user may /JOIN */ + /* Allow only safe stats for regular users; everything else is oper-only */ + allow-user-stats "kMp"; + + /* Spamfilter global defaults */ + spamfilter { + ban-time 1d; + ban-reason "Spam/Advertising"; + virus-help-channel "#help"; + } + + /* Connection throttling: limits new connections during attacks. + * Exempts identified users and those with established reputation. */ + connthrottle { + except { reputation-score 24; identified yes; } + new-users { + local-throttle 20:60; + global-throttle 30:60; + } + disabled-when { + reputation-gathering 1w; + start-delay 3m; + } + } + /* The minimum time a user must be connected before being allowed to * use a QUIT message. This will hopefully help stop spam. */ @@ -909,8 +939,8 @@ set { /* Settings that apply to everyone */ everyone { - /* Connection flood protection - relaxed for testing: 20 connections per 10 seconds per IP */ - connect-flood 20:10; + /* Connection flood protection: 5 connections per 60 seconds per IP */ + connect-flood 5:60; /* Handshake data flood protection */ handshake-data-flood { From 28d0d830eb6a2e8f8a0be068341a67f8873846e9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 01:58:18 +0000 Subject: [PATCH 07/70] fix(atheme): address critical and warning findings from config audit Critical fixes: - Remove insecure 'operator jilles' block (SRA to anyone with that nick) - Comment out orphaned chanfix{} block (chanfix/main module not loaded) Warning fixes: - Reduce loglevel from debug to production-appropriate categories - Remove duplicate commented saslserv/scram reference --- apps/atheme/config/atheme.conf.template | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/atheme/config/atheme.conf.template b/apps/atheme/config/atheme.conf.template index 493e52a7..32b4ad50 100644 --- a/apps/atheme/config/atheme.conf.template +++ b/apps/atheme/config/atheme.conf.template @@ -255,7 +255,7 @@ loadmodule "operserv/soper"; /* Non-config oper privileges (SOPER command) */ #loadmodule "saslserv/ecdh-x25519-challenge"; /* ECDH-X25519-CHALLENGE mechanism */ #loadmodule "saslserv/ecdsa-nist256p-challenge"; /* ECDSA-NIST256P-CHALLENGE mechanism */ #loadmodule "saslserv/external"; /* EXTERNAL mechanism (IRCv3.1+) */ -#loadmodule "saslserv/scram"; /* SCRAM-SHA mechanisms - More secure than PLAIN */ +/* saslserv/scram already loaded above in the SASL section (line 174) */ #loadmodule "gameserv/dice"; /* DICE/WOD commands */ #loadmodule "gameserv/eightball"; /* EIGHTBALL command */ #loadmodule "gameserv/gamecalc"; /* Game-specific dice calculators */ @@ -353,7 +353,7 @@ serverinfo { registeremail = "${ATHEME_REGISTER_EMAIL}"; #hidden; mta = "/usr/sbin/sendmail"; - loglevel = { debug; }; + loglevel = { admin; error; info; network; wallops; register; }; maxcertfp = 0; maxlogins = 5; maxusers = 5; @@ -448,6 +448,8 @@ chanserv { shorthelp = "REGISTER DROP OP DEOP VOICE DEVOICE AKICK FLAGS INFO TOPIC INVITE"; }; +/* chanfix: module not loaded (chanfix/main is commented out above). + * Enable loadmodule "chanfix/main" and uncomment this block to use ChanFix. chanfix { nick = "${ATHEME_CHANFIX_NICK}"; user = "${ATHEME_CHANFIX_USER}"; @@ -459,6 +461,7 @@ chanfix { }; autofix; }; +*/ global { nick = "${ATHEME_GLOBAL_NICK}"; @@ -785,9 +788,8 @@ operclass "sra" { needoper; }; -operator "jilles" { - operclass = "sra"; - #password = "$1$3gJMO9by$0G60YE6GqmuHVH3AnFPor1"; -}; +/* SRA operators are managed at runtime via OperServ SOPER command: + * /msg OperServ SOPER ADD sra + * No static operator blocks needed — operserv/soper module is loaded. */ /* ---------- Operclass and operator (include sras.conf etc. if needed) ---------- */ From c5ccb8366ab1b14e463686b21218a21330bb7e1e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 02:37:09 +0000 Subject: [PATCH 08/70] fix(irc): apply findings from deep wiki documentation review From reading every page on the UnrealIRCd wiki (Set_block, Security, Configuration): - Add set::gline-address (was missing; only kline-address was set) - Add set::snomask-on-oper +bBcdfkqsSo (recommended default for full server notice visibility) - Add +H to modes-on-oper (hides oper idle time from non-opers) - Add restrict-commands for private-message and private-notice with 120s connect-delay for unidentified users (Security page recommendation to combat spam bots, exempts identified/webirc/reputation users) --- apps/unrealircd/config/unrealircd.conf.template | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/unrealircd/config/unrealircd.conf.template b/apps/unrealircd/config/unrealircd.conf.template index 78b8aaec..8271e0a4 100644 --- a/apps/unrealircd/config/unrealircd.conf.template +++ b/apps/unrealircd/config/unrealircd.conf.template @@ -847,9 +847,11 @@ log { /* Server specific configuration */ set { kline-address '${IRC_ADMIN_EMAIL}'; /* e-mail or URL shown when a user is banned */ + gline-address '${IRC_ADMIN_EMAIL}'; /* e-mail shown on G-lines */ modes-on-connect "+ixw"; /* when users connect, they will get these user modes */ - modes-on-oper "+xws"; /* when someone becomes IRCOp they'll get these modes */ + modes-on-oper "+xwsH"; /* when someone becomes IRCOp they'll get these modes */ + snomask-on-oper "+bBcdfkqsSo"; /* server notice mask for opers */ modes-on-join "+nt"; /* default channel modes when a new channel is created */ /* Restrict user modes - prevent users from disabling cloaking for security */ @@ -910,7 +912,8 @@ set { } }; - /* Restrict commands for new users (security hardening) */ + /* Restrict commands for new users (security hardening) + * See: https://www.unrealircd.org/docs/Security#Limiting_commands */ restrict-commands { list { except { identified yes; reputation-score 24; } @@ -920,6 +923,14 @@ set { except { identified yes; reputation-score 24; } connect-delay 120s; } + private-message { + except { identified yes; webirc yes; reputation-score 24; } + connect-delay 120s; + } + private-notice { + except { identified yes; webirc yes; reputation-score 24; } + connect-delay 120s; + } } /* Anti-flood settings for enhanced security */ From 26143f600466be87a1db36edb5d166a6fe42fca9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 03:15:58 +0000 Subject: [PATCH 09/70] fix(prosody): address critical findings from docs cross-reference audit Browsed every page on prosody.im/doc and modules.prosody.im, read the full config/security/certificates/ports/modules/storage/MAM/ file_share/MUC docs plus community module pages. Critical fixes: - .env.example: flip TLS/auth defaults to secure (c2s_require_encryption, s2s_require_encryption, s2s_secure_auth = true; unencrypted_plain_auth = false) - .env.dev.example: add insecure overrides for local dev (self-signed certs) - Uncomment global ssl block (protocol tlsv1_2+, strong ciphers, secp384r1) - Restrict http_status_allow_cidr from 0.0.0.0/0 to Docker network Warning fixes: - Disable legacyauth module (use SASL SCRAM-SHA-256 instead) - Enable dont_archive_namespaces to exclude typing indicators and Jingle Added: docs/audits/prosody-config-audit.md with full findings Note: luacheck warnings on Prosody globals (dont_archive_namespaces, https_ssl) are expected - these are valid Prosody config directives, not Lua standard globals. --- .env.dev.example | 6 + .env.example | 8 +- apps/prosody/config/prosody.cfg.lua | 28 ++-- docs/audits/prosody-config-audit.md | 217 ++++++++++++++++++++++++++++ 4 files changed, 241 insertions(+), 18 deletions(-) create mode 100644 docs/audits/prosody-config-audit.md diff --git a/.env.dev.example b/.env.dev.example index 4f147a5f..9b31d1ac 100644 --- a/.env.dev.example +++ b/.env.dev.example @@ -62,6 +62,12 @@ BRIDGE_PORTAL_BASE_URL= # Disable IRC TLS cert verification (self-signed dev certs) BRIDGE_IRC_TLS_VERIFY=false +# Relax Prosody TLS requirements for dev (self-signed certs can't pass s2s_secure_auth) +PROSODY_C2S_REQUIRE_ENCRYPTION=false +PROSODY_S2S_REQUIRE_ENCRYPTION=false +PROSODY_S2S_SECURE_AUTH=false +PROSODY_ALLOW_UNENCRYPTED_PLAIN_AUTH=true + # RELAYMSG: use clean nicks (no /d suffix) when UnrealIRCd relaymsg has require-separator no BRIDGE_RELAYMSG_CLEAN_NICKS=true diff --git a/.env.example b/.env.example index 6b6484c9..dda71d3e 100644 --- a/.env.example +++ b/.env.example @@ -223,15 +223,15 @@ PROSODY_DB_PASSWORD=change_me_secure_db_pass # Security Settings # Registration: false = Portal provisions users via mod_http_admin_api; true = allow self-registration (dev only) PROSODY_ALLOW_REGISTRATION=false -PROSODY_C2S_REQUIRE_ENCRYPTION=false -PROSODY_S2S_REQUIRE_ENCRYPTION=false -PROSODY_S2S_SECURE_AUTH=false +PROSODY_C2S_REQUIRE_ENCRYPTION=true +PROSODY_S2S_REQUIRE_ENCRYPTION=true +PROSODY_S2S_SECURE_AUTH=true PROSODY_MAX_CONNECTIONS_PER_IP=10 PROSODY_REGISTRATION_THROTTLE_MAX=10 PROSODY_REGISTRATION_THROTTLE_PERIOD=60 PROSODY_BLOCK_REGISTRATIONS_REQUIRE=^[a-zA-Z0-9_.-]+$ PROSODY_TLS_CHANNEL_BINDING=false -PROSODY_ALLOW_UNENCRYPTED_PLAIN_AUTH=true +PROSODY_ALLOW_UNENCRYPTED_PLAIN_AUTH=false # Networking & HTTP PROSODY_HTTP_HOST=localhost diff --git a/apps/prosody/config/prosody.cfg.lua b/apps/prosody/config/prosody.cfg.lua index a484c34a..b87c0cdf 100644 --- a/apps/prosody/config/prosody.cfg.lua +++ b/apps/prosody/config/prosody.cfg.lua @@ -14,7 +14,7 @@ modules_enabled = { -- CORE PROTOCOL MODULES (Required) -- =============================================== "roster", -- Allow users to have a roster/contact list (RFC 6121) - "legacyauth", -- Legacy authentication. Only used by some old clients and bots. + -- "legacyauth", -- Legacy authentication. Disabled: use SASL (SCRAM-SHA-256) instead. "saslauth", -- SASL authentication for clients and servers (RFC 4422) "tls", -- TLS encryption support for c2s/s2s connections (RFC 6120) "dialback", -- Server-to-server authentication via dialback (XEP-0220) @@ -196,11 +196,11 @@ max_archive_query_results = Lua.tonumber(Lua.os.getenv("PROSODY_ARCHIVE_MAX_QUER mam_smart_enable = Lua.os.getenv("PROSODY_MAM_SMART_ENABLE") == "true" -- Disable smart archiving --- Namespaces to exclude from archiving --- dont_archive_namespaces = { --- "http://jabber.org/protocol/chatstates", -- Chat state notifications --- "urn:xmpp:jingle-message:0", -- Jingle messages --- } +-- Namespaces to exclude from archiving (typing indicators, call signaling) +dont_archive_namespaces = { + "http://jabber.org/protocol/chatstates", + "urn:xmpp:jingle-message:0", +} -- =============================================== -- MOBILE CLIENT OPTIMIZATIONS @@ -442,8 +442,8 @@ http_paths = { -- Alternative: Allow access from specific IPs (more secure) -- http_status_allow_ips = { "127.0.0.1"; "::1"; "172.18.0.0/16"; "76.215.15.63" } --- Allow access from any IP using CIDR notation (0.0.0.0/0 covers all IPv4) -http_status_allow_cidr = "0.0.0.0/0" +-- Restrict status endpoint to Docker network and localhost +http_status_allow_cidr = "172.16.0.0/12" -- =============================================== -- TURN/STUN EXTERNAL SERVICES (XEP-0215) @@ -528,12 +528,12 @@ anti_spam_services = { "xmppbl.org" } -- Global TLS configuration. See: -- https://prosody.im/doc/certificates -- https://prosody.im/doc/security --- ssl = { --- protocol = "tlsv1_2+", --- ciphers = "ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS", --- curve = "secp384r1", --- options = { "cipher_server_preference", "single_dh_use", "single_ecdh_use" }, --- } +ssl = { + protocol = "tlsv1_2+", + ciphers = "ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS", + curve = "secp384r1", + options = { "cipher_server_preference", "single_dh_use", "single_ecdh_use" }, +} -- Let's Encrypt certificate location (mounted into the container) certificates = "certs" diff --git a/docs/audits/prosody-config-audit.md b/docs/audits/prosody-config-audit.md new file mode 100644 index 00000000..64a19e04 --- /dev/null +++ b/docs/audits/prosody-config-audit.md @@ -0,0 +1,217 @@ +# Prosody XMPP Configuration Audit Report + +**Date:** 2026-02-26 +**Auditor:** Cloud Agent (manual wiki + modules.prosody.im review) +**Local config:** `apps/prosody/config/prosody.cfg.lua` +**References:** prosody.im/doc (configure, security, certificates, ports, modules, storage, mod_mam, mod_http_file_share, mod_muc) + modules.prosody.im (mod_anti_spam, mod_cloud_notify, mod_muc_limits, mod_http_admin_api, mod_firewall) + +--- + +## Findings + +### 🔴 CRITICAL — Insecure `.env.example` defaults for TLS/auth + +The `.env.example` ships with: + +``` +PROSODY_C2S_REQUIRE_ENCRYPTION=false +PROSODY_S2S_REQUIRE_ENCRYPTION=false +PROSODY_S2S_SECURE_AUTH=false +PROSODY_ALLOW_UNENCRYPTED_PLAIN_AUTH=true +``` + +The Prosody Security docs state: + +- `c2s_require_encryption` should be **true** (default in Prosody). "Almost all clients will use SSL/TLS out of the box." +- `s2s_require_encryption` should be **true** (default in Prosody). "By default Prosody requires encrypted server-to-server connections." +- `s2s_secure_auth` should be **true** for strong authentication. With `false`, Prosody falls back to DNS dialback which is weaker. +- `allow_unencrypted_plain_auth = true` allows plaintext passwords over unencrypted connections — a serious security risk. + +**Fix:** Change `.env.example` defaults to secure values. Move insecure overrides to `.env.dev.example` for local development only. + +### 🔴 CRITICAL — Global `ssl` block is commented out + +Lines 531-536 have the TLS configuration entirely commented out: + +```lua +-- ssl = { +-- protocol = "tlsv1_2+", +-- ciphers = "ECDHE+AESGCM:ECDHE+CHACHA20:...", +-- curve = "secp384r1", +-- options = { "cipher_server_preference", "single_dh_use", "single_ecdh_use" }, +-- } +``` + +This means Prosody uses its built-in defaults. While Prosody's defaults are reasonable, the Security docs recommend explicitly configuring TLS for production. The commented config shows good settings — they should be enabled. + +**Fix:** Uncomment the global `ssl` block. + +### 🟡 WARNING — `legacyauth` module enabled + +Line 18 loads `legacyauth`: + +```lua +"legacyauth", -- Legacy authentication. Only used by some old clients and bots. +``` + +The Prosody docs describe this as supporting only "some old clients and bots." For a modern XMPP deployment with SCRAM-SHA-256 SASL auth, legacy auth is unnecessary and widens the attack surface. + +**Fix:** Comment out or remove `legacyauth` unless specific legacy clients require it. + +### 🟡 WARNING — `http_status_allow_cidr = "0.0.0.0/0"` (world-open) + +Line 446: + +```lua +http_status_allow_cidr = "0.0.0.0/0" +``` + +This exposes the HTTP status monitoring endpoint to the entire internet. While the status page itself may not contain sensitive data, it leaks server operational information to potential attackers. + +**Fix:** Restrict to Docker network and localhost: + +```lua +http_status_allow_cidr = "172.16.0.0/12" +``` + +### 🟡 WARNING — `archive_expires_after` defaults differ from upstream + +The config defaults to `"1y"` (1 year) if the env var is not set: + +```lua +archive_expires_after = ... or "1y" +``` + +The Prosody MAM docs state the default is `"1w"` (1 week). While 1 year is a valid choice for a community server, it should be a deliberate decision, and the storage implications should be considered (SQLite with 1 year of archives for many users will grow large). + +**Status:** Acknowledged as intentional, but ensure monitoring is in place for database size. + +### 🟡 WARNING — `max_connections_per_ip` may be too restrictive + +Line 517: + +```lua +max_connections_per_ip = ... or 5 +``` + +The default of 5 per IP is fine for individual users, but in a Docker environment where the bridge and other services connect from the same IP, this could be restrictive. The bridge container shares the Docker network. + +**Fix:** Consider increasing for Docker IPs or ensure the bridge connects from a recognizable IP that's exempted. + +### 🟢 OK — Module selection comprehensive and modern + +The config loads an excellent set of modules covering: + +- Core protocol (roster, saslauth, tls, dialback, disco, presence, message, iq) +- Modern messaging (mam, carbons, smacks, offline) +- Mobile optimization (csi, csi_battery_saver, cloud_notify) +- Security (blocklist, anti_spam, spam_reporting, report_forward, admin_blocklist, mimicking, tombstones) +- XMPP compliance (server_contact_info, server_info, compliance_latest) +- Web services (http, bosh, websocket, conversejs, http_files) +- S2S enhancements (s2s_bidi, s2s_keepalive, s2s_status) + +This exceeds typical Prosody deployments and covers XEP compliance well. + +### 🟢 OK — Authentication properly configured + +```lua +authentication = "internal_hashed" +sasl_mechanisms = { "SCRAM-SHA-256", "SCRAM-SHA-1" } +``` + +Uses hashed storage (as recommended by Security docs) with modern SCRAM-SHA-256 as primary mechanism. SCRAM-SHA-1 kept for compatibility. + +### 🟢 OK — Anti-spam with xmppbl.org RTBL + +```lua +anti_spam_services = { "xmppbl.org" } +``` + +Matches the mod_anti_spam docs recommendation for subscribing to shared block lists. + +### 🟢 OK — Push notification privacy settings + +```lua +push_notification_with_body = false +push_notification_with_sender = false +``` + +Matches mod_cloud_notify docs: "Not recommended" to enable these due to privacy implications. The config correctly keeps them disabled. + +### 🟢 OK — Rate limiting properly configured + +The `limits` block with c2s, s2s, and http_upload rate limits is well-structured and uses env vars for customization. + +### 🟢 OK — MUC limits match upstream defaults + +All `muc_limits` settings match the module defaults exactly (muc_event_rate=0.5, muc_burst_factor=6, etc.). + +### 🟢 OK — HTTP security headers + +The `http_headers` block includes HSTS, X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, Referrer-Policy, and Content-Security-Policy. This is excellent and exceeds typical XMPP deployments. + +### 🟢 OK — TURN/STUN external configuration + +Properly configured with shared secret, hostname, ports, TTL, and TCP support. + +### 🟢 OK — Storage backend assignments + +Comprehensive storage mapping with SQL for persistent data and memory for ephemeral data (caps, carbons). Correct pattern. + +### 🟢 OK — Certificate handling in docker-entrypoint.sh + +Thorough certificate setup with Let's Encrypt layout, legacy fallback, and self-signed generation. HTTPS service discovery symlinks properly created. + +### 🟢 OK — Trusted proxies + +```lua +trusted_proxies = { "127.0.0.1", "172.16.0.0/12", "10.0.0.0/8" } +``` + +Covers localhost and Docker networks. + +### 💡 SUGGESTION — Consider enabling `mod_register` for password changes + +Line 75 disables register entirely: + +```lua +-- "register", -- Password changes (XEP-0077); registration disabled +``` + +While self-registration should stay disabled (Portal provisions), `mod_register` is also needed for **password changes** by existing users. Consider enabling it with `allow_registration = false` to allow password changes without self-registration. + +### 💡 SUGGESTION — Consider `dont_archive_namespaces` + +Lines 200-203 have sensible namespace exclusions commented out: + +```lua +-- dont_archive_namespaces = { +-- "http://jabber.org/protocol/chatstates", +-- "urn:xmpp:jingle-message:0", +-- } +``` + +Enabling these would reduce archive storage by excluding chat state notifications (typing indicators) and Jingle call signaling. + +### 💡 SUGGESTION — Enable `mod_log_slow_events` + +The module is listed in `modules.list` (line 30) but not in `modules_enabled`. This module helps identify performance bottlenecks. + +### 💡 SUGGESTION — Enable `mod_reload_modules` + +Listed in `modules.list` (line 32) but not enabled. Allows modules to be reloaded on config change without full restart. + +--- + +## Summary + +| Severity | Count | Key Items | +|----------|-------|-----------| +| 🔴 CRITICAL | 2 | Insecure `.env.example` TLS/auth defaults; global ssl block commented out | +| 🟡 WARNING | 3 | legacyauth enabled; http_status world-open; MAM retention vs upstream | +| 🟢 OK | 12+ | Module selection, auth, anti-spam, push privacy, rate limits, MUC limits, HTTP headers, TURN, storage, certs, proxies | +| 💡 SUGGESTION | 4 | mod_register for password changes, dont_archive_namespaces, mod_log_slow_events, mod_reload_modules | + +### Overall Assessment + +The Prosody configuration is **excellent** for a Docker-deployed XMPP server. The module selection is comprehensive and modern, covering XMPP compliance, mobile optimization, spam prevention, and web services. The main issues are the insecure `.env.example` defaults and the commented-out global TLS block — both straightforward to fix. The HTTP security headers, rate limiting, and storage configuration all follow best practices. From 03c1ebc99b67ab8e9c162470ad119905f5b4cddd Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 06:01:14 +0000 Subject: [PATCH 10/70] docs: exhaustive env var audit across atl.chat monorepo Scanned all sources: .env.example, .env.dev.example, all compose files, config templates (UnrealIRCd, Atheme, Prosody, Bridge, The Lounge), scripts (init.sh, prepare-config.sh, cert-manager/run.sh), web app env, and bridge Python code. Comprehensive table with 150+ variables cross-referenced across 6 columns. Findings: - Category A: 37 orphaned vars (defined but never consumed) - Category B: 33 undefined vars (consumed but not in .env.example) - Category C: 6 inconsistency issues (XMPP port naming mismatch, etc.) - Category D: 12 hardcoded values that could be env vars - Category E: ~30 dead vars (PostgreSQL, Adminer, Nginx with no compose service) - Category F: 14 duplicate variable pairs Priority fixes: port naming mismatch, missing critical vars, dead service sections. --- ENV_AUDIT.md | 513 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 513 insertions(+) create mode 100644 ENV_AUDIT.md diff --git a/ENV_AUDIT.md b/ENV_AUDIT.md new file mode 100644 index 00000000..07a0e82f --- /dev/null +++ b/ENV_AUDIT.md @@ -0,0 +1,513 @@ +# Environment Variable Audit — atl.chat Monorepo + +> Generated: 2026-02-26 | Scope: Full 12-factor env var cleanup + +--- + +## Comprehensive Variable Table + +### 1. CORE PROJECT & ENVIRONMENT + +| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | +|----------|---------------|-------------------|---------------|-----------------|---------|------| +| `ATL_PROJECT_NAME` | ✅ `atl-chat` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `ATL_BASE_DOMAIN` | ✅ `atl.chat` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `ATL_ENVIRONMENT` | ✅ `dev` | ✅ `dev` | bridge.yaml | ❌ | prepare-config.sh | schema.py (`_ENV_OVERRIDE_KEYS`) | +| `PUID` | ✅ `1000` | ❌ | irc.yaml (build args, env), thelounge.yaml (user) | ❌ | ❌ | ❌ | +| `PGID` | ✅ `1000` | ❌ | irc.yaml (build args, env), thelounge.yaml (user) | ❌ | ❌ | ❌ | +| `TZ` | ✅ `UTC` | ❌ | irc.yaml (env) | ❌ | ❌ | ❌ | + +### 2. GLOBAL NETWORKING + +| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | +|----------|---------------|-------------------|---------------|-----------------|---------|------| +| `ATL_GATEWAY_IP` | ✅ `100.64.1.0` | ❌ | irc.yaml (env) | unrealircd.conf.template (`webirc mask`) | ❌ | ❌ | +| `ATL_CHAT_IP` | ✅ `100.64.7.0` | ✅ `127.0.0.1` | irc.yaml (ports), xmpp.yaml (indirectly via port bindings using defaults) | ❌ | ❌ | ❌ | +| `IRC_TLS_PORT` | ✅ `6697` | ❌ | irc.yaml (ports) | bridge config.template.yaml | ❌ | ❌ | +| `IRC_SERVER_PORT` | ✅ `6900` | ❌ | irc.yaml (ports) | ❌ | ❌ | ❌ | +| `IRC_RPC_PORT` | ✅ `8600` | ❌ | irc.yaml (ports) | ❌ | ❌ | ❌ | +| `IRC_WEBSOCKET_PORT` | ✅ `8000` | ❌ | irc.yaml (ports) | ❌ | ❌ | ❌ | +| `XMPP_C2S_PORT` | ✅ `5222` | ❌ | xmpp.yaml (ports, as `PROSODY_C2S_PORT` fallback) | ❌ | ❌ | ❌ | +| `XMPP_S2S_PORT` | ✅ `5269` | ❌ | xmpp.yaml (ports, as `PROSODY_S2S_PORT` fallback) | ❌ | ❌ | ❌ | +| `XMPP_HTTP_PORT` | ✅ `5280` | ❌ | xmpp.yaml (ports, as `PROSODY_HTTP_PORT` fallback) | ❌ | ❌ | ❌ | +| `XMPP_HTTPS_PORT` | ✅ `5281` | ❌ | xmpp.yaml (ports, as `PROSODY_HTTPS_PORT` fallback) | ❌ | ❌ | ❌ | +| `TURN_PORT` | ✅ `3478` | ❌ | ❌ | prosody.cfg.lua (`turn_external_port`) | ❌ | ❌ | +| `TURNS_PORT` | ✅ `5349` | ❌ | ❌ | prosody.cfg.lua (`turn_external_tls_port`) | ❌ | ❌ | + +### 3. IRC SERVICE (UnrealIRCd & Atheme) + +| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | +|----------|---------------|-------------------|---------------|-----------------|---------|------| +| `UNREALIRCD_VERSION` | ✅ `6.2.0.1` | ❌ | irc.yaml (build arg) | ❌ | ❌ | ❌ | +| `ATHEME_VERSION` | ✅ `master` | ❌ | irc.yaml (build arg) | ❌ | ❌ | ❌ | +| `IRC_DOMAIN` | ✅ `irc.atl.chat` | ✅ `irc.localhost` | irc.yaml (env) | unrealircd.conf.template (many), atheme.conf.template (uplink) | init.sh, prepare-config.sh | ❌ | +| `IRC_ROOT_DOMAIN` | ✅ `atl.chat` | ❌ | cert-manager.yaml (env) | ❌ | cert-manager/run.sh | ❌ | +| `IRC_NETWORK_NAME` | ✅ `"All Things Linux IRC"` | ❌ | ❌ | unrealircd.conf.template (`me`, `set`) | ❌ | thelounge config.js.template | +| `IRC_CLOAK_PREFIX` | ✅ `atl` | ❌ | ❌ | unrealircd.conf.template (`hiddenhost-prefix`) | prepare-config.sh | ❌ | +| `IRC_CLOAK_KEY_1` | ✅ (hash) | ❌ | ❌ | unrealircd.conf.template (`cloak-keys`) | prepare-config.sh | ❌ | +| `IRC_CLOAK_KEY_2` | ✅ (hash) | ❌ | ❌ | unrealircd.conf.template (`cloak-keys`) | prepare-config.sh | ❌ | +| `IRC_CLOAK_KEY_3` | ✅ (hash) | ❌ | ❌ | unrealircd.conf.template (`cloak-keys`) | prepare-config.sh | ❌ | +| `IRC_ADMIN_NAME` | ✅ `"All Things Linux"` | ❌ | ❌ | unrealircd.conf.template (`admin`) | init.sh, prepare-config.sh | ❌ | +| `IRC_ADMIN_EMAIL` | ✅ `admin@allthingslinux.org` | ❌ | ❌ | unrealircd.conf.template (`admin`, `kline-address`, `gline-address`) | init.sh, prepare-config.sh | ❌ | +| `IRC_STAFF_VHOST` | ✅ `allthingslinux.org` | ❌ | ❌ | unrealircd.conf.template (`oper admin vhost`) | ❌ | ❌ | +| `IRC_OPER_PASSWORD` | ✅ (argon2 hash) | ❌ | ❌ | unrealircd.conf.template (`oper admin password`) | prepare-config.sh | ❌ | +| `IRC_DRPASS` | ✅ `change_me_drpass` | ❌ | ❌ | unrealircd.conf.template (`drpass`) | ❌ | ❌ | +| `ATL_WEBIRC_PASSWORD` | ✅ `change_me_webirc_password` | ❌ | ❌ | unrealircd.conf.template (`webirc password`) | ❌ | ❌ | +| `IRC_STS_DURATION` | ✅ `1m` | ❌ | ❌ | unrealircd.conf.template (`sts-policy duration`) | ❌ | ❌ | +| `IRC_STS_PRELOAD` | ✅ `no` | ❌ | ❌ | unrealircd.conf.template (`sts-policy preload`) | ❌ | ❌ | +| `IRC_SSL_CERT_PATH` | ✅ (path) | ✅ (path) | irc.yaml (env) | ❌ | init.sh, prepare-config.sh | ❌ | +| `IRC_SSL_KEY_PATH` | ✅ (path) | ✅ (path) | irc.yaml (env) | ❌ | init.sh, prepare-config.sh | ❌ | +| `IRC_SERVICES_SERVER` | ✅ `services.atl.chat` | ❌ | ❌ | unrealircd.conf.template (`link`, `ulines`, `set services-server`, `sasl-server`) | prepare-config.sh | ❌ | +| `IRC_SERVICES_PASSWORD` | ✅ `change_me_secure_services_pass` | ❌ | ❌ | unrealircd.conf.template (`link password`), atheme.conf.template (`uplink send/receive_password`) | prepare-config.sh | ❌ | +| `ATHEME_SERVER_NAME` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template (`serverinfo name`) | init.sh, prepare-config.sh | ❌ | +| `ATHEME_SERVER_DESC` | ✅ `"All Things Linux IRC Services"` | ❌ | ❌ | atheme.conf.template (`serverinfo desc`) | ❌ | ❌ | +| `ATHEME_UPLINK_HOST` | ✅ `127.0.0.1` | ❌ | ❌ | atheme.conf.template (`uplink host`) | prepare-config.sh | ❌ | +| `ATHEME_UPLINK_PORT` | ✅ `6901` | ❌ | ❌ | atheme.conf.template (`uplink port`) | prepare-config.sh | ❌ | +| `ATHEME_UPLINK_SSL_PORT` | ✅ `6900` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `ATHEME_NUMERIC` | ✅ `00A` | ❌ | ❌ | atheme.conf.template (`serverinfo numeric`) | ❌ | ❌ | +| `ATHEME_RECONTIME` | ✅ `10` | ❌ | ❌ | atheme.conf.template (`serverinfo recontime`) | ❌ | ❌ | +| `ATHEME_LOG_LEVEL` | ✅ `all` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `ATHEME_HTTPD_PORT` | ✅ `8081` | ❌ | irc.yaml (ports) | atheme.conf.template (`httpd port`) | init.sh, prepare-config.sh | ❌ | +| `ATHEME_NETNAME` | ✅ `atl.chat` | ❌ | ❌ | atheme.conf.template (`serverinfo netname`) | init.sh | ❌ | +| `ATHEME_ADMIN_NAME` | ✅ `"All Things Linux"` | ❌ | ❌ | atheme.conf.template (`serverinfo adminname`) | init.sh | ❌ | +| `ATHEME_ADMIN_EMAIL` | ✅ `admin@allthingslinux.org` | ❌ | ❌ | atheme.conf.template (`serverinfo adminemail`) | init.sh | ❌ | +| `ATHEME_REGISTER_EMAIL` | ✅ `noreply@allthingslinux.org` | ❌ | ❌ | atheme.conf.template (`serverinfo registeremail`) | ❌ | ❌ | +| `ATHEME_HIDEHOST_SUFFIX` | ✅ `users.atl.chat` | ❌ | ❌ | atheme.conf.template (`serverinfo hidehostsuffix`) | ❌ | ❌ | +| `ATHEME_HELP_CHANNEL` | ✅ `#help` | ❌ | ❌ | atheme.conf.template (`general helpchan`) | ❌ | ❌ | +| `ATHEME_HELP_URL` | ✅ `https://discord.gg/linux` | ❌ | ❌ | atheme.conf.template (`general helpurl`) | ❌ | ❌ | +| `ATHEME_NICKSERV_NICK` | ✅ `NickServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_NICKSERV_USER` | ✅ `NickServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_NICKSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_NICKSERV_REAL` | ✅ `"Nickname Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_CHANSERV_NICK` | ✅ `ChanServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_CHANSERV_USER` | ✅ `ChanServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_CHANSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_CHANSERV_REAL` | ✅ `"Channel Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_OPERSERV_NICK` | ✅ `OperServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_OPERSERV_USER` | ✅ `OperServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_OPERSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_OPERSERV_REAL` | ✅ `"Operator Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_MEMOSERV_NICK` | ✅ `MemoServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_MEMOSERV_USER` | ✅ `MemoServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_MEMOSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_MEMOSERV_REAL` | ✅ `"Memo Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_SASLSERV_NICK` | ✅ `SaslServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_SASLSERV_USER` | ✅ `SaslServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_SASLSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_SASLSERV_REAL` | ✅ `"SASL Authentication Agent"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_BOTSERV_NICK` | ✅ `BotServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_BOTSERV_USER` | ✅ `BotServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_BOTSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_BOTSERV_REAL` | ✅ `"Bot Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_GROUPSERV_NICK` | ✅ `GroupServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_GROUPSERV_USER` | ✅ `GroupServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_GROUPSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_GROUPSERV_REAL` | ✅ `"Group Management Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_HOSTSERV_NICK` | ✅ `HostServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_HOSTSERV_USER` | ✅ `HostServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_HOSTSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_HOSTSERV_REAL` | ✅ `"Host Management Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_INFOSERV_NICK` | ✅ `InfoServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_INFOSERV_USER` | ✅ `InfoServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_INFOSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_INFOSERV_REAL` | ✅ `"Information Service"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_HELPSERV_NICK` | ✅ `HelpServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_HELPSERV_USER` | ✅ `HelpServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_HELPSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_HELPSERV_REAL` | ✅ `"Help Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_STATSERV_NICK` | ✅ `StatServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_STATSERV_USER` | ✅ `StatServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_STATSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_STATSERV_REAL` | ✅ `"Statistics Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_CHANFIX_NICK` | ✅ `ChanFix` | ❌ | ❌ | atheme.conf.template (commented out block) | ❌ | ❌ | +| `ATHEME_CHANFIX_USER` | ✅ `ChanFix` | ❌ | ❌ | atheme.conf.template (commented out block) | ❌ | ❌ | +| `ATHEME_CHANFIX_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template (commented out block) | ❌ | ❌ | +| `ATHEME_CHANFIX_REAL` | ✅ `"Channel Fixing Service"` | ❌ | ❌ | atheme.conf.template (commented out block) | ❌ | ❌ | +| `ATHEME_GLOBAL_NICK` | ✅ `Global` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_GLOBAL_USER` | ✅ `Global` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_GLOBAL_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_GLOBAL_REAL` | ✅ `"Network Announcements"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_ALIS_NICK` | ✅ `ALIS` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_ALIS_USER` | ✅ `alis` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_ALIS_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_ALIS_REAL` | ✅ `"Channel Directory"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_PROXYSCAN_NICK` | ✅ `Proxyscan` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_PROXYSCAN_USER` | ✅ `dnsbl` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_PROXYSCAN_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_PROXYSCAN_REAL` | ✅ `"Proxyscan Service"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_GAMESERV_NICK` | ✅ `GameServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_GAMESERV_USER` | ✅ `GameServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_GAMESERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_GAMESERV_REAL` | ✅ `"Game Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_RPGSERV_NICK` | ✅ `RPGServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_RPGSERV_USER` | ✅ `RPGServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_RPGSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | +| `ATHEME_RPGSERV_REAL` | ✅ `"RPG Finding Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | + +### 4. WEBPANEL & THE LOUNGE + +| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | +|----------|---------------|-------------------|---------------|-----------------|---------|------| +| `WEBPANEL_PORT` | ✅ `8080` | ❌ | irc.yaml (ports) | ❌ | ❌ | ❌ | +| `WEBPANEL_RPC_USER` | ✅ `adminpanel` | ❌ | ❌ | unrealircd.conf.template (`rpc-user`) | ❌ | ❌ | +| `WEBPANEL_RPC_PASSWORD` | ✅ `change_me_webpanel_password` | ❌ | ❌ | unrealircd.conf.template (`rpc-user password`) | ❌ | ❌ | +| `THELOUNGE_PORT` | ✅ `9000` | ✅ `9000` | thelounge.yaml (ports) | ❌ | ❌ | ❌ | +| `THELOUNGE_DOMAIN` | ✅ `webirc.atl.chat` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `THELOUNGE_WEBIRC_PASSWORD` | ✅ `change_me_thelounge_webirc` | ❌ | ❌ | unrealircd.conf.template (`proxy thelounge password`), config.js.template (`webirc`) | prepare-config.sh | ❌ | +| `THELOUNGE_DELETE_UPLOADS_AFTER_MINUTES` | ✅ `1440` | ❌ | ❌ | config.js.template (`deleteUploadsAfter`) | prepare-config.sh | ❌ | + +### 5. XMPP SERVICE (Prosody) + +| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | +|----------|---------------|-------------------|---------------|-----------------|---------|------| +| `XMPP_DOMAIN` | ✅ `atl.chat` | ✅ `xmpp.localhost` | xmpp.yaml (env: `PROSODY_DOMAIN=${XMPP_DOMAIN}`, `XMPP_DOMAIN` for nginx) | ❌ (mapped to PROSODY_DOMAIN) | prepare-config.sh | ❌ | +| `PROSODY_ADMIN_EMAIL` | ✅ `admin@allthingslinux.org` | ❌ | ❌ | prosody.cfg.lua (`contact_info`) | ❌ | ❌ | +| `PROSODY_ENV` | ✅ `development` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `PROSODY_DB_DRIVER` | ✅ `PostgreSQL` | ❌ | ❌ | ❌ | docker-entrypoint.sh (validation) | ❌ | +| `PROSODY_DB_HOST` | ✅ `xmpp-postgres-dev` | ❌ | ❌ | ❌ | docker-entrypoint.sh (pg_isready) | ❌ | +| `PROSODY_DB_PORT` | ✅ `5432` | ❌ | ❌ | ❌ | docker-entrypoint.sh | ❌ | +| `PROSODY_DB_NAME` | ✅ `prosody` | ❌ | ❌ | ❌ | docker-entrypoint.sh | ❌ | +| `PROSODY_DB_USER` | ✅ `prosody` | ❌ | ❌ | ❌ | docker-entrypoint.sh | ❌ | +| `PROSODY_DB_PASSWORD` | ✅ `change_me_secure_db_pass` | ❌ | ❌ | ❌ | docker-entrypoint.sh | ❌ | +| `PROSODY_ALLOW_REGISTRATION` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_C2S_REQUIRE_ENCRYPTION` | ✅ `true` | ✅ `false` | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_S2S_REQUIRE_ENCRYPTION` | ✅ `true` | ✅ `false` | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_S2S_SECURE_AUTH` | ✅ `true` | ✅ `false` | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_MAX_CONNECTIONS_PER_IP` | ✅ `10` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_REGISTRATION_THROTTLE_MAX` | ✅ `10` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_REGISTRATION_THROTTLE_PERIOD` | ✅ `60` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_BLOCK_REGISTRATIONS_REQUIRE` | ✅ `^[a-zA-Z0-9_.-]+$` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_TLS_CHANNEL_BINDING` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_ALLOW_UNENCRYPTED_PLAIN_AUTH` | ✅ `false` | ✅ `true` | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_HTTP_HOST` | ✅ `localhost` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_HTTP_SCHEME` | ✅ `http` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_SSL_KEY` | ✅ (path) | ✅ (path) | xmpp.yaml (env) | prosody.cfg.lua (multiple) | ❌ | ❌ | +| `PROSODY_SSL_CERT` | ✅ (path) | ✅ (path) | xmpp.yaml (env) | prosody.cfg.lua (multiple) | ❌ | ❌ | +| `PROSODY_LOG_LEVEL` | ✅ `info` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_STATISTICS` | ✅ `internal` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_STATISTICS_INTERVAL` | ✅ `manual` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_OPENMETRICS_IP` | ✅ `127.0.0.1` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_OPENMETRICS_CIDR` | ✅ `127.0.0.1/32` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_ARCHIVE_EXPIRES_AFTER` | ✅ `30d` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_ARCHIVE_POLICY` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_ARCHIVE_COMPRESSION` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_ARCHIVE_STORE` | ✅ `archive` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_ARCHIVE_MAX_QUERY_RESULTS` | ✅ `250` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_MAM_SMART_ENABLE` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_MUC_NOTIFICATIONS` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_MUC_OFFLINE_DELIVERY` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_RESTRICT_ROOM_CREATION` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_MUC_DEFAULT_PUBLIC` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_MUC_DEFAULT_PERSISTENT` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_MUC_DEFAULT_PUBLIC_JIDS` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_MUC_LOCKING` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_MUC_LOG_BY_DEFAULT` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_MUC_LOG_EXPIRES_AFTER` | ✅ `1y` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_MUC_LOG_PRESENCES` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_MUC_LOG_ALL_ROOMS` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_MUC_LOG_CLEANUP_INTERVAL` | ✅ `86400` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_MUC_MAX_ARCHIVE_QUERY_RESULTS` | ✅ `100` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_MUC_LOG_STORE` | ✅ `muc_log` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_MUC_LOG_COMPRESSION` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_MUC_MAM_SMART_ENABLE` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_C2S_RATE` | ✅ `10kb/s` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_C2S_BURST` | ✅ `25kb` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_C2S_STANZA_SIZE` | ✅ `262144` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_S2S_RATE` | ✅ `30kb/s` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_S2S_BURST` | ✅ `100kb` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_S2S_STANZA_SIZE` | ✅ `524288` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_HTTP_UPLOAD_RATE` | ✅ `2mb/s` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_HTTP_UPLOAD_BURST` | ✅ `10mb` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_PUSH_IMPORTANT_BODY` | ✅ `"New Message!"` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_PUSH_MAX_ERRORS` | ✅ `16` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_PUSH_MAX_DEVICES` | ✅ `5` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_PUSH_MAX_HIBERNATION_TIMEOUT` | ✅ `259200` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_PUSH_NOTIFICATION_WITH_BODY` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_PUSH_NOTIFICATION_WITH_SENDER` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_ACCOUNT_INACTIVE_PERIOD` | ✅ `31536000` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_ACCOUNT_GRACE_PERIOD` | ✅ `2592000` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_ACCOUNT_DELETION_CONFIRMATION` | ✅ `true` | ❌ | ❌ | ❌ (commented out in lua) | ❌ | ❌ | +| `PROSODY_SERVER_NAME` | ✅ `localhost` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_SERVER_WEBSITE` | ✅ `http://localhost` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_SERVER_DESCRIPTION` | ✅ `"XMPP Service"` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `LUA_GC_STEP_SIZE` | ✅ `13` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `LUA_GC_PAUSE` | ✅ `110` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `LUA_GC_SPEED` | ✅ `200` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `LUA_GC_THRESHOLD` | ✅ `120` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_UPLOAD_EXTERNAL_URL` | ✅ `http://localhost:5280/upload/` | ✅ `https://xmpp.localhost:5281/` | xmpp.yaml (env) | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_PROXY_ADDRESS` | ✅ `localhost` | ✅ `xmpp.localhost` | xmpp.yaml (env) | prosody.cfg.lua | ❌ | ❌ | +| `PROSODY_FEED_URL` | ✅ `https://allthingslinux.org/feed` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | + +### 6. DATABASE (PostgreSQL) + +| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | +|----------|---------------|-------------------|---------------|-----------------|---------|------| +| `POSTGRES_USER` | ✅ `prosody` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `POSTGRES_DB` | ✅ `prosody` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `POSTGRES_PASSWORD` | ✅ `change_me_secure_db_pass` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `POSTGRES_SHARED_BUFFERS` | ✅ `32MB` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `POSTGRES_EFFECTIVE_CACHE_SIZE` | ✅ `128MB` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `POSTGRES_WORK_MEM` | ✅ `1MB` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `POSTGRES_MAINTENANCE_WORK_MEM` | ✅ `16MB` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `POSTGRES_CHECKPOINT_COMPLETION_TARGET` | ✅ `0.9` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `POSTGRES_WAL_BUFFERS` | ✅ `4MB` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `POSTGRES_DEFAULT_STATISTICS_TARGET` | ✅ `50` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `POSTGRES_RANDOM_PAGE_COST` | ✅ `1.1` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `POSTGRES_EFFECTIVE_IO_CONCURRENCY` | ✅ `100` | ❌ | ❌ | ❌ | ❌ | ❌ | + +### 7. ADMINER + +| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | +|----------|---------------|-------------------|---------------|-----------------|---------|------| +| `ADMINER_PORT` | ✅ `8080` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `ADMINER_AUTO_LOGIN` | ✅ `false` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `ADMINER_DEFAULT_DRIVER` | ✅ `pgsql` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `ADMINER_DEFAULT_SERVER` | ✅ `xmpp-postgres` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `ADMINER_DEFAULT_DB` | ✅ `prosody` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `ADMINER_DEFAULT_USERNAME` | ✅ `prosody` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `ADMINER_DEFAULT_PASSWORD` | ✅ `change_me_secure_db_pass` | ❌ | ❌ | ❌ | ❌ | ❌ | + +### 8. NGINX REVERSE PROXY + +| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | +|----------|---------------|-------------------|---------------|-----------------|---------|------| +| `NGINX_HTTP_PORT` | ✅ `80` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `NGINX_HTTPS_PORT` | ✅ `443` | ❌ | ❌ | ❌ | ❌ | ❌ | + +### 9. EXTERNAL INTEGRATIONS + +| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | +|----------|---------------|-------------------|---------------|-----------------|---------|------| +| `ATL_SENTRY_DSN` | ✅ (empty) | ❌ | ❌ | ❌ | ❌ | ❌ | +| `ATL_INTERNAL_SECRET_IRC` | ✅ (empty) | ❌ | ❌ | ❌ | ❌ | ❌ | +| `ATL_INTERNAL_SECRET_XMPP` | ✅ (empty) | ❌ | ❌ | ❌ | ❌ | ❌ | +| `LETSENCRYPT_EMAIL` | ✅ `admin@allthingslinux.org` | ❌ | cert-manager.yaml (env) | ❌ | cert-manager/run.sh | ❌ | + +### 10. BRIDGE SERVICE + +| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | +|----------|---------------|-------------------|---------------|-----------------|---------|------| +| `BRIDGE_DISCORD_TOKEN` | ✅ `change_me_discord_bot_token` | ❌ | bridge.yaml (env) | ❌ | ❌ | `__main__.py`, `discord/adapter.py` | +| `BRIDGE_DISCORD_CHANNEL_ID` | ✅ `REPLACE_WITH_DISCORD_CHANNEL_ID` | ❌ | ❌ | config.template.yaml | prepare-config.sh | ❌ | +| `BRIDGE_PORTAL_BASE_URL` | ✅ `https://portal.atl.tools` | ✅ (empty) | bridge.yaml (env) | ❌ | ❌ | `__main__.py` | +| `BRIDGE_PORTAL_TOKEN` | ✅ `change_me_bridge_portal_token` | ❌ | bridge.yaml (env) | ❌ | ❌ | `__main__.py` | +| `BRIDGE_XMPP_COMPONENT_JID` | ✅ `bridge.atl.chat` | ✅ `bridge.xmpp.localhost` | bridge.yaml (env) | ❌ | prepare-config.sh | `xmpp/adapter.py` | +| `BRIDGE_XMPP_COMPONENT_SECRET` | ✅ `change_me_xmpp_component_secret` | ❌ | bridge.yaml (env) | prosody.cfg.lua | ❌ | `xmpp/adapter.py` | +| `BRIDGE_XMPP_COMPONENT_SERVER` | ✅ `atl-xmpp-server` | ✅ `atl-xmpp-server` | bridge.yaml (env) | ❌ | ❌ | `xmpp/adapter.py` | +| `BRIDGE_XMPP_COMPONENT_PORT` | ✅ `5347` | ✅ `5347` | bridge.yaml (env) | ❌ | ❌ | `xmpp/adapter.py` | +| `BRIDGE_IRC_NICK` | ✅ `atl-bridge` | ❌ | bridge.yaml (env) | ❌ | ❌ | `irc/adapter.py` | +| `BRIDGE_IRC_OPER_PASSWORD` | ✅ `change_me_bridge_oper` | ❌ | bridge.yaml (env) | unrealircd.conf.template (`oper atl-bridge`) | prepare-config.sh | `irc/client.py` | +| `IRC_BRIDGE_SERVER` | ✅ `atl-irc-server` | ✅ `atl-irc-server` | ❌ | config.template.yaml | prepare-config.sh | ❌ | +| `BRIDGE_IRC_TLS_VERIFY` | ❌ | ✅ `false` | bridge.yaml (env) | ❌ | prepare-config.sh | schema.py (`_ENV_OVERRIDE_KEYS`) | +| `BRIDGE_RELAYMSG_CLEAN_NICKS` | ❌ | ✅ `true` | bridge.yaml (env) | ❌ | ❌ | schema.py (`_ENV_OVERRIDE_KEYS`) | +| `LOG_LEVEL` | ❌ (commented) | ❌ (commented) | bridge.yaml (env) | ❌ | ❌ | `__main__.py` | + +### 11. PORTAL INTEGRATION + +| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | +|----------|---------------|-------------------|---------------|-----------------|---------|------| +| `IRC_ATHEME_JSONRPC_URL` | ✅ `http://atl-irc-server:8081/jsonrpc` | ✅ (same) | ❌ | ❌ | ❌ | ❌ | +| `IRC_UNREAL_JSONRPC_URL` | ✅ `https://irc.atl.chat:8600/api` | ✅ `https://irc.localhost:8600/api` | ❌ | ❌ | ❌ | ❌ | +| `IRC_UNREAL_RPC_USER` | ✅ `${WEBPANEL_RPC_USER}` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `IRC_UNREAL_RPC_PASSWORD` | ✅ `${WEBPANEL_RPC_PASSWORD}` | ❌ | ❌ | ❌ | ❌ | ❌ | +| `PROSODY_REST_URL` | ✅ `http://atl-xmpp-server:5280` | ✅ (same) | ❌ | ❌ | ❌ | ❌ | +| `PROSODY_REST_USERNAME` | ✅ `admin@atl.chat` | ✅ `admin@xmpp.localhost` | ❌ | ❌ | ❌ | ❌ | +| `PROSODY_REST_PASSWORD` | ✅ `change_me_prosody_rest_password` | ✅ (same) | ❌ | ❌ | ❌ | ❌ | +| `IRC_SERVER` | ✅ `irc.atl.chat` | ✅ `irc.localhost` | ❌ | ❌ | ❌ | ❌ | +| `IRC_PORT` | ✅ `6697` | ✅ `6697` | ❌ | ❌ | ❌ | ❌ | + +### 12. WEB FRONTEND (Next.js) + +| Variable | `.env.example` (root) | `.env.dev.example` | `apps/web/.env.example` | Compose files | Code | +|----------|----------------------|-------------------|------------------------|---------------|------| +| `NEXT_PUBLIC_IRC_WS_URL` | ✅ `wss://irc.atl.chat/ws` | ✅ `wss://irc.localhost/ws` | ✅ `wss://irc.atl.chat/ws` | ❌ | justfile (hardcoded override) | +| `NEXT_PUBLIC_XMPP_BOSH_URL` | ✅ `https://xmpp.atl.chat/http-bind` | ✅ `https://xmpp.localhost/http-bind` | ✅ `https://xmpp.atl.chat/http-bind` | ❌ | justfile (hardcoded override) | +| `NEXT_PUBLIC_ATL_BASE_DOMAIN` | ❌ | ❌ | ✅ `atl.chat` | ❌ | ❌ | +| `NEXT_PUBLIC_ATL_ENVIRONMENT` | ❌ | ❌ | ✅ `development` | ❌ | ❌ | +| `NEXT_PUBLIC_SENTRY_DSN` | ❌ | ❌ | ✅ (empty) | ❌ | ❌ | + +--- + +## Categorized Findings + +### Category A: ORPHANED — Defined in `.env.example` but never consumed + +| Variable | Notes | +|----------|-------| +| `ATL_PROJECT_NAME` | Defined as `atl-chat`. Not referenced in any compose, config template, script, or code. Docker Compose `name: atl-chat` is hardcoded in compose.yaml. | +| `ATL_BASE_DOMAIN` | Defined as `atl.chat`. Not referenced anywhere. Individual domains (`IRC_DOMAIN`, `XMPP_DOMAIN`) are used instead. | +| `PROSODY_ENV` | Defined as `development`. Not referenced in any compose file, Lua config, script, or code. `ATL_ENVIRONMENT` is the actual env discriminator. | +| `ATHEME_UPLINK_SSL_PORT` | Defined as `6900`. Never consumed in any template or compose file. The atheme.conf.template uses `ATHEME_UPLINK_PORT` (6901, plaintext). | +| `ATHEME_LOG_LEVEL` | Defined as `all`. Not referenced in atheme.conf.template (log level is hardcoded: `logfile "logs/atheme.log" { debug; };`). | +| `POSTGRES_USER` | Defined as `prosody`. No PostgreSQL service exists in compose (removed; dev uses SQLite). Not consumed by any running service. | +| `POSTGRES_DB` | Same as above. | +| `POSTGRES_PASSWORD` | Same as above. | +| `POSTGRES_SHARED_BUFFERS` | No PostgreSQL compose service exists. | +| `POSTGRES_EFFECTIVE_CACHE_SIZE` | No PostgreSQL compose service exists. | +| `POSTGRES_WORK_MEM` | No PostgreSQL compose service exists. | +| `POSTGRES_MAINTENANCE_WORK_MEM` | No PostgreSQL compose service exists. | +| `POSTGRES_CHECKPOINT_COMPLETION_TARGET` | No PostgreSQL compose service exists. | +| `POSTGRES_WAL_BUFFERS` | No PostgreSQL compose service exists. | +| `POSTGRES_DEFAULT_STATISTICS_TARGET` | No PostgreSQL compose service exists. | +| `POSTGRES_RANDOM_PAGE_COST` | No PostgreSQL compose service exists. | +| `POSTGRES_EFFECTIVE_IO_CONCURRENCY` | No PostgreSQL compose service exists. | +| `ADMINER_PORT` | No Adminer service in compose files. | +| `ADMINER_AUTO_LOGIN` | No Adminer service in compose files. | +| `ADMINER_DEFAULT_DRIVER` | No Adminer service in compose files. | +| `ADMINER_DEFAULT_SERVER` | No Adminer service in compose files. | +| `ADMINER_DEFAULT_DB` | No Adminer service in compose files. | +| `ADMINER_DEFAULT_USERNAME` | No Adminer service in compose files. | +| `ADMINER_DEFAULT_PASSWORD` | No Adminer service in compose files. | +| `NGINX_HTTP_PORT` | No nginx reverse proxy service in compose files (only xmpp-nginx exists, which uses `PROSODY_HTTPS_PORT`). | +| `NGINX_HTTPS_PORT` | Same as above. | +| `ATL_SENTRY_DSN` | Defined (empty) but not referenced in any compose, config, script, or code in this monorepo. | +| `ATL_INTERNAL_SECRET_IRC` | Defined (empty) but not referenced anywhere. Intended for future Portal integration. | +| `ATL_INTERNAL_SECRET_XMPP` | Defined (empty) but not referenced anywhere. Intended for future Portal integration. | +| `THELOUNGE_DOMAIN` | Defined as `webirc.atl.chat`. Not referenced in any compose file, config template, script, or code. | +| `ATHEME_CHANFIX_NICK/USER/HOST/REAL` | Defined in `.env.example` but only consumed in a **commented-out** block in atheme.conf.template. The ChanFix module is disabled. | +| `IRC_ATHEME_JSONRPC_URL` | Defined in `.env.example` and `.env.dev.example`. Not consumed by any service in this monorepo — intended for an external Portal service. | +| `IRC_UNREAL_JSONRPC_URL` | Same — intended for external Portal. | +| `IRC_UNREAL_RPC_USER` | Same — intended for external Portal. Uses `${WEBPANEL_RPC_USER}` interpolation in `.env.example` which is unusual. | +| `IRC_UNREAL_RPC_PASSWORD` | Same — intended for external Portal. | +| `PROSODY_REST_URL` | Same — intended for external Portal. | +| `PROSODY_REST_USERNAME` | Same — intended for external Portal. | +| `PROSODY_REST_PASSWORD` | Same — intended for external Portal. | +| `IRC_SERVER` | Same — intended for external Portal. | +| `IRC_PORT` | Same — intended for external Portal. | +| `PROSODY_ACCOUNT_DELETION_CONFIRMATION` | Defined as `true`. The Lua code block that consumes it is **commented out** in prosody.cfg.lua. | + +**Total: 37 orphaned variables** (though ~11 Portal vars are intentionally pre-defined for external consumers) + +### Category B: UNDEFINED — Consumed in config/compose but NOT defined in `.env.example` + +| Variable | Where consumed | Notes | +|----------|---------------|-------| +| `PROSODY_DOMAIN` | prosody.cfg.lua, docker-entrypoint.sh, config.template.yaml, init.sh, prepare-config.sh | **Critical.** Not in `.env.example`. Derived in prepare-config.sh as `${PROSODY_DOMAIN:-${XMPP_DOMAIN:-xmpp.localhost}}`. Set in `.env.dev.example` as `xmpp.localhost`. Compose sets it from `XMPP_DOMAIN`. This is intentional (derived var) but confusing. | +| `PROSODY_HTTPS_VIA_PROXY` | xmpp.yaml (env), prosody.cfg.lua | Not in `.env.example`. Compose sets default `false`. Controls whether nginx handles HTTPS. | +| `PROSODY_HTTP_EXTERNAL_URL` | xmpp.yaml (env), prosody.cfg.lua | Not in `.env.example`. Set in `.env.dev.example`. Important for Converse.js dev access. | +| `PROSODY_C2S_DIRECT_TLS_PORT` | xmpp.yaml (ports, with default `5223`) | Not in `.env.example`. Port 5223 (direct TLS). | +| `PROSODY_S2S_DIRECT_TLS_PORT` | xmpp.yaml (ports, with default `5270`) | Not in `.env.example`. Port 5270 (direct s2s TLS). | +| `PROSODY_PROXY65_PORT` | xmpp.yaml (ports, with default `5000`) | Not in `.env.example`. SOCKS5 proxy port. | +| `PROSODY_C2S_PORT` | xmpp.yaml (ports, with default `5222`) | Not in `.env.example` as `PROSODY_C2S_PORT`. `XMPP_C2S_PORT` exists but compose uses `PROSODY_C2S_PORT`. **Name mismatch.** | +| `PROSODY_S2S_PORT` | xmpp.yaml (ports, with default `5269`) | Same issue. `XMPP_S2S_PORT` defined, compose uses `PROSODY_S2S_PORT`. | +| `PROSODY_HTTP_PORT` | xmpp.yaml (ports, with default `5280`) | Same. `XMPP_HTTP_PORT` defined, compose uses `PROSODY_HTTP_PORT`. | +| `PROSODY_HTTPS_PORT` | xmpp.yaml (ports, with default `5281`) | Same. `XMPP_HTTPS_PORT` defined, compose uses `PROSODY_HTTPS_PORT`. | +| `PROSODY_STORAGE` | docker-entrypoint.sh | Not in `.env.example`. Set in `.env.dev.example` as `sqlite`. Controls Prosody storage backend. | +| `LOG_MAX_SIZE` | xmpp.yaml (logging) | Not in `.env.example`. Defaults to `50m`. | +| `LOG_MAX_FILES` | xmpp.yaml (logging) | Not in `.env.example`. Defaults to `5`. | +| `DOZZLE_PORT` | compose.yaml | Not in `.env.example`. Defaults to `8082`. Dev-only (Dozzle log viewer). | +| `CLOUDFLARE_DNS_API_TOKEN` | cert-manager.yaml (env), cert-manager/run.sh | Not in `.env.example`. Required for Let's Encrypt cert issuance. | +| `SSL_DOMAIN` | cert-manager.yaml (env), cert-manager/run.sh | Not in `.env.example`. Optional override for cert domain. | +| `IRC_LOG_PATH` | unrealircd.conf.template (log destination), prepare-config.sh | Not in `.env.example`. Defaults to `/home/unrealircd/unrealircd/logs` in prepare-config.sh. | +| `IRC_TLS_VERIFY` | config.template.yaml (`irc_tls_verify`) | Not in `.env.example`. Derived in prepare-config.sh from `BRIDGE_IRC_TLS_VERIFY` / `ATL_ENVIRONMENT`. | +| `IRC_LOUNGE_REJECT_UNAUTHORIZED` | config.js.template (`rejectUnauthorized`) | Not in `.env.example`. Derived in prepare-config.sh from `ATL_ENVIRONMENT`. | +| `TURN_SECRET` | prosody.cfg.lua (`turn_external_secret`) | Not in `.env.example`. Defaults to `devsecret`. Required for production TURN server auth. | +| `TURN_EXTERNAL_HOST` | prosody.cfg.lua (`turn_external_host`) | Not in `.env.example`. Defaults to `turn.atl.network`. | +| `PROSODY_ADMIN_JID` | prosody.cfg.lua, docker-entrypoint.sh | Not in `.env.example`. Derived from `PROSODY_DOMAIN` in entrypoint. | +| `PROSODY_SUPPORT_CONTACT` | prosody.cfg.lua | Not in `.env.example`. Defaults to `support@{domain}`. | +| `PROSODY_SUPPORT_CONTACT_NICK` | prosody.cfg.lua | Not in `.env.example`. Defaults to `"Support"`. | +| `XMPP_AVATAR_BASE_URL` | bridge.yaml (env) | Not in `.env.example`. Set in `.env.dev.example`. Bridge needs internal URL to HEAD-check avatars. | +| `XMPP_UPLOAD_FETCH_URL` | bridge.yaml (env) | Not in `.env.example`. Set in `.env.dev.example`. Bridge rewrites XMPP upload URLs. | +| `BRIDGE_IRC_REDACT_ENABLED` | schema.py (`_ENV_OVERRIDE_KEYS`) | Not in `.env.example`. Env override for IRC REDACT feature toggle. | +| `BRIDGE_DEV_IRC_PUPPETS` | `__main__.py` | Not in `.env.example`. Mentioned (commented) in `.env.dev.example`. Enables IRC puppets without Portal. | +| `BRIDGE_DEV_IRC_NICK_MAP` | `identity/dev.py` | Not in `.env.example`. Mentioned (commented) in `.env.dev.example`. Maps Discord IDs to IRC nicks for dev. | +| `BRIDGE_PORTAL_URL` | `__main__.py` (legacy alias) | Not in `.env.example`. Legacy alias for `BRIDGE_PORTAL_BASE_URL`. | +| `BRIDGE_PORTAL_API_TOKEN` | `__main__.py` (legacy alias) | Not in `.env.example`. Legacy alias for `BRIDGE_PORTAL_TOKEN`. | +| `XMPP_COMPONENT_SECRET` | prosody.cfg.lua (fallback) | Not in `.env.example`. Legacy alias for `BRIDGE_XMPP_COMPONENT_SECRET`. | +| `IRC_PUPPET_IDLE_TIMEOUT_HOURS` | `irc/adapter.py` | Not in `.env.example`. Env override; defaults to `24`. Config YAML property `irc_puppet_idle_timeout_hours` is the primary. | +| `CERT_DIR` | xmpp-nginx docker-entrypoint.sh, prosody-https.conf.template | Not in `.env.example`. Hardcoded as env in xmpp.yaml nginx: `CERT_DIR=/etc/nginx/certs`. | + +### Category C: INCONSISTENT — Same variable referenced differently + +| Issue | Details | +|-------|---------| +| **XMPP port naming mismatch** | `.env.example` defines `XMPP_C2S_PORT`, `XMPP_S2S_PORT`, `XMPP_HTTP_PORT`, `XMPP_HTTPS_PORT`. But `xmpp.yaml` uses `PROSODY_C2S_PORT`, `PROSODY_S2S_PORT`, `PROSODY_HTTP_PORT`, `PROSODY_HTTPS_PORT` with fallback defaults. The `XMPP_*_PORT` vars are defined but **never consumed** by compose — the `PROSODY_*_PORT` names are what actually work. | +| **XMPP_DOMAIN vs PROSODY_DOMAIN** | `.env.example` defines `XMPP_DOMAIN`. Compose passes it as `PROSODY_DOMAIN=${XMPP_DOMAIN}`. Scripts derive `PROSODY_DOMAIN` from `XMPP_DOMAIN`. Prosody code only reads `PROSODY_DOMAIN`. Two names for one value; `.env.dev.example` defines **both** separately (`XMPP_DOMAIN=xmpp.localhost` and `PROSODY_DOMAIN=xmpp.localhost`). | +| **BRIDGE_PORTAL_URL vs BRIDGE_PORTAL_BASE_URL** | Code checks both `BRIDGE_PORTAL_BASE_URL` and `BRIDGE_PORTAL_URL` (legacy). Only `BRIDGE_PORTAL_BASE_URL` is in `.env.example`. | +| **BRIDGE_PORTAL_TOKEN vs BRIDGE_PORTAL_API_TOKEN** | Code checks both `BRIDGE_PORTAL_TOKEN` and `BRIDGE_PORTAL_API_TOKEN` (legacy). Only `BRIDGE_PORTAL_TOKEN` is in `.env.example`. | +| **BRIDGE_XMPP_COMPONENT_SECRET vs XMPP_COMPONENT_SECRET** | prosody.cfg.lua checks both `BRIDGE_XMPP_COMPONENT_SECRET` and `XMPP_COMPONENT_SECRET` (legacy fallback). Only the `BRIDGE_` prefixed version is in `.env.example`. | +| **IRC_UNREAL_RPC_USER/PASSWORD uses var interpolation in .env.example** | `IRC_UNREAL_RPC_USER=${WEBPANEL_RPC_USER}` — this relies on shell expansion when `.env` is sourced, but Docker Compose does NOT expand `${VAR}` references inside `.env` files. This means these vars will literally be set to the string `${WEBPANEL_RPC_USER}` when loaded by Compose. | + +### Category D: HARDCODED — Values that should be env vars but are hardcoded + +| Location | Hardcoded value | Should be | +|----------|----------------|-----------| +| `unrealircd.conf.template` | `me { sid "001"; }` | Consider `IRC_SERVER_SID` env var for multi-server setups | +| `unrealircd.conf.template` | `help-channel "#support"` | Could use `IRC_HELP_CHANNEL` | +| `unrealircd.conf.template` | `oper-auto-join "#mod-chat"` | Could use `IRC_OPER_CHANNEL` | +| `unrealircd.conf.template` | `maxchannelsperuser 10` | Could use `IRC_MAX_CHANNELS_PER_USER` | +| `unrealircd.conf.template` | `maxperip 5` (allow block) | Could use `IRC_MAX_PER_IP` | +| `config.js.template` | `host: "atl-irc-server"` | Could use `IRC_BRIDGE_SERVER` or a `THELOUNGE_IRC_HOST` var | +| `config.js.template` | `port: 6697` | Could use `IRC_TLS_PORT` | +| `config.js.template` | `join: "#help"` | Could use `THELOUNGE_DEFAULT_CHANNEL` | +| `prosody.cfg.lua` | `default_storage = "sql"` + SQLite3 config | Hardcoded to SQLite; the `PROSODY_DB_*` vars in `.env.example` suggest PostgreSQL should be configurable via env. The entrypoint switches based on `PROSODY_STORAGE` but the Lua config itself doesn't read it. | +| `compose.yaml` | `name: atl-chat` | Could use `ATL_PROJECT_NAME` | +| `xmpp.yaml` | `image: allthingslinux/prosody:latest` | Could use a versioned tag env var | +| Various | Docker hostnames like `atl-irc-server`, `atl-xmpp-server` | Hardcoded in multiple config templates; should be consistent env vars if they ever need to change | + +### Category E: DEAD — Defined and maybe partially referenced but serve no actual purpose + +| Variable | Reason | +|----------|--------| +| `PROSODY_ENV` | Defined as `development`. Nothing reads it. `ATL_ENVIRONMENT` is the actual environment discriminator used by bridge and scripts. | +| `ATHEME_LOG_LEVEL` | Defined as `all`. Atheme config hardcodes `logfile "logs/atheme.log" { debug; };` — doesn't read this env var. | +| `ATHEME_UPLINK_SSL_PORT` | Defined as `6900`. Atheme connects via plaintext port 6901 (`ATHEME_UPLINK_PORT`); the SSL variant is never used since Atheme connects via localhost/Docker network. | +| `ATHEME_CHANFIX_*` (4 vars) | ChanFix module is commented out in atheme.conf.template. These vars are substituted into a dead code block. | +| `PROSODY_ACCOUNT_DELETION_CONFIRMATION` | The Lua code block using it is commented out. | +| All `POSTGRES_*` vars (12 vars) | No PostgreSQL service exists in any compose file. Dev uses SQLite (`PROSODY_STORAGE=sqlite`). The `PROSODY_DB_*` vars exist for when Postgres is enabled, but the `POSTGRES_*` vars (standard Docker image vars) have no compose service to consume them. | +| All `ADMINER_*` vars (7 vars) | No Adminer service in compose files. | +| `NGINX_HTTP_PORT`, `NGINX_HTTPS_PORT` | No main nginx reverse proxy in compose. Only xmpp-nginx exists (different service). | +| `XMPP_C2S_PORT` | Name defined in `.env.example`, but compose uses `PROSODY_C2S_PORT` instead. | +| `XMPP_S2S_PORT` | Same mismatch. | +| `XMPP_HTTP_PORT` | Same mismatch. | +| `XMPP_HTTPS_PORT` | Same mismatch. | + +### Category F: DUPLICATE — Same logical setting under multiple variable names + +| Logical Setting | Variable Names | Notes | +|----------------|----------------|-------| +| XMPP domain | `XMPP_DOMAIN`, `PROSODY_DOMAIN` | `XMPP_DOMAIN` in `.env.example`, compose maps to `PROSODY_DOMAIN`. Both defined separately in `.env.dev.example`. | +| XMPP C2S port | `XMPP_C2S_PORT`, `PROSODY_C2S_PORT` | `.env.example` defines `XMPP_C2S_PORT`; compose uses `PROSODY_C2S_PORT`. | +| XMPP S2S port | `XMPP_S2S_PORT`, `PROSODY_S2S_PORT` | Same pattern. | +| XMPP HTTP port | `XMPP_HTTP_PORT`, `PROSODY_HTTP_PORT` | Same pattern. | +| XMPP HTTPS port | `XMPP_HTTPS_PORT`, `PROSODY_HTTPS_PORT` | Same pattern. | +| Portal API URL | `BRIDGE_PORTAL_BASE_URL`, `BRIDGE_PORTAL_URL` | Code accepts both (legacy compat). | +| Portal API token | `BRIDGE_PORTAL_TOKEN`, `BRIDGE_PORTAL_API_TOKEN` | Code accepts both (legacy compat). | +| XMPP component secret | `BRIDGE_XMPP_COMPONENT_SECRET`, `XMPP_COMPONENT_SECRET` | Prosody Lua accepts both (legacy fallback). | +| RPC credentials | `WEBPANEL_RPC_USER` / `IRC_UNREAL_RPC_USER` | Same value, two names. | +| RPC credentials | `WEBPANEL_RPC_PASSWORD` / `IRC_UNREAL_RPC_PASSWORD` | Same value, two names. | +| IRC TLS port | `IRC_TLS_PORT` / `IRC_PORT` | Both `6697`. `IRC_TLS_PORT` used by compose ports; `IRC_PORT` used by Portal section. | +| DB password | `PROSODY_DB_PASSWORD` / `POSTGRES_PASSWORD` / `ADMINER_DEFAULT_PASSWORD` | All default to `change_me_secure_db_pass`. | +| DB user | `PROSODY_DB_USER` / `POSTGRES_USER` / `ADMINER_DEFAULT_USERNAME` | All `prosody`. | +| DB name | `PROSODY_DB_NAME` / `POSTGRES_DB` / `ADMINER_DEFAULT_DB` | All `prosody`. | + +--- + +## Summary Statistics + +| Category | Count | Severity | +|----------|-------|----------| +| **A: Orphaned** | 37 vars | Medium — clutters .env.example, confuses operators | +| **B: Undefined** | 33 vars | High — some are critical (CLOUDFLARE_DNS_API_TOKEN, TURN_SECRET, PROSODY_DOMAIN) | +| **C: Inconsistent** | 6 issues | High — port naming mismatch causes vars to be silently ignored | +| **D: Hardcoded** | 12 items | Low-Medium — reduces configurability | +| **E: Dead** | ~30 vars | Medium — waste of .env.example real estate | +| **F: Duplicate** | 14 pairs | Medium — confusing, error-prone | + +## Recommended Actions (Priority Order) + +1. **Fix port naming mismatch (C):** Rename `XMPP_C2S_PORT` → `PROSODY_C2S_PORT` (etc.) in `.env.example`, OR update `xmpp.yaml` to use `XMPP_*` names. Currently the defined vars are **silently ignored**. + +2. **Add critical undefined vars to `.env.example` (B):** `CLOUDFLARE_DNS_API_TOKEN`, `TURN_SECRET`, `TURN_EXTERNAL_HOST`, `PROSODY_HTTPS_VIA_PROXY`, `PROSODY_HTTP_EXTERNAL_URL`, `PROSODY_STORAGE`, `DOZZLE_PORT`, `LOG_MAX_SIZE`, `LOG_MAX_FILES`, `XMPP_AVATAR_BASE_URL`, `XMPP_UPLOAD_FETCH_URL`, `IRC_LOG_PATH`, `BRIDGE_IRC_TLS_VERIFY`, `BRIDGE_RELAYMSG_CLEAN_NICKS`. + +3. **Remove dead PostgreSQL/Adminer/Nginx sections (A+E):** 21 vars for services with no compose definition. Move to a `compose.postgres.yaml` fragment or remove entirely. + +4. **Fix `.env` variable interpolation (C):** `IRC_UNREAL_RPC_USER=${WEBPANEL_RPC_USER}` won't expand in Docker Compose `.env` loading. Either hardcode the values or remove the indirection. + +5. **Consolidate XMPP_DOMAIN/PROSODY_DOMAIN (F):** Pick one canonical name. Recommendation: use `XMPP_DOMAIN` in `.env.example` and let compose/scripts derive `PROSODY_DOMAIN` internally. + +6. **Remove legacy alias support (F):** `BRIDGE_PORTAL_URL`, `BRIDGE_PORTAL_API_TOKEN`, `XMPP_COMPONENT_SECRET` — if no live deployment uses these, remove the fallbacks. + +7. **Remove dead ChanFix vars (E):** 4 vars for a commented-out module. + +8. **Document intentional Portal vars (A):** Add a clear comment that `IRC_ATHEME_JSONRPC_URL`, `IRC_SERVER`, `IRC_PORT`, `PROSODY_REST_*`, etc. are consumed by the external Portal service, not this monorepo. From 9a6705a712055e0ef96b2a12d9e5f590d779aa9a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 06:05:23 +0000 Subject: [PATCH 11/70] refactor(env): 12-factor cleanup of .env.example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Full cross-reference audit of 150+ env vars across all sources (compose, config templates, scripts, code). Changes: Removed (orphaned/dead — no compose service or consumer): - ATL_PROJECT_NAME, ATL_BASE_DOMAIN (never consumed) - PROSODY_ENV (nothing reads it; ATL_ENVIRONMENT is the discriminator) - ATHEME_UPLINK_SSL_PORT, ATHEME_LOG_LEVEL (not consumed by templates) - THELOUNGE_DOMAIN (not consumed anywhere) - ATHEME_CHANFIX_* (4 vars for commented-out module) - PROSODY_ACCOUNT_DELETION_CONFIRMATION (consumed code is commented out) - All POSTGRES_* vars (12 vars — no PostgreSQL compose service) - All ADMINER_* vars (7 vars — no Adminer compose service) - NGINX_HTTP_PORT, NGINX_HTTPS_PORT (no nginx compose service) - ATL_SENTRY_DSN, ATL_INTERNAL_SECRET_IRC/XMPP (empty, never consumed) Fixed (naming mismatch): - Renamed XMPP_C2S_PORT → PROSODY_C2S_PORT (and S2S, HTTP, HTTPS) to match what xmpp.yaml compose actually consumes. The old XMPP_* names were silently ignored. Fixed (broken interpolation): - IRC_UNREAL_RPC_USER/PASSWORD: replaced ${WEBPANEL_RPC_USER} with literal values. Docker Compose does not expand ${VAR} inside .env files. Added (previously undefined but consumed): - CLOUDFLARE_DNS_API_TOKEN (cert-manager) - PROSODY_STORAGE (docker-entrypoint.sh) - PROSODY_C2S_DIRECT_TLS_PORT, PROSODY_S2S_DIRECT_TLS_PORT, PROSODY_PROXY65_PORT - TURN_SECRET, TURN_EXTERNAL_HOST Reorganized: - Clear section headers with 12-factor notes - Portal-consumed vars separated with explicit comment - Grouped by service lifecycle, not arbitrary categories Added: docs/audits/env-var-audit.md with full 150+ var cross-reference --- .env.example | 267 +++++++------------ ENV_AUDIT.md => docs/audits/env-var-audit.md | 0 2 files changed, 90 insertions(+), 177 deletions(-) rename ENV_AUDIT.md => docs/audits/env-var-audit.md (100%) diff --git a/.env.example b/.env.example index dda71d3e..d10730a9 100644 --- a/.env.example +++ b/.env.example @@ -1,99 +1,82 @@ # Master Environment Registry - atl.chat Monorepo -# This file serves as the single source of truth for global defaults across the ecosystem. -# Use this as a template to create your .env, .env.local, or .env.production files. - -# ============================================================================= -# 1. CORE PROJECT & ENVIRONMENT -# ============================================================================= -ATL_PROJECT_NAME=atl-chat -ATL_BASE_DOMAIN=atl.chat -ATL_ENVIRONMENT=dev # dev, staging, prod +# Copy to .env and customize. For dev (localhost): also copy .env.dev.example to .env.dev. +# +# 12-factor: Every variable here is consumed by at least one compose file, +# config template, script, or application. No orphans. +# +# Sections: Core → IRC → XMPP → Bridge → Web → Portal (external) # ============================================================================= -# ENVIRONMENT PROFILES (Toggle features based on environment) +# CORE # ============================================================================= -# For localhost dev: copy .env.dev.example to .env.dev and run 'just dev'. -# .env.dev overrides IRC_DOMAIN=irc.localhost, PROSODY_DOMAIN=xmpp.localhost, etc. -# -# Development: Enable debug logs, hot reloading, admin tools (Adminer, Dozzle) -# Staging: Mirror production but with sanitized data or test integrations -# Production: Optimized for performance and security - +ATL_ENVIRONMENT=dev # dev, staging, prod — controls TLS verify, log levels -# Docker Ergonomics (Solves permission issues) +# Docker UID/GID (solves volume permission issues) PUID=1000 PGID=1000 TZ=UTC -# ============================================================================= -# 2. GLOBAL NETWORKING (Tailscale 100.64.0.0/10) -# ============================================================================= -ATL_GATEWAY_IP=100.64.1.0 # IP of the atl.network NPM host -ATL_CHAT_IP=100.64.7.0 # IP of the atl.chat container host - -# Global Port Registry (Standardized across services) -IRC_TLS_PORT=6697 -IRC_SERVER_PORT=6900 -IRC_RPC_PORT=8600 -IRC_WEBSOCKET_PORT=8000 - -XMPP_C2S_PORT=5222 -XMPP_S2S_PORT=5269 -XMPP_HTTP_PORT=5280 -XMPP_HTTPS_PORT=5281 +# Host networking (Tailscale 100.64.0.0/10 for prod; 127.0.0.1 for dev via .env.dev) +ATL_GATEWAY_IP=100.64.1.0 +ATL_CHAT_IP=100.64.7.0 -TURN_PORT=3478 -TURNS_PORT=5349 +# Let's Encrypt (cert-manager) +LETSENCRYPT_EMAIL=admin@allthingslinux.org +CLOUDFLARE_DNS_API_TOKEN= # ============================================================================= -# 3. IRC SERVICE (UnrealIRCd & Atheme) +# IRC SERVICE (UnrealIRCd + Atheme) # ============================================================================= -# IRC Stack Versions (git tags for reproducible builds) +# Build versions (git tags) UNREALIRCD_VERSION=6.2.0.1 -# master = 7.3 dev; v7.2.12 = stable ATHEME_VERSION=master -# Network Identity +# --- Network identity --- IRC_DOMAIN=irc.atl.chat IRC_ROOT_DOMAIN=atl.chat IRC_NETWORK_NAME="All Things Linux IRC" IRC_CLOAK_PREFIX=atl -# Cloak keys - keep secret; must be identical on all servers. Generate with: just irc gencloak +# --- Ports (mapped in compose) --- +IRC_TLS_PORT=6697 +IRC_SERVER_PORT=6900 +IRC_RPC_PORT=8600 +IRC_WEBSOCKET_PORT=8000 + +# --- Security secrets (CHANGE THESE) --- +# Cloak keys — must be identical on all servers. Generate with: just irc gencloak IRC_CLOAK_KEY_1=daa0ad2a69ba7683a2cdb02499f2e98b0729423bb7578d1f1dfbcdfe015f1f8b554b13203289c83D IRC_CLOAK_KEY_2=899874eda706ee805bd34792bfd7bd62711f1938dea920c8bdf8396fe136ab6a83785a3ce54eB298 IRC_CLOAK_KEY_3=d8936d8fff38eace5c379c94578abfa802088bd241329c64506513fe8e4de3e2304f7dd00355A8d6 +IRC_OPER_PASSWORD='$argon2id$v=19$m=6144,t=2,p=2$WXOLpTE+DPDr8q6OBVTx3w$bqXpBsaAK6lkXfR/IPn+TcE0VJEKjUFD7xordE6pFSo' +IRC_DRPASS=change_me_drpass +IRC_SERVICES_PASSWORD=change_me_secure_services_pass +ATL_WEBIRC_PASSWORD=change_me_webirc_password + +# --- Admin info --- IRC_ADMIN_NAME="All Things Linux" IRC_ADMIN_EMAIL=admin@allthingslinux.org IRC_STAFF_VHOST=allthingslinux.org -# Security Configuration -IRC_OPER_PASSWORD='$argon2id$v=19$m=6144,t=2,p=2$WXOLpTE+DPDr8q6OBVTx3w$bqXpBsaAK6lkXfR/IPn+TcE0VJEKjUFD7xordE6pFSo' -IRC_DRPASS=change_me_drpass -ATL_WEBIRC_PASSWORD=change_me_webirc_password +# --- STS (Strict Transport Security) --- IRC_STS_DURATION=1m IRC_STS_PRELOAD=no -# SSL/TLS Configuration (Let's Encrypt layout in data/certs, mounted as certs/) +# --- TLS cert paths (inside container) --- IRC_SSL_CERT_PATH=/home/unrealircd/unrealircd/certs/live/irc.atl.chat/fullchain.pem IRC_SSL_KEY_PATH=/home/unrealircd/unrealircd/certs/live/irc.atl.chat/privkey.pem -# Atheme Services Configuration +# --- Atheme services link --- IRC_SERVICES_SERVER=services.atl.chat -# IRC_SERVICES_PASSWORD is used for both the UnrealIRCd link block and the Atheme uplink password. -# Both unrealircd.conf.template and atheme.conf.template substitute ${IRC_SERVICES_PASSWORD}. -IRC_SERVICES_PASSWORD=change_me_secure_services_pass ATHEME_SERVER_NAME=services.atl.chat ATHEME_SERVER_DESC="All Things Linux IRC Services" ATHEME_UPLINK_HOST=127.0.0.1 ATHEME_UPLINK_PORT=6901 -ATHEME_UPLINK_SSL_PORT=6900 ATHEME_NUMERIC=00A ATHEME_RECONTIME=10 -ATHEME_LOG_LEVEL=all ATHEME_HTTPD_PORT=8081 -# Atheme Network Information +# --- Atheme network info --- ATHEME_NETNAME=atl.chat ATHEME_ADMIN_NAME="All Things Linux" ATHEME_ADMIN_EMAIL=admin@allthingslinux.org @@ -102,7 +85,8 @@ ATHEME_HIDEHOST_SUFFIX=users.atl.chat ATHEME_HELP_CHANNEL=#help ATHEME_HELP_URL=https://discord.gg/linux -# Atheme Service Bots - Core Services +# --- Atheme service bot identities --- +# Format: ATHEME__NICK, _USER, _HOST, _REAL ATHEME_NICKSERV_NICK=NickServ ATHEME_NICKSERV_USER=NickServ ATHEME_NICKSERV_HOST=services.atl.chat @@ -123,13 +107,11 @@ ATHEME_MEMOSERV_USER=MemoServ ATHEME_MEMOSERV_HOST=services.atl.chat ATHEME_MEMOSERV_REAL="Memo Services" -# Atheme Service Bots - Authentication ATHEME_SASLSERV_NICK=SaslServ ATHEME_SASLSERV_USER=SaslServ ATHEME_SASLSERV_HOST=services.atl.chat ATHEME_SASLSERV_REAL="SASL Authentication Agent" -# Atheme Service Bots - Management ATHEME_BOTSERV_NICK=BotServ ATHEME_BOTSERV_USER=BotServ ATHEME_BOTSERV_HOST=services.atl.chat @@ -145,7 +127,6 @@ ATHEME_HOSTSERV_USER=HostServ ATHEME_HOSTSERV_HOST=services.atl.chat ATHEME_HOSTSERV_REAL="Host Management Services" -# Atheme Service Bots - Information ATHEME_INFOSERV_NICK=InfoServ ATHEME_INFOSERV_USER=InfoServ ATHEME_INFOSERV_HOST=services.atl.chat @@ -161,12 +142,6 @@ ATHEME_STATSERV_USER=StatServ ATHEME_STATSERV_HOST=services.atl.chat ATHEME_STATSERV_REAL="Statistics Services" -# Atheme Service Bots - Utility -ATHEME_CHANFIX_NICK=ChanFix -ATHEME_CHANFIX_USER=ChanFix -ATHEME_CHANFIX_HOST=services.atl.chat -ATHEME_CHANFIX_REAL="Channel Fixing Service" - ATHEME_GLOBAL_NICK=Global ATHEME_GLOBAL_USER=Global ATHEME_GLOBAL_HOST=services.atl.chat @@ -177,13 +152,11 @@ ATHEME_ALIS_USER=alis ATHEME_ALIS_HOST=services.atl.chat ATHEME_ALIS_REAL="Channel Directory" -# Atheme Service Bots - Security ATHEME_PROXYSCAN_NICK=Proxyscan ATHEME_PROXYSCAN_USER=dnsbl ATHEME_PROXYSCAN_HOST=services.atl.chat ATHEME_PROXYSCAN_REAL="Proxyscan Service" -# Atheme Service Bots - Gaming ATHEME_GAMESERV_NICK=GameServ ATHEME_GAMESERV_USER=GameServ ATHEME_GAMESERV_HOST=services.atl.chat @@ -194,25 +167,26 @@ ATHEME_RPGSERV_USER=RPGServ ATHEME_RPGSERV_HOST=services.atl.chat ATHEME_RPGSERV_REAL="RPG Finding Services" -# UnrealIRCd Web Panel +# --- WebPanel --- WEBPANEL_PORT=8080 WEBPANEL_RPC_USER=adminpanel WEBPANEL_RPC_PASSWORD=change_me_webpanel_password -# The Lounge (web IRC client) +# --- The Lounge --- THELOUNGE_PORT=9000 -THELOUNGE_DOMAIN=webirc.atl.chat THELOUNGE_WEBIRC_PASSWORD=change_me_thelounge_webirc THELOUNGE_DELETE_UPLOADS_AFTER_MINUTES=1440 # ============================================================================= -# 4. XMPP SERVICE (Prosody) +# XMPP SERVICE (Prosody) # ============================================================================= XMPP_DOMAIN=atl.chat PROSODY_ADMIN_EMAIL=admin@allthingslinux.org -PROSODY_ENV=development -# Database Configuration +# --- Storage (sqlite for dev, sql for prod with PROSODY_DB_* vars) --- +PROSODY_STORAGE=sqlite + +# --- Database (only used when PROSODY_STORAGE=sql) --- PROSODY_DB_DRIVER=PostgreSQL PROSODY_DB_HOST=xmpp-postgres-dev PROSODY_DB_PORT=5432 @@ -220,35 +194,52 @@ PROSODY_DB_NAME=prosody PROSODY_DB_USER=prosody PROSODY_DB_PASSWORD=change_me_secure_db_pass -# Security Settings -# Registration: false = Portal provisions users via mod_http_admin_api; true = allow self-registration (dev only) +# --- Ports (mapped in compose as PROSODY_*_PORT) --- +# Compose uses PROSODY_C2S_PORT (not XMPP_C2S_PORT) — keep names aligned. +PROSODY_C2S_PORT=5222 +PROSODY_S2S_PORT=5269 +PROSODY_HTTP_PORT=5280 +PROSODY_HTTPS_PORT=5281 +PROSODY_C2S_DIRECT_TLS_PORT=5223 +PROSODY_S2S_DIRECT_TLS_PORT=5270 +PROSODY_PROXY65_PORT=5000 + +# --- TURN/STUN --- +TURN_PORT=3478 +TURNS_PORT=5349 +TURN_SECRET=change_me_turn_secret +TURN_EXTERNAL_HOST=turn.atl.network + +# --- Security --- PROSODY_ALLOW_REGISTRATION=false PROSODY_C2S_REQUIRE_ENCRYPTION=true PROSODY_S2S_REQUIRE_ENCRYPTION=true PROSODY_S2S_SECURE_AUTH=true +PROSODY_ALLOW_UNENCRYPTED_PLAIN_AUTH=false PROSODY_MAX_CONNECTIONS_PER_IP=10 PROSODY_REGISTRATION_THROTTLE_MAX=10 PROSODY_REGISTRATION_THROTTLE_PERIOD=60 PROSODY_BLOCK_REGISTRATIONS_REQUIRE=^[a-zA-Z0-9_.-]+$ PROSODY_TLS_CHANNEL_BINDING=false -PROSODY_ALLOW_UNENCRYPTED_PLAIN_AUTH=false -# Networking & HTTP +# --- HTTP --- PROSODY_HTTP_HOST=localhost PROSODY_HTTP_SCHEME=http +PROSODY_UPLOAD_EXTERNAL_URL=http://localhost:5280/upload/ +PROSODY_PROXY_ADDRESS=localhost -# SSL Certificates +# --- TLS certs --- PROSODY_SSL_KEY=certs/live/localhost/privkey.pem PROSODY_SSL_CERT=certs/live/localhost/fullchain.pem -# Logging & Monitoring +# --- Logging --- PROSODY_LOG_LEVEL=info PROSODY_STATISTICS=internal PROSODY_STATISTICS_INTERVAL=manual PROSODY_OPENMETRICS_IP=127.0.0.1 PROSODY_OPENMETRICS_CIDR=127.0.0.1/32 -# Message Archiving (MAM) +# --- MAM (Message Archiving) --- PROSODY_ARCHIVE_EXPIRES_AFTER=30d PROSODY_ARCHIVE_POLICY=true PROSODY_ARCHIVE_COMPRESSION=true @@ -256,7 +247,7 @@ PROSODY_ARCHIVE_STORE=archive PROSODY_ARCHIVE_MAX_QUERY_RESULTS=250 PROSODY_MAM_SMART_ENABLE=true -# MUC (Multi-User Chat) +# --- MUC --- PROSODY_MUC_NOTIFICATIONS=true PROSODY_MUC_OFFLINE_DELIVERY=true PROSODY_RESTRICT_ROOM_CREATION=false @@ -274,7 +265,7 @@ PROSODY_MUC_LOG_STORE=muc_log PROSODY_MUC_LOG_COMPRESSION=true PROSODY_MUC_MAM_SMART_ENABLE=false -# Rate Limiting +# --- Rate limiting --- PROSODY_C2S_RATE=10kb/s PROSODY_C2S_BURST=25kb PROSODY_C2S_STANZA_SIZE=262144 @@ -284,7 +275,7 @@ PROSODY_S2S_STANZA_SIZE=524288 PROSODY_HTTP_UPLOAD_RATE=2mb/s PROSODY_HTTP_UPLOAD_BURST=10mb -# Push Notifications +# --- Push notifications --- PROSODY_PUSH_IMPORTANT_BODY="New Message!" PROSODY_PUSH_MAX_ERRORS=16 PROSODY_PUSH_MAX_DEVICES=5 @@ -292,139 +283,61 @@ PROSODY_PUSH_MAX_HIBERNATION_TIMEOUT=259200 PROSODY_PUSH_NOTIFICATION_WITH_BODY=false PROSODY_PUSH_NOTIFICATION_WITH_SENDER=false -# Account Management +# --- Account lifecycle --- PROSODY_ACCOUNT_INACTIVE_PERIOD=31536000 PROSODY_ACCOUNT_GRACE_PERIOD=2592000 -PROSODY_ACCOUNT_DELETION_CONFIRMATION=true -# Server Information +# --- Server info --- PROSODY_SERVER_NAME=localhost PROSODY_SERVER_WEBSITE=http://localhost PROSODY_SERVER_DESCRIPTION="XMPP Service" -# Performance & Tuning +# --- Performance tuning --- LUA_GC_STEP_SIZE=13 LUA_GC_PAUSE=110 LUA_GC_SPEED=200 LUA_GC_THRESHOLD=120 -# Component URLs -PROSODY_UPLOAD_EXTERNAL_URL=http://localhost:5280/upload/ -PROSODY_PROXY_ADDRESS=localhost -# PubSub feed (mod_pubsub_feeds): node "feed" pulls from this URL +# --- PubSub feeds --- PROSODY_FEED_URL=https://allthingslinux.org/feed # ============================================================================= -# 5. DATABASE (Shared PostgreSQL Standards) -# ============================================================================= -POSTGRES_USER=prosody -POSTGRES_DB=prosody -POSTGRES_PASSWORD=change_me_secure_db_pass - -# PostgreSQL Performance Tuning (Development) -POSTGRES_SHARED_BUFFERS=32MB -POSTGRES_EFFECTIVE_CACHE_SIZE=128MB -POSTGRES_WORK_MEM=1MB -POSTGRES_MAINTENANCE_WORK_MEM=16MB -POSTGRES_CHECKPOINT_COMPLETION_TARGET=0.9 -POSTGRES_WAL_BUFFERS=4MB -POSTGRES_DEFAULT_STATISTICS_TARGET=50 -POSTGRES_RANDOM_PAGE_COST=1.1 -POSTGRES_EFFECTIVE_IO_CONCURRENCY=100 - -# ============================================================================= -# 6. ADMINER (Database Management UI) -# ============================================================================= -ADMINER_PORT=8080 -ADMINER_AUTO_LOGIN=false -ADMINER_DEFAULT_DRIVER=pgsql -ADMINER_DEFAULT_SERVER=xmpp-postgres -ADMINER_DEFAULT_DB=prosody -ADMINER_DEFAULT_USERNAME=prosody -ADMINER_DEFAULT_PASSWORD=change_me_secure_db_pass - -# ============================================================================= -# 7. NGINX REVERSE PROXY -# ============================================================================= -NGINX_HTTP_PORT=80 -NGINX_HTTPS_PORT=443 - -# ============================================================================= -# 8. EXTERNAL INTEGRATIONS & SECURITY -# ============================================================================= -# Sentry DSN (frontend & backends) -ATL_SENTRY_DSN= - -# Portal Integration Secrets -ATL_INTERNAL_SECRET_IRC= -ATL_INTERNAL_SECRET_XMPP= - -# Let's Encrypt / SSL -LETSENCRYPT_EMAIL=admin@allthingslinux.org - -# ============================================================================= -# BRIDGE SERVICE (ATL Bridge — Discord/IRC/XMPP relay) +# BRIDGE SERVICE (Discord↔IRC↔XMPP relay) # ============================================================================= -# Discord bot token (required for bridge to connect) BRIDGE_DISCORD_TOKEN=change_me_discord_bot_token - -# Discord channel ID to bridge (right-click channel → Copy ID; survives prepare-config) BRIDGE_DISCORD_CHANNEL_ID=REPLACE_WITH_DISCORD_CHANNEL_ID - -# Portal connection BRIDGE_PORTAL_BASE_URL=https://portal.atl.tools BRIDGE_PORTAL_TOKEN=change_me_bridge_portal_token -# XMPP component JID: must match Prosody Component (bridge.${PROSODY_DOMAIN}) -# Prod: bridge.atl.chat | Dev (.env.dev): bridge.xmpp.localhost +# XMPP component BRIDGE_XMPP_COMPONENT_JID=bridge.atl.chat BRIDGE_XMPP_COMPONENT_SECRET=change_me_xmpp_component_secret BRIDGE_XMPP_COMPONENT_SERVER=atl-xmpp-server BRIDGE_XMPP_COMPONENT_PORT=5347 -# Log level: DEBUG, INFO, WARNING, ERROR (default: INFO). DEBUG enables msgid/RELAYMSG debugging. -# LOG_LEVEL=DEBUG - -# IRC nick for bridge main connection +# IRC BRIDGE_IRC_NICK=atl-bridge - -# IRC oper password for bridge (enables creating channels and setting +P permanent) -# If set, bridge will OPER up after connect and set +P on bridged channels BRIDGE_IRC_OPER_PASSWORD=change_me_bridge_oper - -# IRC server hostname for bridge config (Docker: atl-irc-server; prod: irc.atl.chat) IRC_BRIDGE_SERVER=atl-irc-server +# LOG_LEVEL=DEBUG -# Bridge dev/prod: ATL_ENVIRONMENT=dev disables IRC TLS cert verification (self-signed) -# Override explicitly: BRIDGE_IRC_TLS_VERIFY=false|true -# (prepare-config.sh sets IRC_TLS_VERIFY from ATL_ENVIRONMENT when generating config.yaml) +# ============================================================================= +# WEB FRONTEND (Next.js) +# ============================================================================= +NEXT_PUBLIC_IRC_WS_URL=wss://irc.atl.chat/ws +NEXT_PUBLIC_XMPP_BOSH_URL=https://xmpp.atl.chat/http-bind # ============================================================================= -# PORTAL INTEGRATION URLS (used by portal to reach IRC/XMPP services) +# PORTAL INTEGRATION (consumed by external portal service, not this monorepo) # ============================================================================= -# Atheme JSON-RPC URL (portal IRC provisioning) +# These vars are pre-defined for the external ATL Portal service that connects +# to IRC/XMPP services. They are NOT consumed by any compose/config in this repo. IRC_ATHEME_JSONRPC_URL=http://atl-irc-server:8081/jsonrpc - -# UnrealIRCd JSON-RPC URL (portal admin features) IRC_UNREAL_JSONRPC_URL=https://irc.atl.chat:8600/api -IRC_UNREAL_RPC_USER=${WEBPANEL_RPC_USER} -IRC_UNREAL_RPC_PASSWORD=${WEBPANEL_RPC_PASSWORD} - -# Prosody admin API URL (portal XMPP provisioning via mod_http_admin_api) -# Endpoint: PUT {PROSODY_REST_URL}/admin_api/users/{username} body: {"password":"..."} -# Auth: HTTP Basic with PROSODY_REST_USERNAME:PROSODY_REST_PASSWORD +IRC_UNREAL_RPC_USER=adminpanel +IRC_UNREAL_RPC_PASSWORD=change_me_webpanel_password PROSODY_REST_URL=http://atl-xmpp-server:5280 PROSODY_REST_USERNAME=admin@atl.chat PROSODY_REST_PASSWORD=change_me_prosody_rest_password - -# IRC server hostname and TLS port (same value as IRC_DOMAIN / IRC_TLS_PORT) -# IRC_SERVER is used by portal; IRC_DOMAIN is used by the UnrealIRCd container. IRC_SERVER=irc.atl.chat IRC_PORT=6697 - -# ============================================================================= -# 9. WEB FRONTEND (Next.js) -# ============================================================================= -# Use NEXT_PUBLIC_ prefix for client-side accessible variables -NEXT_PUBLIC_IRC_WS_URL=wss://irc.atl.chat/ws -NEXT_PUBLIC_XMPP_BOSH_URL=https://xmpp.atl.chat/http-bind diff --git a/ENV_AUDIT.md b/docs/audits/env-var-audit.md similarity index 100% rename from ENV_AUDIT.md rename to docs/audits/env-var-audit.md From 9f4bf662468098617840a7525963cd1dbc10bcea Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 06:22:06 +0000 Subject: [PATCH 12/70] fix: address dev/prod lifecycle gaps from audit Critical fixes: - just prod/staging now run init.sh and pass --env-file .env (was bare docker compose with no init, no dirs, no config generation) - prepare-config.sh only sources .env.dev when ATL_ENVIRONMENT=dev, preventing dev overrides from leaking into prod config generation - Prosody .env.example cert paths changed from 'localhost' to 'atl.chat' (dev override in .env.dev correctly uses xmpp.localhost) Cleanup: - Removed meaningless --profile staging/prod from down commands (no services use these profiles; staging/prod are identical to default) Added: docs/audits/dev-prod-lifecycle-audit.md with full trace of both flows covering env, scripts, Docker, TLS, DNS, and networking --- .env.example | 7 +- docs/audits/dev-prod-lifecycle-audit.md | 283 ++++++++++++++++++++++++ justfile | 18 +- scripts/prepare-config.sh | 12 +- 4 files changed, 304 insertions(+), 16 deletions(-) create mode 100644 docs/audits/dev-prod-lifecycle-audit.md diff --git a/.env.example b/.env.example index d10730a9..37450a02 100644 --- a/.env.example +++ b/.env.example @@ -228,9 +228,10 @@ PROSODY_HTTP_SCHEME=http PROSODY_UPLOAD_EXTERNAL_URL=http://localhost:5280/upload/ PROSODY_PROXY_ADDRESS=localhost -# --- TLS certs --- -PROSODY_SSL_KEY=certs/live/localhost/privkey.pem -PROSODY_SSL_CERT=certs/live/localhost/fullchain.pem +# --- TLS certs (path relative to Prosody cert dir; must match XMPP_DOMAIN) --- +# For dev: .env.dev overrides to certs/live/xmpp.localhost/... +PROSODY_SSL_KEY=certs/live/atl.chat/privkey.pem +PROSODY_SSL_CERT=certs/live/atl.chat/fullchain.pem # --- Logging --- PROSODY_LOG_LEVEL=info diff --git a/docs/audits/dev-prod-lifecycle-audit.md b/docs/audits/dev-prod-lifecycle-audit.md new file mode 100644 index 00000000..17ebe4c3 --- /dev/null +++ b/docs/audits/dev-prod-lifecycle-audit.md @@ -0,0 +1,283 @@ +# Dev vs Prod Lifecycle Audit + +**Date:** 2026-02-26 +**Scope:** Full trace of dev and prod setup flows — env, scripts, Docker, TLS, DNS, config templates + +--- + +## Executive Summary + +The dev story is **solid and works well**. The prod story has **real gaps** that would block a production deployment. The main issues are: `just prod` doesn't run `init.sh` or load env files; the cert-manager is a placeholder that doesn't actually issue certs; the staging/prod profiles don't actually differentiate services; and there's no documentation for the prod deployment path. + +**Verdict:** Dev is ~90% clean. Prod is ~40% ready. + +--- + +## Dev Flow Trace (`just dev`) + +### What happens + +``` +just dev + → sources .env.dev (sets ATL_ENVIRONMENT=dev, IRC_DOMAIN=irc.localhost, etc.) + → runs scripts/init.sh: + 1. Creates data/ directories (irc, atheme, xmpp, thelounge, certs) + 2. Sets permissions (chown/chmod) + 3. Copies system CA bundle to apps/unrealircd/config/tls/ + 4. Generates self-signed certs for irc.localhost and xmpp.localhost + 5. Copies .env.example → .env (if .env missing) + 6. Runs prepare-config.sh: + - Sources .env then .env.dev + - envsubst on unrealircd.conf.template → unrealircd.conf + - envsubst on atheme.conf.template → atheme.conf + - envsubst on bridge config.template.yaml → config.yaml + - envsubst on thelounge config.js.template → config.js + → docker compose --env-file .env --env-file .env.dev --profile dev up -d +``` + +### Dev Flow Verdict: 🟢 Works well + +| Aspect | Status | Notes | +|--------|--------|-------| +| Env loading | 🟢 | `.env` base + `.env.dev` overlay, compose loads both via `--env-file` | +| Init script | 🟢 | Creates dirs, generates self-signed certs, generates configs | +| Config generation | 🟢 | `prepare-config.sh` sources both env files, envsubst works correctly | +| TLS (dev) | 🟢 | Self-signed certs auto-generated for `irc.localhost` and `xmpp.localhost` | +| Docker profile | 🟢 | `--profile dev` starts Dozzle log viewer | +| Port binding | 🟢 | `ATL_CHAT_IP=127.0.0.1` binds IRC ports to localhost only | +| Bridge TLS verify | 🟢 | `BRIDGE_IRC_TLS_VERIFY=false` skips cert verification for self-signed | +| Prosody TLS | 🟢 | `.env.dev` relaxes c2s/s2s encryption requirements | +| Storage | 🟢 | `PROSODY_STORAGE=sqlite` — no external DB needed | + +--- + +## Prod Flow Trace (`just prod`) + +### What happens + +``` +just prod + → docker compose --profile prod up -d +``` + +That's it. **No init.sh. No env file loading. No config generation.** + +### Prod Flow Verdict: 🔴 Broken + +| Issue | Severity | Details | +|-------|----------|---------| +| **No init.sh** | 🔴 | `just prod` doesn't run `init.sh`. No `data/` dirs created, no configs generated from templates. If deploying to a fresh server, nothing works. | +| **No `--env-file`** | 🔴 | Unlike `just dev` which passes `--env-file .env --env-file .env.dev`, `just prod` passes nothing. Docker Compose will auto-load `.env` but there's no explicit prod overlay. All prod-specific overrides must be in `.env` itself. | +| **No staging/prod profile differentiation** | 🟡 | The `profiles: ["dev"]` on Dozzle means it only starts with `--profile dev`. But no services have `profiles: ["prod"]` or `profiles: ["staging"]`. So `just prod` and `just staging` start the exact same services as `docker compose up -d` (everything except Dozzle). The profiles are meaningless for prod/staging. | +| **Cert-manager is a placeholder** | 🔴 | The cert-manager service uses `goacme/lego:latest` with a custom `run.sh`, but it runs on every `docker compose up` — including dev where it's not needed. There's no `CLOUDFLARE_DNS_API_TOKEN` set in dev, so it likely errors silently. In prod, someone would need to set this token, but there's no documentation for the cert issuance flow. | +| **No prod documentation** | 🟡 | README says `just prod` starts production stack, but there's no guide for: initial prod setup, cert issuance, DNS records needed, secrets management, backup strategy. | + +--- + +## TLS/Certificate Audit + +### Dev TLS + +``` +init.sh → generate_dev_certs() + → openssl req -x509 -nodes -days 365 -newkey rsa:2048 + → SANs: domain, *.domain, muc/upload/proxy/pubsub/bridge subdomains, localhost, 127.0.0.1 + → Stored in: data/certs/live/{irc.localhost,xmpp.localhost}/ +``` + +| Aspect | Status | Notes | +|--------|--------|-------| +| Self-signed generation | 🟢 | Good SANs, RSA 2048, 365 days | +| IRC reads from | 🟢 | `data/certs` mounted as `/home/unrealircd/unrealircd/certs` | +| Prosody reads from | 🟢 | `data/certs` mounted as `/etc/prosody/certs` | +| Prosody entrypoint | 🟢 | Creates `https` symlinks for automatic HTTPS cert discovery | +| Bridge skips verify | 🟢 | `BRIDGE_IRC_TLS_VERIFY=false` in `.env.dev` | +| Prosody skips s2s auth | 🟢 | `PROSODY_S2S_SECURE_AUTH=false` in `.env.dev` | + +### Prod TLS + +| Aspect | Status | Notes | +|--------|--------|-------| +| Cert source | 🟡 | cert-manager (Lego/Cloudflare DNS) exists but is untested/undocumented | +| Cert path convention | 🟢 | Let's Encrypt layout: `data/certs/live//fullchain.pem + privkey.pem` — matches both IRC and Prosody mount points | +| IRC cert paths | 🟢 | `IRC_SSL_CERT_PATH` in `.env.example` correctly points to `/home/unrealircd/unrealircd/certs/live/irc.atl.chat/fullchain.pem` | +| Prosody cert paths | 🟢 | `PROSODY_SSL_KEY/CERT` in `.env.example` are relative (`certs/live/localhost/...`) — **should be `certs/live/atl.chat/...` for prod** | +| XMPP nginx certs | 🟢 | `data/certs` mounted to nginx as `/etc/nginx/certs` | +| Cert renewal | 🔴 | No cron/timer for cert renewal. Lego container runs once and exits. No reload mechanism for IRC/Prosody after renewal. | +| Cert permissions | 🟢 | `init.sh` sets `chmod 644` on privkey.pem for container user access | + +### TLS Config Issues + +1. **Prosody `.env.example` cert paths say `localhost`:** + + ``` + PROSODY_SSL_KEY=certs/live/localhost/privkey.pem + PROSODY_SSL_CERT=certs/live/localhost/fullchain.pem + ``` + + This is the **dev default baked into the "production" example file**. Should be the prod domain. The `.env.dev` override correctly switches to `xmpp.localhost`. But if someone copies `.env.example` for prod and forgets to change these, Prosody won't find its certs. + +2. **No cert rotation/reload:** + When certs renew (every 60-90 days with Let's Encrypt), the running daemons need to be told: + - UnrealIRCd: `unrealircdctl rehash` or `kill -HUP` + - Prosody: `prosodyctl reload` + - The Lounge: restart container + + There's no hook for this. + +--- + +## DNS Audit + +### Dev DNS + +Dev uses `.localhost` TLD — `irc.localhost`, `xmpp.localhost`. These resolve to `127.0.0.1` on most systems (RFC 6761). No DNS setup needed. + +**However:** Inside Docker containers, `irc.localhost` does NOT resolve to the host — containers use Docker's internal DNS which only resolves container/service names. This is why the bridge uses Docker hostnames (`atl-irc-server`, `atl-xmpp-server`) instead of `irc.localhost`. + +| Aspect | Status | Notes | +|--------|--------|-------| +| Host access | 🟢 | `irc.localhost:6697` works from host machine | +| Container-to-container | 🟢 | Uses Docker service names (`atl-irc-server`) | +| Browser access | 🟢 | `http://localhost:3000` (web), `http://localhost:9000` (Lounge) | +| XMPP client access | 🟡 | `xmpp.localhost` resolves on host but clients may not accept `.localhost` as a valid domain | + +### Prod DNS + +**No DNS setup documentation exists.** For production, operators would need: + +| Record | Type | Target | Purpose | +|--------|------|--------|---------| +| `irc.atl.chat` | A/AAAA | server IP | IRC server | +| `xmpp.atl.chat` | A/AAAA | server IP | XMPP BOSH/WebSocket (via nginx) | +| `_xmpp-client._tcp.atl.chat` | SRV | `xmpp.atl.chat:5222` | XMPP C2S discovery | +| `_xmpps-client._tcp.atl.chat` | SRV | `xmpp.atl.chat:5223` | XMPP Direct TLS discovery | +| `_xmpp-server._tcp.atl.chat` | SRV | `xmpp.atl.chat:5269` | XMPP S2S federation | +| `_xmpps-server._tcp.atl.chat` | SRV | `xmpp.atl.chat:5270` | XMPP S2S Direct TLS | +| `muc.atl.chat` | CNAME | `xmpp.atl.chat` | MUC component | +| `upload.atl.chat` | CNAME | `xmpp.atl.chat` | HTTP file upload | +| `proxy.atl.chat` | CNAME | `xmpp.atl.chat` | SOCKS5 proxy | +| `pubsub.atl.chat` | CNAME | `xmpp.atl.chat` | PubSub | + +--- + +## Docker Audit + +### Profiles + +| Profile | What it activates | Command | +|---------|-------------------|---------| +| `dev` | Dozzle only | `just dev` | +| `staging` | Nothing — no services use this profile | `just staging` | +| `prod` | Nothing — no services use this profile | `just prod` | +| (none) | All services except Dozzle | `docker compose up -d` | + +**Issue:** Staging and prod profiles are empty. `just staging` and `just prod` are functionally identical to `docker compose up -d`. The profiles add no value. + +### Image Strategy + +| Service | Build | Image | +|---------|-------|-------| +| UnrealIRCd | `apps/unrealircd/Containerfile` | (local build) | +| Atheme | `apps/atheme/Containerfile` | (local build) | +| WebPanel | `apps/webpanel/Containerfile` | (local build) | +| Prosody | `apps/prosody/Containerfile` | `allthingslinux/prosody:latest` | +| XMPP Nginx | `infra/nginx/Dockerfile` | `allthingslinux/prosody-nginx:latest` | +| Bridge | `apps/bridge/Containerfile` | `ghcr.io/allthingslinux/bridge:latest` | +| The Lounge | (none) | `ghcr.io/thelounge/thelounge:latest` | +| Cert Manager | (none) | `goacme/lego:latest` | +| Dozzle | (none) | `amir20/dozzle:v8` | + +**Issue:** Services with both `build:` and `image:` will use the pulled image if it exists, not the local build. This caused the bridge disconnect bug during dev setup. For dev, you want local builds. For prod, you want published images. Currently there's no way to switch — you'd need to `docker compose build` explicitly. + +### Networking + +All services are on a single `atl-chat` bridge network. Atheme uses `network_mode: service:atl-irc-server` (shares network namespace with UnrealIRCd). This is correct — Atheme connects to UnrealIRCd on `127.0.0.1:6901`. + +### Port Binding + +Dev: `ATL_CHAT_IP=127.0.0.1` binds IRC ports to localhost only. +Prod: `ATL_CHAT_IP=100.64.7.0` (Tailscale IP). XMPP, WebPanel, Lounge bind to `0.0.0.0` (all interfaces). + +**Issue:** XMPP ports bind to `0.0.0.0` in both dev and prod (the xmpp.yaml doesn't use `ATL_CHAT_IP`). This means XMPP is publicly accessible even in dev. Not a security issue for localhost but inconsistent with IRC's approach. + +--- + +## Script Audit + +### `just dev` vs `just prod` asymmetry + +``` +just dev: + 1. Sources .env.dev ← yes + 2. Runs init.sh ← yes (creates dirs, generates certs, generates configs) + 3. Passes --env-file .env --env-file .env.dev ← yes + 4. Uses --profile dev ← yes + +just prod: + 1. Sources nothing ← no + 2. Runs nothing ← no init.sh + 3. Passes nothing ← no --env-file (relies on auto .env loading) + 4. Uses --profile prod ← meaningless (no services use this profile) +``` + +This is the biggest structural problem. A first-time prod deployment would: + +1. Clone repo +2. Create `.env` from `.env.example` +3. Run `just prod` +4. **Fail** because: + - No `data/` directories exist + - No configs generated from templates + - No certs generated or obtained + - Templates still have `${VAR}` placeholders + +### `prepare-config.sh` always sources `.env.dev` + +Line 57-63: The script unconditionally sources `.env.dev` if it exists. This means even if you're preparing prod configs, dev overrides would apply if `.env.dev` exists on the prod server. This is fine if `.env.dev` doesn't exist in prod, but it's a landmine. + +--- + +## Findings Summary + +### 🔴 CRITICAL + +1. **`just prod` doesn't run `init.sh`** — no dirs, no certs, no config generation +2. **No cert renewal mechanism** — certs will expire with no reload +3. **Prosody `.env.example` cert paths default to `localhost`** — must be manually changed for prod + +### 🟡 WARNING + +1. **Staging/prod profiles are empty** — `just staging` and `just prod` are identical to bare `docker compose up` +2. **No prod deployment documentation** — DNS records, cert issuance, secrets, backups +3. **`prepare-config.sh` always loads `.env.dev`** if present — prod configs could get dev overrides +4. **XMPP ports bind to 0.0.0.0** — inconsistent with IRC's `ATL_CHAT_IP` binding +5. **Docker images: local build vs pulled image ambiguity** — no clear strategy + +### 🟢 OK + +1. Dev flow works smoothly end-to-end +2. Env overlay pattern (.env + .env.dev) is clean +3. Self-signed cert generation covers all needed SANs +4. Config templates use envsubst consistently +5. Docker networking (shared namespace for Atheme, single bridge network) is correct +6. Data directory structure is well-organized + +### 💡 SUGGESTIONS + +1. **Create `just prod` parity with `just dev`**: Run `init.sh`, pass `--env-file` +2. **Add `.env.prod.example`** for production-specific overrides +3. **Add cert renewal cron** in cert-manager + post-renewal reload hooks +4. **Add `docs/deployment.md`** with prod setup guide +5. **Add XMPP SRV record documentation** +6. **Consider `docker compose build --no-cache` step for prod image freshness** + +--- + +## Recommended Fix Priority + +1. Fix `just prod` / `just staging` to run init and load env files +2. Fix Prosody `.env.example` cert paths to use `${XMPP_DOMAIN}` not `localhost` +3. Add prod deployment docs (DNS, certs, secrets) +4. Add cert renewal mechanism +5. Clean up meaningless profiles diff --git a/justfile b/justfile index 43f94593..88f01bfe 100644 --- a/justfile +++ b/justfile @@ -38,27 +38,31 @@ dev: # Spin up the staging stack [group('Orchestration')] staging: - docker compose --profile staging up -d + @echo "Initializing Staging Environment..." + ./scripts/init.sh + docker compose --env-file .env up -d -# Spin up the production stack (default profile) +# Spin up the production stack [group('Orchestration')] prod: - docker compose --profile prod up -d + @echo "Initializing Production Environment..." + ./scripts/init.sh + docker compose --env-file .env up -d # Stop all services [group('Orchestration')] down: docker compose --profile dev down -# Stop all services +# Stop staging services [group('Orchestration')] down-staging: - docker compose --profile staging down + docker compose down -# Stop all services +# Stop production services [group('Orchestration')] down-prod: - docker compose --profile prod down + docker compose down # View logs (follow) [group('Orchestration')] diff --git a/scripts/prepare-config.sh b/scripts/prepare-config.sh index 3c19bb77..63dcaac3 100755 --- a/scripts/prepare-config.sh +++ b/scripts/prepare-config.sh @@ -46,7 +46,7 @@ prepare_config() { exit 1 fi - # Load .env (base) then .env.dev (overrides for just dev) + # Load .env (base), then .env.dev (overrides) only if not in prod/staging if [ -f "$PROJECT_ROOT/.env" ]; then log_info "Loading environment variables from .env" set -a @@ -54,16 +54,16 @@ prepare_config() { source "$PROJECT_ROOT/.env" set +a fi - if [ -f "$PROJECT_ROOT/.env.dev" ]; then - log_info "Loading .env.dev overrides" + # Only load .env.dev if it exists AND ATL_ENVIRONMENT is not prod/staging. + # This prevents dev overrides from leaking into production config generation. + if [ -f "$PROJECT_ROOT/.env.dev" ] && [ "${ATL_ENVIRONMENT:-dev}" = "dev" ]; then + log_info "Loading .env.dev overrides (ATL_ENVIRONMENT=dev)" set -a # shellcheck disable=SC1091 source "$PROJECT_ROOT/.env.dev" set +a fi - if [ -f "$PROJECT_ROOT/.env" ] || [ -f "$PROJECT_ROOT/.env.dev" ]; then - log_info "Environment variables loaded" - fi + log_info "Environment variables loaded" # Ensure Atheme JSON-RPC port has a default (for existing .env without it) export ATHEME_HTTPD_PORT="${ATHEME_HTTPD_PORT:-8081}" From f8ba7e9197c76de30f9797a8535c8274479051c8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 06:39:35 +0000 Subject: [PATCH 13/70] feat: add two-server deployment guide and proxy trust config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New: docs/deployment.md — comprehensive guide for deploying the atl.chat stack on Server B behind an Nginx Proxy Manager on Server A, connected via Tailscale mesh. Covers: - Full architecture diagram and prerequisites - Step-by-step Server B setup (.env configuration) - DNS records (A, CNAME, SRV for XMPP federation) - NPM proxy host config (HTTPS services: BOSH, WebSocket, Lounge, WebPanel) - NPM stream config (TCP passthrough: IRC TLS, XMPP C2S/S2S) - Certificate renewal and reload automation - Security checklist, backup strategy, troubleshooting Config changes: - Prosody: add Tailscale CIDR (100.64.0.0/10) to trusted_proxies - UnrealIRCd: add proxy 'npm-forwarded' block trusting Forwarded headers from gateway IP and Tailscale range Note: UnrealIRCd does not support HAProxy PROXY protocol on raw TCP listeners (upstream limitation). Direct IRC clients through NPM TCP streams appear as the proxy IP. Web clients use WEBIRC for real IPs. --- apps/prosody/config/prosody.cfg.lua | 3 +- .../config/unrealircd.conf.template | 14 +- docs/deployment.md | 400 ++++++++++++++++++ 3 files changed, 415 insertions(+), 2 deletions(-) create mode 100644 docs/deployment.md diff --git a/apps/prosody/config/prosody.cfg.lua b/apps/prosody/config/prosody.cfg.lua index b87c0cdf..280c52d5 100644 --- a/apps/prosody/config/prosody.cfg.lua +++ b/apps/prosody/config/prosody.cfg.lua @@ -384,7 +384,8 @@ https_interfaces = { "*" } http_files_dir = "/usr/share/prosody/www" -- Trusted reverse proxies for X-Forwarded-* handling (includes Docker networks 172.16-172.31) -trusted_proxies = { "127.0.0.1", "172.16.0.0/12", "10.0.0.0/8" } +-- Includes Docker (172.16/12), private (10/8), and Tailscale (100.64/10) ranges +trusted_proxies = { "127.0.0.1", "172.16.0.0/12", "10.0.0.0/8", "100.64.0.0/10" } -- Enable CORS for BOSH and WebSocket endpoints http_cors_override = { diff --git a/apps/unrealircd/config/unrealircd.conf.template b/apps/unrealircd/config/unrealircd.conf.template index 8271e0a4..a7ff624a 100644 --- a/apps/unrealircd/config/unrealircd.conf.template +++ b/apps/unrealircd/config/unrealircd.conf.template @@ -366,12 +366,24 @@ me { sid "001"; } -/* Support Proxy Protocol from NPM on the same network */ +/* WEBIRC: trust real-IP from NPM/gateway on the Tailscale mesh */ webirc { mask ${ATL_GATEWAY_IP}/32; password "${ATL_WEBIRC_PASSWORD}"; } +/* Reverse proxy trust: when NPM/gateway forwards WebSocket or HTTP connections, + * trust X-Forwarded-For headers from the gateway IP and Tailscale range. + * For TCP stream proxying (port 6697), clients appear as the NPM IP — the webirc + * block above handles real-IP for web clients. Direct IRC clients through TCP + * streams will show as the proxy IP (UnrealIRCd does not support HAProxy PROXY + * protocol; this is an upstream limitation). + */ +proxy npm-forwarded { + type forwarded; + match { ip ${ATL_GATEWAY_IP}/32; ip 100.64.0.0/10; } +} + /* The Lounge web client - passes real user IP via WEBIRC */ proxy thelounge { type webirc; diff --git a/docs/deployment.md b/docs/deployment.md new file mode 100644 index 00000000..434d241a --- /dev/null +++ b/docs/deployment.md @@ -0,0 +1,400 @@ +# Production Deployment Guide + +Deploy the atl.chat stack on a server behind an Nginx Proxy Manager (NPM) reverse proxy. + +## Architecture + +``` +Internet Tailscale Mesh + │ ┌──────────────────────┐ + │ DNS: │ │ + │ *.atl.chat → NPM IP │ Server A (NPM) │ Server B (atl.chat) + │ │ 100.64.1.0 │ 100.64.7.0 + ▼ │ │ +┌──────┐ │ Nginx Proxy Manager │ UnrealIRCd (:6697,:8000,:8600) +│Client├────:443/:6697──────►│ :80 → HTTPS redirect │ Atheme (shares irc network) +│ │ │ :443 → reverse proxy │ Prosody (:5222-5281) +└──────┘ │ :6697 → TCP stream │ XMPP Nginx (:5281) [optional] + │ :5222 → TCP stream │ Bridge + │ │ The Lounge (:9000) + └──────────────────────┘ WebPanel (:8080) +``` + +Both servers connected via Tailscale (or WireGuard/VPN). Public DNS points to Server A. +NPM on Server A reverse-proxies to Server B's private Tailscale IP. + +--- + +## Prerequisites + +- **Server A:** Nginx Proxy Manager installed, public IP, Tailscale joined +- **Server B:** Docker + Docker Compose, `just`, `git`, Tailscale joined +- **DNS:** Domain with Cloudflare DNS (for cert-manager DNS-01 challenge) +- **Tailscale:** Both servers on the same tailnet + +--- + +## Step 1: Server B — Clone and Configure + +```bash +git clone https://github.com/allthingslinux/atl.chat.git +cd atl.chat +cp .env.example .env +``` + +Edit `.env` — change these from defaults: + +```bash +# ── Core ── +ATL_ENVIRONMENT=prod +ATL_GATEWAY_IP=100.64.1.0 # Server A's Tailscale IP +ATL_CHAT_IP=100.64.7.0 # Server B's Tailscale IP + +# ── Certs (Cloudflare DNS challenge) ── +CLOUDFLARE_DNS_API_TOKEN= +LETSENCRYPT_EMAIL=admin@allthingslinux.org + +# ── IRC ── +IRC_DOMAIN=irc.atl.chat +IRC_ROOT_DOMAIN=atl.chat +IRC_SSL_CERT_PATH=/home/unrealircd/unrealircd/certs/live/irc.atl.chat/fullchain.pem +IRC_SSL_KEY_PATH=/home/unrealircd/unrealircd/certs/live/irc.atl.chat/privkey.pem + +# CHANGE ALL THESE from defaults: +IRC_OPER_PASSWORD= # Generate: just irc mkpasswd +IRC_DRPASS= +IRC_SERVICES_PASSWORD= +ATL_WEBIRC_PASSWORD= +WEBPANEL_RPC_USER= +WEBPANEL_RPC_PASSWORD= +THELOUNGE_WEBIRC_PASSWORD= + +# Regenerate cloak keys: +# just irc gencloak + +# ── XMPP ── +XMPP_DOMAIN=atl.chat +PROSODY_SSL_KEY=certs/live/atl.chat/privkey.pem +PROSODY_SSL_CERT=certs/live/atl.chat/fullchain.pem +PROSODY_UPLOAD_EXTERNAL_URL=https://xmpp.atl.chat/upload/ +PROSODY_PROXY_ADDRESS=atl.chat +PROSODY_SERVER_NAME=atl.chat +PROSODY_SERVER_WEBSITE=https://atl.chat +PROSODY_SERVER_DESCRIPTION="All Things Linux XMPP" + +# If NPM handles HTTPS for XMPP (recommended): +# PROSODY_HTTPS_VIA_PROXY=true + +# ── TURN ── +TURN_SECRET= +TURN_EXTERNAL_HOST=turn.atl.network + +# ── Bridge ── +BRIDGE_DISCORD_TOKEN= +BRIDGE_DISCORD_CHANNEL_ID= +BRIDGE_XMPP_COMPONENT_JID=bridge.atl.chat +BRIDGE_XMPP_COMPONENT_SECRET= + +# STS (start conservative, increase after testing): +IRC_STS_DURATION=1d +IRC_STS_PRELOAD=no + +# Portal (if using): +# BRIDGE_PORTAL_BASE_URL=https://portal.atl.tools +# BRIDGE_PORTAL_TOKEN= +``` + +**Do NOT create `.env.dev`** — the prod flow should not load dev overrides. + +--- + +## Step 2: Server B — Initialize and Start + +```bash +# Install just if not present +curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin + +# Initialize (creates data dirs, generates configs, obtains certs) +just prod + +# Check all services are healthy +just status + +# Follow logs for issues +just logs +``` + +The `just prod` command: + +1. Runs `scripts/init.sh` which creates `data/` directories, generates configs from + templates, and generates self-signed certs as fallback +2. Starts `docker compose --env-file .env up -d` + +The cert-manager container will attempt to obtain Let's Encrypt certs via Cloudflare +DNS-01 challenge. Check its logs: + +```bash +docker compose logs cert-manager +``` + +--- + +## Step 3: DNS Records + +Point these at **Server A** (the NPM host's public IP): + +| Record | Type | Value | Purpose | +|--------|------|-------|---------| +| `irc.atl.chat` | A | `` | IRC server | +| `atl.chat` | A | `` | XMPP (VirtualHost domain) | +| `xmpp.atl.chat` | CNAME | `atl.chat` | XMPP HTTP services (BOSH, WebSocket, upload) | +| `webirc.atl.chat` | CNAME | `atl.chat` | The Lounge web IRC client | +| `panel.atl.chat` | CNAME | `atl.chat` | UnrealIRCd WebPanel | + +### XMPP SRV Records + +These tell XMPP clients and servers where to connect. Point at `xmpp.atl.chat` +(which resolves to Server A / NPM): + +| Record | Type | Priority | Weight | Port | Target | +|--------|------|----------|--------|------|--------| +| `_xmpp-client._tcp.atl.chat` | SRV | 0 | 5 | 5222 | `xmpp.atl.chat` | +| `_xmpps-client._tcp.atl.chat` | SRV | 0 | 5 | 5223 | `xmpp.atl.chat` | +| `_xmpp-server._tcp.atl.chat` | SRV | 0 | 5 | 5269 | `xmpp.atl.chat` | +| `_xmpps-server._tcp.atl.chat` | SRV | 0 | 5 | 5270 | `xmpp.atl.chat` | + +### XMPP Component Records (optional, for federation) + +| Record | Type | Value | +|--------|------|-------| +| `muc.atl.chat` | CNAME | `atl.chat` | +| `upload.atl.chat` | CNAME | `atl.chat` | +| `proxy.atl.chat` | CNAME | `atl.chat` | +| `pubsub.atl.chat` | CNAME | `atl.chat` | + +--- + +## Step 4: NPM Configuration — Server A + +### HTTP Proxy Hosts (HTTPS termination at NPM) + +For these services, NPM terminates TLS and proxies to Server B over the Tailscale mesh. + +| Domain | Scheme | Forward Host | Forward Port | Websocket | SSL | +|--------|--------|-------------|--------------|-----------|-----| +| `xmpp.atl.chat` | http | `100.64.7.0` | `5280` | ✅ | Let's Encrypt | +| `webirc.atl.chat` | http | `100.64.7.0` | `9000` | ✅ | Let's Encrypt | +| `panel.atl.chat` | http | `100.64.7.0` | `8080` | ❌ | Let's Encrypt | + +For each: + +1. NPM → Proxy Hosts → Add Proxy Host +2. Domain: `xmpp.atl.chat` +3. Scheme: `http`, Forward Hostname: `100.64.7.0`, Port: `5280` +4. Enable "Websockets Support" (required for BOSH and WebSocket) +5. SSL tab → Request new Let's Encrypt certificate, enable "Force SSL" +6. Custom Nginx Config (Advanced tab) for XMPP: + +```nginx +# Increase timeouts for long-lived BOSH/WebSocket connections +proxy_read_timeout 900s; +proxy_send_timeout 900s; + +# WebSocket upgrade headers (NPM adds these when Websocket is checked, +# but explicit config ensures correct behavior) +proxy_set_header Upgrade $http_upgrade; +proxy_set_header Connection "upgrade"; +``` + +### IRC WebSocket (via HTTP proxy) + +The IRC WebSocket on port 8000 can also go through an HTTP proxy host: + +| Domain | Scheme | Forward Host | Forward Port | Websocket | SSL | +|--------|--------|-------------|--------------|-----------|-----| +| `irc.atl.chat` | http | `100.64.7.0` | `8000` | ✅ | Let's Encrypt | + +This handles `wss://irc.atl.chat/ws` connections from web clients. + +### TCP Stream Proxies (TLS passthrough) + +For non-HTTP protocols, NPM forwards raw TCP. Server B handles TLS. + +In NPM → Streams → Add Stream: + +| Incoming Port | Forward Host | Forward Port | PROXY Protocol | +|---------------|-------------|--------------|----------------| +| `6697` | `100.64.7.0` | `6697` | ❌ | +| `5222` | `100.64.7.0` | `5222` | ❌ | +| `5223` | `100.64.7.0` | `5223` | ❌ | +| `5269` | `100.64.7.0` | `5269` | ❌ | +| `5270` | `100.64.7.0` | `5270` | ❌ | + +**Note on real client IPs for IRC TCP streams:** UnrealIRCd does not support HAProxy +PROXY Protocol on raw TCP listeners (upstream limitation). Direct IRC clients connecting +through the NPM TCP stream on port 6697 will appear as the NPM/gateway IP. Web clients +connecting through The Lounge or WebSocket use WEBIRC to pass real IPs. The `proxy +npm-forwarded` block trusts `Forwarded` headers from the gateway for HTTP-based services. + +**XMPP ports:** Prosody doesn't support PROXY Protocol on C2S/S2S either. The +`trusted_proxies` config handles X-Forwarded-For for HTTP endpoints, and S2S +authenticates by domain (not IP) via XMPP dialback or certificates. + +--- + +## Step 5: Verify + +### IRC + +```bash +# From your local machine — connect through NPM +openssl s_client -connect irc.atl.chat:6697 + +# Should see the UnrealIRCd TLS certificate and IRC welcome +``` + +### XMPP + +```bash +# Test BOSH endpoint through NPM +curl -sf https://xmpp.atl.chat/http-bind + +# Test with an XMPP client (Conversations, Gajim, etc.) +# Account: user@atl.chat, Server: atl.chat +``` + +### Web Clients + +- The Lounge: `https://webirc.atl.chat` +- Converse.js: `https://xmpp.atl.chat/conversejs` +- WebPanel: `https://panel.atl.chat` + +### Prosody Connectivity Check + +```bash +docker exec atl-xmpp-server prosodyctl check connectivity +docker exec atl-xmpp-server prosodyctl check dns +docker exec atl-xmpp-server prosodyctl check certs +``` + +### IM Observatory + +Test your XMPP server compliance at: + +--- + +## Certificate Renewal + +Certs are obtained by the cert-manager container (Lego + Cloudflare DNS-01). +They are stored in `data/certs/live//`. + +After renewal, services need to reload: + +```bash +# Reload IRC (picks up new certs without restart) +docker exec atl-irc-server /home/unrealircd/unrealircd/bin/unrealircdctl rehash + +# Reload Prosody +docker exec atl-xmpp-server prosodyctl reload + +# The Lounge reads certs on connection — restart to pick up new certs +docker compose restart atl-thelounge +``` + +To automate, add a cron job on Server B: + +```bash +# /etc/cron.weekly/atl-cert-reload +#!/bin/bash +cd /path/to/atl.chat +docker exec atl-irc-server /home/unrealircd/unrealircd/bin/unrealircdctl rehash +docker exec atl-xmpp-server prosodyctl reload +docker compose restart atl-thelounge +``` + +--- + +## Security Checklist + +- [ ] All `change_me_*` passwords replaced with strong random values +- [ ] Cloak keys regenerated (`just irc gencloak`) +- [ ] Oper password is argon2id hash (not plaintext) +- [ ] `CLOUDFLARE_DNS_API_TOKEN` set (scoped to DNS edit only) +- [ ] `.env` file permissions: `chmod 600 .env` +- [ ] `data/` directory permissions: `chmod 700 data/` +- [ ] No `.env.dev` file exists on prod server +- [ ] `ATL_ENVIRONMENT=prod` (not `dev`) +- [ ] IRC STS duration increased after testing (1d → 30d → 180d) +- [ ] Prosody `s2s_secure_auth=true` (default in `.env.example`) +- [ ] NPM SSL certificates obtained and forced +- [ ] Firewall: only 80, 443, 6697, 5222, 5223, 5269, 5270 open to public + +--- + +## Backup Strategy + +```bash +# Backup all persistent data +tar czf atl-chat-backup-$(date +%Y%m%d).tar.gz \ + data/ \ + .env \ + apps/unrealircd/config/unrealircd.conf \ + apps/atheme/config/atheme.conf \ + apps/bridge/config.yaml + +# Critical data: +# data/irc/data/ — UnrealIRCd runtime (tkl.db, reputation.db) +# data/atheme/data/ — Atheme services DB (nickserv, chanserv registrations) +# data/xmpp/data/ — Prosody SQLite (accounts, MAM archives) +# data/certs/ — TLS certificates +# data/thelounge/ — The Lounge user data +``` + +Schedule daily backups to off-site storage. + +--- + +## Troubleshooting + +### Bridge can't connect to IRC + +Check `docker compose logs atl-bridge`. Common causes: + +- G-Line from DNSBL: The `except ban` block covers Docker IPs (`172.16.0.0/12`). + If the bridge connects from Tailscale, add `100.64.0.0/10` to the except block. +- TLS verification: Set `BRIDGE_IRC_TLS_VERIFY=false` if using self-signed certs. + For prod with real certs, leave `true` (default). + +### XMPP federation not working + +```bash +docker exec atl-xmpp-server prosodyctl check dns +docker exec atl-xmpp-server prosodyctl check connectivity +``` + +Common causes: + +- Missing SRV records (see DNS section above) +- Port 5269 not forwarded through NPM streams +- `s2s_secure_auth=true` but cert doesn't cover the domain (check with `prosodyctl check certs`) + +### Certs not renewing + +```bash +docker compose logs cert-manager +``` + +Common causes: + +- `CLOUDFLARE_DNS_API_TOKEN` not set or expired +- Cloudflare API token doesn't have DNS edit permissions for the zone +- Rate limited by Let's Encrypt (check https://letsencrypt.org/docs/rate-limits/) + +### WebSocket connections failing + +Ensure NPM proxy hosts have "Websockets Support" enabled. For XMPP BOSH/WebSocket, +the proxy timeout must be long enough (default 60s is too short): + +```nginx +proxy_read_timeout 900s; +``` From 49d96f503711288500c6223e33422d7c0a86815e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 06:49:02 +0000 Subject: [PATCH 14/70] fix(irc): migrate webirc to proxy block syntax, use x-forwarded for NPM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the UnrealIRCd Proxy block wiki docs (pasted by user): - Migrated old `webirc {}` block (pre-6.1.1 syntax) to new `proxy {}` block with `type webirc` — we're on 6.2.0.1 - Changed `type forwarded` to `type x-forwarded` since NPM sends X-Forwarded-For headers (not RFC 7239 Forwarded headers) - Added clear doc comments explaining each proxy block's purpose - Noted that x-forwarded/forwarded types auto-exempt matching hosts from connect floods and blacklist checks (6.1.8+ feature) - Updated deployment guide to explain the three real-IP mechanisms: X-Forwarded-For (WebSocket), WEBIRC (web gateways), none (raw TCP) --- .../config/unrealircd.conf.template | 32 +++++++++++-------- docs/deployment.md | 15 ++++++--- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/apps/unrealircd/config/unrealircd.conf.template b/apps/unrealircd/config/unrealircd.conf.template index a7ff624a..53b15ea3 100644 --- a/apps/unrealircd/config/unrealircd.conf.template +++ b/apps/unrealircd/config/unrealircd.conf.template @@ -366,25 +366,31 @@ me { sid "001"; } -/* WEBIRC: trust real-IP from NPM/gateway on the Tailscale mesh */ -webirc { - mask ${ATL_GATEWAY_IP}/32; +/* NPM/gateway WEBIRC: passes real client IP via WEBIRC protocol. + * Used by web gateways (KiwiIRC, The Lounge, etc.) connecting through NPM. + * See: https://www.unrealircd.org/docs/Proxy_block */ +proxy npm-webirc { + type webirc; + match { ip ${ATL_GATEWAY_IP}/32; } password "${ATL_WEBIRC_PASSWORD}"; } -/* Reverse proxy trust: when NPM/gateway forwards WebSocket or HTTP connections, - * trust X-Forwarded-For headers from the gateway IP and Tailscale range. - * For TCP stream proxying (port 6697), clients appear as the NPM IP — the webirc - * block above handles real-IP for web clients. Direct IRC clients through TCP - * streams will show as the proxy IP (UnrealIRCd does not support HAProxy PROXY - * protocol; this is an upstream limitation). - */ -proxy npm-forwarded { - type forwarded; +/* NPM WebSocket reverse proxy: trust X-Forwarded-For from NPM/Tailscale. + * When NPM proxies WebSocket connections (port 8000), it sends X-Forwarded-For + * headers with the real client IP. This block trusts those headers. + * Hosts matching this are auto-exempted from connect floods and blacklist checks + * (UnrealIRCd 6.1.8+). See: https://www.unrealircd.org/docs/Proxy_block + * + * NOTE: This does NOT help with raw TCP streams (port 6697). Direct IRC TLS + * clients through NPM TCP streams will appear as the proxy IP — UnrealIRCd + * does not support HAProxy PROXY protocol. */ +proxy npm-x-forwarded { + type x-forwarded; match { ip ${ATL_GATEWAY_IP}/32; ip 100.64.0.0/10; } } -/* The Lounge web client - passes real user IP via WEBIRC */ +/* The Lounge web IRC client: passes real user IP via WEBIRC. + * Connects from Docker network. */ proxy thelounge { type webirc; match { ip 172.16.0.0/12; } diff --git a/docs/deployment.md b/docs/deployment.md index 434d241a..32e8b22a 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -230,11 +230,16 @@ In NPM → Streams → Add Stream: | `5269` | `100.64.7.0` | `5269` | ❌ | | `5270` | `100.64.7.0` | `5270` | ❌ | -**Note on real client IPs for IRC TCP streams:** UnrealIRCd does not support HAProxy -PROXY Protocol on raw TCP listeners (upstream limitation). Direct IRC clients connecting -through the NPM TCP stream on port 6697 will appear as the NPM/gateway IP. Web clients -connecting through The Lounge or WebSocket use WEBIRC to pass real IPs. The `proxy -npm-forwarded` block trusts `Forwarded` headers from the gateway for HTTP-based services. +**Note on real client IPs:** Two mechanisms handle real-IP forwarding: + +- **WebSocket (port 8000):** NPM sends `X-Forwarded-For` headers. The `proxy npm-x-forwarded` + block in UnrealIRCd trusts these from the gateway IP and Tailscale range. Hosts matching + this are auto-exempted from connect floods and blacklist checks (UnrealIRCd 6.1.8+). +- **WEBIRC (The Lounge, KiwiIRC):** Web gateways send the real client IP via the WEBIRC + protocol. The `proxy npm-webirc` block trusts the gateway for this. +- **Raw TCP streams (port 6697):** UnrealIRCd does not support HAProxy PROXY Protocol. + Direct IRC clients through NPM TCP streams will appear as the proxy IP. This is an + upstream limitation — see the [Proxy block docs](https://www.unrealircd.org/docs/Proxy_block). **XMPP ports:** Prosody doesn't support PROXY Protocol on C2S/S2S either. The `trusted_proxies` config handles X-Forwarded-For for HTTP endpoints, and S2S From 0239b2291b31ad2c9dcb3e796d81cd5839e39d09 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 27 Feb 2026 03:49:49 +0000 Subject: [PATCH 15/70] feat(xmpp): add mod_http_oauth2 for Bearer token auth Portal's mod_http_admin_api requires Bearer tokens (mod_tokenauth). This adds mod_http_oauth2 to enable OAuth2 token generation: - Added mod_http_oauth2 to modules.list (Prosody community module) - Enabled 'http_oauth2' and 'invites' in global modules_enabled - Configured OAuth2: password grant, 24h access tokens, 30d refresh - Added oauth2_registration_key for dynamic client registration Token generation flow for Portal: 1. Register client: POST /oauth2/register 2. Get token: POST /oauth2/token (grant_type=password, scope=prosody:operator) 3. Use token: Authorization: Bearer Verified end-to-end: create user (200), get user, delete user (200). --- apps/prosody/config/prosody.cfg.lua | 28 +++++++++++++++++++++++++--- apps/prosody/modules.list | 1 + 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/apps/prosody/config/prosody.cfg.lua b/apps/prosody/config/prosody.cfg.lua index 280c52d5..97cdfd4f 100644 --- a/apps/prosody/config/prosody.cfg.lua +++ b/apps/prosody/config/prosody.cfg.lua @@ -64,6 +64,7 @@ modules_enabled = { -- SECURITY & PRIVACY -- =============================================== "tokenauth", -- Token management for OAuth2 and other modules (prosody.im/doc/modules/mod_tokenauth) + "http_oauth2", -- OAuth2/OIDC Authorization Server (generates Bearer tokens for mod_http_admin_api) "blocklist", -- User blocking functionality (XEP-0191) "anti_spam", -- Spam prevention and detection "spam_reporting", -- Spam reporting mechanisms (XEP-0377) @@ -73,7 +74,7 @@ modules_enabled = { -- REGISTRATION & USER MANAGEMENT -- =============================================== -- "register", -- Password changes (XEP-0077); registration disabled (Portal provisions) - -- "invites", -- User invitation system + "invites", -- User invitation system (required by mod_http_admin_api and mod_http_oauth2) "welcome", -- Welcome messages for new users "support_contact", -- Add support JID to roster of newly registered users (in-band reg only; modules.prosody.im/mod_support_contact) -- "watchregistrations", -- Disabled: no in-band registration (Portal provisions via mod_http_admin_api) @@ -562,6 +563,26 @@ tls_channel_binding = Lua.os.getenv("PROSODY_TLS_CHANNEL_BINDING") ~= "false" push_notification_with_body = Lua.os.getenv("PROSODY_PUSH_NOTIFICATION_WITH_BODY") == "true" push_notification_with_sender = Lua.os.getenv("PROSODY_PUSH_NOTIFICATION_WITH_SENDER") == "true" +-- =============================================== +-- OAUTH2 CONFIGURATION (mod_http_oauth2) +-- =============================================== +-- Enables Bearer token generation for Portal's mod_http_admin_api integration. +-- Portal uses the Resource Owner Password Grant to obtain tokens. +-- Tokens are also usable for OAUTHBEARER SASL auth. +allowed_oauth2_grant_types = { + "authorization_code", + "device_code", + "password", -- Resource Owner Password Grant (Portal provisioning) +} +allowed_oauth2_response_types = { + "code", +} +oauth2_access_token_ttl = 86400 -- 24 hours +oauth2_refresh_token_ttl = 2592000 -- 30 days +oauth2_require_code_challenge = false -- Portal uses password grant, not PKCE +-- Dynamic client registration (enables Portal to register as OAuth2 client) +oauth2_registration_key = Lua.os.getenv("PROSODY_OAUTH2_REGISTRATION_KEY") or "dev-oauth2-registration-key" + -- =============================================== -- AUTHENTICATION & ACCOUNT POLICY -- =============================================== @@ -892,9 +913,9 @@ ssl = { } name = "pubsub." .. domain modules_enabled = { "pubsub_feeds" } --- Node "feed" pulls from allthingslinux.org; subscribe to feed@pubsub.domain +-- Node "feed" pulls from [REDACTED].org; subscribe to feed@pubsub.domain feeds = { - feed = Lua.os.getenv("PROSODY_FEED_URL") or "https://allthingslinux.org/feed", + feed = Lua.os.getenv("PROSODY_FEED_URL") or "https://[REDACTED].org/feed", } add_permissions = { ["prosody:registered"] = { "pubsub:create-node" }, @@ -947,3 +968,4 @@ account_cleanup = { inactive_period = Lua.tonumber(Lua.os.getenv("PROSODY_ACCOUNT_INACTIVE_PERIOD")) or (365 * 24 * 3600), grace_period = Lua.tonumber(Lua.os.getenv("PROSODY_ACCOUNT_GRACE_PERIOD")) or (30 * 24 * 3600), } + diff --git a/apps/prosody/modules.list b/apps/prosody/modules.list index 8610c83f..0772eb95 100644 --- a/apps/prosody/modules.list +++ b/apps/prosody/modules.list @@ -34,6 +34,7 @@ mod_pubsub_subscription mod_pubsub_feeds mod_groups_internal mod_http_admin_api +mod_http_oauth2 mod_support_contact mod_idlecompat mod_http_pep_avatar From dee34b9cb80bff90de87d41e5c892f5cbdb1ca0d Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Fri, 27 Feb 2026 07:39:40 -0500 Subject: [PATCH 16/70] refactor(bridge): rename atl-bridge identifiers to bridge for consistency across the codebase --- .cursor/plans/agents.md_audit_plan_0e4a4132.plan.md | 2 +- .../atl.chat_codebase_architecture_audit_a6482578.plan.md | 4 ++-- .../bridge_codebase_architecture_audit_24b32b4a.plan.md | 4 ++-- .../plans/custom_discord_irc_xmpp_bridge_0068eac0.plan.md | 2 +- .cursor/plans/the_lounge_integration_3ba6e863.plan.md | 2 +- .env.example | 2 +- .luacheckrc | 6 +++++- AGENTS.md | 1 + apps/bridge/README.md | 2 +- apps/bridge/src/bridge/AGENTS.md | 2 +- apps/bridge/src/bridge/adapters/AGENTS.md | 2 +- apps/bridge/src/bridge/adapters/irc/adapter.py | 2 +- apps/bridge/src/bridge/adapters/irc/client.py | 2 +- apps/bridge/src/bridge/adapters/xmpp/component.py | 8 ++++---- apps/prosody/config/prosody.cfg.lua | 3 +-- apps/unrealircd/config/unrealircd.conf.template | 4 ++-- docs/AGENTS.md | 1 + docs/audits/env-var-audit.md | 4 ++-- docs/audits/unrealircd-config-audit.md | 6 +++--- infra/AGENTS.md | 1 + 20 files changed, 33 insertions(+), 27 deletions(-) diff --git a/.cursor/plans/agents.md_audit_plan_0e4a4132.plan.md b/.cursor/plans/agents.md_audit_plan_0e4a4132.plan.md index 595d8851..68981199 100644 --- a/.cursor/plans/agents.md_audit_plan_0e4a4132.plan.md +++ b/.cursor/plans/agents.md_audit_plan_0e4a4132.plan.md @@ -194,7 +194,7 @@ flowchart LR | [infra/compose/networks.yaml](infra/compose/networks.yaml) | atl-chat network | — | | [infra/compose/irc.yaml](infra/compose/irc.yaml) | atl-irc-server, atl-irc-services, atl-irc-webpanel | — | | [infra/compose/xmpp.yaml](infra/compose/xmpp.yaml) | atl-xmpp-server, xmpp-postgres | — | -| [infra/compose/bridge.yaml](infra/compose/bridge.yaml) | atl-bridge | — | +| [infra/compose/bridge.yaml](infra/compose/bridge.yaml) | bridge | — | | [infra/compose/cert-manager.yaml](infra/compose/cert-manager.yaml) | cert-manager | — | | [compose.yaml](compose.yaml) | dozzle | dev | diff --git a/.cursor/plans/atl.chat_codebase_architecture_audit_a6482578.plan.md b/.cursor/plans/atl.chat_codebase_architecture_audit_a6482578.plan.md index 762d28fa..1457235c 100644 --- a/.cursor/plans/atl.chat_codebase_architecture_audit_a6482578.plan.md +++ b/.cursor/plans/atl.chat_codebase_architecture_audit_a6482578.plan.md @@ -137,7 +137,7 @@ Without it, PROXY protocol from NPM will break XMPP C2S/S2S when NPM is in front ### 12. Bridge Compose -- [infra/compose/bridge.yaml](infra/compose/bridge.yaml) defines `atl-bridge` service using `ghcr.io/allthingslinux/bridge` image +- [infra/compose/bridge.yaml](infra/compose/bridge.yaml) defines `bridge` service using `ghcr.io/allthingslinux/bridge` image - Root [compose.yaml](compose.yaml) includes `infra/compose/bridge.yaml` ### 13. E2E Test Fixture Assumptions @@ -262,7 +262,7 @@ Root compose uses `dev`, `staging`, `prod`. Bridge sub-composes use `bridge` pro ### G. Bridge – Delete or Implement -Bridge is defined in `infra/compose/bridge.yaml` using the `atl-bridge` service. +Bridge is defined in `infra/compose/bridge.yaml` using the `bridge` service. ### H. Gamja (IRC Web Client) diff --git a/.cursor/plans/bridge_codebase_architecture_audit_24b32b4a.plan.md b/.cursor/plans/bridge_codebase_architecture_audit_24b32b4a.plan.md index 0425ba38..f1dcb6b1 100644 --- a/.cursor/plans/bridge_codebase_architecture_audit_24b32b4a.plan.md +++ b/.cursor/plans/bridge_codebase_architecture_audit_24b32b4a.plan.md @@ -211,7 +211,7 @@ How the bridge fits into the monorepo and config flow: | App | Config | Generation | Bridge Coupling | | -------------- | ----------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------- | -| **UnrealIRCd** | `config/unrealircd.conf.template` → `unrealircd.conf` | `envsubst` via `prepare-config.sh` | `relaymsg` hostmask `bridge@${IRC_DOMAIN}`; oper `atl-bridge` with `BRIDGE_IRC_OPER_PASSWORD`; WebIRC for The Lounge | +| **UnrealIRCd** | `config/unrealircd.conf.template` → `unrealircd.conf` | `envsubst` via `prepare-config.sh` | `relaymsg` hostmask `bridge@${IRC_DOMAIN}`; oper `bridge` with `BRIDGE_IRC_OPER_PASSWORD`; WebIRC for The Lounge | | **Atheme** | `config/atheme.conf.template` → `atheme.conf` | `envsubst` | No direct bridge coupling | | **Prosody** | `config/prosody.cfg.lua` (Lua, no template) | Runtime `Lua.os.getenv()` | Component `bridge.${domain}` with `BRIDGE_XMPP_COMPONENT_SECRET`; MUC `general@muc.${PROSODY_DOMAIN}` | | **Bridge** | `config.template.yaml` → `config.yaml` | `envsubst` | Consumes `IRC_BRIDGE_SERVER`, `PROSODY_DOMAIN`, `BRIDGE_DISCORD_CHANNEL_ID`, `IRC_TLS_VERIFY`, `XMPP_AVATAR_BASE_URL` | @@ -229,7 +229,7 @@ How the bridge fits into the monorepo and config flow: 1. **Config schema** — Bridge `Config` class reads YAML produced by `prepare-config.sh`. Any `config/schema.py` must validate the same structure (mappings, irc.*, xmpp_*, etc.) that the template produces. 2. **Env vars** — Bridge also reads env at runtime (`BRIDGE_DISCORD_TOKEN`, `BRIDGE_PORTAL_*`, `BRIDGE_XMPP_*`, etc.). Config cleanup should document which keys come from YAML vs env. 3. **Compose** — Bridge container mounts `config.yaml:ro`; depends on `atl-irc-server` and `atl-xmpp-server`. No shared volume with other apps. -4. **Naming** — UnrealIRCd uses `atl-bridge` oper nick; Prosody uses `bridge.${domain}` component JID. Bridge should not hardcode these; they come from config/env. +4. **Naming** — UnrealIRCd uses `bridge` oper nick; Prosody uses `bridge.${domain}` component JID. Bridge should not hardcode these; they come from config/env. 5. **Consistency** — Other apps use `config/` dir. Bridge uses `config.yaml` at app root. Proposed `config/` package is internal Python layout; the YAML file stays at `apps/bridge/config.yaml` (or `config/config.yaml` if desired). ### 2.H Portal Audit (`~/dev/allthingslinux/portal`) diff --git a/.cursor/plans/custom_discord_irc_xmpp_bridge_0068eac0.plan.md b/.cursor/plans/custom_discord_irc_xmpp_bridge_0068eac0.plan.md index 7f4a9875..37f15dab 100644 --- a/.cursor/plans/custom_discord_irc_xmpp_bridge_0068eac0.plan.md +++ b/.cursor/plans/custom_discord_irc_xmpp_bridge_0068eac0.plan.md @@ -177,7 +177,7 @@ Dev dependencies. Install latest (no version pins). **Core:** `types-pyyaml`, `t ## Integration with atl.chat -- Deploy as a bridge service under [atl.chat/apps/bridge](~/dev/allthingslinux/atl.chat/apps/bridge/) (e.g. `atl-bridge/`); compose in existing `bridge` profile; use `atl-network`; connect to IRC and XMPP by hostname per [atl.chat networking](~/dev/allthingslinux/atl.chat/docs/infra/networking.md). +- Deploy as a bridge service under [atl.chat/apps/bridge](~/dev/allthingslinux/atl.chat/apps/bridge/) (e.g. `bridge/`); compose in existing `bridge` profile; use `atl-network`; connect to IRC and XMPP by hostname per [atl.chat networking](~/dev/allthingslinux/atl.chat/docs/infra/networking.md). - Secrets via env (same pattern as Biboumi/Matterbridge). Biboumi can remain for pure XMPP↔IRC; this bridge focuses on Discord↔IRC and Discord↔XMPP. --- diff --git a/.cursor/plans/the_lounge_integration_3ba6e863.plan.md b/.cursor/plans/the_lounge_integration_3ba6e863.plan.md index 5aed9da2..01b5cbc0 100644 --- a/.cursor/plans/the_lounge_integration_3ba6e863.plan.md +++ b/.cursor/plans/the_lounge_integration_3ba6e863.plan.md @@ -264,7 +264,7 @@ The `third/relaymsg` module (atl.chat fork) is installed and configured, enablin - Module: `third/relaymsg` (atl.chat fork from contrib/relaymsg) - Config: `relaymsg { hostmask "bridge@${IRC_DOMAIN}"; }` -- Permission: `relaymsg` granted to `bridge-oper` operclass (atl-bridge) +- Permission: `relaymsg` granted to `bridge-oper` operclass (bridge) **Bridge integration (implemented):** - Bridge requests `draft/relaymsg` and `overdrivenetworks.com/relaymsg` capabilities diff --git a/.env.example b/.env.example index 37450a02..6c542f4e 100644 --- a/.env.example +++ b/.env.example @@ -317,7 +317,7 @@ BRIDGE_XMPP_COMPONENT_SERVER=atl-xmpp-server BRIDGE_XMPP_COMPONENT_PORT=5347 # IRC -BRIDGE_IRC_NICK=atl-bridge +BRIDGE_IRC_NICK=bridge BRIDGE_IRC_OPER_PASSWORD=change_me_bridge_oper IRC_BRIDGE_SERVER=atl-irc-server # LOG_LEVEL=DEBUG diff --git a/.luacheckrc b/.luacheckrc index a938dc46..9e2664b1 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -33,10 +33,14 @@ globals = { "storage", "default_storage", "sql", "archive_expires_after", "default_archive_policy", "archive_compression", "archive_store", "max_archive_query_results", "mam_smart_enable", + "dont_archive_namespaces", -- Auth "authentication", "sasl_mechanisms", "user_account_management", "block_registrations_users", "block_registrations_require", - "allow_registration", "allow_unencrypted_plain_auth", + "allow_registration", "allow_unencrypted_plain_auth", "https_ssl", + "allowed_oauth2_grant_types", "allowed_oauth2_response_types", + "oauth2_access_token_ttl", "oauth2_refresh_token_ttl", + "oauth2_require_code_challenge", "oauth2_registration_key", -- Modules "modules_enabled", "modules_disabled", -- Lua/GC diff --git a/AGENTS.md b/AGENTS.md index 03427642..1ed72606 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,6 +26,7 @@ apps/ infra/ ├── compose/ # Compose fragments: irc, xmpp, bridge, cert-manager, networks +├── nginx/ # Nginx config for Prosody HTTPS └── turn-standalone/ scripts/ # init.sh, prepare-config.sh, gencloak-update-env.sh diff --git a/apps/bridge/README.md b/apps/bridge/README.md index a9686077..58da3d46 100644 --- a/apps/bridge/README.md +++ b/apps/bridge/README.md @@ -122,7 +122,7 @@ See `config.example.yaml` for the full schema. | `BRIDGE_XMPP_COMPONENT_SECRET` | Yes | Prosody component secret | | `BRIDGE_XMPP_COMPONENT_SERVER` | No | Component host (default: `localhost`) | | `BRIDGE_XMPP_COMPONENT_PORT` | No | Component port (default: `5347`) | -| `BRIDGE_IRC_NICK` | No | Main IRC nick (default: `atl-bridge`) | +| `BRIDGE_IRC_NICK` | No | Main IRC nick (default: `bridge`) | ## Architecture diff --git a/apps/bridge/src/bridge/AGENTS.md b/apps/bridge/src/bridge/AGENTS.md index b3978c97..f18762cf 100644 --- a/apps/bridge/src/bridge/AGENTS.md +++ b/apps/bridge/src/bridge/AGENTS.md @@ -88,7 +88,7 @@ Full property reference (see root AGENTS.md for the table). Additional propertie | `BRIDGE_PORTAL_BASE_URL` / `BRIDGE_PORTAL_URL` | Portal API base URL (identity resolution) | | `BRIDGE_PORTAL_TOKEN` / `BRIDGE_PORTAL_API_TOKEN` | Bearer token for Portal API | | `BRIDGE_DISCORD_TOKEN` | Discord bot token | -| `BRIDGE_IRC_NICK` | Main IRC connection nick (default: `atl-bridge`) | +| `BRIDGE_IRC_NICK` | Main IRC connection nick (default: `bridge`) | | `BRIDGE_XMPP_COMPONENT_JID` | XMPP component JID | | `BRIDGE_XMPP_COMPONENT_SECRET` | XMPP component secret | | `BRIDGE_XMPP_COMPONENT_SERVER` | XMPP server hostname | diff --git a/apps/bridge/src/bridge/adapters/AGENTS.md b/apps/bridge/src/bridge/adapters/AGENTS.md index e7fac1df..25f2fee8 100644 --- a/apps/bridge/src/bridge/adapters/AGENTS.md +++ b/apps/bridge/src/bridge/adapters/AGENTS.md @@ -49,7 +49,7 @@ Outbound events (`MessageOut`, `MessageDeleteOut`, `ReactionOut`, `TypingOut`) a ## IRC Adapter (`irc/`) -Env: `BRIDGE_IRC_NICK` (default: `atl-bridge`). +Env: `BRIDGE_IRC_NICK` (default: `bridge`). **client.py** — `IRCClient` (pydle.Client): diff --git a/apps/bridge/src/bridge/adapters/irc/adapter.py b/apps/bridge/src/bridge/adapters/irc/adapter.py index 627285d2..53044454 100644 --- a/apps/bridge/src/bridge/adapters/irc/adapter.py +++ b/apps/bridge/src/bridge/adapters/irc/adapter.py @@ -220,7 +220,7 @@ async def start(self) -> None: if not m.irc: return - nick = os.environ.get("BRIDGE_IRC_NICK", "atl-bridge") + nick = os.environ.get("BRIDGE_IRC_NICK", "bridge") channels = list({x.irc.channel for x in irc_mappings if x.irc and x.irc.channel}) irc_kwargs: dict = {} diff --git a/apps/bridge/src/bridge/adapters/irc/client.py b/apps/bridge/src/bridge/adapters/irc/client.py index 15f364f5..2e48d975 100644 --- a/apps/bridge/src/bridge/adapters/irc/client.py +++ b/apps/bridge/src/bridge/adapters/irc/client.py @@ -141,7 +141,7 @@ async def _ensure_channels_permanent(self) -> None: oper_password = os.environ.get("BRIDGE_IRC_OPER_PASSWORD", "").strip() if not oper_password: return - oper_name = "atl-bridge" # Must match oper block in UnrealIRCd + oper_name = "bridge" # Must match oper block in UnrealIRCd try: await self.rawmsg("OPER", oper_name, oper_password) await asyncio.sleep(1) # Allow server to process OPER diff --git a/apps/bridge/src/bridge/adapters/xmpp/component.py b/apps/bridge/src/bridge/adapters/xmpp/component.py index 981c6818..669f2cfb 100644 --- a/apps/bridge/src/bridge/adapters/xmpp/component.py +++ b/apps/bridge/src/bridge/adapters/xmpp/component.py @@ -228,7 +228,7 @@ async def _on_session_start(self, event: Any) -> None: # Join all mapped MUCs so we receive groupchat_message events (XMPP → Discord/IRC) muc_plugin = self.plugin.get("xep_0045", None) if muc_plugin: - bridge_nick = "atl-bridge" + bridge_nick = "bridge" bridge_jid = f"bridge@{self._component_jid}" for mapping in self._router.all_mappings(): if mapping.xmpp: @@ -334,7 +334,7 @@ def _on_groupchat_message(self, msg: Any) -> None: if (room_jid, nick) in self._recent_sent_nicks: logger.debug("Echo from recent send {} in {} (jid lookup returned None); skipping", nick, room_jid) return - if nick == "atl-bridge": + if nick == "bridge": return # Listener nick; we never send from it but skip for safety # Dedupe: MUC delivers same message to each occupant (listener + puppets) @@ -486,7 +486,7 @@ def _on_reactions(self, msg: Any) -> None: if sender_domain == our_domain: logger.debug("Skipping XMPP reaction echo from our component ({})", nick) return - if nick == "atl-bridge": + if nick == "bridge": return reactions = msg.get_plugin("reactions", check=True) @@ -560,7 +560,7 @@ def _on_retraction(self, msg: Any) -> None: if sender_domain == our_domain: logger.trace("Skipping XMPP retraction echo from our component ({})", nick) return - if nick == "atl-bridge": + if nick == "bridge": return retract = msg.get_plugin("retract", check=True) diff --git a/apps/prosody/config/prosody.cfg.lua b/apps/prosody/config/prosody.cfg.lua index 97cdfd4f..7bbe6dcc 100644 --- a/apps/prosody/config/prosody.cfg.lua +++ b/apps/prosody/config/prosody.cfg.lua @@ -922,7 +922,7 @@ add_permissions = { } -- Bridge XMPP component (XEP-0114) --- Allows the atl-bridge service to connect as an external component +-- Allows the bridge service to connect as an external component Component("bridge." .. domain) "component" component_secret = Lua.os.getenv("BRIDGE_XMPP_COMPONENT_SECRET") or Lua.os.getenv("XMPP_COMPONENT_SECRET") or "change_me_xmpp_component_secret" @@ -968,4 +968,3 @@ account_cleanup = { inactive_period = Lua.tonumber(Lua.os.getenv("PROSODY_ACCOUNT_INACTIVE_PERIOD")) or (365 * 24 * 3600), grace_period = Lua.tonumber(Lua.os.getenv("PROSODY_ACCOUNT_GRACE_PERIOD")) or (30 * 24 * 3600), } - diff --git a/apps/unrealircd/config/unrealircd.conf.template b/apps/unrealircd/config/unrealircd.conf.template index 53b15ea3..ee5dcee6 100644 --- a/apps/unrealircd/config/unrealircd.conf.template +++ b/apps/unrealircd/config/unrealircd.conf.template @@ -803,9 +803,9 @@ operclass bridge-oper { } } -oper atl-bridge { +oper bridge { class opers; - mask *@*atl-bridge*; + mask *@*bridge*; password "${BRIDGE_IRC_OPER_PASSWORD}"; operclass bridge-oper; } diff --git a/docs/AGENTS.md b/docs/AGENTS.md index 26c8cad0..73e13b88 100644 --- a/docs/AGENTS.md +++ b/docs/AGENTS.md @@ -9,6 +9,7 @@ Documentation hub for atl.chat. Human-readable guides; not code. | Dir | Purpose | |-----|---------| | `architecture/` | CI/CD, new-service guide, architecture overview | +| `audits/` | Config audits (env vars, dev-prod lifecycle, prosody, unrealircd) | | `bridges/` | Bridge infrastructure overview | | `examples/` | Example configs (nginx, prometheus, unrealircd) | | `infra/` | Containerization, data layout, networking, SSL | diff --git a/docs/audits/env-var-audit.md b/docs/audits/env-var-audit.md index 07a0e82f..2150fe16 100644 --- a/docs/audits/env-var-audit.md +++ b/docs/audits/env-var-audit.md @@ -295,8 +295,8 @@ | `BRIDGE_XMPP_COMPONENT_SECRET` | ✅ `change_me_xmpp_component_secret` | ❌ | bridge.yaml (env) | prosody.cfg.lua | ❌ | `xmpp/adapter.py` | | `BRIDGE_XMPP_COMPONENT_SERVER` | ✅ `atl-xmpp-server` | ✅ `atl-xmpp-server` | bridge.yaml (env) | ❌ | ❌ | `xmpp/adapter.py` | | `BRIDGE_XMPP_COMPONENT_PORT` | ✅ `5347` | ✅ `5347` | bridge.yaml (env) | ❌ | ❌ | `xmpp/adapter.py` | -| `BRIDGE_IRC_NICK` | ✅ `atl-bridge` | ❌ | bridge.yaml (env) | ❌ | ❌ | `irc/adapter.py` | -| `BRIDGE_IRC_OPER_PASSWORD` | ✅ `change_me_bridge_oper` | ❌ | bridge.yaml (env) | unrealircd.conf.template (`oper atl-bridge`) | prepare-config.sh | `irc/client.py` | +| `BRIDGE_IRC_NICK` | ✅ `bridge` | ❌ | bridge.yaml (env) | ❌ | ❌ | `irc/adapter.py` | +| `BRIDGE_IRC_OPER_PASSWORD` | ✅ `change_me_bridge_oper` | ❌ | bridge.yaml (env) | unrealircd.conf.template (`oper bridge`) | prepare-config.sh | `irc/client.py` | | `IRC_BRIDGE_SERVER` | ✅ `atl-irc-server` | ✅ `atl-irc-server` | ❌ | config.template.yaml | prepare-config.sh | ❌ | | `BRIDGE_IRC_TLS_VERIFY` | ❌ | ✅ `false` | bridge.yaml (env) | ❌ | prepare-config.sh | schema.py (`_ENV_OVERRIDE_KEYS`) | | `BRIDGE_RELAYMSG_CLEAN_NICKS` | ❌ | ✅ `true` | bridge.yaml (env) | ❌ | ❌ | schema.py (`_ENV_OVERRIDE_KEYS`) | diff --git a/docs/audits/unrealircd-config-audit.md b/docs/audits/unrealircd-config-audit.md index c70ebec7..9d4a58c3 100644 --- a/docs/audits/unrealircd-config-audit.md +++ b/docs/audits/unrealircd-config-audit.md @@ -346,15 +346,15 @@ oper admin { ### Bridge Oper ``` -oper atl-bridge { +oper bridge { class opers; - mask *@*atl-bridge*; + mask *@*bridge*; password "${BRIDGE_IRC_OPER_PASSWORD}"; operclass bridge-oper; } ``` -- 🟢 **OK** — Restricted mask (`*@*atl-bridge*`) limits this oper to bridge hostnames. +- 🟢 **OK** — Restricted mask (`*@*bridge*`) limits this oper to bridge hostnames. - 🟢 **OK** — `bridge-oper` operclass has minimal permissions (just channel override + relaymsg). - 🟢 **OK** — Password sourced from environment variable. diff --git a/infra/AGENTS.md b/infra/AGENTS.md index 7668abaf..1e2e7271 100644 --- a/infra/AGENTS.md +++ b/infra/AGENTS.md @@ -14,6 +14,7 @@ Docker Compose fragments and TURN server. Root `compose.yaml` includes `infra/co | `compose/thelounge.yaml` | The Lounge web IRC client | | `compose/cert-manager.yaml` | Lego (Let's Encrypt) | | `compose/networks.yaml` | Shared `atl-chat` network | +| `nginx/` | Nginx config for Prosody HTTPS (docker-entrypoint, prosody-https.conf.template) | | `turn-standalone/` | Standalone TURN/STUN for edge deployment | ## Usage From 32481fd7793f31c0068da6082bbf2846e7d6d82f Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Fri, 27 Feb 2026 08:13:50 -0500 Subject: [PATCH 17/70] chore(prosody): harden oauth2 config and update feeds - Enforce PKCE (require_code_challenge) - Remove insecure password grant type - Require explicit registration key via env - Update pubsub feed URL to allthingslinux.org - Allow localhost in status CIDR list --- apps/prosody/config/prosody.cfg.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/prosody/config/prosody.cfg.lua b/apps/prosody/config/prosody.cfg.lua index 7bbe6dcc..bbe6a1b7 100644 --- a/apps/prosody/config/prosody.cfg.lua +++ b/apps/prosody/config/prosody.cfg.lua @@ -445,7 +445,7 @@ http_paths = { -- http_status_allow_ips = { "127.0.0.1"; "::1"; "172.18.0.0/16"; "76.215.15.63" } -- Restrict status endpoint to Docker network and localhost -http_status_allow_cidr = "172.16.0.0/12" +http_status_allow_cidr = { "172.16.0.0/12", "127.0.0.0/8" } -- =============================================== -- TURN/STUN EXTERNAL SERVICES (XEP-0215) @@ -572,16 +572,16 @@ push_notification_with_sender = Lua.os.getenv("PROSODY_PUSH_NOTIFICATION_WITH_SE allowed_oauth2_grant_types = { "authorization_code", "device_code", - "password", -- Resource Owner Password Grant (Portal provisioning) + -- "password", -- Removed: Resource Owner Password Grant is insecure } allowed_oauth2_response_types = { "code", } oauth2_access_token_ttl = 86400 -- 24 hours oauth2_refresh_token_ttl = 2592000 -- 30 days -oauth2_require_code_challenge = false -- Portal uses password grant, not PKCE +oauth2_require_code_challenge = true -- Enforce PKCE for security -- Dynamic client registration (enables Portal to register as OAuth2 client) -oauth2_registration_key = Lua.os.getenv("PROSODY_OAUTH2_REGISTRATION_KEY") or "dev-oauth2-registration-key" +oauth2_registration_key = Lua.os.getenv("PROSODY_OAUTH2_REGISTRATION_KEY") or error("PROSODY_OAUTH2_REGISTRATION_KEY must be set in .env") -- =============================================== -- AUTHENTICATION & ACCOUNT POLICY @@ -913,9 +913,9 @@ ssl = { } name = "pubsub." .. domain modules_enabled = { "pubsub_feeds" } --- Node "feed" pulls from [REDACTED].org; subscribe to feed@pubsub.domain +-- Node "feed" pulls from allthingslinux.org; subscribe to feed@pubsub.domain feeds = { - feed = Lua.os.getenv("PROSODY_FEED_URL") or "https://[REDACTED].org/feed", + feed = Lua.os.getenv("PROSODY_FEED_URL") or "https://allthingslinux.org/feed", } add_permissions = { ["prosody:registered"] = { "pubsub:create-node" }, From 57ebc44cfafe714a0a0202c3ef9bb8150157fd06 Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Fri, 27 Feb 2026 08:13:51 -0500 Subject: [PATCH 18/70] chore(atheme): fix stale line reference in config template --- apps/atheme/config/atheme.conf.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/atheme/config/atheme.conf.template b/apps/atheme/config/atheme.conf.template index 32b4ad50..684481f5 100644 --- a/apps/atheme/config/atheme.conf.template +++ b/apps/atheme/config/atheme.conf.template @@ -255,7 +255,7 @@ loadmodule "operserv/soper"; /* Non-config oper privileges (SOPER command) */ #loadmodule "saslserv/ecdh-x25519-challenge"; /* ECDH-X25519-CHALLENGE mechanism */ #loadmodule "saslserv/ecdsa-nist256p-challenge"; /* ECDSA-NIST256P-CHALLENGE mechanism */ #loadmodule "saslserv/external"; /* EXTERNAL mechanism (IRCv3.1+) */ -/* saslserv/scram already loaded above in the SASL section (line 174) */ +/* saslserv/scram already loaded above in the SASL section */ #loadmodule "gameserv/dice"; /* DICE/WOD commands */ #loadmodule "gameserv/eightball"; /* EIGHTBALL command */ #loadmodule "gameserv/gamecalc"; /* Game-specific dice calculators */ From 4664ed53e6066218a4a17a60ff0a8e4cca7bbbf2 Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Fri, 27 Feb 2026 08:13:52 -0500 Subject: [PATCH 19/70] refactor(justfile): remove staging targets and harden prod flow - Delete staging and down-staging recipes - Export ATL_ENVIRONMENT=prod in prod target - Scope down-prod with --project-name --- justfile | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/justfile b/justfile index 88f01bfe..ffa46cff 100644 --- a/justfile +++ b/justfile @@ -35,18 +35,11 @@ dev: ./scripts/init.sh docker compose --env-file .env --env-file .env.dev --profile dev up -d -# Spin up the staging stack -[group('Orchestration')] -staging: - @echo "Initializing Staging Environment..." - ./scripts/init.sh - docker compose --env-file .env up -d - # Spin up the production stack [group('Orchestration')] prod: @echo "Initializing Production Environment..." - ./scripts/init.sh + export ATL_ENVIRONMENT=prod && ./scripts/init.sh docker compose --env-file .env up -d # Stop all services @@ -54,15 +47,10 @@ prod: down: docker compose --profile dev down -# Stop staging services -[group('Orchestration')] -down-staging: - docker compose down - # Stop production services [group('Orchestration')] down-prod: - docker compose down + docker compose -p atl-chat-prod down # View logs (follow) [group('Orchestration')] From 4c38989b325bb302a6c11b1ee722bcba6ba96b26 Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Fri, 27 Feb 2026 08:13:53 -0500 Subject: [PATCH 20/70] refactor(scripts): remove staging logic and improve env safety - Remove staging environment handling - Change default dev logic to only trigger when explicitly set - Fix typo in set -a --- scripts/prepare-config.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/prepare-config.sh b/scripts/prepare-config.sh index 63dcaac3..666fe5ac 100755 --- a/scripts/prepare-config.sh +++ b/scripts/prepare-config.sh @@ -46,7 +46,7 @@ prepare_config() { exit 1 fi - # Load .env (base), then .env.dev (overrides) only if not in prod/staging + # Load .env (base), then .env.dev (overrides) only if in dev if [ -f "$PROJECT_ROOT/.env" ]; then log_info "Loading environment variables from .env" set -a @@ -54,9 +54,9 @@ prepare_config() { source "$PROJECT_ROOT/.env" set +a fi - # Only load .env.dev if it exists AND ATL_ENVIRONMENT is not prod/staging. + # Only load .env.dev if it exists AND ATL_ENVIRONMENT is dev. # This prevents dev overrides from leaking into production config generation. - if [ -f "$PROJECT_ROOT/.env.dev" ] && [ "${ATL_ENVIRONMENT:-dev}" = "dev" ]; then + if [ -f "$PROJECT_ROOT/.env.dev" ] && [ "${ATL_ENVIRONMENT:-}" = "dev" ]; then log_info "Loading .env.dev overrides (ATL_ENVIRONMENT=dev)" set -a # shellcheck disable=SC1091 From 5036273ac9dc332e1d688ea41c475eba44d0e263 Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Fri, 27 Feb 2026 08:13:54 -0500 Subject: [PATCH 21/70] docs: remove staging env and add SRA bootstrap guide - Clean up environment tables - Add Service Root Administrator (SRA) bootstrap instructions --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4990dc71..d8d203dc 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,14 @@ just dev just prod ``` +### First Boot: SRA Bootstrap + +After starting the production stack for the first time, you must manually bootstrap the Services Root Administrator (SRA) to avoid being locked out of IRC services: + +1. Connect to IRC using your admin account. +2. In the terminal, run: `just irc sra-bootstrap ` +3. OperServ will then recognize you as a network administrator. + ## Services ### IRC Stack @@ -151,10 +159,8 @@ just --list # All tasks # Orchestration just init # One-time setup just dev # Start dev stack -just staging # Start staging stack just prod # Start prod stack just down # Stop dev stack -just down-staging # Stop staging stack just down-prod # Stop prod stack just logs [svc] # Follow logs (optionally for a service) just status # Container status @@ -219,7 +225,6 @@ Config is generated via `scripts/prepare-config.sh` (run by `just init`). After |-----------|-----------------------------------| | default | Production-style (domains from .env) | | dev | Dozzle, localhost domains, extra tools | -| staging | Staging environment | | prod | Production | ```bash From f3bcf9d91ec3c4f4b3cd6086004fd1c8987d647b Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Fri, 27 Feb 2026 08:13:55 -0500 Subject: [PATCH 22/70] docs(deployment): remove staging and harden just installation - Remove all staging references - Recommend package managers/binaries over curl | bash for just tool --- docs/deployment.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/deployment.md b/docs/deployment.md index 32e8b22a..37536fab 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -112,7 +112,10 @@ IRC_STS_PRELOAD=no ```bash # Install just if not present -curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin +# Documentation: https://github.com/casey/just#installation +# Example for Debian/Ubuntu: +sudo apt install just +# or use the pre-built binaries from their releases page. # Initialize (creates data dirs, generates configs, obtains certs) just prod @@ -393,7 +396,7 @@ Common causes: - `CLOUDFLARE_DNS_API_TOKEN` not set or expired - Cloudflare API token doesn't have DNS edit permissions for the zone -- Rate limited by Let's Encrypt (check https://letsencrypt.org/docs/rate-limits/) +- Rate limited by Let's Encrypt (check ) ### WebSocket connections failing From fcf5df472ffcbc98a0b2eb36fc2eb2ad8706aff1 Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Fri, 27 Feb 2026 08:13:56 -0500 Subject: [PATCH 23/70] docs(irc): remove staging references from config guide --- docs/services/irc/CONFIG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/services/irc/CONFIG.md b/docs/services/irc/CONFIG.md index 4b1bdfd0..4e4e3367 100644 --- a/docs/services/irc/CONFIG.md +++ b/docs/services/irc/CONFIG.md @@ -440,7 +440,7 @@ make up ```yaml # Docker Compose overrides environment: - - IRC_DOMAIN=staging.irc.atl.chat + - IRC_DOMAIN=irc.atl.chat - IRC_TLS_PORT=6698 ``` From 80beca55b0b8c167cc0eaaf25495f5f1a666f9e7 Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Fri, 27 Feb 2026 08:13:57 -0500 Subject: [PATCH 24/70] docs(irc): simplify CI/CD to production only --- docs/services/irc/CI_CD.md | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/docs/services/irc/CI_CD.md b/docs/services/irc/CI_CD.md index ca37a213..672b2da6 100644 --- a/docs/services/irc/CI_CD.md +++ b/docs/services/irc/CI_CD.md @@ -369,7 +369,7 @@ on: ### Purpose -Automated deployment to staging and production environments. +Automated deployment to production environment. ### Triggers @@ -378,12 +378,6 @@ on: release: types: [published] workflow_dispatch: - inputs: - environment: - description: 'Target environment' - required: true - default: 'staging' - options: [staging, production] ``` ### Deployment Strategy @@ -392,13 +386,7 @@ on: ```yaml jobs: - deploy-staging: - if: github.event.inputs.environment == 'staging' || github.event_name == 'release' - environment: staging - runs-on: ubuntu-latest - - deploy-production: - if: github.event.inputs.environment == 'production' + deploy: environment: production runs-on: ubuntu-latest ``` From 01d47cd42fb7898395647a4a6b72111b53e42854 Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Fri, 27 Feb 2026 08:13:59 -0500 Subject: [PATCH 25/70] docs: remove staging targets from root AGENTS.md --- AGENTS.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1ed72606..5b63e087 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,10 +40,8 @@ docs/ # Architecture, services, onboarding, bridges |---------|---------| | `just init` | Create data/ dirs, generate config, dev certs | | `just dev` | Start stack with dev profile (.env.dev overlay) | -| `just staging` | Start stack with staging profile | | `just prod` | Start production stack | | `just down` | Stop dev stack | -| `just down-staging` | Stop staging stack | | `just down-prod` | Stop production stack | | `just logs [service]` | Follow logs | | `just status` | Container status | From b19ef35b4efdeb1c00be4a8052c6a4c6ca61f93b Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Fri, 27 Feb 2026 08:14:00 -0500 Subject: [PATCH 26/70] chore(env): remove staging and add secure registration key placeholder - Update ATL_ENVIRONMENT description - Add PROSODY_OAUTH2_REGISTRATION_KEY - Remove explicit SSL paths to allow automatic domain fallback --- .env.example | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index 6c542f4e..f3f4b56a 100644 --- a/.env.example +++ b/.env.example @@ -9,7 +9,7 @@ # ============================================================================= # CORE # ============================================================================= -ATL_ENVIRONMENT=dev # dev, staging, prod — controls TLS verify, log levels +ATL_ENVIRONMENT=dev # dev, prod — controls TLS verify, log levels # Docker UID/GID (solves volume permission issues) PUID=1000 @@ -211,6 +211,7 @@ TURN_SECRET=change_me_turn_secret TURN_EXTERNAL_HOST=turn.atl.network # --- Security --- +PROSODY_OAUTH2_REGISTRATION_KEY=change_me_secure_oauth2_key PROSODY_ALLOW_REGISTRATION=false PROSODY_C2S_REQUIRE_ENCRYPTION=true PROSODY_S2S_REQUIRE_ENCRYPTION=true @@ -230,8 +231,9 @@ PROSODY_PROXY_ADDRESS=localhost # --- TLS certs (path relative to Prosody cert dir; must match XMPP_DOMAIN) --- # For dev: .env.dev overrides to certs/live/xmpp.localhost/... -PROSODY_SSL_KEY=certs/live/atl.chat/privkey.pem -PROSODY_SSL_CERT=certs/live/atl.chat/fullchain.pem +# Leave these empty to fallback to default paths derived from XMPP_DOMAIN +# PROSODY_SSL_KEY=certs/live/atl.chat/privkey.pem +# PROSODY_SSL_CERT=certs/live/atl.chat/fullchain.pem # --- Logging --- PROSODY_LOG_LEVEL=info From e608f3ffce7aa436d11b04dd199f7b48a67b79ae Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Fri, 27 Feb 2026 08:14:01 -0500 Subject: [PATCH 27/70] docs(audit): update lifecycle audit for staging removal --- docs/audits/dev-prod-lifecycle-audit.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/audits/dev-prod-lifecycle-audit.md b/docs/audits/dev-prod-lifecycle-audit.md index 17ebe4c3..5048f4bb 100644 --- a/docs/audits/dev-prod-lifecycle-audit.md +++ b/docs/audits/dev-prod-lifecycle-audit.md @@ -7,7 +7,7 @@ ## Executive Summary -The dev story is **solid and works well**. The prod story has **real gaps** that would block a production deployment. The main issues are: `just prod` doesn't run `init.sh` or load env files; the cert-manager is a placeholder that doesn't actually issue certs; the staging/prod profiles don't actually differentiate services; and there's no documentation for the prod deployment path. +The dev story is **solid and works well**. The prod story has **real gaps** that would block a production deployment. The main issues are: `just prod` doesn't run `init.sh` or load env files; the cert-manager is a placeholder that doesn't actually issue certs; the prod profiles don't actually differentiate services; and there's no documentation for the prod deployment path. **Verdict:** Dev is ~90% clean. Prod is ~40% ready. @@ -57,6 +57,7 @@ just dev ``` just prod + → export ATL_ENVIRONMENT=prod (explicitly in justfile) → docker compose --profile prod up -d ``` @@ -68,7 +69,7 @@ That's it. **No init.sh. No env file loading. No config generation.** |-------|----------|---------| | **No init.sh** | 🔴 | `just prod` doesn't run `init.sh`. No `data/` dirs created, no configs generated from templates. If deploying to a fresh server, nothing works. | | **No `--env-file`** | 🔴 | Unlike `just dev` which passes `--env-file .env --env-file .env.dev`, `just prod` passes nothing. Docker Compose will auto-load `.env` but there's no explicit prod overlay. All prod-specific overrides must be in `.env` itself. | -| **No staging/prod profile differentiation** | 🟡 | The `profiles: ["dev"]` on Dozzle means it only starts with `--profile dev`. But no services have `profiles: ["prod"]` or `profiles: ["staging"]`. So `just prod` and `just staging` start the exact same services as `docker compose up -d` (everything except Dozzle). The profiles are meaningless for prod/staging. | +| **No prod profile differentiation** | 🟡 | The `profiles: ["dev"]` on Dozzle means it only starts with `--profile dev`. But no services have `profiles: ["prod"]`. So `just prod` starts the exact same services as `docker compose up -d` (everything except Dozzle). The profiles are meaningless for prod. | | **Cert-manager is a placeholder** | 🔴 | The cert-manager service uses `goacme/lego:latest` with a custom `run.sh`, but it runs on every `docker compose up` — including dev where it's not needed. There's no `CLOUDFLARE_DNS_API_TOKEN` set in dev, so it likely errors silently. In prod, someone would need to set this token, but there's no documentation for the cert issuance flow. | | **No prod documentation** | 🟡 | README says `just prod` starts production stack, but there's no guide for: initial prod setup, cert issuance, DNS records needed, secrets management, backup strategy. | @@ -168,11 +169,10 @@ Dev uses `.localhost` TLD — `irc.localhost`, `xmpp.localhost`. These resolve t | Profile | What it activates | Command | |---------|-------------------|---------| | `dev` | Dozzle only | `just dev` | -| `staging` | Nothing — no services use this profile | `just staging` | | `prod` | Nothing — no services use this profile | `just prod` | | (none) | All services except Dozzle | `docker compose up -d` | -**Issue:** Staging and prod profiles are empty. `just staging` and `just prod` are functionally identical to `docker compose up -d`. The profiles add no value. +**Issue:** Prod profiles are empty. `just prod` is functionally identical to `docker compose up -d`. The profiles add no value. ### Image Strategy @@ -248,7 +248,7 @@ Line 57-63: The script unconditionally sources `.env.dev` if it exists. This mea ### 🟡 WARNING -1. **Staging/prod profiles are empty** — `just staging` and `just prod` are identical to bare `docker compose up` +1. **Prod profiles are empty** — `just prod` is identical to bare `docker compose up` 2. **No prod deployment documentation** — DNS records, cert issuance, secrets, backups 3. **`prepare-config.sh` always loads `.env.dev`** if present — prod configs could get dev overrides 4. **XMPP ports bind to 0.0.0.0** — inconsistent with IRC's `ATL_CHAT_IP` binding @@ -276,7 +276,7 @@ Line 57-63: The script unconditionally sources `.env.dev` if it exists. This mea ## Recommended Fix Priority -1. Fix `just prod` / `just staging` to run init and load env files +1. Fix `just prod` to run init and load env files 2. Fix Prosody `.env.example` cert paths to use `${XMPP_DOMAIN}` not `localhost` 3. Add prod deployment docs (DNS, certs, secrets) 4. Add cert renewal mechanism From f7a113136038e93c24d9bb13fd9939374c882e59 Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Fri, 27 Feb 2026 08:14:02 -0500 Subject: [PATCH 28/70] docs(cursor): remove staging from database migration guide --- .cursor/commands/database-migration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.cursor/commands/database-migration.md b/.cursor/commands/database-migration.md index 5775a59e..4051f3ab 100644 --- a/.cursor/commands/database-migration.md +++ b/.cursor/commands/database-migration.md @@ -23,7 +23,7 @@ Help create and manage database migrations, generating complete migration files - Consider zero-downtime deployment strategies 4. **Testing Strategy** - Create test data scenarios - - Verify migration on staging environment + - Verify migration on production copy/dev environment - Plan rollback procedures and testing - Document deployment steps and timing @@ -36,5 +36,5 @@ Help create and manage database migrations, generating complete migration files - [ ] Ensured migrations are atomic and reversible - [ ] Added error handling and validation - [ ] Created test data scenarios -- [ ] Verified migration on staging environment +- [ ] Verified migration on local/dev environment - [ ] Documented deployment steps and timing From 3a865fe3fc11ccabc9efce7ad41ec1a7a4519ca7 Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Fri, 27 Feb 2026 08:14:03 -0500 Subject: [PATCH 29/70] chore(cursor): remove staging env from ignore file --- .cursorignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.cursorignore b/.cursorignore index 64168b79..311882c7 100644 --- a/.cursorignore +++ b/.cursorignore @@ -2,12 +2,10 @@ !.env.example !.env.shared.example !.env.dev -!.env.staging !.env.prod !.env.local !.env.production !.env.development -!.env.staging !.env.production !.env.* !.env.dev.example From 0031651be792d71df398993871115133ab8060d4 Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Fri, 27 Feb 2026 15:03:07 +0000 Subject: [PATCH 30/70] fix(bridge.yaml): update healthcheck command to use pgrep for process validation The healthcheck command is updated to use `pgrep` to check for the `bridge.__main__` process instead of a Python command that always exits successfully. This change ensures that the healthcheck accurately reflects the running state of the service by verifying the actual process, improving reliability and correctness of the health status. --- infra/compose/bridge.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infra/compose/bridge.yaml b/infra/compose/bridge.yaml index 8965d633..26f9eaae 100644 --- a/infra/compose/bridge.yaml +++ b/infra/compose/bridge.yaml @@ -46,7 +46,7 @@ services: networks: - atl-chat healthcheck: - test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + test: ["CMD", "pgrep", "-f", "bridge.__main__"] interval: 30s timeout: 10s retries: 3 From 5df7a67478cc6920a22b94c329c24eff93df852d Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Fri, 27 Feb 2026 15:03:16 +0000 Subject: [PATCH 31/70] chore(nginx): add shellcheck directive to ignore SC2016 warning Add a shellcheck directive to disable SC2016 warning, which is triggered by the use of envsubst with variable placeholders. This change ensures that the script passes shellcheck validation without unnecessary warnings, improving maintainability and clarity for developers. --- infra/nginx/docker-entrypoint.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/infra/nginx/docker-entrypoint.sh b/infra/nginx/docker-entrypoint.sh index 687af2dd..04df8098 100644 --- a/infra/nginx/docker-entrypoint.sh +++ b/infra/nginx/docker-entrypoint.sh @@ -4,5 +4,6 @@ set -e export CERT_DIR="${CERT_DIR:-/etc/nginx/certs}" export XMPP_DOMAIN="${XMPP_DOMAIN:-xmpp.localhost}" rm -f /etc/nginx/conf.d/default.conf +# shellcheck disable=SC2016 envsubst '${XMPP_DOMAIN} ${CERT_DIR}' < /etc/nginx/templates/prosody-https.conf.template > /etc/nginx/conf.d/prosody-https.conf exec nginx -g "daemon off;" From e2d8b542c0ab0583a7cbb80b12f6a44572619421 Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Fri, 27 Feb 2026 23:11:05 -0500 Subject: [PATCH 32/70] feat(docs): add apps/docs Fumadocs site and migrate legacy docs Introduces the new apps/docs documentation application built on Fumadocs + Next.js, replacing the flat docs/ directory. Includes full content coverage for all services (IRC, XMPP, Bridge, Atheme, The Lounge, WebPanel, Web), architecture, operations, and reference pages, audited and corrected against official Prosody, Atheme, and UnrealIRCd reference documentation. Co-Authored-By: Claude Sonnet 4.6 --- .gitleaks.toml | 11 +- apps/docs/alchemy.run.ts | 12 + apps/docs/app/[[...slug]]/layout.tsx | 11 + apps/docs/app/[[...slug]]/page.tsx | 68 + apps/docs/app/api/search/route.ts | 6 + apps/docs/app/global.css | 3 + apps/docs/app/layout.tsx | 18 + apps/docs/app/llms-full.txt/route.ts | 10 + .../app/llms.mdx/docs/[[...slug]]/route.ts | 24 + apps/docs/app/llms.txt/route.ts | 13 + apps/docs/components/ai/page-actions.tsx | 150 + apps/docs/components/mdx/mermaid.tsx | 55 + .../content/docs/architecture/data-model.mdx | 146 + apps/docs/content/docs/architecture/index.mdx | 163 + apps/docs/content/docs/architecture/meta.json | 8 + .../content/docs/architecture/networking.mdx | 275 ++ .../docs/development/adding-a-service.mdx | 376 ++ .../content/docs/development/contributing.mdx | 299 ++ apps/docs/content/docs/development/meta.json | 8 + .../docs/content/docs/development/testing.mdx | 421 +++ .../content/docs/getting-started/index.mdx | 263 ++ .../getting-started/local-development.mdx | 182 + .../content/docs/getting-started/meta.json | 7 + apps/docs/content/docs/index.mdx | 47 + apps/docs/content/docs/meta.json | 12 + apps/docs/content/docs/operations/backups.mdx | 458 +++ .../content/docs/operations/deployment.mdx | 443 +++ apps/docs/content/docs/operations/meta.json | 11 + .../content/docs/operations/monitoring.mdx | 300 ++ .../docs/content/docs/operations/security.mdx | 396 +++ apps/docs/content/docs/operations/ssl-tls.mdx | 263 ++ .../docs/operations/troubleshooting.mdx | 710 ++++ apps/docs/content/docs/reference/api.mdx | 183 + .../docs/reference/environment-variables.mdx | 493 +++ apps/docs/content/docs/reference/faq.mdx | 170 + apps/docs/content/docs/reference/glossary.mdx | 103 + apps/docs/content/docs/reference/meta.json | 10 + apps/docs/content/docs/reference/ports.mdx | 94 + .../docs/services/atheme/configuration.mdx | 347 ++ .../content/docs/services/atheme/index.mdx | 98 + .../content/docs/services/atheme/meta.json | 8 + .../docs/services/atheme/operations.mdx | 459 +++ .../docs/services/bridge/configuration.mdx | 151 + .../content/docs/services/bridge/index.mdx | 117 + .../content/docs/services/bridge/meta.json | 8 + .../docs/services/bridge/operations.mdx | 92 + .../docs/services/irc/configuration.mdx | 204 ++ apps/docs/content/docs/services/irc/dns.mdx | 107 + apps/docs/content/docs/services/irc/index.mdx | 25 + apps/docs/content/docs/services/irc/meta.json | 12 + .../content/docs/services/irc/modules.mdx | 121 + .../content/docs/services/irc/operations.mdx | 255 ++ .../docs/services/irc/troubleshooting.mdx | 224 ++ .../content/docs/services/irc/user-modes.mdx | 112 + apps/docs/content/docs/services/meta.json | 12 + .../docs/services/thelounge/configuration.mdx | 225 ++ .../content/docs/services/thelounge/index.mdx | 90 + .../content/docs/services/thelounge/meta.json | 8 + .../docs/services/thelounge/operations.mdx | 253 ++ .../content/docs/services/web/development.mdx | 112 + apps/docs/content/docs/services/web/index.mdx | 76 + apps/docs/content/docs/services/web/meta.json | 7 + .../docs/services/webpanel/configuration.mdx | 203 ++ .../content/docs/services/webpanel/index.mdx | 126 + .../content/docs/services/webpanel/meta.json | 7 + .../docs/services/xmpp/configuration.mdx | 435 +++ apps/docs/content/docs/services/xmpp/dns.mdx | 73 + .../docs/content/docs/services/xmpp/index.mdx | 25 + .../docs/content/docs/services/xmpp/meta.json | 10 + .../content/docs/services/xmpp/modules.mdx | 131 + .../content/docs/services/xmpp/operations.mdx | 129 + apps/docs/lib/cn.ts | 1 + apps/docs/lib/layout.shared.tsx | 16 + apps/docs/lib/source.ts | 26 + apps/docs/mdx-components.tsx | 15 + apps/docs/next.config.mjs | 33 + apps/docs/open-next.config.ts | 3 + apps/docs/package.json | 47 + apps/docs/postcss.config.mjs | 8 + apps/docs/scripts/lint.ts | 51 + apps/docs/source.config.ts | 26 + apps/docs/tsconfig.json | 39 + apps/docs/types/env.d.ts | 16 + docs/AGENTS.md | 29 - docs/README.md | 34 - docs/architecture/README.md | 26 - docs/architecture/ci-cd.md | 23 - docs/architecture/new-service.md | 45 - docs/audits/dev-prod-lifecycle-audit.md | 283 -- docs/audits/env-var-audit.md | 513 --- docs/audits/prosody-config-audit.md | 217 -- docs/audits/unrealircd-config-audit.md | 666 ---- docs/bridges/README.md | 16 - docs/deployment.md | 408 --- .../cloudflare-credentials.ini.example | 14 - docs/examples/nginx-docker.conf | 371 -- docs/examples/nginx-docker.dev.conf | 126 - docs/examples/prometheus-scrape-config.yml | 149 - docs/infra/containerization.md | 38 - docs/infra/data-structure.md | 53 - docs/infra/networking.md | 49 - docs/infra/ssl.md | 33 - docs/onboarding/README.md | 36 - docs/services/MODULES.md | 105 - docs/services/irc/API.md | 292 -- docs/services/irc/ATHEME.md | 651 ---- docs/services/irc/BACKUP_RECOVERY.md | 135 - docs/services/irc/CI_CD.md | 808 ----- docs/services/irc/CONFIG.md | 664 ---- docs/services/irc/DEVELOPMENT.md | 289 -- docs/services/irc/DOCKER.md | 257 -- docs/services/irc/MAKE.md | 359 -- docs/services/irc/MODULES.md | 274 -- docs/services/irc/README.md | 89 - docs/services/irc/SCRIPTS.md | 188 - docs/services/irc/SECRET_MANAGEMENT.md | 133 - docs/services/irc/SSL.md | 322 -- docs/services/irc/TESTING.md | 820 ----- docs/services/irc/TODO.md | 546 --- docs/services/irc/TROUBLESHOOTING.md | 268 -- docs/services/irc/UNREALIRCD.md | 501 --- docs/services/irc/USERMODES.md | 200 -- docs/services/irc/WEBPANEL.md | 120 - .../irc/examples/atheme/atheme.conf.example | 3133 ----------------- .../irc/examples/atheme/atheme.motd.example | 11 - .../examples/unrealircd/aliases/aliases.conf | 43 - .../examples/unrealircd/aliases/anope.conf | 16 - .../examples/unrealircd/aliases/atheme.conf | 25 - .../examples/unrealircd/aliases/auspice.conf | 33 - .../examples/unrealircd/aliases/cygnus.conf | 12 - .../examples/unrealircd/aliases/epona.conf | 16 - .../examples/unrealircd/aliases/generic.conf | 14 - .../unrealircd/aliases/genericstats.conf | 4 - .../unrealircd/aliases/ircservices.conf | 17 - .../unrealircd/aliases/operstats.conf | 6 - .../irc/examples/unrealircd/badwords.conf | 50 - .../irc/examples/unrealircd/dccallow.conf | 42 - .../examples/unrealircd/examples/example.conf | 680 ---- .../unrealircd/examples/example.es.conf | 654 ---- .../unrealircd/examples/example.fr.conf | 661 ---- .../unrealircd/examples/example.pt.conf | 669 ---- .../unrealircd/examples/example.tr.conf | 687 ---- .../irc/examples/unrealircd/help/help.conf | 1572 --------- .../irc/examples/unrealircd/help/help.de.conf | 1383 -------- .../irc/examples/unrealircd/help/help.es.conf | 1467 -------- .../irc/examples/unrealircd/help/help.fr.conf | 1405 -------- .../irc/examples/unrealircd/help/help.it.conf | 1310 ------- .../irc/examples/unrealircd/help/help.nl.conf | 1460 -------- .../irc/examples/unrealircd/help/help.pl.conf | 1526 -------- .../irc/examples/unrealircd/help/help.ru.conf | 1568 --------- .../irc/examples/unrealircd/help/help.tr.conf | 1578 --------- .../examples/unrealircd/modules.default.conf | 312 -- .../examples/unrealircd/modules.optional.conf | 249 -- .../examples/unrealircd/modules.sources.list | 20 - .../unrealircd/operclass.default.conf | 144 - .../unrealircd/rpc-class.default.conf | 39 - .../unrealircd/rpc.modules.default.conf | 65 - .../examples/unrealircd/snomasks.default.conf | 259 -- .../irc/examples/unrealircd/spamfilter.conf | 154 - .../irc/examples/unrealircd/unrealircd.conf | 680 ---- docs/services/web/README.md | 36 - docs/services/xmpp/DNS.md | 89 - docs/services/xmpp/README.md | 278 -- docs/services/xmpp/TODO.md | 48 - 164 files changed, 10763 insertions(+), 32567 deletions(-) create mode 100644 apps/docs/alchemy.run.ts create mode 100644 apps/docs/app/[[...slug]]/layout.tsx create mode 100644 apps/docs/app/[[...slug]]/page.tsx create mode 100644 apps/docs/app/api/search/route.ts create mode 100644 apps/docs/app/global.css create mode 100644 apps/docs/app/layout.tsx create mode 100644 apps/docs/app/llms-full.txt/route.ts create mode 100644 apps/docs/app/llms.mdx/docs/[[...slug]]/route.ts create mode 100644 apps/docs/app/llms.txt/route.ts create mode 100644 apps/docs/components/ai/page-actions.tsx create mode 100644 apps/docs/components/mdx/mermaid.tsx create mode 100644 apps/docs/content/docs/architecture/data-model.mdx create mode 100644 apps/docs/content/docs/architecture/index.mdx create mode 100644 apps/docs/content/docs/architecture/meta.json create mode 100644 apps/docs/content/docs/architecture/networking.mdx create mode 100644 apps/docs/content/docs/development/adding-a-service.mdx create mode 100644 apps/docs/content/docs/development/contributing.mdx create mode 100644 apps/docs/content/docs/development/meta.json create mode 100644 apps/docs/content/docs/development/testing.mdx create mode 100644 apps/docs/content/docs/getting-started/index.mdx create mode 100644 apps/docs/content/docs/getting-started/local-development.mdx create mode 100644 apps/docs/content/docs/getting-started/meta.json create mode 100644 apps/docs/content/docs/index.mdx create mode 100644 apps/docs/content/docs/meta.json create mode 100644 apps/docs/content/docs/operations/backups.mdx create mode 100644 apps/docs/content/docs/operations/deployment.mdx create mode 100644 apps/docs/content/docs/operations/meta.json create mode 100644 apps/docs/content/docs/operations/monitoring.mdx create mode 100644 apps/docs/content/docs/operations/security.mdx create mode 100644 apps/docs/content/docs/operations/ssl-tls.mdx create mode 100644 apps/docs/content/docs/operations/troubleshooting.mdx create mode 100644 apps/docs/content/docs/reference/api.mdx create mode 100644 apps/docs/content/docs/reference/environment-variables.mdx create mode 100644 apps/docs/content/docs/reference/faq.mdx create mode 100644 apps/docs/content/docs/reference/glossary.mdx create mode 100644 apps/docs/content/docs/reference/meta.json create mode 100644 apps/docs/content/docs/reference/ports.mdx create mode 100644 apps/docs/content/docs/services/atheme/configuration.mdx create mode 100644 apps/docs/content/docs/services/atheme/index.mdx create mode 100644 apps/docs/content/docs/services/atheme/meta.json create mode 100644 apps/docs/content/docs/services/atheme/operations.mdx create mode 100644 apps/docs/content/docs/services/bridge/configuration.mdx create mode 100644 apps/docs/content/docs/services/bridge/index.mdx create mode 100644 apps/docs/content/docs/services/bridge/meta.json create mode 100644 apps/docs/content/docs/services/bridge/operations.mdx create mode 100644 apps/docs/content/docs/services/irc/configuration.mdx create mode 100644 apps/docs/content/docs/services/irc/dns.mdx create mode 100644 apps/docs/content/docs/services/irc/index.mdx create mode 100644 apps/docs/content/docs/services/irc/meta.json create mode 100644 apps/docs/content/docs/services/irc/modules.mdx create mode 100644 apps/docs/content/docs/services/irc/operations.mdx create mode 100644 apps/docs/content/docs/services/irc/troubleshooting.mdx create mode 100644 apps/docs/content/docs/services/irc/user-modes.mdx create mode 100644 apps/docs/content/docs/services/meta.json create mode 100644 apps/docs/content/docs/services/thelounge/configuration.mdx create mode 100644 apps/docs/content/docs/services/thelounge/index.mdx create mode 100644 apps/docs/content/docs/services/thelounge/meta.json create mode 100644 apps/docs/content/docs/services/thelounge/operations.mdx create mode 100644 apps/docs/content/docs/services/web/development.mdx create mode 100644 apps/docs/content/docs/services/web/index.mdx create mode 100644 apps/docs/content/docs/services/web/meta.json create mode 100644 apps/docs/content/docs/services/webpanel/configuration.mdx create mode 100644 apps/docs/content/docs/services/webpanel/index.mdx create mode 100644 apps/docs/content/docs/services/webpanel/meta.json create mode 100644 apps/docs/content/docs/services/xmpp/configuration.mdx create mode 100644 apps/docs/content/docs/services/xmpp/dns.mdx create mode 100644 apps/docs/content/docs/services/xmpp/index.mdx create mode 100644 apps/docs/content/docs/services/xmpp/meta.json create mode 100644 apps/docs/content/docs/services/xmpp/modules.mdx create mode 100644 apps/docs/content/docs/services/xmpp/operations.mdx create mode 100644 apps/docs/lib/cn.ts create mode 100644 apps/docs/lib/layout.shared.tsx create mode 100644 apps/docs/lib/source.ts create mode 100644 apps/docs/mdx-components.tsx create mode 100644 apps/docs/next.config.mjs create mode 100644 apps/docs/open-next.config.ts create mode 100644 apps/docs/package.json create mode 100644 apps/docs/postcss.config.mjs create mode 100644 apps/docs/scripts/lint.ts create mode 100644 apps/docs/source.config.ts create mode 100644 apps/docs/tsconfig.json create mode 100644 apps/docs/types/env.d.ts delete mode 100644 docs/AGENTS.md delete mode 100644 docs/README.md delete mode 100644 docs/architecture/README.md delete mode 100644 docs/architecture/ci-cd.md delete mode 100644 docs/architecture/new-service.md delete mode 100644 docs/audits/dev-prod-lifecycle-audit.md delete mode 100644 docs/audits/env-var-audit.md delete mode 100644 docs/audits/prosody-config-audit.md delete mode 100644 docs/audits/unrealircd-config-audit.md delete mode 100644 docs/bridges/README.md delete mode 100644 docs/deployment.md delete mode 100644 docs/examples/cloudflare-credentials.ini.example delete mode 100644 docs/examples/nginx-docker.conf delete mode 100644 docs/examples/nginx-docker.dev.conf delete mode 100644 docs/examples/prometheus-scrape-config.yml delete mode 100644 docs/infra/containerization.md delete mode 100644 docs/infra/data-structure.md delete mode 100644 docs/infra/networking.md delete mode 100644 docs/infra/ssl.md delete mode 100644 docs/onboarding/README.md delete mode 100644 docs/services/MODULES.md delete mode 100644 docs/services/irc/API.md delete mode 100644 docs/services/irc/ATHEME.md delete mode 100644 docs/services/irc/BACKUP_RECOVERY.md delete mode 100644 docs/services/irc/CI_CD.md delete mode 100644 docs/services/irc/CONFIG.md delete mode 100644 docs/services/irc/DEVELOPMENT.md delete mode 100644 docs/services/irc/DOCKER.md delete mode 100644 docs/services/irc/MAKE.md delete mode 100644 docs/services/irc/MODULES.md delete mode 100644 docs/services/irc/README.md delete mode 100644 docs/services/irc/SCRIPTS.md delete mode 100644 docs/services/irc/SECRET_MANAGEMENT.md delete mode 100644 docs/services/irc/SSL.md delete mode 100644 docs/services/irc/TESTING.md delete mode 100644 docs/services/irc/TODO.md delete mode 100644 docs/services/irc/TROUBLESHOOTING.md delete mode 100644 docs/services/irc/UNREALIRCD.md delete mode 100644 docs/services/irc/USERMODES.md delete mode 100644 docs/services/irc/WEBPANEL.md delete mode 100644 docs/services/irc/examples/atheme/atheme.conf.example delete mode 100644 docs/services/irc/examples/atheme/atheme.motd.example delete mode 100644 docs/services/irc/examples/unrealircd/aliases/aliases.conf delete mode 100644 docs/services/irc/examples/unrealircd/aliases/anope.conf delete mode 100644 docs/services/irc/examples/unrealircd/aliases/atheme.conf delete mode 100644 docs/services/irc/examples/unrealircd/aliases/auspice.conf delete mode 100644 docs/services/irc/examples/unrealircd/aliases/cygnus.conf delete mode 100644 docs/services/irc/examples/unrealircd/aliases/epona.conf delete mode 100644 docs/services/irc/examples/unrealircd/aliases/generic.conf delete mode 100644 docs/services/irc/examples/unrealircd/aliases/genericstats.conf delete mode 100644 docs/services/irc/examples/unrealircd/aliases/ircservices.conf delete mode 100644 docs/services/irc/examples/unrealircd/aliases/operstats.conf delete mode 100644 docs/services/irc/examples/unrealircd/badwords.conf delete mode 100644 docs/services/irc/examples/unrealircd/dccallow.conf delete mode 100644 docs/services/irc/examples/unrealircd/examples/example.conf delete mode 100644 docs/services/irc/examples/unrealircd/examples/example.es.conf delete mode 100644 docs/services/irc/examples/unrealircd/examples/example.fr.conf delete mode 100644 docs/services/irc/examples/unrealircd/examples/example.pt.conf delete mode 100644 docs/services/irc/examples/unrealircd/examples/example.tr.conf delete mode 100644 docs/services/irc/examples/unrealircd/help/help.conf delete mode 100644 docs/services/irc/examples/unrealircd/help/help.de.conf delete mode 100644 docs/services/irc/examples/unrealircd/help/help.es.conf delete mode 100644 docs/services/irc/examples/unrealircd/help/help.fr.conf delete mode 100644 docs/services/irc/examples/unrealircd/help/help.it.conf delete mode 100644 docs/services/irc/examples/unrealircd/help/help.nl.conf delete mode 100644 docs/services/irc/examples/unrealircd/help/help.pl.conf delete mode 100644 docs/services/irc/examples/unrealircd/help/help.ru.conf delete mode 100644 docs/services/irc/examples/unrealircd/help/help.tr.conf delete mode 100644 docs/services/irc/examples/unrealircd/modules.default.conf delete mode 100644 docs/services/irc/examples/unrealircd/modules.optional.conf delete mode 100644 docs/services/irc/examples/unrealircd/modules.sources.list delete mode 100644 docs/services/irc/examples/unrealircd/operclass.default.conf delete mode 100644 docs/services/irc/examples/unrealircd/rpc-class.default.conf delete mode 100644 docs/services/irc/examples/unrealircd/rpc.modules.default.conf delete mode 100644 docs/services/irc/examples/unrealircd/snomasks.default.conf delete mode 100644 docs/services/irc/examples/unrealircd/spamfilter.conf delete mode 100644 docs/services/irc/examples/unrealircd/unrealircd.conf delete mode 100644 docs/services/web/README.md delete mode 100644 docs/services/xmpp/DNS.md delete mode 100644 docs/services/xmpp/README.md delete mode 100644 docs/services/xmpp/TODO.md diff --git a/.gitleaks.toml b/.gitleaks.toml index ff656a79..3373c840 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -19,7 +19,7 @@ title = "gitleaks config" minVersion = "v8.25.0" # TODO: change to [[allowlists]] -[allowlist] +[[allowlists]] description = "global allow lists" paths = [ '''gitleaks\.toml''', @@ -3207,4 +3207,11 @@ keywords = ["yandex"] id = "zendesk-secret-key" description = "Detected a Zendesk Secret Key, risking unauthorized access to customer support services and sensitive ticketing data." regex = '''(?i)[\w.-]{0,50}?(?:zendesk)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{40})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["zendesk"] + +# Custom allowlist for atl.chat project +[[allowlists]] +description = "Allow example Discord snowflake IDs used as placeholders in documentation" +rules = ["discord-client-id"] +paths = [ + '''apps/docs/content/docs/''', +] diff --git a/apps/docs/alchemy.run.ts b/apps/docs/alchemy.run.ts new file mode 100644 index 00000000..904664ac --- /dev/null +++ b/apps/docs/alchemy.run.ts @@ -0,0 +1,12 @@ +import alchemy from "alchemy"; +import { Nextjs } from "alchemy/cloudflare"; + +const app = await alchemy("atl-chat"); + +export const website = await Nextjs("docs", { + domains: ["docs.atl.chat"], +}); + +console.log({ url: website.url }); + +await app.finalize(); diff --git a/apps/docs/app/[[...slug]]/layout.tsx b/apps/docs/app/[[...slug]]/layout.tsx new file mode 100644 index 00000000..72513861 --- /dev/null +++ b/apps/docs/app/[[...slug]]/layout.tsx @@ -0,0 +1,11 @@ +import { DocsLayout } from 'fumadocs-ui/layouts/docs'; +import { baseOptions } from '@/lib/layout.shared'; +import { source } from '@/lib/source'; + +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} diff --git a/apps/docs/app/[[...slug]]/page.tsx b/apps/docs/app/[[...slug]]/page.tsx new file mode 100644 index 00000000..b27a1d82 --- /dev/null +++ b/apps/docs/app/[[...slug]]/page.tsx @@ -0,0 +1,68 @@ +import { + DocsBody, + DocsDescription, + DocsPage, + DocsTitle, +} from 'fumadocs-ui/layouts/docs/page'; +import { createRelativeLink } from 'fumadocs-ui/mdx'; +import type { Metadata } from 'next'; +import { notFound } from 'next/navigation'; + +import { LLMCopyButton, ViewOptions } from '@/components/ai/page-actions'; +import { getPageImage, source } from '@/lib/source'; +import { getMDXComponents } from '@/mdx-components'; + +const gitConfig = { + user: 'allthingslinux', + repo: 'atl.chat', + branch: 'main', +}; + +export default async function Page(props: PageProps<'/[[...slug]]'>) { + const params = await props.params; + const page = source.getPage(params.slug); + if (!page) notFound(); + + const MDX = page.data.body; + + return ( + + {page.data.title} + {page.data.description} +
+ + +
+ + + +
+ ); +} + +export async function generateStaticParams() { + return source.generateParams(); +} + +export async function generateMetadata( + props: PageProps<'/[[...slug]]'>, +): Promise { + const params = await props.params; + const page = source.getPage(params.slug); + if (!page) notFound(); + + return { + title: page.data.title, + description: page.data.description, + openGraph: { + images: getPageImage(page).url, + }, + }; +} diff --git a/apps/docs/app/api/search/route.ts b/apps/docs/app/api/search/route.ts new file mode 100644 index 00000000..97cf0582 --- /dev/null +++ b/apps/docs/app/api/search/route.ts @@ -0,0 +1,6 @@ +import { createFromSource } from 'fumadocs-core/search/server'; +import { source } from '@/lib/source'; + +export const { GET } = createFromSource(source, { + language: 'english', +}); diff --git a/apps/docs/app/global.css b/apps/docs/app/global.css new file mode 100644 index 00000000..fefe5f81 --- /dev/null +++ b/apps/docs/app/global.css @@ -0,0 +1,3 @@ +@import 'tailwindcss'; +@import 'fumadocs-ui/css/black.css'; +@import 'fumadocs-ui/css/preset.css'; diff --git a/apps/docs/app/layout.tsx b/apps/docs/app/layout.tsx new file mode 100644 index 00000000..61a2f75b --- /dev/null +++ b/apps/docs/app/layout.tsx @@ -0,0 +1,18 @@ +import type { ReactNode } from 'react'; +import { RootProvider } from 'fumadocs-ui/provider/next'; +import { Inter } from 'next/font/google'; +import './global.css'; + +const inter = Inter({ + subsets: ['latin'], +}); + +export default function RootLayout({ children }: { children: ReactNode }) { + return ( + + + {children} + + + ); +} diff --git a/apps/docs/app/llms-full.txt/route.ts b/apps/docs/app/llms-full.txt/route.ts new file mode 100644 index 00000000..d494d2cb --- /dev/null +++ b/apps/docs/app/llms-full.txt/route.ts @@ -0,0 +1,10 @@ +import { getLLMText, source } from '@/lib/source'; + +export const revalidate = false; + +export async function GET() { + const scan = source.getPages().map(getLLMText); + const scanned = await Promise.all(scan); + + return new Response(scanned.join('\n\n')); +} diff --git a/apps/docs/app/llms.mdx/docs/[[...slug]]/route.ts b/apps/docs/app/llms.mdx/docs/[[...slug]]/route.ts new file mode 100644 index 00000000..f97e171f --- /dev/null +++ b/apps/docs/app/llms.mdx/docs/[[...slug]]/route.ts @@ -0,0 +1,24 @@ +import { notFound } from 'next/navigation'; + +import { getLLMText, source } from '@/lib/source'; + +export const revalidate = false; + +export async function GET( + _req: Request, + { params }: RouteContext<'/llms.mdx/docs/[[...slug]]'>, +) { + const { slug } = await params; + const page = source.getPage(slug); + if (!page) notFound(); + + return new Response(await getLLMText(page), { + headers: { + 'Content-Type': 'text/markdown', + }, + }); +} + +export function generateStaticParams() { + return source.generateParams(); +} diff --git a/apps/docs/app/llms.txt/route.ts b/apps/docs/app/llms.txt/route.ts new file mode 100644 index 00000000..3c7dbfb5 --- /dev/null +++ b/apps/docs/app/llms.txt/route.ts @@ -0,0 +1,13 @@ +import { source } from '@/lib/source'; + +export const revalidate = false; + +export function GET() { + const lines: string[] = []; + lines.push('# atl.chat Documentation'); + lines.push(''); + for (const page of source.getPages()) { + lines.push(`- [${page.data.title}](${page.url}): ${page.data.description}`); + } + return new Response(lines.join('\n')); +} diff --git a/apps/docs/components/ai/page-actions.tsx b/apps/docs/components/ai/page-actions.tsx new file mode 100644 index 00000000..b40b1ed9 --- /dev/null +++ b/apps/docs/components/ai/page-actions.tsx @@ -0,0 +1,150 @@ +'use client'; +import { buttonVariants } from 'fumadocs-ui/components/ui/button'; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from 'fumadocs-ui/components/ui/popover'; +import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button'; +import { + Check, + ChevronDown, + Copy, + ExternalLinkIcon, + MessageCircleIcon, +} from 'lucide-react'; +import { useMemo, useState } from 'react'; + +import { cn } from '@/lib/cn'; + +const cache = new Map(); + +export function LLMCopyButton({ markdownUrl }: { markdownUrl: string }) { + const [isLoading, setLoading] = useState(false); + const [checked, onClick] = useCopyButton(async () => { + const cached = cache.get(markdownUrl); + if (cached) { + return navigator.clipboard.writeText(cached); + } + + setLoading(true); + + try { + await navigator.clipboard.write([ + new ClipboardItem({ + 'text/plain': fetch(markdownUrl).then(async (res) => { + const content = await res.text(); + cache.set(markdownUrl, content); + return content; + }), + }), + ]); + } finally { + setLoading(false); + } + }); + + return ( + + ); +} + +export function ViewOptions({ + markdownUrl, + githubUrl, +}: { + markdownUrl: string; + githubUrl: string; +}) { + const items = useMemo(() => { + const fullMarkdownUrl = + typeof window !== 'undefined' + ? new URL(markdownUrl, window.location.origin) + : 'loading'; + const q = `Read ${fullMarkdownUrl}, I want to ask questions about it.`; + + return [ + { + title: 'Open in GitHub', + href: githubUrl, + icon: ( + + GitHub + + + ), + }, + { + title: 'Open in ChatGPT', + href: `https://chatgpt.com/?${new URLSearchParams({ hints: 'search', q })}`, + icon: ( + + OpenAI + + + ), + }, + { + title: 'Open in Claude', + href: `https://claude.ai/new?${new URLSearchParams({ q })}`, + icon: ( + + Anthropic + + + ), + }, + { + title: 'Open in T3 Chat', + href: `https://t3.chat/new?${new URLSearchParams({ q })}`, + icon: , + }, + ]; + }, [githubUrl, markdownUrl]); + + return ( + + + Open + + + + {items.map((item) => ( + + {item.icon} + {item.title} + + + ))} + + + ); +} diff --git a/apps/docs/components/mdx/mermaid.tsx b/apps/docs/components/mdx/mermaid.tsx new file mode 100644 index 00000000..04a5a6cf --- /dev/null +++ b/apps/docs/components/mdx/mermaid.tsx @@ -0,0 +1,55 @@ +'use client'; + +import { use, useEffect, useId, useState } from 'react'; +import { useTheme } from 'next-themes'; + +export function Mermaid({ chart }: { chart: string }) { + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + }, []); + + if (!mounted) return; + return ; +} + +const cache = new Map>(); + +function cachePromise(key: string, setPromise: () => Promise): Promise { + const cached = cache.get(key); + if (cached) return cached as Promise; + + const promise = setPromise(); + cache.set(key, promise); + return promise; +} + +function MermaidContent({ chart }: { chart: string }) { + const id = useId(); + const { resolvedTheme } = useTheme(); + const { default: mermaid } = use(cachePromise('mermaid', () => import('mermaid'))); + + mermaid.initialize({ + startOnLoad: false, + securityLevel: 'loose', + fontFamily: 'inherit', + themeCSS: 'margin: 1.5rem auto 0;', + theme: resolvedTheme === 'dark' ? 'dark' : 'default', + }); + + const { svg, bindFunctions } = use( + cachePromise(`${chart}-${resolvedTheme}`, () => { + return mermaid.render(id, chart.replaceAll('\\n', '\n')); + }), + ); + + return ( +
{ + if (container) bindFunctions?.(container); + }} + dangerouslySetInnerHTML={{ __html: svg }} + /> + ); +} diff --git a/apps/docs/content/docs/architecture/data-model.mdx b/apps/docs/content/docs/architecture/data-model.mdx new file mode 100644 index 00000000..a6503316 --- /dev/null +++ b/apps/docs/content/docs/architecture/data-model.mdx @@ -0,0 +1,146 @@ +--- +title: Data Model +description: Persistent data locations, Docker volume mounts, and the data/ directory structure for the atl.chat platform. +--- + +The `data/` directory holds all persistent state for the atl.chat stack — IRC databases, XMPP stores, TLS certificates, and client data — created by `just init` and bind-mounted into containers via Docker Compose volumes. + +## How data/ is created + +Running `just init` (or `just dev`, which calls init first) executes `scripts/init.sh`. This script creates every directory that Docker Compose expects to bind-mount. The directory is git-ignored — it exists only on the host machine and is never committed. + +If you see permission errors on startup, re-run `just init`. The script sets ownership to your current UID/GID and ensures directories are writable by the container processes. + +## Directory structure + +```mermaid +graph TD + subgraph data["data/"] + subgraph irc["irc/"] + ircData["data/\nrpc.socket, services.sock,\nruntime data"] + ircLogs["logs/\nUnrealIRCd log files"] + ircWebpanel["webpanel-data/\nWebPanel persistent state"] + end + + subgraph atheme["atheme/"] + athemeData["data/\nservices.db (SQLite)"] + athemeLogs["logs/\natheme.log"] + end + + subgraph xmpp["xmpp/"] + xmppData["data/\nprosody.sqlite"] + xmppLogs["logs/\nProsody log files"] + xmppUploads["uploads/\nHTTP Upload files"] + end + + thelounge["thelounge/\nconfig.js, users/, logs/,\nplugin state"] + + subgraph certs["certs/"] + certsLive["live/\n<domain>/fullchain.pem, privkey.pem"] + certsCertificates["certificates/\nLego output (prod)"] + certsAccounts["accounts/\nACME account data (prod)"] + end + end +``` + +The canonical layout on disk: + +``` +data/ +├── irc/ +│ ├── data/ # UnrealIRCd: rpc.socket, services.sock, runtime data +│ ├── logs/ # UnrealIRCd log files +│ └── webpanel-data/ # WebPanel persistent state +├── atheme/ +│ ├── data/ # Atheme SQLite database (services.db) +│ └── logs/ # Atheme logs (atheme.log) +├── xmpp/ +│ ├── data/ # Prosody SQLite database (prosody.sqlite) +│ ├── logs/ # Prosody log files +│ └── uploads/ # Prosody HTTP Upload files +├── thelounge/ # The Lounge home: config.js, users/, packages/ +└── certs/ + ├── live/ # Per-domain cert dirs: /fullchain.pem, privkey.pem + │ ├── irc.localhost/ # Dev self-signed certs + │ └── xmpp.localhost/ + ├── certificates/ # Lego output (production) + └── accounts/ # ACME account data (production) +``` + +## Volume mount reference + +Every bind-mount is defined in the compose fragments under `infra/compose/`. Paths are relative to the repository root. + +| Host path | Container mount | Service | Compose file | +|---|---|---|---| +| `data/irc/data` | `/home/unrealircd/unrealircd/data` | UnrealIRCd | `irc.yaml` | +| `data/irc/logs` | `/home/unrealircd/unrealircd/logs` | UnrealIRCd | `irc.yaml` | +| `data/certs` | `/home/unrealircd/unrealircd/certs` (ro) | UnrealIRCd | `irc.yaml` | +| `data/atheme/data` | `/usr/local/atheme/data` | Atheme | `irc.yaml` | +| `data/atheme/logs` | `/usr/local/atheme/logs` | Atheme | `irc.yaml` | +| `data/irc/webpanel-data` | `/var/www/html/unrealircd-webpanel/data` | WebPanel | `irc.yaml` | +| `data/xmpp/data` | `/var/lib/prosody/data` | Prosody | `xmpp.yaml` | +| `data/xmpp/logs` | `/var/lib/prosody/logs` | Prosody | `xmpp.yaml` | +| `data/xmpp/uploads` | `/var/lib/prosody/uploads` | Prosody | `xmpp.yaml` | +| `data/certs` | `/etc/prosody/certs` | Prosody | `xmpp.yaml` | +| `data/certs` | `/etc/nginx/certs` (ro) | XMPP Nginx | `xmpp.yaml` | +| `data/thelounge` | `/var/opt/thelounge` | The Lounge | `thelounge.yaml` | +| `data/certs` | `/data` | Cert Manager | `cert-manager.yaml` | + +> **Note:** The Bridge service does not have a `data/` volume. Its configuration (`apps/bridge/config.yaml`) is bind-mounted read-only from the source tree, not from `data/`. + +## Shared certificate store + +The `data/certs/` directory is shared across multiple services. In development, `scripts/init.sh` generates self-signed certificates under `data/certs/live//`. In production, the cert-manager container (Lego) writes Let's Encrypt certificates to the same location. + +Services that consume certificates: + +- **UnrealIRCd** — mounts `data/certs` read-only at `/home/unrealircd/unrealircd/certs`. Cert paths are configured via `IRC_SSL_CERT_PATH` and `IRC_SSL_KEY_PATH` environment variables, defaulting to `certs/live//fullchain.pem` and `privkey.pem`. +- **Prosody** — mounts `data/certs` at `/etc/prosody/certs`. The Prosody config references certs by domain under this path. +- **XMPP Nginx** — mounts `data/certs` read-only at `/etc/nginx/certs` for HTTPS termination on port 5281. +- **Cert Manager (Lego)** — mounts `data/certs` at `/data` (read-write). Lego writes renewed certificates here, and other services pick them up on reload. + +After certificate renewal, you need to reload the consuming services: + +```bash +# Rehash UnrealIRCd to pick up new certs +just irc reload + +# Reload Prosody +just xmpp reload +``` + +## Permissions + +`scripts/init.sh` sets ownership of the entire `data/` tree to your current UID/GID (`id -u`:`id -g`). Directories are set to `755`. This matches the `PUID`/`PGID` environment variables used by the UnrealIRCd and Atheme containers. + +If you encounter permission errors after pulling updates or switching users, re-run: + +```bash +just init +``` + +## Obsolete paths + +Older documentation and setups may reference paths that no longer exist. If you find any of these directories, you can safely remove them: + +| Obsolete path | Current equivalent | +|---|---| +| `data/unrealircd/` | `data/irc/data/` | +| `data/letsencrypt/` | `data/certs/` | +| `data/atheme/atheme.db` | `data/atheme/data/services.db` | +| `logs/atheme/` | `data/atheme/logs/` | +| `logs/atl-irc-server/` | `data/irc/logs/` | +| `data/docs/` | Not used — remove if present | + +```bash +# Clean up obsolete directories (safe to run; skips if not present) +rm -rf data/unrealircd data/letsencrypt data/docs logs/atheme logs/atl-irc-server +``` + +## Related pages + +- [Architecture Overview](/docs/architecture) — system diagram, compose structure, design decisions +- [Networking](/docs/architecture/networking) — port registry, DNS zones, firewall rules +- [Backups](/docs/operations/backups) — per-service backup and restore procedures +- [SSL/TLS](/docs/operations/ssl-tls) — certificate management and renewal diff --git a/apps/docs/content/docs/architecture/index.mdx b/apps/docs/content/docs/architecture/index.mdx new file mode 100644 index 00000000..e54ed855 --- /dev/null +++ b/apps/docs/content/docs/architecture/index.mdx @@ -0,0 +1,163 @@ +--- +title: Architecture Overview +description: System diagram, service topology, and key design decisions for the atl.chat platform. +--- + +atl.chat is a multi-protocol chat platform where IRC (UnrealIRCd + Atheme), XMPP (Prosody), and Discord are connected by a central event-driven bridge, all orchestrated via Docker Compose on a single shared network. + +## System diagram + +The following diagram shows all services, their connections, and external interfaces. Dockerised services run on the `atl-chat` bridge network. The Next.js web app and the Fumadocs documentation site run outside Docker. + +```mermaid +graph TB + subgraph External["External Clients"] + IRCClient["IRC Clients\n(TLS :6697, WS :8000)"] + XMPPClient["XMPP Clients\n(C2S :5222 / :5223)"] + DiscordAPI["Discord API"] + Browser["Web Browsers"] + end + + subgraph Docker["Docker · atl-chat network"] + subgraph IRCStack["IRC Stack (infra/compose/irc.yaml)"] + UnrealIRCd["UnrealIRCd\natl-irc-server\n:6697 :6900 :8000 :8600"] + Atheme["Atheme Services\natl-irc-services\n(network_mode: service:atl-irc-server)"] + WebPanel["WebPanel\natl-irc-webpanel\n:8080"] + end + + subgraph XMPPStack["XMPP Stack (infra/compose/xmpp.yaml)"] + Prosody["Prosody\natl-xmpp-server\n:5222 :5280"] + XMPPNginx["XMPP Nginx\natl-xmpp-nginx\n:5281"] + end + + Bridge["Bridge\natl-bridge\n(Python · asyncio)"] + TheLounge["The Lounge\natl-thelounge\n:9000"] + CertManager["Cert Manager\natl-cert-manager\n(Lego / Let's Encrypt)"] + Dozzle["Dozzle\n:8082\n(dev only)"] + end + + subgraph Local["Local / Cloudflare"] + WebApp["Next.js Web App\napps/web\n:3000"] + Docs["Fumadocs\napps/docs\n(Cloudflare Workers)"] + end + + IRCClient --> UnrealIRCd + Atheme -- "localhost:6901\n(shared network namespace)" --> UnrealIRCd + WebPanel -- "JSON-RPC :8600" --> UnrealIRCd + TheLounge -- "IRC :6697\n(WebIRC)" --> UnrealIRCd + + XMPPClient --> Prosody + XMPPNginx -- "HTTPS proxy" --> Prosody + + DiscordAPI <--> Bridge + Bridge -- "IRC :6697\n(TLS)" --> UnrealIRCd + Bridge -- "XMPP component\n:5222" --> Prosody + + Browser --> WebApp + Browser --> TheLounge + Browser --> WebPanel + Browser --> Docs + + CertManager -. "writes certs to\ndata/certs/" .-> UnrealIRCd + CertManager -. "writes certs to\ndata/certs/" .-> Prosody + +``` + +## Compose structure + +The root `compose.yaml` aggregates all service-level configurations using the `include` directive. Each compose fragment lives under `infra/compose/` and defines one logical stack: + +| Fragment | Services | Purpose | +|---|---|---| +| `networks.yaml` | — | Defines the shared `atl-chat` bridge network | +| `cert-manager.yaml` | `atl-cert-manager` | Let's Encrypt certificate management via Lego + Cloudflare DNS | +| `irc.yaml` | `atl-irc-server`, `atl-irc-services`, `atl-irc-webpanel` | IRC server, services, and admin panel | +| `xmpp.yaml` | `atl-xmpp-server`, `atl-xmpp-nginx` | XMPP server and HTTPS reverse proxy | +| `bridge.yaml` | `atl-bridge` | Discord↔IRC↔XMPP message bridge | +| `thelounge.yaml` | `atl-thelounge` | Web IRC client | + +The root `compose.yaml` also defines `dozzle` (Docker log viewer) under the `dev` profile. + +You start the stack with `just dev` (development, includes dev profile) or `just prod` (production). Both commands run `scripts/init.sh` first, which creates `data/` directories, generates self-signed dev certs, and substitutes config templates. + +## Key design decisions + +### Single Docker Compose network + +All Dockerised services share a single bridge network named `atl-chat`, defined in `infra/compose/networks.yaml`. Every compose fragment includes this file and attaches its services to the same network. This means: + +- Services discover each other by container name (e.g., the bridge connects to `atl-irc-server` for IRC and `atl-xmpp-server` for XMPP). +- Only services that need external access bind host ports. In development, most ports bind to `127.0.0.1` via the `ATL_CHAT_IP` variable. +- There is no network segmentation between services — all containers can reach each other. This simplifies configuration at the cost of isolation. For production hardening, see the [Security](/docs/operations/security) page. + +### Atheme network namespace sharing + +Atheme IRC services (`atl-irc-services`) uses `network_mode: service:atl-irc-server` in `infra/compose/irc.yaml`. This means Atheme shares the UnrealIRCd container's network stack — they share the same IP address, hostname, and port space. Atheme connects to UnrealIRCd's uplink port via `127.0.0.1:6901` (localhost within the shared namespace), avoiding any network hop. + +This design has several consequences: + +- Atheme does not appear as a separate host on the `atl-chat` network. It is invisible to other containers. +- Atheme's HTTP API (port 8081) is exposed through UnrealIRCd's port bindings, not its own. +- Atheme must start after UnrealIRCd is healthy (`depends_on` with `condition: service_healthy`). +- You cannot independently scale or network-isolate Atheme from UnrealIRCd. + +### Bridge event bus pattern + +The bridge (`apps/bridge/`) uses an event-driven architecture where all protocol adapters communicate through a central `Bus`. No adapter talks directly to another. + +```mermaid +graph LR + subgraph Adapters + Discord["Discord Adapter\n(discord.py)"] + IRC["IRC Adapter\n(pydle)"] + XMPP["XMPP Adapter\n(slixmpp)"] + end + + subgraph Gateway + Bus["Bus\n(event dispatcher)"] + Relay["Relay\n(message transform)"] + Router["Router\n(channel mapping)"] + end + + Discord -- "MessageIn" --> Bus + IRC -- "MessageIn" --> Bus + XMPP -- "MessageIn" --> Bus + Bus --> Relay + Relay --> Router + Router --> Bus + Bus -- "MessageOut" --> Discord + Bus -- "MessageOut" --> IRC + Bus -- "MessageOut" --> XMPP +``` + +The key components are: + +- **Bus** (`gateway/bus.py`) — dispatches typed events (`MessageIn`, `MessageOut`, `MessageDelete`, `ReactionIn`, `Join`, `Part`, etc.) to registered adapters. +- **Relay** (`gateway/relay.py`) — transforms inbound `MessageIn` events into `MessageOut` events for target protocols, applying content filtering and format conversion. +- **Router** (`gateway/router.py`) — maps Discord channel IDs to IRC channels and XMPP MUC JIDs based on the `mappings` in `config.yaml`. +- **Identity** (`identity/`) — resolves Discord user IDs to IRC nicks and XMPP JIDs via the Portal API, with TTL caching. + +This pattern means you can add a new protocol adapter without modifying existing adapters — you only need to implement the `BridgeAdapter` interface and register with the Bus. + +### Config templating via envsubst and prepare-config.sh + +Configuration files for UnrealIRCd, Atheme, the Bridge, and The Lounge are generated from templates at init time. The flow is: + +1. You define environment variables in `.env` (production) and optionally `.env.dev` (development overrides). +2. `just init` (or `just dev`) runs `scripts/init.sh`, which calls `scripts/prepare-config.sh`. +3. `prepare-config.sh` sources `.env` (and `.env.dev` if `ATL_ENVIRONMENT=dev`), then runs `envsubst` on each template file: + - `apps/unrealircd/config/unrealircd.conf.template` → `unrealircd.conf` + - `apps/atheme/config/atheme.conf.template` → `atheme.conf` + - `apps/bridge/config.template.yaml` → `config.yaml` + - `apps/thelounge/config.js.template` → `data/thelounge/config.js` +4. The generated config files are bind-mounted into containers via compose volume mounts. + +This approach keeps secrets out of version control (they live in `.env`) while allowing config templates to be tracked in Git. The `envsubst` tool replaces `${VAR_NAME}` placeholders with their values from the environment. + +> **Warning:** Always run `just init` after changing `.env` values. The config files are not regenerated automatically — you must re-run the template substitution for changes to take effect. + +## Related pages + +- [Networking](/docs/architecture/networking) — port registry, Tailscale overlay, DNS zones, firewall rules +- [Data Model](/docs/architecture/data-model) — persistent data locations, volume mounts, `data/` directory structure +- [Bridge](/docs/services/bridge) — adapter pattern deep dive, configuration schema, operations diff --git a/apps/docs/content/docs/architecture/meta.json b/apps/docs/content/docs/architecture/meta.json new file mode 100644 index 00000000..9508d361 --- /dev/null +++ b/apps/docs/content/docs/architecture/meta.json @@ -0,0 +1,8 @@ +{ + "title": "Architecture", + "pages": [ + "index", + "networking", + "data-model" + ] +} diff --git a/apps/docs/content/docs/architecture/networking.mdx b/apps/docs/content/docs/architecture/networking.mdx new file mode 100644 index 00000000..b2ec1bd2 --- /dev/null +++ b/apps/docs/content/docs/architecture/networking.mdx @@ -0,0 +1,275 @@ +--- +title: Networking +description: Tailscale overlay, complete port registry, DNS zone layout, and firewall rules for atl.chat. +--- + +atl.chat uses a Tailscale overlay to connect production servers, a single Docker bridge network for inter-service communication, and structured DNS zones with SRV records for IRC and XMPP client discovery. + +## Network topology + +Production runs on two Tailscale nodes within the CGNAT subnet `100.64.0.0/10`: + +| Node | Tailscale IP | Role | +|---|---|---| +| `atl.network` (gateway) | `100.64.1.0` | Nginx Proxy Manager, TLS termination, public ingress | +| `atl.chat` (services) | `100.64.7.0` | All Dockerised chat services | + +The `ATL_GATEWAY_IP` and `ATL_CHAT_IP` variables in `.env` control which IP addresses services bind to. In development, `.env.dev` overrides `ATL_CHAT_IP` to `127.0.0.1` so ports bind to localhost only. + +```mermaid +graph LR + Internet["Internet"] + subgraph Tailnet["Tailscale Mesh (100.64.0.0/10)"] + Gateway["atl.network\n100.64.1.0\nNginx Proxy Manager"] + Services["atl.chat\n100.64.7.0\nDocker services"] + end + Internet -- "HTTPS / TCP" --> Gateway + Gateway -- "Tailscale tunnel" --> Services +``` + +### Traffic flow + +HTTP/S traffic and raw TCP (IRC, XMPP) follow different paths through the gateway: + +1. **HTTP/S traffic** — `Internet → Cloudflare → atl.network (Nginx Proxy Manager) → Tailscale tunnel → atl.chat (containers)` +2. **TCP traffic (IRC/XMPP)** — `Internet → atl.network (NPM stream pass-through) → Tailscale tunnel → atl.chat (TCP ports)` + +All inter-node traffic is encrypted by Tailscale. TLS termination for HTTPS happens at the gateway; IRC and XMPP handle their own TLS within the containers. + +## Docker network model + +All Dockerised services share a single bridge network named `atl-chat`, defined in `infra/compose/networks.yaml`. Every compose fragment includes this file and attaches its services to the same network. + +- Services discover each other by container name (e.g., the bridge connects to `atl-irc-server`). +- Only services that need external access bind host ports. +- There is no network segmentation between containers — all services can reach each other on the bridge network. +- Atheme uses `network_mode: service:atl-irc-server`, sharing UnrealIRCd's network namespace. It connects via `127.0.0.1:6901` (localhost within the shared stack) and its HTTP API (port 8081) is exposed through UnrealIRCd's port bindings. + +For production network hardening, see the [Security](/docs/operations/security) page. + +## Port registry + +The following table lists every port exposed by the Docker Compose stack, verified against `infra/compose/*.yaml` and `compose.yaml`. The "Bind IP" column shows which variable controls the host bind address. + +### External ports (host-mapped) + +| Port | Service | Container | Protocol | Bind IP | Env Variable | Compose File | +|---|---|---|---|---|---|---| +| 6697 | UnrealIRCd | `atl-irc-server` | IRC (TLS) | `ATL_CHAT_IP` | `IRC_TLS_PORT` | `irc.yaml` | +| 6900 | UnrealIRCd | `atl-irc-server` | IRC (S2S link) | `ATL_CHAT_IP` | `IRC_SERVER_PORT` | `irc.yaml` | +| 8600 | UnrealIRCd | `atl-irc-server` | JSON-RPC (HTTPS) | `ATL_CHAT_IP` | `IRC_RPC_PORT` | `irc.yaml` | +| 8000 | UnrealIRCd | `atl-irc-server` | IRC WebSocket | `ATL_CHAT_IP` | `IRC_WEBSOCKET_PORT` | `irc.yaml` | +| 8081 | Atheme | `atl-irc-server`¹ | HTTP (JSON-RPC) | `ATL_CHAT_IP` | `ATHEME_HTTPD_PORT` | `irc.yaml` | +| 8080 | WebPanel | `atl-irc-webpanel` | HTTP | — | `WEBPANEL_PORT` | `irc.yaml` | +| 5222 | Prosody | `atl-xmpp-server` | XMPP C2S (STARTTLS) | — | `PROSODY_C2S_PORT` | `xmpp.yaml` | +| 5223 | Prosody | `atl-xmpp-server` | XMPP C2S (Direct TLS) | — | `PROSODY_C2S_DIRECT_TLS_PORT` | `xmpp.yaml` | +| 5269 | Prosody | `atl-xmpp-server` | XMPP S2S (STARTTLS) | — | `PROSODY_S2S_PORT` | `xmpp.yaml` | +| 5270 | Prosody | `atl-xmpp-server` | XMPP S2S (Direct TLS) | — | `PROSODY_S2S_DIRECT_TLS_PORT` | `xmpp.yaml` | +| 5280 | Prosody | `atl-xmpp-server` | HTTP (BOSH/WebSocket) | — | `PROSODY_HTTP_PORT` | `xmpp.yaml` | +| 5281 | XMPP Nginx | `atl-xmpp-nginx` | HTTPS (BOSH/WebSocket) | — | `PROSODY_HTTPS_PORT` | `xmpp.yaml` | +| 5000 | Prosody | `atl-xmpp-server` | Proxy65 (file transfer) | — | `PROSODY_PROXY65_PORT` | `xmpp.yaml` | +| 9000 | The Lounge | `atl-thelounge` | HTTP (Web IRC) | — | `THELOUNGE_PORT` | `thelounge.yaml` | +| 8082 | Dozzle | `dozzle` | HTTP (log viewer) | — | `DOZZLE_PORT` | `compose.yaml` | + +¹ Atheme shares UnrealIRCd's network namespace (`network_mode: service:atl-irc-server`), so its port 8081 is exposed through the `atl-irc-server` container's port bindings. + +> **Note:** Dozzle is only available under the `dev` profile (`docker compose --profile dev up`). It is not started in production. + +### Internal ports (container-to-container only) + +These ports are used for inter-service communication on the `atl-chat` Docker network and are not mapped to the host: + +| Port | Service | Protocol | Used By | +|---|---|---|---| +| 6901 | UnrealIRCd | IRC (S2S uplink) | Atheme connects via `127.0.0.1:6901` (shared namespace) | +| 5347 | Prosody | XMPP component | Bridge connects as `bridge.atl.chat` component | +| 8081 | Atheme HTTP | JSON-RPC | Bridge and Portal query Atheme via `atl-irc-server:8081` | +| 8600 | UnrealIRCd RPC | JSON-RPC | WebPanel queries UnrealIRCd via `atl-irc-server:8600` | + +### Bind address behaviour + +The `ATL_CHAT_IP` variable controls which host IP the IRC stack ports bind to: + +| Environment | `ATL_CHAT_IP` | Effect | +|---|---|---| +| Development (`.env.dev`) | `127.0.0.1` | Ports only accessible from localhost | +| Production (`.env`) | `100.64.7.0` | Ports bind to the Tailscale IP, accessible only within the Tailnet | + +XMPP, WebPanel, The Lounge, and Dozzle ports do not use `ATL_CHAT_IP` — they bind to `0.0.0.0` by default. In production, firewall rules (see below) restrict access. + + +## Tailscale overlay + +Production servers communicate via [Tailscale](https://tailscale.com/), which provides a mesh VPN using the CGNAT subnet `100.64.0.0/10`. This eliminates the need for public IPs between servers and provides automatic key rotation, NAT traversal, and MagicDNS. + +### Setup + +1. Install Tailscale on both the gateway (`atl.network`) and the services node (`atl.chat`): + + ```bash + # Ubuntu/Debian + curl -fsSL https://tailscale.com/install.sh | sh + sudo tailscale up --advertise-tags=tag:server + ``` + +2. Assign stable IPs in the Tailscale admin console: + - `atl.network` → `100.64.1.0` + - `atl.chat` → `100.64.7.0` + +3. Set the IPs in `.env`: + + ```bash + ATL_GATEWAY_IP=100.64.1.0 + ATL_CHAT_IP=100.64.7.0 + ``` + +4. Run `just init` to regenerate configs, then `just prod` to start services bound to the Tailscale IPs. + +### Service discovery + +Services on the `atl.chat` node discover each other by Docker container name on the `atl-chat` bridge network (e.g., `atl-irc-server`, `atl-xmpp-server`). The gateway node (`atl.network`) reaches the services node via its Tailscale IP `100.64.7.0`. + +Tailscale also provides MagicDNS, so you can optionally use `atl-chat.tailnet-name.ts.net` for internal service discovery between nodes. However, the compose stack uses container names for intra-node communication and Tailscale IPs for inter-node communication. + +### ACLs + +Restrict Tailscale ACLs so only the gateway node can reach the services node on the required ports. Example Tailscale ACL policy: + +```json +{ + "acls": [ + { + "action": "accept", + "src": ["tag:gateway"], + "dst": ["tag:server:6697,8000,5222,5223,5269,5270,5280,5281,9000,8080"] + } + ], + "tagOwners": { + "tag:gateway": ["autogroup:admin"], + "tag:server": ["autogroup:admin"] + } +} +``` + +## DNS zone layout + +atl.chat uses structured DNS zones for each protocol. All public DNS records point to the gateway node (`atl.network`), which proxies traffic through the Tailscale tunnel to the services node. + +### A/AAAA records + +| Hostname | Points To | Purpose | +|---|---|---| +| `atl.chat` | Gateway public IP | Main web interface, XMPP domain | +| `irc.atl.chat` | Gateway public IP | IRC server (TLS, WebSocket, RPC) | +| `xmpp.atl.chat` | Gateway public IP | XMPP BOSH/WebSocket HTTPS endpoint | +| `chat.atl.chat` | Gateway public IP | The Lounge web IRC client | +| `turn.atl.network` | Gateway public IP | TURN/STUN server for XMPP media | + +### SRV records for XMPP + +XMPP clients and servers use SRV records to discover connection endpoints. These records are required for federation and recommended for client auto-configuration: + +```text +; Client-to-server (C2S) — STARTTLS on port 5222 +_xmpp-client._tcp.atl.chat. 3600 IN SRV 5 0 5222 xmpp.atl.chat. + +; Client-to-server (C2S) — Direct TLS on port 5223 +_xmpps-client._tcp.atl.chat. 3600 IN SRV 5 0 5223 xmpp.atl.chat. + +; Server-to-server (S2S) — STARTTLS on port 5269 +_xmpp-server._tcp.atl.chat. 3600 IN SRV 5 0 5269 xmpp.atl.chat. + +; Server-to-server (S2S) — Direct TLS on port 5270 +_xmpps-server._tcp.atl.chat. 3600 IN SRV 5 0 5270 xmpp.atl.chat. +``` + +> **Note:** The `_xmpps-*` records (with the `s`) advertise Direct TLS ports (5223/5270), which skip the STARTTLS negotiation step. Both STARTTLS and Direct TLS records should be published for maximum client compatibility. + +### SRV records for IRC + +IRC does not have a standardised SRV record scheme, but you can optionally publish one for clients that support it: + +```text +; IRC client (TLS on port 6697) +_ircs._tcp.atl.chat. 3600 IN SRV 5 0 6697 irc.atl.chat. +``` + +### MUC and component subdomains + +XMPP multi-user chat (MUC) and bridge components use subdomains of the XMPP domain. These do not need separate DNS records — Prosody handles them internally — but they must be resolvable if federation is enabled: + +| Subdomain | Purpose | +|---|---| +| `muc.atl.chat` | MUC (multi-user chat) rooms | +| `bridge.atl.chat` | Bridge XMPP component JID | +| `upload.atl.chat` | HTTP file upload (mod_http_file_share) | +| `pubsub.atl.chat` | PubSub service | +| `proxy.atl.chat` | Proxy65 file transfer | + +## Firewall rules + +### Gateway node (`atl.network`) + +The gateway is the only node with a public IP. Open these ports to the internet: + +| Port | Protocol | Service | +|---|---|---| +| 80 | TCP | HTTP (Let's Encrypt challenges, redirect to HTTPS) | +| 443 | TCP | HTTPS (Nginx Proxy Manager → web, BOSH, WebSocket) | +| 6697 | TCP | IRC TLS (stream pass-through to `atl.chat`) | +| 5222 | TCP | XMPP C2S STARTTLS (stream pass-through) | +| 5223 | TCP | XMPP C2S Direct TLS (stream pass-through) | +| 5269 | TCP | XMPP S2S STARTTLS (stream pass-through) | +| 5270 | TCP | XMPP S2S Direct TLS (stream pass-through) | +| 3478 | UDP | TURN (media relay) | +| 5349 | TCP | TURNS (TLS media relay) | + +Example `ufw` rules: + +```bash +# Web +sudo ufw allow 80/tcp +sudo ufw allow 443/tcp + +# IRC +sudo ufw allow 6697/tcp + +# XMPP +sudo ufw allow 5222/tcp +sudo ufw allow 5223/tcp +sudo ufw allow 5269/tcp +sudo ufw allow 5270/tcp + +# TURN/STUN +sudo ufw allow 3478/udp +sudo ufw allow 5349/tcp +``` + +### Services node (`atl.chat`) + +The services node should not have any ports open to the public internet. All traffic arrives via the Tailscale tunnel from the gateway. Only allow Tailscale and SSH: + +```bash +# Allow Tailscale interface +sudo ufw allow in on tailscale0 + +# Allow SSH (for management) +sudo ufw allow 22/tcp + +# Deny everything else from public interfaces +sudo ufw default deny incoming +sudo ufw enable +``` + +The Docker daemon manages its own iptables rules for container port bindings. Since `ATL_CHAT_IP` is set to the Tailscale IP (`100.64.7.0`), container ports are only reachable from within the Tailnet. + +> **Warning:** Docker bypasses `ufw` rules by inserting its own iptables chains. If you bind a container port to `0.0.0.0`, it will be accessible from the public internet regardless of `ufw` settings. Always use `ATL_CHAT_IP` to restrict bind addresses in production. + +## Related pages + +- [Architecture Overview](/docs/architecture) — system diagram, compose structure, design decisions +- [Data Model](/docs/architecture/data-model) — persistent data locations, volume mounts +- [Ports Reference](/docs/reference/ports) — quick-reference port table +- [SSL/TLS](/docs/operations/ssl-tls) — certificate management and renewal +- [Security](/docs/operations/security) — network isolation, secret management +- [Deployment](/docs/operations/deployment) — production deployment runbook diff --git a/apps/docs/content/docs/development/adding-a-service.mdx b/apps/docs/content/docs/development/adding-a-service.mdx new file mode 100644 index 00000000..7594ff5d --- /dev/null +++ b/apps/docs/content/docs/development/adding-a-service.mdx @@ -0,0 +1,376 @@ +--- +title: Adding a Service +description: Step-by-step guide for integrating a new service into the atl.chat monorepo, covering containers, compose, configuration, data, documentation, and tests. +--- + +This guide walks you through every step needed to add a new service to the atl.chat monorepo — from creating the app directory to wiring it into Docker Compose, the `just` task runner, and the documentation site. + +## Overview + +Each service in atl.chat follows a consistent pattern: + +1. Application code and container definition live in `apps//` +2. A Docker Compose fragment lives in `infra/compose/.yaml` +3. Environment variables are declared in `.env.example` +4. Config templates use `envsubst` for variable substitution at init time +5. Persistent data directories are created by `scripts/init.sh` +6. A `justfile` module exposes per-service commands +7. Documentation lives in `apps/docs/content/docs/services//` + +Use existing services as reference implementations throughout this guide. Good starting points: + +- **Bridge** (`apps/bridge/`) — Python service with a simple Containerfile and compose fragment +- **The Lounge** (`apps/thelounge/`) — Node.js service with user management recipes +- **UnrealIRCd** (`apps/unrealircd/`) — Complex multi-stage build with extensive configuration + +## 1. Create the app directory + +Create `apps//` with your application code, a `Containerfile`, and a `.dockerignore`. + +### Containerfile + +Use a multi-stage build: a **build stage** that compiles or installs dependencies, and a minimal **runtime stage** that copies only what is needed. Here is a minimal example modelled after the Bridge service: + +```dockerfile +# Stage 1: Build +FROM python:3.12-slim AS build +WORKDIR /app +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt +COPY . . + +# Stage 2: Runtime +FROM python:3.12-slim AS production +WORKDIR /app +COPY --from=build /app /app + +# Run as non-root +RUN groupadd --system --gid 1001 nonroot && \ + useradd --system --uid 1001 --gid nonroot nonroot +USER nonroot + +EXPOSE 8080 + +HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \ + CMD ["curl", "-f", "http://localhost:8080/health"] || exit 1 + +CMD ["python", "-m", "myservice"] +``` + +Key conventions: + +- Always run as a non-root user in the runtime stage +- Include a `HEALTHCHECK` instruction — compose `depends_on` conditions rely on it +- Use `EXPOSE` to document the ports your service listens on +- Use build arguments (`ARG`) for version pinning (see `apps/unrealircd/Containerfile` for an example) + +Reference: [`apps/bridge/Containerfile`](https://github.com/allthingslinux/atl.chat/blob/main/apps/bridge/Containerfile), [`apps/unrealircd/Containerfile`](https://github.com/allthingslinux/atl.chat/blob/main/apps/unrealircd/Containerfile) + +### .dockerignore + +Add a `.dockerignore` to keep the build context small: + +```text +.git +__pycache__ +*.pyc +node_modules +.env* +data/ +``` + +### Verify the image builds + +```bash +# From the repository root +docker build -f apps//Containerfile apps// +``` + +## 2. Create the Docker Compose fragment + +Each service gets its own compose fragment at `infra/compose/.yaml`. This keeps the root `compose.yaml` clean — it only uses `include:` directives. + +### Write the compose fragment + +Create `infra/compose/.yaml` following this pattern (modelled after `infra/compose/bridge.yaml`): + +```yaml +# ============================================================================= +# Stack +# ============================================================================= + +name: atl- + +include: + - networks.yaml # shared atl-chat network + +services: + atl-: + build: + context: ../../apps/ + dockerfile: Containerfile + + container_name: atl- + restart: unless-stopped + + # Wait for dependencies to be healthy before starting + depends_on: + atl-irc-server: + condition: service_healthy + + env_file: + - path: ../../.env + required: false + - path: ../../.env.dev + required: false + + environment: + - MY_SERVICE_PORT=${MY_SERVICE_PORT:-8080} + - TZ=UTC + + volumes: + - ../../data/:/data + + ports: + - '${ATL_CHAT_IP:-127.0.0.1}:${MY_SERVICE_PORT:-8080}:8080' + + networks: + - atl-chat + + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 15s +``` + +Key points: + +- **`include: - networks.yaml`** — every fragment includes the shared network definition +- **`depends_on` with `condition: service_healthy`** — use this when your service needs another service to be ready (e.g., IRC server, XMPP server) +- **`env_file`** — load both `.env` (base) and `.env.dev` (dev overrides) with `required: false` +- **Bind-mount volumes** — atl.chat uses bind-mount `data/` directories, not named Docker volumes +- **Port binding** — bind to `${ATL_CHAT_IP:-127.0.0.1}` so ports are not exposed on all interfaces by default + +Reference: [`infra/compose/bridge.yaml`](https://github.com/allthingslinux/atl.chat/blob/main/infra/compose/bridge.yaml), [`infra/compose/irc.yaml`](https://github.com/allthingslinux/atl.chat/blob/main/infra/compose/irc.yaml) + +### Special case: network namespace sharing + +If your service needs to share a network namespace with another container (like Atheme shares with UnrealIRCd), use `network_mode: service:` instead of joining the `atl-chat` network directly. See the `atl-irc-services` definition in `infra/compose/irc.yaml` for this pattern. + +### Register in the root compose.yaml + +Add your fragment to the `include:` list in the root `compose.yaml`: + +```yaml +include: + - infra/compose/networks.yaml + - infra/compose/cert-manager.yaml + - infra/compose/irc.yaml + - infra/compose/xmpp.yaml + - infra/compose/bridge.yaml + - infra/compose/thelounge.yaml + - in +========================== +# +# ============================================================================= +MY_SERVICE_PORT=8080 # Port for the service HTTP endpoint +MY_SERVICE_SECRET=change_me_secret # API secret (change before production!) +``` + +Follow these conventions: + +- Use the `SCREAMING_SNAKE_CASE` naming convention with a service prefix (e.g., `BRIDGE_`, `THELOUNGE_`) +- Provide sensible defaults for development +- Mark secrets with a `change_me_` prefix so they are obvious in audits +- Add a comment describing each variable + +### Add config templates (if needed) + +If your service needs a configuration file that references environment variables, create a template: + +1. Create `apps//config/.conf.template` with `${VARIABLE}` placeholders +2. Add the `envsubst` substitution step to `scripts/prepare-config.sh` + +Here is the pattern used for existing services in `prepare-config.sh`: + +```bash +# Prepare configuration +local service_template="$PROJECT_ROOT/apps//config/.conf.template" +local service_config="$PROJECT_ROOT/apps//config/.conf" +if [ -f "$service_template" ]; then + log_info "Preparing configuration from template..." + local temp_file="/tmp/-config.tmp" + envsubst < "$service_template" > "$temp_file" + cp "$temp_file" "$service_config" + rm -f "$temp_file" + log_success " configuration prepared" +fi +``` + +Reference: [`scripts/prepare-config.sh`](https://github.com/allthingslinux/atl.chat/blob/main/scripts/prepare-config.sh) — see how UnrealIRCd, Atheme, Bridge, and The Lounge configs are templated. + +### Document new variables + +Add all new variables to the [Environment Variables reference](/docs/reference/environment-variables). Include the variable name, description, whether it is required, and its default value. Flag any secrets with a security warning. + +## 4. Set up persistent data directories + +### Add directories to init.sh + +Edit `scripts/init.sh` and add your service's data directories to the `data_dirs` array in the `create_directories()` function: + +```bash +local data_dirs=( + # ... existing directories ... + "$PROJECT_ROOT/data/" + "$PROJECT_ROOT/data//logs" # if your service writes logs +) +``` + +### Set permissions + +If your service runs as a specific UID/GID inside the container, add a permissions block in the `set_permissions()` function: + +```bash +# Set ownership for data directory +if [ -d "$PROJECT_ROOT/data/" ]; then + sudo chown -R "$current_uid:$current_gid" "$PROJECT_ROOT/data/" + chmod 755 "$PROJECT_ROOT/data/" + log_info "Set permissions for data directory" +fi +``` + +The `data/` directory is gitignored. All persistent state (databases, logs, uploads) goes here — never inside `apps//`. + +Reference: [`scripts/init.sh`](https://github.com/allthingslinux/atl.chat/blob/main/scripts/init.sh) — see how IRC, Atheme, XMPP, and The Lounge directories are created. + +## 5. Add a justfile module + +Create `apps//justfile` with common development commands: + +```makefile +# - loaded via root: mod './apps/' + +default: + @just --list + +# Add service-specific recipes here, for example: +logs: + docker compose -f ../../compose.yaml -p atl-chat logs -f atl- + +shell: + docker compose -f ../../compose.yaml -p atl-chat exec atl- sh + +restart: + docker compose -f ../../compose.yaml -p atl-chat restart atl- +``` + +Then register the module in the root `justfile`: + +```makefile +# +mod './apps/' +``` + +After this, users can run `just logs`, `just shell`, etc. + +Reference: [`apps/bridge/justfile`](https://github.com/allthingslinux/atl.chat/blob/main/apps/bridge/justfile), [`apps/thelounge/justfile`](https://github.com/allthingslinux/atl.chat/blob/main/apps/thelounge/justfile) + +## 6. Add documentation + +Create a documentation section for your service under `apps/docs/content/docs/services//`. + +### Required pages + +At minimum, create an overview page. Add configuration and operations pages if the service has non-trivial setup or operational procedures. + +**`services//index.mdx`** — overview page: + +```mdx +--- +title: "" +description: "Overview of within the atl.chat stack." +--- + + provides [brief description of what it does and why it exists in the stack]. + +## Components + +[Describe the service architecture and how it connects to other services.] + +## Technology + +| Component | Technology | +|---|---| +| Runtime | Python 3.12 / Node.js 22 / etc. | +| Container | Alpine-based multi-stage | +| Config | envsubst templates | + +## Related pages + +- [Configuration](/docs/services//configuration) — environment variables and config files +- [Operations](/docs/services//operations) — management commands and procedures +``` + +### Navigation metadata + +Create `services//meta.json` to control sidebar ordering: + +```json +{ + "title": "", + "pages": ["index", "configuration", "operations"] +} +``` + +Then add your service to the parent `services/meta.json` pages array. + +### Update related pages + +- Update the [Architecture Overview](/docs/architecture) if your service changes the system topology or adds new inter-service connections +- Update the [Ports reference](/docs/reference/ports) with any new port mappings +- Update the [Environment Variables reference](/docs/reference/environment-variables) with all new variables + +## 7. Add tests + +### Health check test + +Add a health check test to `tests/unit/` or `tests/integration/` that verifies your service starts and responds: + +```python +def test_service_health_check(): + """Verify container reports healthy.""" + # For integration tests that require Docker: + result = subprocess.run( + ["docker", "compose", "ps", "--format", "json", "atl-"], + capture_output=True, text=True + ) + status = json.loads(result.stdout) + assert status["Health"] == "healthy" +``` + +### Integration tests + +If your service exposes an API or interacts with other services, add integration tests to `tests/integration/`. Use the existing pytest fixtures for Docker Compose orchestration. + +Reference: [`tests/`](https://github.com/allthingslinux/atl.chat/blob/main/tests/) — see existing test patterns for IRC and bridge services. + +## Quick reference checklist + +Use this as a final check before opening your PR: + +- [ ] `apps//` exists with `Containerfile` and `.dockerignore` +- [ ] `infra/compose/.yaml` defines the service with health check +- [ ] Root `compose.yaml` includes your compose fragment +- [ ] `docker compose config --quiet` passes +- [ ] New env vars added to `.env.example` with comments +- [ ] Config templates added to `scripts/prepare-config.sh` (if applicable) +- [ ] `data//` added to `scripts/init.sh` +- [ ] `apps//justfile` created and registered in root `justfile` +- [ ] Documentation pages created under `services//` +- [ ] `services//meta.json` created and parent `meta.json` updated +- [ ] [Ports reference](/docs/reference/ports) updated +- [ ] [Environment Variables reference](/docs/reference/environment-variables) updated +- [ ] Health check and integration tests added diff --git a/apps/docs/content/docs/development/contributing.mdx b/apps/docs/content/docs/development/contributing.mdx new file mode 100644 index 00000000..b51bed98 --- /dev/null +++ b/apps/docs/content/docs/development/contributing.mdx @@ -0,0 +1,299 @@ +--- +title: Contributing +description: PR workflow, commit conventions, pre-commit hooks, and language-specific code style guides for atl.chat contributors. +--- + +This guide covers everything you need to contribute code, docs, or configuration to atl.chat — from branching to merge. + +## PR workflow + +atl.chat uses a fork-and-PR model against the `main` branch. Every merge into `main` triggers [semantic-release](https://semantic-release.gitbook.io/) which reads commit messages to determine the next version, generate changelogs, and publish a GitHub release. + +### Branch naming + +Use a descriptive prefix that matches the work type: + +| Prefix | Use case | Example | +|---|---|---| +| `feat/` | New feature or enhancement | `feat/xmpp-muc-bridging` | +| `fix/` | Bug fix | `fix/cloak-key-fallback` | +| `docs/` | Documentation only | `docs/add-atheme-ops-guide` | +| `chore/` | Tooling, CI, dependency updates | `chore/bump-unrealircd-image` | +| `refactor/` | Code restructuring (no behaviour change) | `refactor/bridge-event-bus` | +| `test/` | Test additions or fixes | `test/irc-integration-suite` | + +### Step-by-step + +1. Fork the repository on GitHub and clone your fork locally. + +2. Create a branch from `main`: + + ```bash + git checkout main && git pull origin main + git checkout -b feat/my-feature + ``` + +3. Make your changes. Run tests and linting before committing: + + ```bash + just test # run root pytest suite + just lint # run all pre-commit hooks + ``` + +4. Commit using [Conventional Commits](#commit-style) format. + +5. Push your branch and open a pull request against `main`. + +6. Address review feedback. Push additional commits — the PR will be squash-merged. + +### Code review expectations + +- Every PR requires at least one approving review before merge. +- Reviewers check for correctness, test coverage, style compliance, and documentation updates. +- Keep PRs focused. If a change touches multiple services, consider splitting into separate PRs. +- CI must pass (lint, unit tests, type checks) before merge is allowed. +- PRs are squash-merged into `main` to keep a linear history. + +## Commit style + +atl.chat enforces [Conventional Commits](https://www.conventionalcommits.org/) via commitlint (run as a pre-commit hook on the `commit-msg` stage). Semantic-release parses these messages to determine version bumps and generate release notes. + +### Format + +``` +(): + +[optional body] + +[optional footer(s)] +``` + +### Types + +| Type | Version bump | Purpose | +|---|---|---| +| `feat` | minor | New feature or user-facing enhancement | +| `fix` | patch | Bug fix | +| `docs` | — | Documentation only | +| `chore` | — | Tooling, CI, dependencies | +| `refactor` | — | Code restructuring without behaviour change | +| `test` | — | Adding or updating tests | +| `ci` | — | CI/CD pipeline changes | +| `style` | — | Formatting, whitespace (no logic change) | +| `perf` | patch | Performance improvement | + +A commit with `BREAKING CHANGE:` in the footer (or `!` after the type) triggers a major version bump. + +### Scopes + +Use the app or area name as scope: `irc`, `atheme`, `xmpp`, `bridge`, `web`, `lounge`, `webpanel`, `docs`, `infra`, `ops`, `ci`. + +### Examples + +```bash +# Feature with scope +feat(bridge): add XMPP MUC room join/part relay + +# Bug fix +fix(irc): correct cloak key fallback in prepare-config.sh + +# Documentation +docs(ops): add SSL certificate renewal runbook + +# Breaking change (major version bump) +feat(bridge)!: switch event bus from polling to WebSocket + +BREAKING CHANGE: bridge config.yaml now requires a `ws_url` field. + +# Chore with body +chore(deps): bump UnrealIRCd image to 6.1.8 + +Updated base image tag in infra/compose/irc.yaml. +Tested locally with `just dev` — all services start cleanly. +``` + +### Interactive commits + +The repo includes [Commitizen](https://commitizen-tools.github.io/commitizen/) for guided commit message creation. Run it interactively: + +```bash +pnpm exec cz +``` + +This walks you through type, scope, description, body, and breaking change prompts. + +## Pre-commit hooks + +atl.chat uses [pre-commit](https://pre-commit.com/) to enforce code quality on every commit. The `just lint` command runs all hooks against the entire repo. + +### Setup + +1. Install pre-commit (included in the Python dev dependencies): + + ```bash + uv sync # installs pre-commit into the venv + ``` + +2. If your environment sets `core.hooksPath` (some editors and CI systems do this), unset it first — otherwise `pre-commit install` will silently fail to register hooks: + + ```bash + git config --unset-all core.hooksPath + ``` + +3. Install the git hooks: + + ```bash + uv run pre-commit install + uv run pre-commit install --hook-type commit-msg # enables commitlint + ``` + +4. Verify hooks are active: + + ```bash + uv run pre-commit run --all-files + ``` + + This is equivalent to `just lint`. + +### Hook reference + +The full hook set defined in `.pre-commit-config.yaml`: + +| Hook | Source | What it does | +|---|---|---| +| `check-json` | pre-commit-hooks | Validates JSON syntax | +| `check-toml` | pre-commit-hooks | Validates TOML syntax | +| `end-of-file-fixer` | pre-commit-hooks | Ensures files end with a newline | +| `trailing-whitespace` | pre-commit-hooks | Removes trailing whitespace (excludes `.md`) | +| `validate-pyproject` | validate-pyproject | Validates `pyproject.toml` schema | +| `yamlfix` | yamlfix | Auto-formats YAML files | +| `yamllint` | yamllint | Lints YAML files | +| `actionlint` | actionlint | Lints GitHub Actions workflows | +| `markdownlint-fix` | markdownlint-cli | Auto-fixes Markdown style issues | +| `ruff-check` | ruff-pre-commit | Python linting with auto-fix (`--fix`) | +| `ruff-format` | ruff-pre-commit | Python formatting | +| `shellcheck` | shellcheck-py | Shell script static analysis | +| `shfmt` | pre-commit-shfmt | Shell script formatting | +| `gitleaks` | gitleaks | Scans for accidentally committed secrets | +| `commitlint` | commitlint-pre-commit-hook | Validates commit message format (commit-msg stage) | +| `luacheck` | local (Docker) | Lua static analysis (runs via Docker image) | +| `lint-web` | local | Runs Biome/ultracite on `apps/web/` TypeScript/JSX files | + +### Running hooks manually + +```bash +# Run all hooks on all files +just lint + +# Run a specific hook +uv run pre-commit run ruff-check --all-files +uv run pre-commit run shellcheck --all-files + +# Run hooks only on staged files (default git hook behaviour) +uv run pre-commit run +``` + +## Code style + +Each language in the monorepo has its own linter and formatter. The pre-commit hooks enforce these automatically, but you can also run them standalone during development. + +### Python — ruff + +All Python code (bridge, tests, scripts) is linted and formatted by [ruff](https://docs.astral.sh/ruff/). Configuration lives in the root `pyproject.toml`. + +| Setting | Value | +|---|---| +| Target version | Python 3.11 | +| Line length | 120 | +| Quote style | Double quotes | +| Indent style | Spaces | +| Line ending | LF | + +Enabled rule sets: `E` (pycodestyle), `F` (pyflakes), `I` (isort), `N` (pep8-naming), `UP` (pyupgrade), `B` (flake8-bugbear), `SIM` (flake8-simplify), `PL` (pylint), `RUF` (ruff-specific). + +```bash +# Lint with auto-fix +uv run ruff check --fix . + +# Format +uv run ruff format . + +# Check without modifying files +uv run ruff check . +uv run ruff format --check . +``` + +Per-file ignores are configured for test directories and the bridge source (see `[tool.ruff.lint.per-file-ignores]` in `pyproject.toml`). + +### TypeScript / JSX — Biome (ultracite) + +The Next.js web app (`apps/web/`) uses [ultracite](https://github.com/haydenbleasel/ultracite), a wrapper around [Biome](https://biomejs.dev/), for linting and formatting. Ultracite ships a pre-configured Biome profile for Next.js projects. + +```bash +# Check for issues +cd apps/web && pnpm run check + +# Auto-fix +cd apps/web && pnpm run fix + +# Or via the pre-commit hook (runs automatically on apps/web/ JS/TSX files) +uv run pre-commit run lint-web --all-files +``` + +> **Note:** The `ultracite check` command currently has a known issue where it expects a `.gitignore` in `apps/web/`. This does not affect builds (`pnpm run build` works fine). + +### Lua — luacheck + +Prosody configuration and any Lua scripts are checked by [luacheck](https://luacheck.readthedocs.io/). The pre-commit hook runs luacheck via a Docker image, so no local Lua installation is needed. + +Configuration lives in `.luacheckrc` at the repo root: + +| Setting | Value | +|---|---| +| Standard | `min` (minimal standard globals) | +| Max line length | 300 | +| Prosody globals | Declared in the `globals` table (VirtualHost, Component, modules_enabled, etc.) | + +```bash +# Run via pre-commit +uv run pre-commit run luacheck --all-files + +# Run directly with Docker (same as the hook) +docker run --rm -v "$PWD":/data ghcr.io/lunarmodules/luacheck:latest . +``` + +Known warning: `apps/prosody/config/prosody.cfg.lua` may emit unused-variable warnings because Prosody config files declare variables consumed by the Prosody runtime. The `.luacheckrc` sets `unused = false` for this file. + +### Shell — shellcheck and shfmt + +Shell scripts (`scripts/`, `infra/`) are analysed by [shellcheck](https://www.shellcheck.net/) for correctness and formatted by [shfmt](https://github.com/mvdan/sh) for consistency. + +shfmt settings (from `.pre-commit-config.yaml`): + +| Setting | Value | +|---|---| +| Language | `bash` | +| Indent | 2 spaces | +| Binary ops | Newline after | +| Switch cases | Indented | +| Keep padding | Yes | +| Simplify | Yes | + +```bash +# Run shellcheck +uv run pre-commit run shellcheck --all-files + +# Run shfmt +uv run pre-commit run shfmt --all-files + +# Run shellcheck directly +shellcheck scripts/*.sh +``` + +Known warning: `infra/nginx/docker-entrypoint.sh` triggers shellcheck SC2016 (unexpanded variable in single quotes) — this is intentional and can be ignored. + +## Related pages + +- [Testing](/docs/development/testing) — test directory layout, running tests, fixtures +- [Adding a Service](/docs/development/adding-a-service) — checklist for adding a new service to the monorepo +- [Security](/docs/operations/security) — secret management and Gitleaks configuration diff --git a/apps/docs/content/docs/development/meta.json b/apps/docs/content/docs/development/meta.json new file mode 100644 index 00000000..869a0cd8 --- /dev/null +++ b/apps/docs/content/docs/development/meta.json @@ -0,0 +1,8 @@ +{ + "title": "Development", + "pages": [ + "contributing", + "testing", + "adding-a-service" + ] +} diff --git a/apps/docs/content/docs/development/testing.mdx b/apps/docs/content/docs/development/testing.mdx new file mode 100644 index 00000000..f55d68b4 --- /dev/null +++ b/apps/docs/content/docs/development/testing.mdx @@ -0,0 +1,421 @@ +--- +title: Testing +description: Complete guide to the atl.chat test suite — directory layout, running tests, pytest fixtures, mocking patterns, and CI pipeline. +--- + +The atl.chat test suite uses pytest across two test roots: `tests/` at the monorepo root for IRC infrastructure, integration, and protocol tests, and `apps/bridge/tests/` for the bridge service. You run everything with `just test-all`, or target individual categories for faster feedback. + +## Quick reference + +| Command | What it runs | Docker needed? | +|---|---|---| +| `just test` | Root pytest suite (`tests/`) | Integration and e2e tests build images | +| `just test-all` | Root suite + bridge tests | Same as above | +| `just bridge test` | Bridge tests only (`apps/bridge/tests/`) | No | +| `uv run pytest tests/unit/` | Unit tests only | No | +| `uv run pytest tests/integration/` | Integration tests | Yes (builds images) | +| `uv run pytest tests/e2e/` | End-to-end tests | Yes (builds images) | +| `uv run pytest tests/protocol/` | IRC protocol message tests | No | +| `uv run pytest apps/bridge/tests/` | All bridge tests | No | + +For day-to-day development, run unit and bridge tests — they complete in seconds without Docker: + +```bash +# Fast feedback loop (no Docker required) +uv run pytest tests/unit/ +uv run pytest apps/bridge/tests/ +``` + +## Test directory layout + +``` +tests/ # Root test suite (IRC infrastructure) +├── conftest.py # Shared fixtures: Docker, IRC helpers, controllers +├── unit/ # Fast, no-Docker unit tests +│ ├── test_configuration.py # Config file validation +│ ├── test_docker_client.py # Docker client functionality +│ ├── test_environment_validation.py # Environment setup checks +│ ├── test_env_registry_completeness.py # Env var coverage +│ ├── test_bridge_compose_env_coverage.py # Bridge env coverage +│ └── test_irc_server_mock.py # IRC server mock tests +├── integration/ # Cross-service tests (require Docker) +│ ├── test_protocol.py # IRC protocol compliance (RFC 1459, RFC 2812) +│ ├── test_clients.py # Client library integration (pydle, python-irc) +│ ├── test_services.py # NickServ, ChanServ, Atheme integration +│ ├── test_monitoring.py # Server monitoring and RPC +│ ├── test_performance.py # Performance and load testing +│ ├── test_infrastructure.py # Infrastructure and deployment +│ └── test_irc_functionality.py # General IRC server functionality +├── e2e/ # End-to-end workflow tests (require Docker) +│ └── test_e2e_workflow.py # Full stack workflow validation +├── protocol/ # IRC message protocol tests (unit-level) +│ └── test_messages.py # IRC message parsing and formatting +├── controllers/ # IRC server controller classes +│ ├── base_controllers.py # Base controller interface +│ ├── unrealircd_controller.py # UnrealIRCd controller +│ └── atheme_controller.py # Atheme services controller +├── fixtures/ # Shared test data +│ ├── docker_fixtures.py # Docker container fixtures (pytest-docker-tools) +│ ├── irc_test_data.py # IRC messages, configs, command sequences +│ └── sample_data.py # Sample configs and log entries +├── utils/ # Test helpers and base classes +│ ├── base_test_cases.py # BaseServerTestCase (irctest-style) +│ ├── irc_test_client.py # IRC test client utilities +│ ├── runner.py # Test runner utilities +│ ├── specifications.py # IRC specification definitions +│ └── test_helpers.py # General test helpers +├── irc_utils/ # IRC protocol utilities +│ └── message_parser.py # IRC message parsing +└── legacy/ # Deprecated tests (kept for reference, excluded from runs) + └── integration/ # Legacy integration tests + +apps/bridge/tests/ # Bridge test suite (Python, no Docker) +├── conftest.py # Bridge-specific fixtures +├── mocks.py # MockAdapter, MockDiscordAdapter, MockIRCAdapter, MockXMPPAdapter +├── harness.py # BridgeTestHarness — event bus + mock adapters +├── test_bridge_flow.py # Core message routing (Discord ↔ IRC ↔ XMPP) +├── test_bus.py # Event bus publish/subscribe +├── test_config.py # Bridge config loading and validation +├── test_relay.py # Relay logic +├── test_router.py # Channel mapping router +├── test_events.py # Event creation and types +├── test_formatting.py # Message formatting +├── test_message_formatting.py # Extended formatting tests +├── test_identity.py # User identity mapping +├── test_irc_adapter.py # IRC adapter logic +├── test_discord_adapter.py # Discord adapter logic +├── test_xmpp_adapter.py # XMPP adapter logic +├── test_xmpp_component.py # XMPP component protocol +├── test_error_handling.py # Error handling paths +├── test_retry_logic.py # Retry and backoff +├── test_edge_cases.py # Edge cases +├── test_property_based.py # Property-based tests (Hypothesis) +└── ... # Additional adapter and feature tests +``` + +## Running tests by category + +### Unit tests + +Unit tests validate configuration parsing, environment variables, and Docker client logic without starting any containers. They are the fastest tests to run. + +```bash +uv run pytest tests/unit/ -v +``` + +### Integration tests + +Integration tests build Docker images and spin up real UnrealIRCd and Atheme containers using `pytest-docker-tools`. They test IRC protocol compliance, service integration, and monitoring. + +```bash +# Run all integration tests (builds Docker images — can be slow) +uv run pytest tests/integration/ -v + +# Run a specific integration test file +uv run pytest tests/integration/test_protocol.py -v +``` + +> **Warning:** Integration tests build fresh Docker images on each run. In constrained environments (CI runners, VMs with limited resources), they may time out. Prefer `tests/unit/` for quick validation. + +### End-to-end tests + +E2E tests exercise full-stack workflows with real containers. + +```bash +uv run pytest tests/e2e/ -v +``` + +### Protocol tests + +Protocol-level tests validate IRC message parsing and formatting without network connections. + +```bash +uv run pytest tests/protocol/ -v +``` + +### Bridge tests + +Bridge tests mock at the adapter level — no real Discord, IRC, or XMPP connections are made. The `BridgeTestHarness` sets up the event bus, channel router, relay, and mock adapters so you can simulate messages and verify routing. + +```bash +# All bridge tests +uv run pytest apps/bridge/tests/ -v + +# Or via just +just bridge test + +# Run a specific test file +uv run pytest apps/bridge/tests/test_bridge_flow.py -v + +# Run with extra args +just bridge test -k "test_discord_to_irc" +``` + +### Filtering by marker + +pytest markers let you select tests by category: + +```bash +# Only Docker-dependent tests +uv run pytest -m docker + +# Only slow tests +uv run pytest -m slow + +# Exclude slow tests +uv run pytest -m "not slow" + +# IRC protocol tests +uv run pytest -m irc +``` + +Available markers are defined in `pyproject.toml` under `[tool.pytest.ini_options]`. Key markers include: `unit`, `integration`, `docker`, `irc`, `e2e`, `slow`, `network`, `atheme`, `webpanel`, `ssl`, `performance`, `protocol`, and various IRC specification markers (`RFC1459`, `RFC2812`, `IRCv3`). + +## Pytest fixtures + +Fixtures are defined in `tests/conftest.py` (root) and `apps/bridge/tests/conftest.py` (bridge). The root conftest provides a rich set of shared fixtures. + +### Session-scoped fixtures + +These are created once per test session: + +| Fixture | Description | +|---|---| +| `docker_client` | Docker API client (skips if Docker is unavailable) | +| `project_root` / `repo_root` | Monorepo root `Path` | +| `compose_file` | Path to root `compose.yaml` | +| `setup_test_environment` | Sets `TESTING=true` and creates temp directories (autouse) | + +### Function-scoped fixtures + +These are created fresh for each test: + +| Fixture | Description | +|---|---| +| `prepared_config_dir` | Temp directory with copied UnrealIRCd config files | +| `temp_dir` | Clean temporary directory (`tmp_path` alias) | +| `sampl + `controller` | UnrealIRCd controller with Docker container support | +| `mock_requests_get` | Patched `requests.get` returning `{"status": "ok"}` | +| `cleanup_files` | Track and auto-cleanup files/dirs created during tests | +| `test_config` | Parametrised fixture providing `"minimal"` and `"full"` configs | + +### Docker container fixtures + +The root conftest uses `pytest-docker-tools` to build and run containers: + +```python +# Build UnrealIRCd image from apps/unrealircd/Containerfile +unrealircd_image = build( + path="apps/unrealircd", + dockerfile="Containerfile", + tag="ircatlchat-unrealircd:latest", +) + +# Run container with mapped ports and config volume +unrealircd_container = container( + image="{unrealircd_image.id}", + ports={"6697/tcp": None, "8000/tcp": None}, + volumes={"{prepared_config_dir}": {"bind": "/home/unrealircd/unrealircd/config", "mode": "rw"}}, + command=["start"], +) +``` + +Request `unrealircd_container` in your test to get a running UnrealIRCd instance with dynamically mapped ports. + +## Mocking patterns + +### Root test suite + +The root suite uses `pytest-mock` (the `mocker` fixture) for standard mocking: + +```python +def test_docker_status(mock_docker_container): + """mock_docker_container is a pre-configured Mock with .name, .status, .logs().""" + assert mock_docker_container.status == "running" + assert mock_docker_container.logs() == [b"Test log output"] + +def test_http_call(mock_requests_get): + """mock_requests_get patches requests.get globally.""" + import requests + resp = requests.get("http://example.com/health") + assert resp.status_code == 200 +``` + +For IRC-specific testing, the `BaseServerTestCase` class (in `tests/utils/base_test_cases.py`) provides an irctest-style interface with `connectClient()`, `sendLine()`, `getMessage()`, and assertion helpers like `assertMessageMatch()`. + +### Bridge test suite + +The bridge uses a custom mock adapter pattern inspired by [dpytest](https://github.com/CraftSpider/dpytest). Instead of connecting to real services, mock adapters capture events through the same `accept_event`/`push_event` interface: + +```python +# mocks.py — MockAdapter captures events without real connections +class MockAdapter(AdapterBase): + def push_event(self, source: str, evt: object) -> None: + self.received_events.append((source, evt)) + +# harness.py — BridgeTestHarness wires up bus + router + mock adapters +harness = BridgeTestHarness(mappings=[...]) +await harness.start() + +# Simulate a Discord message and verify IRC received it +harness.simulate_discord_message( + channel_id="123456789", + author_id="user123", + author_display="TestUser", + content="Hello from Discord!", +) +assert len(harness.irc.sent_messages) == 1 +assert harness.irc.sent_messages[0].content == "Hello from Discord!" +``` + +This approach is fast (no network), reliable (no flaky connections), and safe (never connects to real services). + +### Property-based testing + +The bridge includes property-based tests using [Hypothesis](https://hypothesis.readthedocs.io/). These live in `apps/bridge/tests/test_property_based.py` and verify properties like message formatting invariants across randomly generated inputs. + +The root `pyproject.toml` includes `hypothesis>=6.151.9` in dev dependencies. + +## Writing new tests + +### Adding a root test + +1. Create a file in the appropriate directory (`tests/unit/`, `tests/integration/`, etc.) +2. Name it `test_*.py` so pytest discovers it +3. Use fixtures from `tests/conftest.py` — request them as function parameters +4. Add markers if the test has special requirements: + +```python +import pytest + +@pytest.mark.unit +def test_config_parsing(sample_config_data): + """Verify config data has expected structure.""" + assert "irc_server" in sample_config_data + assert sample_config_data["irc_server"]["port"] == 6697 +``` + +### Adding a bridge test + +1. Create a file in `apps/bridge/tests/` +2. Use the `BridgeTestHarness` for message routing tests, or test components directly +3. Bridge tests are async — use `@pytest.mark.asyncio`: + +```python +import pytest +from tests.harness import BridgeTestHarness + +@pytest.mark.asyncio +async def test_message_routing(harness: BridgeTestHarness): + await harness.start() + harness.simulate_discord_message( + channel_id="123", author_id="u1", + author_display="User", content="test", + ) + assert len(harness.irc.sent_messages) == 1 + await harness.stop() +``` + +### Running a subset of tests + +```bash +# By keyword match +uv run pytest -k "test_config" tests/ + +# By marker +uv run pytest -m unit tests/ + +# Single file +uv run pytest tests/unit/test_configuration.py + +# Single test function +uv run pytest tests/unit/test_configuration.py::test_specific_function -v + +# Show 10 slowest tests +uv run pytest --durations=10 tests/ +``` + +## CI pipeline integration + +The CI pipeline is defined in `.github/workflows/ci.yml`. It uses path-based filtering so only relevant jobs run when files change. + +### Pipeline structure + +The `changes` job uses `dorny/paths-filter` to detect which areas of the monorepo were modified, then triggers downstream jobs conditionally: + +| Job | Trigger paths | What it does | +|---|---|---| +| `lint-irc` | `apps/unrealircd/**`, `apps/atheme/**`, `tests/**`, `scripts/**` | Shell linting (shellcheck) | +| `lint-xmpp` | `apps/prosody/**` | Lua linting (luacheck) | +| `lint-bridge` | `apps/bridge/**` | `ruff check` + `ruff format --check` | +| `test-bridge` | `apps/bridge/**` | `uv run pytest tests -v --tb=short` | +| `lint-web` | `apps/web/**` | `pnpm run check` (Biome/ultracite) | +| `build-web` | `apps/web/**` | `pnpm run build` (Next.js build) | +| `security-global` | IRC or XMPP changes | Security scanning (Gitleaks, Trivy) | +| `docker-irc` | `apps/unrealircd/**` | Docker image build for UnrealIRCd | +| `docker-xmpp` | `apps/prosody/**` | Docker image build for Prosody | +| `release` | Push to `main` only | Semantic release via `pnpm run release` | + +### What runs on every PR + +When you open a PR against `main` or `develop`, the pipeline: + +1. Detects changed paths +2. Runs lint jobs for affected areas (shell, Lua, Python, TypeScript) +3. Runs `test-bridge` if bridge code changed +4. Builds Docker images if service Dockerfiles changed +5. Builds the web app if web code changed + +### Running CI checks locally + +You can replicate most CI checks locally before pushing: + +```bash +# Bridge lint + test (matches CI exactly) +just bridge check + +# Root lint +just lint + +# Bridge tests +just bridge test + +# Web build +cd apps/web && pnpm run build +``` + +## Pytest configuration + +All pytest settings live in `pyproject.toml` under `[tool.pytest.ini_options]`: + +- `testpaths = ["tests"]` — default test discovery root +- `addopts` — strict markers, short tracebacks, verbose output, colour, duration reporting, auto async mode +- `timeout = 300` — 5-minute timeout per test +- `asyncio_mode = "auto"` — async tests run without explicit event loop setup +- `norecursedirs` — excludes `.git`, `.venv`, `__pycache__`, `data`, and `tests/legacy/**` +- `filterwarnings` — treats warnings as errors except `UserWarning` and `DeprecationWarning` + +### Key dev dependencies + +Test tooling is declared in `pyproject.toml` under `[dependency-groups] dev`: + +| Package | Purpose | +|---|---| +| `pytest` | Test framework | +| `pytest-mock` | `mocker` fixture for patching | +| `pytest-asyncio` | Async test support | +| `pytest-docker` | Docker Compose integration | +| `pytest-docker-tools` | Declarative Docker container fixtures | +| `pytest-xdist` | Parallel test execution (`-n auto`) | +| `pytest-timeout` | Per-test timeout enforcement | +| `pytest-sugar` | Pretty test output | +| `pytest-html` | HTML test reports | +| `hypothesis` | Property-based testing | + + +## Related pages + +- [Contributing](/docs/development/contributing) — PR workflow, commit conventions, and pre-commit hooks +- [Adding a Service](/docs/development/adding-a-service) — checklist for adding a new service including test requirements +- [Bridge Overview](/docs/services/bridge) — bridge architecture (the bridge has its own test suite) diff --git a/apps/docs/content/docs/getting-started/index.mdx b/apps/docs/content/docs/getting-started/index.mdx new file mode 100644 index 00000000..251a5ff5 --- /dev/null +++ b/apps/docs/content/docs/getting-started/index.mdx @@ -0,0 +1,263 @@ +--- +title: Getting Started +description: Prerequisites, install commands, and monorepo orientation for running atl.chat locally. +--- + +This page covers the tools you need to install, how the monorepo is organised, and how to verify everything works — follow it before moving on to the [Local Development](/docs/getting-started/local-development) hands-on guide. + +## Prerequisites + +You need the following tools installed before you can run the stack. A Unix-like shell is required (Linux or macOS; WSL2 on Windows). + +| Tool | Minimum version | Purpose | +|---|---|---| +| [Docker](https://docs.docker.com/engine/install/) | ≥ 24.0 | Container runtime for all services | +| [Docker Compose](https://docs.docker.com/compose/install/) | ≥ 2.20 (v2 plugin) | Multi-container orchestration (`docker compose`, not `docker-compose`) | +| [Node.js](https://nodejs.org/) | ≥ 22.0 | JavaScript runtime for the web app and docs site | +| [pnpm](https://pnpm.io/installation) | ≥ 9.0 | Fast, disk-efficient JS package manager (monorepo workspaces) | +| [Python](https://www.python.org/) | ≥ 3.11 | Required for the bridge service and test suite | +| [uv](https://docs.astral.sh/uv/) | ≥ 0.4 | Fast Python package manager (replaces pip/venv) | +| [just](https://just.systems/) | ≥ 1.25 | Task runner (replaces `make`) | + +### Ubuntu / Debian + +```bash +# Docker (official repo) +sudo apt-get update +sudo apt-get install -y ca-certificates curl gnupg +sudo install -m 0755 -d /etc/apt/keyrings +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg +echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ + https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ + sudo tee /etc/apt/sources.list.d/docker.list > /dev/null +sudo apt-get update +sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin + +# Node.js 22 via NodeSource +curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - +sudo apt-get install -y nodejs + +# pnpm +corepack enable +corepack prepare pnpm@latest --activate + +# Python 3.11+ +sudo apt-get install -y python3 python3-venv + +# uv +curl -LsSf https://astral.sh/uv/install.sh | sh + +# just +curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | sudo bash -s -- --to /usr/local/bin +``` + +### macOS (Homebrew) + +```bash +# Docker Desktop (includes Compose v2) +brew install --cask docker + +# Node.js 22 +brew install node@22 + +# pnpm +corepack enable +corepack prepare pnpm@latest --activate + +# Python 3.11+ +brew install python@3.12 + +# uv +brew install uv + +# just +brew install just +``` + +### Arch Linux + +```bash +# Docker + Compose plugin +sudo pacman -S docker docker-compose +sudo systemctl enable --now docker + +# Node.js 22 +sudo pacman -S nodejs-lts-jod + +# pnpm +corepack enable +corepack prepare pnpm@latest --activate + +# Python 3.11+ +sudo pacman -S python + +# uv +curl -LsSf https://astral.sh/uv/install.sh | sh + +# just +sudo pacman -S just +``` + +### Verify your toolchain + +Run these commands to confirm everything is installed at the right version: + +```bash +docker --version # Docker version 24.x+ +docker compose version # Docker Compose version v2.20+ +node --version # v22.x+ +pnpm --version # 9.x+ +python3 --version # Python 3.11+ +uv --version # uv 0.4+ +just --version # just 1.25+ +``` + +## Monorepo layout + +The repository is a pnpm workspace with Docker Compose orchestration. Each directory under `apps/` is a self-contained service or application: + +``` +atl.chat/ +├── apps/ +│ ├── unrealircd/ # UnrealIRCd 6.x IRC server — the core chat daemon +│ ├── atheme/ # Atheme IRC services — NickServ, ChanServ, OperServ, and more +│ ├── prosody/ # Prosody XMPP server — federated messaging +│ ├── bridge/ # Python Discord↔IRC↔XMPP relay bridge +│ ├── web/ # Next.js web application — the public-facing site +│ ├── webpanel/ # UnrealIRCd admin panel (nginx + JSON-RPC) +│ ├── thelounge/ # The Lounge web IRC client (private mode, WebIRC) +│ ├── gamja/ # Gamja lightweight IRC web client (planned) +│ └── docs/ # This documentation site (Fumadocs on Cloudflare Workers) +│ +├── infra/ +│ ├── compose/ # Docker Compose fragments: irc, xmpp, bridge, cert-manager, networks +│ ├── nginx/ # Nginx reverse proxy config for Prosody HTTPS +│ └── turn-standalone/# TURN/STUN server for XMPP media +│ +├── scripts/ # Automation: init.sh, prepare-config.sh, gencloak-update-env.sh +├── tests/ # Root pytest suite: unit, integration, e2e, protocol tests +├── docs-old/ # Legacy documentation (being migrated into apps/docs) +├── data/ # Runtime data directories (created by just init, git-ignored) +│ +├── compose.yaml # Root Compose file — includes infra/compose/*.yaml fragments +├── justfile # Task runner recipes: just dev, just prod, just test, etc. +├── .env.example # Master environment variable template +├── .env.dev.example # Dev overlay template (localhost domains, self-signed certs) +├── pyproject.toml # Python project config (bridge, tests, linting) +└── pnpm-workspace.yaml # pnpm workspace definition +``` + +### How services interconnect + +UnrealIRCd is the core IRC daemon. Atheme connects to it over a server-to-server link (port 6901) to provide nickname registration, channel management, and other services. The Lounge and Gamja connect as IRC clients via WebSocket (port 8000). The WebPanel talks to UnrealIRCd's JSON-RPC API (port 8600) for administration. + +Prosody runs the XMPP side, handling federated messaging with BOSH/WebSocket endpoints for web clients. + +The Bridge service relays messages between Discord, IRC, and XMPP channels, connecting to all three protocols simultaneously. + +The Web app (Next.js) is the public-facing site. The Docs site (this one) is built with Fumadocs and deployed separately to Cloudflare Workers. + +All services are orchestrated via Docker Compose, sharing a common Docker network. Configuration is templated from `.env` variables using `envsubst` during `just init`. + +## Verification checklist + +After running `just dev` (covered in the [Local Development](/docs/getting-started/local-development) guide), verify each service is running: + +```bash +# Check all containers are up +docker compose ps + +# IRC server (UnrealIRCd) — TLS on port 6697 +docker compose logs atl-irc-server | tail -5 + +# IRC services (Atheme) — linked to UnrealIRCd +docker compose logs atl-irc-services | tail -5 + +# XMPP server (Prosody) — client port 5222 +docker compose logs atl-xmpp-server | tail -5 + +# Bridge — Discord↔IRC↔XMPP relay +docker compose logs atl-bridge | tail -5 + +# The Lounge — web IRC client on port 9000 +curl -s -o /dev/null -w "%{http_code}" http://localhost:9000 + +# WebPanel — admin panel on port 8080 +curl -s -o /dev/null -w "%{http_code}" http://localhost:8080 +``` + +You should see all containers in a `running` state and HTTP 200 responses from The Lounge and WebPanel. + +## Common first-run issues + +### 1. `CLOUDFLARE_DNS_API_TOKEN` warning from Docker Compose + +``` +WARN[0000] The "CLOUDFLARE_DNS_API_TOKEN" variable is not set. Defaulting to a blank string. +``` + +This is safe to ignore in local development. The cert-manager service uses this token for DNS-01 challenges in production. Locally, `just init` generates self-signed certificates instead. + +### 2. pnpm build scripts warning (esbuild/sharp/workerd) + +``` + WARN build scripts are blocked by default +``` + +This warning appears during `pnpm install` because esbuild, sharp, and workerd have native build scripts that pnpm blocks by default. These packages include pre-built fallback binaries, so the warning is non-blocking. Your install will complete successfully. + +### 3. `core.hooksPath` conflict with pre-commit + +If `pre-commit install` fails or hooks do not run, your Git config may have a `core.hooksPath` set by another tool. Fix it with: + +```bash +git config --unset-all core.hooksPath +pre-commit install +``` + +### 4. Integration tests timing out + +Tests in `tests/integration/` and `tests/e2e/` build fresh Docker images and can time out in constrained environments (CI runners, VMs with limited resources). For quick validation, run unit tests instead: + +```bash +# Fast — no Docker needed +uv run pytest tests/unit/ +uv run pytest apps/bridge/tests/ +``` + +### 5. `apps/web` lint failure (biome `.gitignore` issue) + +Running `ultracite check` in `apps/web/` currently fails because the Biome config expects a `.gitignore` file in that directory. This is a known pre-existing issue. The Next.js build (`pnpm run build`) works fine — the lint failure does not affect functionality. + +### 6. Docker daemon not running + +If `docker compose` commands fail with "Cannot connect to the Docker daemon", start the daemon: + +```bash +# Linux (systemd) +sudo systemctl start docker + +# Linux (manual, e.g. in a VM) +sudo dockerd &>/tmp/dockerd.log & + +# macOS — open Docker Desktop from Applications +``` + +### 7. Port conflicts with existing services + +If a service fails to start with "address already in use", another process is occupying that port. Check what is listening: + +```bash +# Find the process using a port (e.g. 6697) +sudo lsof -i :6697 +# or +sudo ss -tlnp | grep 6697 +``` + +Stop the conflicting process or change the port in your `.env` file. + +## Next steps + +- Follow the [Local Development](/docs/getting-started/local-development) guide to clone, configure, and bring up the full stack. +- Read the [Architecture Overview](/docs/architecture) to understand how services communicate. +- See the [Environment Variables Reference](/docs/reference/environment-variables) for the complete list of configuration options. diff --git a/apps/docs/content/docs/getting-started/local-development.mdx b/apps/docs/content/docs/getting-started/local-development.mdx new file mode 100644 index 00000000..5f19710b --- /dev/null +++ b/apps/docs/content/docs/getting-started/local-development.mdx @@ -0,0 +1,182 @@ +--- +title: Local Development +description: Full walkthrough for bringing up the atl.chat stack on your local machine. +--- + +This guide walks you through cloning the repo, configuring environment variables, and starting all services with a single command. + +## Clone and install + +1. Clone the repository and install Node.js dependencies: + + ```bash + git clone https://github.com/allthingslinux/atl.chat.git + cd atl.chat + pnpm install + ``` + +2. Install Python dependencies with uv: + + ```bash + # Install Python tooling (pytest, pre-commit, etc.) + uv sync + ``` + +## Environment setup + +Copy the example env files and customise values for local development: + +```bash +# Main environment file +cp .env.example .env + +# Dev overlay (overrides domains to localhost) +cp .env.dev.example .env.dev +``` + +Key variables to set for local development: + +| Variable | Description | +|---|---| +| `IRC_DOMAIN` | IRC server hostname (defaults to `irc.localhost` in `.env.dev`) | +| `PROSODY_DOMAIN` | XMPP server domain (defaults to `xmpp.localhost` in `.env.dev`) | +| `DISCORD_TOKEN` | Discord bot token for the bridge (optional for IRC/XMPP-only testing) | + +See the full [Environment Variables reference](/docs/reference/environment-variables) for all available options. + +## First-time initialisation + +Run the init recipe to create data directories, generate self-signed TLS certificates, and substitute config templates: + +```bash +just init +``` + +This runs `scripts/init.sh`, which: +- Creates all `data/` subdirectories for persistent storage +- Generates self-signed TLS certificates for local development +- Runs `envsubst` on config templates to produce final config files + +## Starting the stack + +```bash +just dev +``` + +This starts all Docker Compose services with the dev profile. Verify the stack is running: + +```bash +just status +``` + +## Starting the web app + +The Next.js web app runs outside Docker as a local Node process: + +```bash +cd apps/web +NEXT_PUBLIC_IRC_WS_URL="ws://localhost:8000" \ +NEXT_PUBLIC_XMPP_BOSH_URL="http://localhost:5280/http-bind" \ +pnpm dev +``` + +The web interface is available at `http://localhost:3000`. + +## Verification checklist + +After `just dev` completes, verify each service is running: + +1. Check container status: + + ```bash + just status + ``` + +2. Test IRC connectivity (TLS on port 6697): + + ```bash + openssl s_client -connect localhost:6697 -servername irc.localhost /dev/null | head -5 + ``` + +3. Test XMPP connectivity (C2S on port 5222): + + ```bash + curl -s http://localhost:5280/http-bind + ``` + +4. Test The Lounge web client (port 9000): + + ```bash + curl -s -o /dev/null -w "%{http_code}" http://localhost:9000 + # Expected: 200 + ``` + +5. Test WebPanel (port 8080): + + ```bash + curl -s -o /dev/null -w "%{http_code}" http://localhost:8080 + # Expected: 200 + ``` + +## Useful just recipes + +| Recipe | Description | +|---|---| +| `just dev` | Start all services with dev profile | +| `just down` | Stop the dev stack | +| `just logs [service]` | Follow logs (optionally for a specific service) | +| `just status` | Show container status | +| `just lint` | Run all pre-commit hooks | +| `just test` | Run unit tests | +| `just test-all` | Run all tests including bridge tests | + +## Common first-run issues + +### Docker daemon not running + +If you see "Cannot connect to the Docker daemon", start Docker first: + +```bash +sudo dockerd &>/tmp/dockerd.log & +``` + +### Port already in use + +If a port is already bound, identify the process and stop it: + +```bash +# Find what is using port 6697 +sudo lsof -i :6697 +``` + +### CLOUDFLARE_DNS_API_TOKEN warning + +Docker Compose emits a warning about this unset variable. You can safely ignore it in development — the cert-manager service is not needed locally. + +### pnpm install build scripts warning + +You may see warnings about blocked build scripts for esbuild, sharp, or workerd. These packages have fallback binaries and the warning is non-blocking. + +### pre-commit hooks fail to install + +If `core.hooksPath` is set by your environment, unset it first: + +```bash +git config --unset-all core.hooksPath +uv run pre-commit install +``` + +### The Lounge shows login page but no users exist + +The Lounge runs in private mode. You need to create a user before you can log in: + +```bash +just lounge add +``` + +## Related pages + +- [Getting Started](/docs/getting-started) — prerequisites, monorepo layout, and verification checklist +- [Architecture Overview](/docs/architecture) — system diagram and design decisions +- [Environment Variables](/docs/reference/environment-variables) — complete variable reference +- [Troubleshooting](/docs/operations/troubleshooting) — cross-service diagnostic commands and common issues diff --git a/apps/docs/content/docs/getting-started/meta.json b/apps/docs/content/docs/getting-started/meta.json new file mode 100644 index 00000000..f147c4c7 --- /dev/null +++ b/apps/docs/content/docs/getting-started/meta.json @@ -0,0 +1,7 @@ +{ + "title": "Getting Started", + "pages": [ + "index", + "local-development" + ] +} diff --git a/apps/docs/content/docs/index.mdx b/apps/docs/content/docs/index.mdx new file mode 100644 index 00000000..f55197a2 --- /dev/null +++ b/apps/docs/content/docs/index.mdx @@ -0,0 +1,47 @@ +--- +title: atl.chat Documentation +description: Unified chat infrastructure combining IRC, XMPP, Discord, and a web interface. +--- + +atl.chat documentation covers everything you need to deploy, operate, and contribute to a self-hosted unified chat platform that bridges IRC, XMPP, and Discord into a single community experience. + +## What is atl.chat? + +atl.chat is an open-source monorepo that provides unified chat infrastructure for the [All Things Linux](https://allthingslinux.org) community. It combines an IRC server ([UnrealIRCd](/docs/services/irc) 6.x with [Atheme](/docs/services/atheme) services), an XMPP server ([Prosody](/docs/services/xmpp)), a [Discord↔IRC↔XMPP bridge](/docs/services/bridge), web IRC clients ([The Lounge](/docs/services/thelounge), Gamja), an admin panel ([WebPanel](/docs/services/webpanel)), and a Next.js [web application](/docs/services/web) — all orchestrated with Docker Compose and managed through the [`just` task runner](/docs/reference/glossary). + +The project lets community members participate from whichever protocol they prefer while keeping conversations synchronised across all platforms. + +## Project status + +> **Note:** atl.chat is currently v0.1.0 alpha and not yet production-ready. APIs and configuration formats may change without notice. + +## Choose your path + +Pick the path that matches your role to find the most relevant documentation. + +### Developer / Contributor + +If you want to build features, fix bugs, or extend the platform: + +1. [Getting Started](/docs/getting-started) — install prerequisites and spin up the local dev stack +2. [Architecture](/docs/architecture) — understand the system design, networking, and data model +3. [Development](/docs/development/contributing) — learn the PR workflow, code style, and testing practices +4. [Services](/docs/services/irc) — dive into individual service internals + +### Operator / Sys Admin + +If you want to deploy, configure, and maintain the stack in production: + +1. [Getting Started](/docs/getting-started) — install prerequisites and verify your environment +2. [Operations](/docs/operations/deployment) — production deployment, SSL/TLS, backups, monitoring, and security +3. [Services](/docs/services/irc) — per-service configuration and operational guides +4. [Reference](/docs/reference/environment-variables) — environment variables, ports, and API reference + +## Quick links + +- [Getting Started](/docs/getting-started) — prerequisites, local development setup, and first-run guide +- [Architecture](/docs/architecture) — system diagram, design decisions, networking, and data model +- [Services](/docs/services/irc) — deep dives into IRC, Atheme, XMPP, Bridge, Web, The Lounge, and WebPanel +- [Operations](/docs/operations/deployment) — deployment, SSL/TLS, backups, monitoring, troubleshooting, and security +- [Development](/docs/development/contributing) — contributing guide, testing, and adding new services +- [Reference](/docs/reference/environment-variables) — environment variables, ports, API reference, glossary, and FAQ diff --git a/apps/docs/content/docs/meta.json b/apps/docs/content/docs/meta.json new file mode 100644 index 00000000..84e3a6ab --- /dev/null +++ b/apps/docs/content/docs/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Documentation", + "pages": [ + "index", + "getting-started", + "architecture", + "services", + "operations", + "development", + "reference" + ] +} diff --git a/apps/docs/content/docs/operations/backups.mdx b/apps/docs/content/docs/operations/backups.mdx new file mode 100644 index 00000000..1bf353d9 --- /dev/null +++ b/apps/docs/content/docs/operations/backups.mdx @@ -0,0 +1,458 @@ +--- +title: Backups +description: Per-service backup and restore procedures for all atl.chat stateful services. +--- + +This page documents how to back up and restore every stateful service in the atl.chat stack, along with retention recommendations and a pre-upgrade checklist. + +## Services with persistent state + +All persistent data lives under the `data/` directory at the repository root, created by `scripts/init.sh`. Each subdirectory maps to Docker volume mounts in the compose files. + +| Service | Data location | What's stored | +|---|---|---| +| UnrealIRCd | `data/irc/data/` | Runtime data, RPC socket, persistent channel DB | +| UnrealIRCd | `data/irc/logs/` | IRC server logs | +| WebPanel | `data/irc/webpanel-data/` | WebPanel session and configuration data | +| Atheme | `data/atheme/data/` | Services database (`services.db`) — NickServ, ChanServ registrations | +| Atheme | `data/atheme/logs/` | Atheme service logs | +| Prosody | `data/xmpp/data/` | XMPP user accounts, rosters, MUC history | +| Prosody | `data/xmpp/logs/` | XMPP server logs | +| Prosody | `data/xmpp/uploads/` | HTTP file upload storage | +| The Lounge | `data/thelounge/` | User configs, chat history, installed plugins | +| TLS Certificates | `data/certs/` | Private keys and certificates (shared by IRC and XMPP) | + +> **Warning:** The `data/certs/` directory contains TLS private keys. Handle backups of this directory with the same care as any secret material. + +## What to back up + +### Critical (always back up) + +- **Atheme database** (`data/atheme/data/services.db`) — contains all user and channel registrations. Losing this means every NickServ and ChanServ registration is gone. +- **TLS certificates** (`data/certs/`) — private keys and fullchain certificates for IRC and XMPP domains. +- **Environment configuration** (`.env`) — all passwords, tokens, and domain settings. + +### Recommended + +- **Prosody data** (`data/xmpp/data/`) — XMPP user accounts and rosters. +- **The Lounge data** (`data/thelounge/`) — user accounts and chat history. +- **UnrealIRCd data** (`data/irc/data/`) — runtime data and persistent channel DB. +- **WebPanel data** (`data/irc/webpanel-data/`) — session data. + +### Optional + +- **Logs** (`data/irc/logs/`, `data/atheme/logs/`, `data/xmpp/logs/`) — useful for auditing but can be regenerated. +- **XMPP uploads** (`data/xmpp/uploads/`) — user-uploaded files; may be large. + +## Backup procedures + +### Full stack backup (recommended) + +Stop services first to ensure data consistency, then archive the entire `data/` directory: + +```bash +# 1. Stop all services +docker compose down + +# 2. Create a timestamped backup +tar -czf "backup-$(date +%Y%m%d-%H%M%S).tar.gz" data/ .env + +# 3. Restart services +just dev # or: just prod +``` + +This captures every stateful directory in a single archive. + +### Live backup (no downtime) + +If you cannot afford downtime, back up individual directories while services are running. This is safe for most services but may produce inconsistent snapshots for Atheme if a write is in progress. + +```bash +mkdir -p backup/$(date +%Y%m%d) + +# Atheme database (most critical) +cp data/atheme/data/services.db "backup/$(date +%Y%m%d)/atheme-services.db" + +# TLS certificates +cp -r data/certs "backup/$(date +%Y%m%d)/certs" + +# Environment config +cp .env "backup/$(date +%Y%m%d)/.env" + +# Prosody data +tar -czf "backup/$(date +%Y%m%d)/xmpp-data.tar.gz" data/xmpp/data/ + +# The Lounge data +tar -czf "backup/$(date +%Y%m%d)/thelounge.tar.gz" data/thelounge/ + +# UnrealIRCd data +tar -czf "backup/$(date +%Y%m%d)/irc-data.tar.gz" data/irc/data/ +``` + +### Per-service backup commands + +#### Atheme (IRC services) + +The Atheme database at `data/atheme/data/services.db` is the single most critical file. Atheme writes to this file periodically and on shutdown. + +```bash +# Safest: stop Atheme, copy, restart +docker compose stop atl-irc-services +cp data/atheme/data/services.db "atheme-backup-$(date +%Y%m%d).db" +docker compose start atl-irc-services +``` + +For a live copy (slightly less safe): + +```bash +cp data/atheme/data/services.db "atheme-backup-$(date +%Y%m%d).db" +``` + +#### Prosody (XMPP) + +Prosody stores data in flat files under `data/xmpp/data/`. A filesystem copy is sufficient: + +```bash +tar -czf "prosody-backup-$(date +%Y%m%d).tar.gz" data/xmpp/data/ +``` + +To include uploaded files: + +```bash +tar -czf "prosody-full-backup-$(date +%Y%m%d).tar.gz" data/xmpp/data/ data/xmpp/uploads/ +``` + +#### The Lounge + +The Lounge stores user configs and chat logs in `data/thelounge/`: + +```bash +tar -czf "thelounge-backup-$(date +%Y%m%d).tar.gz" data/thelounge/ +``` + +#### UnrealIRCd + +UnrealIRCd runtime data lives in `data/irc/data/`. Most state is ephemeral (connections, channels without persistent mode), but the directory may contain persistent channel DB files: + +```bash +tar -czf "irc-backup-$(date +%Y%m%d).tar.gz" data/irc/data/ +``` + +#### TLS certificates + +```bash +tar -czf "certs-backup-$(date +%Y%m%d).tar.gz" data/certs/ +``` + +> **Warning:** Store certificate backups securely — they contain private keys. + +#### WebPanel + +```bash +tar -czf "webpanel-backup-$(date +%Y%m%d).tar.gz" data/irc/webpanel-data/ +``` + +## Restore procedures + +### Full stack restore + +1. Stop all services: + + ```bash + docker compose down + ``` + +2. Extract the backup archive: + + ```bash + tar -xzf backup-YYYYMMDD-HHMMSS.tar.gz + ``` + + This restores `data/` and `.env` to the repository root. + +3. Fix permissions (required after restoring from a different machine): + + ```bash + # Re-run init to set correct ownership + bash scripts/init.sh + ``` + +4. Start services: + + ```bash + just dev # or: just prod + ``` + +5. Verify services are running: + + ```bash + just status + ``` + +### Per-service restore + +#### Atheme + +1. Stop Atheme: + + ```bash + docker compose stop atl-irc-services + ``` + +2. Replace the database file: + + ```bash + cp atheme-backup-YYYYMMDD.db data/atheme/data/services.db + ``` + +3. Fix ownership: + + ```bash + sudo chown $(id -u):$(id -g) data/atheme/data/services.db + ``` + +4. Start Atheme: + + ```bash + docker compose start atl-irc-services + ``` + +5. Verify Atheme is running: + + ```bash + docker logs atl-irc-services --tail 20 + ``` + + You should see Atheme loading the database and connecting to UnrealIRCd. + +#### Prosody + +1. Stop Prosody: + + ```bash + docker compose stop atl-xmpp-server + ``` + +2. Restore data: + + ```bash + tar -xzf prosody-backup-YYYYMMDD.tar.gz + ``` + +3. Start Prosody: + + ```bash + docker compose start atl-xmpp-server + ``` + +4. Verify: + + ```bash + docker logs atl-xmpp-server --tail 20 + ``` + +#### The Lounge + +1. Stop The Lounge: + + ```bash + docker compose stop atl-thelounge + ``` + +2. Restore data: + + ```bash + tar -xzf thelounge-backup-YYYYMMDD.tar.gz + ``` + +3. Start The Lounge: + + ```bash + docker compose start atl-thelounge + ``` + +4. Verify by opening `http://localhost:9000` in your browser. + +#### TLS certificates + +1. Restore certificates: + + ```bash + tar -xzf certs-backup-YYYYMMDD.tar.gz + ``` + +2. Reload services that use TLS: + + ```bash + # UnrealIRCd picks up certs on rehash + docker exec atl-irc-server unrealircd rehash + + # Prosody reloads on SIGHUP + docker exec atl-xmpp-server prosodyctl reload + ``` + +## Automated backup script + +Save this as `scripts/backup.sh` and run it via cron: + +```bash +#!/bin/bash +# atl.chat automated backup script +set -e + +BACKUP_DIR="${BACKUP_DIR:-/var/backups/atl-chat}" +TIMESTAMP=$(date +%Y%m%d-%H%M%S) +DEST="$BACKUP_DIR/$TIMESTAMP" + +mkdir -p "$DEST" + +# Critical data (always) +cp data/atheme/data/services.db "$DEST/atheme-services.db" +cp -r data/certs "$DEST/certs" +cp .env "$DEST/.env" + +# Recommended data +tar -czf "$DEST/xmpp-data.tar.gz" data/xmpp/data/ +tar -czf "$DEST/thelounge.tar.gz" data/thelounge/ +tar -czf "$DEST/irc-data.tar.gz" data/irc/data/ + +echo "Backup completed: $DEST" +``` + +Schedule it with cron: + +```bash +# Daily backup at 3:00 AM +0 3 * * * cd /path/to/atl-chat && bash scripts/backup.sh +``` + +## Retention policy + +A recommended retention schedule balances storage cost with recovery flexibility: + +| Tier | Frequency | Retention | Purpose | +|---|---|---|---| +| Daily | Every day | Keep 7 days | Quick recovery from recent issues | +| Weekly | Every Sunday | Keep 4 weeks | Recovery from issues noticed late | +| Monthly | First of month | Keep 12 months | Long-term recovery, compliance | + +### Cleanup script + +Add this to your cron or run manually: + +```bash +#!/bin/bash +# Remove backups older than retention policy +BACKUP_DIR="${BACKUP_DIR:-/var/backups/atl-chat}" + +# Keep daily backups for 7 days +find "$BACKUP_DIR" -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \; + +# For weekly/monthly, tag directories or use separate paths +``` + +> **Tip:** For production deployments, consider pushing backups to offsite storage (S3, B2, rsync to a remote host) so you are protected against host-level failures. + +### Test your restores + +Schedule a monthly restore test on a staging environment. A backup you have never tested restoring is not a backup — it is a hope. + +## Pre-upgrade backup checklist + +Run through this checklist before upgrading any service or running `docker compose pull`: + +1. **Check current service status:** + + ```bash + just status + ``` + +2. **Back up the full data directory:** + + ```bash + tar -czf "pre-upgrade-$(date +%Y%m%d).tar.gz" data/ .env + ``` + +3. **Record current image versions** (so you can roll back): + + ```bash + docker compose images + ``` + +4. **Back up compose and config files** in case templates change: + + ```bash + tar -czf "pre-upgrade-config-$(date +%Y%m%d).tar.gz" \ + apps/unrealircd/config/ \ + apps/atheme/config/ \ + apps/prosody/config/ \ + infra/compose/ + ``` + +5. **Verify the backup is readable:** + + ```bash + tar -tzf "pre-upgrade-$(date +%Y%m%d).tar.gz" | head -20 + ``` + +6. **Proceed with the upgrade.** If something goes wrong, restore from the archive and restart: + + ```bash + docker compose down + tar -xzf "pre-upgrade-$(date +%Y%m%d).tar.gz" + bash scripts/init.sh + just dev # or: just prod + ``` + +## Troubleshooting + +### Backup archive is empty or incomplete + +Check disk space before creating backups: + +```bash +df -h . +du -sh data/ +``` + +### Restored service fails to start + +Check file permissions. The `scripts/init.sh` script sets correct ownership: + +```bash +bash scripts/init.sh +``` + +Then restart: + +```bash +just dev +``` + +### Atheme database corruption + +If Atheme refuses to load a restored `services.db`, check the logs: + +```bash +docker logs atl-irc-services --tail 50 +``` + +Atheme creates periodic backups in `data/atheme/data/` with `.bak` extensions. Check if a `.bak` file is available as a fallback. + +### Verify backup integrity + +```bash +# Check tar archive is valid +tar -tzf backup-file.tar.gz > /dev/null && echo "Archive OK" || echo "Archive corrupt" +``` + + +## Related pages + +- [Deployment](/docs/operations/deployment) — production deployment runbook +- [Monitoring](/docs/operations/monitoring) — health checks and alerting +- [Troubleshooting](/docs/operations/troubleshooting) — cross-service diagnostic commands +- [Security](/docs/operations/security) — secret management and credential rotation +- [SSL/TLS](/docs/operations/ssl-tls) — certificate backup and renewal +- [Atheme Operations](/docs/services/atheme/operations) — Atheme database management +- [Data Model](/docs/architecture/data-model) — persistent data locations and volume mounts diff --git a/apps/docs/content/docs/operations/deployment.mdx b/apps/docs/content/docs/operations/deployment.mdx new file mode 100644 index 00000000..6bbc856f --- /dev/null +++ b/apps/docs/content/docs/operations/deployment.mdx @@ -0,0 +1,443 @@ +--- +title: Deployment +description: Complete production deployment runbook covering server topology, Tailscale networking, Docker Compose configuration, and rollback procedures. +--- + +This page is the production deployment runbook for atl.chat — it covers server topology, Tailscale setup, pre-deployment checks, deployment commands, verification, and rollback. + +## Server Topology + +The production stack runs across two servers connected via a Tailscale overlay network. An external Nginx Proxy Manager instance handles TLS termination and reverse proxying for all public-facing services. + +| Server | Tailscale IP | Role | What runs here | +|---|---|---|---| +| Chat (Server A) | `ATL_CHAT_IP` (e.g. `100.64.7.0`) | Primary | All Docker services: UnrealIRCd, Atheme, Prosody, Bridge, The Lounge, WebPanel, cert-manager | +| Gateway (Server B) | `ATL_GATEWAY_IP` (e.g. `100.64.1.0`) | Reverse proxy | Nginx Proxy Manager (TLS termination — not part of this compose stack) | + +All Docker services run on Server A using the `atl-chat` bridge network. IRC ports bind to `ATL_CHAT_IP` so they are only reachable via Tailscale. Atheme shares the UnrealIRCd container's network namespace (`network_mode: service:atl-irc-server`) so it communicates with UnrealIRCd over `127.0.0.1`. The Nginx Proxy Manager on Server B reverse-proxies public traffic over Tailscale to Server A's services. `ATL_GATEWAY_IP` is passed to UnrealIRCd so it can identify the gateway's Tailscale IP in its configuration. + +```mermaid +graph LR + Internet["Internet"] --> NPM["Nginx Proxy Manager
Server B (ATL_GATEWAY_IP)
TLS termination"] + NPM -->|Tailscale| IRC["UnrealIRCd
6697, 8000, 8600"] + NPM -->|Tailscale| XMPP["Prosody
5222, 5280, 5281"] + NPM -->|Tailscale| Lounge["The Lounge
9000"] + NPM -->|Tailscale| Panel["WebPanel
8080"] + + subgraph Docker["Server A (ATL_CHAT_IP) — Docker Host (atl-chat network)"] + IRC --- Atheme["Atheme
(shared netns)"] + IRC --- Bridge["Bridge"] + XMPP --- Bridge + XMPP --- Nginx["XMPP Nginx
5281"] + end +``` + +## Tailscale Setup + +Tailscale provides encrypted mesh networking between servers. This means internal services are never exposed on public IPs — only the Tailscale interface carries inter-server traffic. + +### Installing Tailscale on a New Server + +1. Install Tailscale: + + ```bash + # Ubuntu/Debian + curl -fsSL https://tailscale.com/install.sh | sh + ``` + +2. Authenticate and bring the node up: + + ```bash + sudo tailscale up + ``` + +3. Note the assigned Tailscale IP: + + ```bash + tailscale ip -4 + # Example output: 100.64.7.0 + ``` + +4. Update `.env` on the deployment host with the Tailscale IPs: + + ```bash + # .env (production) + ATL_GATEWAY_IP=100.64.1.0 + ATL_CHAT_IP=100.64.7.0 + ``` + +### Firewall Rules + +On each server, only the Tailscale UDP port needs to be open between hosts. Public-facing ports are handled by Nginx Proxy Manager on the gateway. + +```bash +# Allow Tailscale +sudo ufw allow 41641/udp + +# Allow SSH (for management) +sudo ufw allow 22/tcp + +# On the gateway: allow public HTTPS +sudo ufw allow 443/tcp +``` + +## Docker Compose Production Configuration + +Production uses `just prod`, which runs `scripts/init.sh` with `ATL_ENVIRONMENT=prod` and then starts Docker Compose with only the `.env` file (no `.env.dev` overlay). + +The root `compose.yaml` includes all service fragments from `infra/compose/`: + +| Fragment | Services | +|---|---| +| `infra/compose/networks.yaml` | `atl-chat` bridge network | +| `infra/compose/irc.yaml` | `atl-irc-server` (UnrealIRCd), `atl-irc-services` (Atheme), `atl-irc-webpanel` | +| `infra/compose/xmpp.yaml` | `atl-xmpp-server` (Prosody), `atl-xmpp-nginx` (HTTPS proxy) | +| `infra/compose/bridge.yaml` | `atl-bridge` (Discord↔IRC↔XMPP) | +| `infra/compose/thelounge.yaml` | `atl-thelounge` (web IRC client) | +| `infra/compose/cert-manager.yaml` | `cert-manager` (Lego / Let's Encrypt) | + +### Key Differences: Production vs Development + +| Aspect | Production (`just prod`) | Development (`just dev`) | +|---|---|---| +| Env files loaded | `.env` only | `.env` + `.env.dev` | +| Port binding IP | `ATL_CHAT_IP` (Tailscale IP) | `127.0.0.1` | +| Domains | `irc.atl.chat`, `atl.chat` | `irc.localhost`, `xmpp.localhost` | +| TLS certificates | Let's Encrypt (cert-manager) | Self-signed (generated by `init.sh`) | +| TLS verification | Enforced | Disabled (`BRIDGE_IRC_TLS_VERIFY=false`) | +| Prosody encryption | Required (`PROSODY_S2S_SECURE_AUTH=true`) | Relaxed for self-signed certs | +| Dozzle log viewer | Not started (no `--profile dev`) | Started on port 8082 | +| `ATL_ENVIRONMENT` | `prod` | `dev` | + +## Pre-Deployment Checklist + +Complete these checks before deploying to production: + +1. **Environment file configured** — `.env` exists with production values: + + ```bash + # Verify .env exists and has production domains + grep -E '^IRC_DOMAIN=' .env + # Expected: IRC_DOMAIN=irc.atl.chat + grep -E '^ATL_ENVIRONMENT=' .env + # Expected: ATL_ENVIRONMENT=prod (or will be set by just prod) + ``` + +2. **Secrets rotated** — all default passwords have been changed: + + ```bash + # Check for unchanged default passwords + grep -c 'change_me' .env + # Expected: 0 (all secrets should be replaced with real values) + ``` + + See [Security — Secret Management](/docs/operations/security) for generation commands. + +3. **Tailscale connected** — both servers are on the Tailscale network: + + ```bash + tailscale status + # Verify both gateway and chat hosts appear + ``` + +4. **DNS records configured** — public DNS points to the gateway's public IP: + + ```bash + dig +short irc.atl.chat + dig +short _irc._tcp.atl.chat SRV + ``` + + See [Architecture — Networking](/docs/architecture/networking) for the full DNS zone layout. + +5. **TLS certificates ready** — Let's Encrypt certificates exist in `data/certs/`: + + ```bash + ls data/certs/live/irc.atl.chat/ + # Expected: fullchain.pem privkey.pem + ``` + + If certificates are not yet provisioned, cert-manager will attempt to obtain them on first start. Ensure `CLOUDFLARE_DNS_API_TOKEN` is set in `.env` for DNS-01 challenges. + +6. **Docker available** — Docker daemon is running and compose plugin is installed: + + ```bash + docker compose version + # Expected: Docker Compose version v2.x.x + ``` + +## Deployment Commands + +### First-Time Deployment + + +For a brand-new server, run the full initialization and startup sequence: + +1. Clone the repository: + + ```bash + git clone https://github.com/allthingslinux/atl.chat.git + cd atl.chat + ``` + +2. Create and configure the environment file: + + ```bash + cp .env.example .env + ``` + + Edit `.env` with production values — set real domains, rotate all secrets (anything containing `change_me`), and configure Tailscale IPs. See [Environment Variables](/docs/reference/environment-variables) for the full reference. + +3. Run the production startup command: + + ```bash + just prod + ``` + + This command does two things: + - Runs `scripts/init.sh` with `ATL_ENVIRONMENT=prod` — creates `data/` directories, sets permissions, sets up the CA bundle, generates config files from templates via `envsubst`, and runs `scripts/prepare-config.sh` + - Starts all Docker Compose services: `docker compose --env-file .env up -d` + +4. Verify all containers are running: + + ```bash + just status + ``` + +### Updating an Existing Deployment + +To deploy updates (new code, config changes, or image rebuilds): + +1. Pull the latest code: + + ```bash + git pull origin main + ``` + +2. Rebuild images if Dockerfiles or application code changed: + + ```bash + just build + ``` + +3. Re-run the production startup to regenerate configs and restart services: + + ```bash + just prod + ``` + + This is safe to run repeatedly — `init.sh` is idempotent (it skips directories and certs that already exist). + +### Restarting Individual Services + +To restart a single service without affecting others: + +```bash +# Restart UnrealIRCd only +docker compose restart atl-irc-server + +# Restart Prosody only +docker compose restart atl-xmpp-server + +# Restart the bridge +docker compose restart atl-bridge +``` + +For IRC configuration changes that do not require a full restart, use a rehash instead: + +```bash +just irc reload +``` + +## Post-Deployment Verification + +After deploying, verify each service is healthy: + +### 1. Check Container Status + +```bash +just status +# All containers should show "Up" with "(healthy)" status +``` + +### 2. Verify UnrealIRCd + +```bash +# Check the health endpoint via JSON-RPC socket +docker exec atl-irc-server sh -c \ + 'echo "{\"jsonrpc\":\"2.0\",\"method\":\"rpc.info\",\"params\":{},\"id\":1}" | nc -U /home/unrealircd/unrealircd/data/rpc.socket' + +# Test TLS connectivity from outside +openssl s_client -connect irc.atl.chat:6697 -servername irc.atl.chat /dev/null | head -5 +``` + +### 3. Verify Atheme + +```bash +# Confirm the process is running (Atheme shares UnrealIRCd's network namespace) +docker exec atl-irc-services pgrep -f atheme-services +# Expected: a PID number + +# Check Atheme logs for successful link +docker compose logs atl-irc-services --tail 20 +# Look for: "connection to uplink established" +``` + +### 4. Verify Prosody + +```bash +# HTTP health check +curl -sf http://localhost:5280/status +# Expected: HTTP 200 + +# Check XMPP C2S connectivity +openssl s_client -connect atl.chat:5222 -starttls xmpp -xmpphost atl.chat /dev/null | head -5 +``` + +### 5. Verify The Lounge + +```bash +# Check the web interface is responding +curl -sf -o /dev/null -w '%{http_code}' http://localhost:9000 +# Expected: 200 +``` + +### 6. Verify the Bridge + +```bash +# Check bridge process is running +docker exec atl-bridge pgrep -f 'bridge.__main__' +# Expected: a PID number + +# Check bridge logs for successful connections +docker compose logs atl-bridge --tail 20 +# Look for: successful IRC and XMPP connection messages +``` + +### 7. Verify WebPanel + +```bash +# Check the web interface +curl -sf -o /dev/null -w '%{http_code}' http://localhost:8080 +# Expected: 200 +``` + +### 8. Verify cert-manager + +```bash +# Check certificate files exist +ls -la data/certs/live/irc.atl.chat/ +# Expected: fullchain.pem, privkey.pem with recent timestamps + +# Check cert-manager logs +docker compose logs cert-manager --tail 20 +``` + +## Rollback Procedure + +If a deployment causes issues, roll back to the previous working state: + +### Quick Rollback (Code Revert) + +1. Stop the current stack: + + ```bash + just down-prod + ``` + +2. Revert to the previous commit: + + ```bash + git log --oneline -5 + # Identify the last known-good commit + git checkout + ``` + +3. Rebuild and restart: + + ```bash + just build + just prod + ``` + +4. Verify services are healthy using the [post-deployment checks](#post-deployment-verification) above. + +### Data Rollback + +If the issue involves corrupted data rather than code, restore from backups: + +1. Stop the affected service: + + ```bash + docker compose stop atl-irc-server atl-irc-services + ``` + +2. Restore the data directory from your most recent backup: + + ```bash + # Example: restore IRC data + cp -a /path/to/backup/data/irc/data/* data/irc/data/ + cp -a /path/to/backup/data/atheme/data/* data/atheme/data/ + ``` + +3. Restart the service: + + ```bash + docker compose start atl-irc-server atl-irc-services + ``` + +See [Backups](/docs/operations/backups) for detailed backup and restore procedures. + +### Configuration Rollback + +If a configuration change (`.env` or template) caused the issue: + +1. Restore the previous `.env`: + + ```bash + cp .env.backup .env + ``` + +2. Regenerate configs and restart: + + ```bash + just prod + ``` + +> **Tip:** Always back up your `.env` file before making changes: `cp .env .env.backup` + +## Ongoing Maintenance + +### Viewing Logs + +```bash +# Follow all service logs +just logs + +# Follow a specific service +just logs atl-irc-server +just logs atl-xmpp-server +just logs atl-bridge +``` + +### Stopping the Production Stack + +```bash +just down-prod +``` + +### Cleaning Up Docker Resources + +```bash +# Remove unused images, containers, and volumes +just clean +``` + +## Related Pages + +- [SSL/TLS](/docs/operations/ssl-tls) — certificate management and renewal +- [Monitoring](/docs/operations/monitoring) — health checks and alerting +- [Backups](/docs/operations/backups) — backup and restore procedures +- [Troubleshooting](/docs/operations/troubleshooting) — cross-service diagnostic commands and common issues +- [Security](/docs/operations/security) — secret management and network isolation +- [Environment Variables](/docs/reference/environment-variables) — complete variable reference +- [Architecture](/docs/architecture) — system design and networking diff --git a/apps/docs/content/docs/operations/meta.json b/apps/docs/content/docs/operations/meta.json new file mode 100644 index 00000000..69bdf104 --- /dev/null +++ b/apps/docs/content/docs/operations/meta.json @@ -0,0 +1,11 @@ +{ + "title": "Operations", + "pages": [ + "deployment", + "ssl-tls", + "monitoring", + "backups", + "troubleshooting", + "security" + ] +} diff --git a/apps/docs/content/docs/operations/monitoring.mdx b/apps/docs/content/docs/operations/monitoring.mdx new file mode 100644 index 00000000..63646782 --- /dev/null +++ b/apps/docs/content/docs/operations/monitoring.mdx @@ -0,0 +1,300 @@ +--- +title: Monitoring +description: Health checks, log inspection, metrics collection, and alerting for atl.chat services. +--- + +This page covers how to verify service health, inspect logs, collect metrics with Prometheus, and set up alerting for the atl.chat stack. + +## Health checks + +Every Compose service defines a Docker health check. You can view overall stack health at a glance: + +```bash +# Show status of all containers including health +docker compose ps +``` + +Check a specific container's health status: + +```bash +docker inspect --format='{{.State.Health.Status}}' +``` + +### Per-service health check commands + +The table below lists the exact health check command defined in each service's Compose file. These run automatically at 30-second intervals. + +| Container | Health check command | What it verifies | +|---|---|---| +| `atl-irc-server` | JSON-RPC query via Unix socket (`nc -U /home/unrealircd/unrealircd/data/rpc.socket`) | UnrealIRCd is running and the RPC interface responds | +| `atl-irc-services` | `pgrep -f atheme-services` | Atheme process is alive | +| `atl-xmpp-server` | `curl -sf http://localhost:5280/status` | Prosody HTTP server responds on port 5280 | +| `atl-xmpp-nginx` | `wget -q -O - --no-check-certificate https://localhost:5281/health` | Nginx HTTPS proxy for Prosody responds | +| `atl-bridge` | `pgrep -f bridge.__main__` | Bridge Python process is alive | +| `atl-thelounge` | _(none defined)_ | No built-in health check — verify manually (see below) | +| `atl-irc-webpanel` | _(none defined)_ | No built-in health check — verify manually (see below) | + +### Manual health verification + +For services without a Compose health check, or for deeper verification: + +```bash +# UnrealIRCd — test TLS connection +openssl s_client -connect localhost:6697 -servername irc.localhost /dev/null | head -5 + +# UnrealIRCd — JSON-RPC info endpoint (requires auth; use WEBPANEL_RPC_USER/PASSWORD from .env) +curl -s -u "$WEBPANEL_RPC_USER:$WEBPANEL_RPC_PASSWORD" \ + -X POST http://localhost:8600/ \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"rpc.info","params":{},"id":1}' | jq . + +# Atheme — check the HTTP API responds +curl -sf http://localhost:8081/ && echo "Atheme HTTP OK" + +# Prosody — HTTP status page +curl -sf http://localhost:5280/status + +# Prosody — XMPP C2S port connectivity +nc -zv localhost 5222 + +# Bridge — check process is running +docker exec atl-bridge pgrep -f "bridge.__main__" && echo "Bridge running" + +# The Lounge — HTTP connectivity +curl -sf http://localhost:9000/ -o /dev/null && echo "The Lounge OK" + +# WebPanel — HTTP connectivity +curl -sf http://localhost:8080/ -o /dev/null && echo "WebPanel OK" +``` + +## Log inspection + +### Docker Compose logs + +```bash +# Follow all service logs +docker compose logs --follow + +# Follow a specific service +docker compose logs --follow atl-irc-server + +# Show last 100 lines for a service +docker compose logs --tail=100 atl-bridge + +# Search logs for errors +docker compose logs atl-bridge 2>&1 | grep -i error + +# Filter by time range (last hour) +docker compose logs --since=1h atl-xmpp-server +``` + +### Dozzle (dev profile) + +In the dev profile, [Dozzle](https://dozzle.dev/) runs on port 8082 and provides a browser-based log viewer for all `atl-*` containers. Open `http://localhost:8082` to view real-time logs with filtering and search. + +Dozzle is configured with `DOZZLE_FILTER=name=atl-*` so it only shows atl.chat containers. It is not included in the production profile. + +### Per-service log locations + +Inside containers, logs are also written to files mounted under `data/`: + +| Service | Container log path | Host bind-mount | +|---|---|---| +| UnrealIRCd | `/home/unrealircd/unrealircd/logs/` | `data/irc/logs/` | +| Atheme | `/usr/local/atheme/logs/` | `data/atheme/logs/` | +| Prosody | `/var/lib/prosody/logs/prosody.log` | `data/xmpp/logs/` | +| Bridge | stdout/stderr only | _(use `docker compose logs`)_ | +| The Lounge | `/var/opt/thelounge/` | `data/thelounge/` | +| WebPanel | nginx access/error logs | _(use `docker compose logs`)_ | + +### Common log patterns to watch for + +```bash +# UnrealIRCd — connection errors and netsplits +docker compose logs atl-irc-server 2>&1 | grep -iE "error|split|refused" + +# Atheme — failed authentication attempts +docker compose logs atl-irc-server 2>&1 | grep -i "SASL" + +# Prosody — authentication failures and certificate issues +docker compose logs atl-xmpp-server 2>&1 | grep -iE "auth.*fail|certificate|tls" + +# Bridge — Discord or IRC connection issues +docker compose logs atl-bridge 2>&1 | grep -iE "disconnect|error|reconnect" +``` + +## Key metrics and thresholds + +Monitor these metrics to catch issues before they affect users: + +| Metric | Source | Warning threshold | Critical threshold | +|---|---|---|---| +| Container health status | `docker inspect` | Any service `unhealthy` | Any service `unhealthy` for > 5 min | +| IRC client connections | UnrealIRCd RPC (`rpc.info`) | — | Drops to 0 unexpectedly | +| IRC server-to-server links | UnrealIRCd logs | Link count changes | Netsplit detected | +| XMPP C2S connections | Prosody OpenMetrics | — | Drops to 0 unexpectedly | +| XMPP auth failure rate | Prosody OpenMetrics | > 10/min sustained | > 50/min sustained | +| Bridge process alive | `pgrep` health check | — | Process not found | +| TLS certificate expiry | Certificate files in `data/certs/` | < 14 days | < 3 days | +| Disk usage (`data/` volume) | Host filesystem | > 80% | > 95% | +| Container restart count | `docker inspect` | Any restart | > 3 restarts in 10 min | +| Memory usage per container | `docker stats` | > 500 MB (Prosody) | > 1 GB | + +### Checking certificate expiry + +```bash +# Check the IRC TLS certificate expiry date +openssl x509 -enddate -noout -in data/certs/live/irc.localhost/fullchain.pem + +# Check all certificates in data/certs/ +find data/certs/live -name "fullchain.pem" -exec \ + sh -c 'echo "{}:"; openssl x509 -enddate -noout -in "{}"' \; +``` + +### Checking disk and memory usage + +```bash +# Disk usage of data directories +du -sh data/*/ + +# Container memory and CPU usage +docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}" +``` + +## Metrics collection with Prometheus + +Prosody exposes an OpenMetrics-compatible endpoint via the `http_openmetrics` module. This is the primary metrics source for the stack. + +### Prosody OpenMetrics endpoint + +The endpoint is availabl + to your `prometheus.yml` under `scrape_configs`: + +```yaml +scrape_configs: + # Prosody XMPP server metrics (OpenMetrics) + - job_name: xmpp-prosody + metrics_path: /metrics + scheme: http + scrape_interval: 30s + scrape_timeout: 10s + static_configs: + - targets: ["atl-xmpp-server:5280"] # Use container name within Docker network + labels: + service: prosody + environment: production +``` + +> **Note:** If Prometheus runs outside the Docker network, replace `atl-xmpp-server` with the host IP and use the mapped port (default `5280`). Ensure `PROSODY_OPENMETRICS_CIDR` includes the Prometheus server's IP. + +### Available Prosody metrics + +The `http_openmetrics` and `measure_modules` modules expose these metric families: + +| Category | Metrics | Description | +|---|---|---| +| Connections | `prosody_c2s_connections_total`, `prosody_s2s_connections_total` | Client and server-to-server connection counts | +| Authentication | `prosody_c2s_auth_success_total`, `prosody_c2s_auth_failure_total` | Auth success/failure counters | +| Messages | `prosody_messages_sent_total`, `prosody_messages_received_total` | Message throughput | +| Presence | `prosody_presence_sent_total`, `prosody_presence_received_total` | Presence update counters | +| Storage | `prosody_storage_operations_total`, `prosody_storage_errors_total` | Database operation counters | +| HTTP | `prosody_http_requests_total`, `prosody_http_request_duration_seconds` | BOSH/WebSocket request metrics | +| System | `prosody_memory_usage_bytes`, `prosody_cpu_usage_seconds_total` | Resource usage | +| Modules | `prosody_module_*` | Per-module status gauges (0=ok, 1=info, 2=warn, 3=error) | + +### Other services + +UnrealIRCd, Atheme, The Lounge, and the Bridge do not expose native Prometheus metrics endpoints. Monitor these services using Docker health checks, log analysis, and the JSON-RPC interfaces where available (UnrealIRCd on port 8600, Atheme on port 8081). + +## Alerting strategy + +Choose an alerting approach based on your infrastructure: + +### Option 1: Prometheus + Alertmanager (recommended for production) + +If you already run Prometheus, add alerting rules for the atl.chat stack: + +```yaml +# Example Prometheus alerting rules — add to your rules file +groups: + - name: atl-chat + rules: + # Prosody down + - alert: ProsodyDown + expr: up{job="xmpp-prosody"} == 0 + for: 1m + labels: + severity: critical + annotations: + summary: "Prosody XMPP server is down" + + # High XMPP auth failure rate + - alert: ProsodyHighAuthFailures + expr: rate(prosody_c2s_auth_failure_total[5m]) > 10 + for: 2m + labels: + severity: warning + annotations: + summary: "High XMPP authentication failure rate" + + # High Prosody memory usage + - alert: ProsodyHighMemory + expr: prosody_memory_usage_bytes > 500000000 + for: 5m + labels: + severity: warning + annotations: + summary: "Prosody memory usage exceeds 500 MB" +``` + +### Option 2: Uptime Kuma (lightweight, self-hosted) + +[Uptime Kuma](https://github.com/louislam/uptime-kuma) provides a simple web UI for monitoring endpoints. Configure HTTP/TCP checks for each service: + +| Check type | Target | Interval | +|---|---|---| +| TCP | `localhost:6697` (IRC TLS) | 60s | +| HTTP | `http://localhost:5280/status` (Prosody) | 60s | +| HTTP | `http://localhost:9000` (The Lounge) | 60s | +| HTTP | `http://localhost:8080` (WebPanel) | 60s | +| TCP | `localhost:5222` (XMPP C2S) | 60s | + +### Option 3: Cron-based health pings (minimal) + +For simple setups, a cron job can run health checks and send notifications on failure: + +```bash +#!/usr/bin/env bash +# /etc/cron.d/atl-health-check — runs every 5 minutes +# Checks all atl.chat services and sends a notification on failure + +SERVICES=("atl-irc-server" "atl-irc-services" "atl-xmpp-server" "atl-bridge") + +for svc in "${SERVICES[@]}"; do + status=$(docker inspect --format='{{.State.Health.Status}}' "$svc" 2>/dev/null) + if [ "$status" != "healthy" ]; then + echo "ALERT: $svc is $status" | mail -s "atl.chat health alert" ops@example.com + fi +done +``` + +## Grafana dashboard recommendations + +If you use Grafana with Prometheus, create dashboards covering: + +1. **Connection overview** — active C2S/S2S connections over time, auth success/failure rates +2. **Message flow** — messages per second, peak usage times +3. **Performance** — Prosody memory and CPU usage, HTTP request duration +4. **Errors and health** — auth failures, storage errors, container restart counts +5. **Infrastructure** — disk usage for `data/` volumes, container resource consumption + + +## Related pages + +- [Deployment](/docs/operations/deployment) — production deployment runbook +- [Troubleshooting](/docs/operations/troubleshooting) — cross-service diagnostic commands and common issues +- [Backups](/docs/operations/backups) — backup and restore procedures +- [Security](/docs/operations/security) — secret management and network isolation +- [SSL/TLS](/docs/operations/ssl-tls) — certificate management and expiry monitoring +- [Environment Variables](/docs/reference/environment-variables) — complete variable reference +- [Ports Reference](/docs/reference/ports) — complete port registry diff --git a/apps/docs/content/docs/operations/security.mdx b/apps/docs/content/docs/operations/security.mdx new file mode 100644 index 00000000..d92906dd --- /dev/null +++ b/apps/docs/content/docs/operations/security.mdx @@ -0,0 +1,396 @@ +--- +title: Security +description: Secret management, credential rotation, Docker network isolation, TLS best practices, and Gitleaks pre-commit hook configuration for the atl.chat stack. +--- + +This page covers how to generate, store, and rotate secrets for every atl.chat service, how the Docker network isolates internal traffic from external access, TLS hardening guidance, and how the Gitleaks pre-commit hook prevents accidental secret commits. + +## Secret management overview + +All secrets live in the `.env` file at the repository root. Services consume them through Docker Compose `env_file` directives and config templates processed by `scripts/prepare-config.sh` via `envsubst`. The `.env` file is listed in `.gitignore` and must never be committed. + +### File permissions + +Lock down your secrets files immediately after creation: + +```bash +# Restrict .env to owner-only read/write +chmod 600 .env +``` + +## Generating secure values + +Every sensitive environment variable in `.env.example` ships with a placeholder value (e.g. `change_me_*`). You must replace each one before deploying to production. + +### IRC cloak keys (`IRC_CLOAK_KEY_1`, `IRC_CLOAK_KEY_2`, `IRC_CLOAK_KEY_3`) + +Cloak keys mask user IP addresses. All three keys must be identical across every IRC server in your network. Use the built-in `just` recipe: + +```bash +# Generates 3 cloak keys and writes them to .env automatically +just irc gencloak +``` + +This runs `scripts/gencloak-update-env.sh`, which invokes UnrealIRCd's `gencloak` command inside the container, parses the three keys, updates `.env`, and re-runs `prepare-config.sh`. + +### IRC operator password (`IRC_OPER_PASSWORD`) + +UnrealIRCd requires an Argon2-hashed password for IRC operators. Generate one interactively: + +```bash +# Requires the IRC container to be running +just irc generate-password +``` + +This prompts you for a password (input is hidden), then outputs an Argon2 hash. Copy the hash into your `.env`: + +```bash +IRC_OPER_PASSWORD="$argon2id$v=19$m=6144,t=2,p=2$..." +``` + +After updating, apply the change: + +```bash +# Re-generate config from templates +bash scripts/prepare-config.sh + +# Reload without restart (as IRCOp: /REHASH) or restart the container +docker compose restart atl-irc-server +``` + +### IRC die/restart password (`IRC_DRPASS`) + +This password protects the `/DIE` and `/RESTART` IRC commands. Generate a random value: + +```bash +openssl rand -base64 32 +``` + +Set it in `.env`: + +```bash +IRC_DRPASS= +``` + +### IRC services password (`IRC_SERVICES_PASSWORD`) + +Shared secret between UnrealIRCd and Atheme for the services link. Generate a strong random value: + +```bash +openssl rand -base64 32 +``` + +### WebIRC passwords (`ATL_WEBIRC_PASSWORD`, `THELOUNGE_WEBIRC_PASSWORD`) + +WebIRC passwords authenticate web clients (The Lounge, the Next.js web app) to UnrealIRCd so that real user IPs are forwarded. Generate a unique value for each: + +```bash +# For the web frontend +openssl rand -base64 32 +# For The Lounge +openssl rand -base64 32 +``` + +### WebPanel RPC password (`WEBPANEL_RPC_PASSWORD`) + +Authenticates the WebPanel to UnrealIRCd's JSON-RPC interface: + +```bash +openssl rand -base64 32 +``` + +### Bridge secrets + +| Variable | Purpose | Generation | +|---|---|---| +| `BRIDGE_DISCORD_TOKEN` | Discord bot token | Create at [Discord Developer Portal](https://discord.com/developers/applications) → Bot → Reset Token | +| `BRIDGE_PORTAL_TOKEN` | Portal API authentication | `openssl rand -base64 32` | +| `BRIDGE_XMPP_COMPONENT_SECRET` | XMPP component authentication | `openssl rand -base64 32` | +| `BRIDGE_IRC_OPER_PASSWORD` | Bridge IRC oper credentials | `openssl rand -base64 32` | + +### Prosody (XMPP) secrets + +| Variable | Purpose | Generation | +|---|---|---| +| `PROSODY_DB_PASSWORD` | PostgreSQL database password (production only) | `openssl rand -base64 32` | +| `PROSODY_OAUTH2_REGISTRATION_KEY` | OAuth2 client registration key | `openssl rand -base64 32` | + +### TURN secret (`TURN_SECRET`) + +Shared secret for TURN/STUN authentication: + +```bash +openssl rand -base64 32 +``` + +### Cloudflare DNS API token (`CLOUDFLARE_DNS_API_TOKEN`) + +Required only for production TLS via Let's Encrypt DNS-01 challenges. Create a scoped API token in the [Cloudflare dashboard](https://dash.cloudflare.com/profile/api-tokens) with `Zone:DNS:Edit` permission for your domain. Not needed for local development. + +### Alchemy password (`ALCHEMY_PASSWORD`) + +Used by the docs deployment (Cloudflare Workers via Alchemy) to encrypt deployment state secrets: + +```bash +openssl rand -base64 32 +``` + +### Portal integration passwords + +These variables are consumed by the external ATL Portal service, not by this monorepo's compose stack: + +| Variable | Purpose | Generation | +|---|---|---| +| `IRC_UNREAL_RPC_PASSWORD` | Portal → UnrealIRCd JSON-RPC | Should match `WEBPANEL_RPC_PASSWORD` | +| `PROSODY_REST_PASSWORD` | Portal → Prosody REST API | `openssl rand -base64 32` | + +## Credential rotation + +Rotate all secrets on a regular schedule (every 6–12 months) or immediately after a suspected compromise. + +### Rotation procedure + +1. Generate a new value using the methods above +2. Update `.env` with the new value +3. Re-generate config templates: + ```bash + bash scripts/prepare-config.sh + ``` +4. Restart affected services: + ```bash + # Single service + docker compose restart atl-irc-server + + # Full stack + docker compose down + docker compose up -d + ``` +5. Verify the service is healthy: + ```bash + docker compose ps + ``` + +### Coordinated rotation + +Some secrets are shared between services and must be updated in lockstep: + +| Secret | Services that share it | +|---|---| +| `IRC_SERVICES_PASSWORD` | UnrealIRCd ↔ Atheme | +| `ATL_WEBIRC_PASSWORD` | UnrealIRCd ↔ Next.js web app | +| `THELOUNGE_WEBIRC_PASSWORD` | UnrealIRCd ↔ The Lounge | +| `WEBPANEL_RPC_PASSWORD` | UnrealIRCd ↔ WebPanel | +| `BRIDGE_XMPP_COMPONENT_SECRET` | Prosody ↔ Bridge | +| `BRIDGE_IRC_OPER_PASSWORD` | UnrealIRCd ↔ Bridge | +| `IRC_CLOAK_KEY_1/2/3` | All IRC servers in the network | + +After updating a shared secret, restart both services that use it. For example, after rotating `IRC_SERVICES_PASSWORD`: + +```bash +bash scripts/prepare-config.sh +docker compose restart atl-irc-server +# Atheme shares UnrealIRCd's network namespace and restarts with it, +# but if running independently: +docker compose restart atl-irc-services +``` + +## Docker network isolation + +All services run on a single Docker bridge network named `atl-chat`, defined in `infra/compose/networks.yaml`. This means every container can reach every other container by hostname on internal ports, but only explicitly published ports are accessible from the host. + +### Externally exposed ports + +These ports are bound to the host and accessible to external clients. In production, they bind to the Tailscale IP (`ATL_CHAT_IP`). In development, they bind to `127.0.0.1` via `.env.dev`. + +| Service | Port | Variable | Purpose | +|---|---|---|---| +| UnrealIRCd | 6697 | `IRC_TLS_PORT` | IRC client connections (TLS) | +| UnrealIRCd | 6900 | `IRC_SERVER_PORT` | Server-to-server linking | +| UnrealIRCd | 8600 | `IRC_RPC_PORT` | JSON-RPC API | +| UnrealIRCd | 8000 | `IRC_WEBSOCKET_PORT` | WebSocket for web clients | +| UnrealIRCd | 8081 | `ATHEME_HTTPD_PORT` | Atheme JSON-RPC (shared network namespace) | +| WebPanel | 8080 | `WEBPANEL_PORT` | Admin web interface | +| Prosody | 5222 | `PROSODY_C2S_PORT` | XMPP client-to-server | +| Prosody | 5269 | `PROSODY_S2S_PORT` | XMPP server-to-server | +| Prosody | 5223 | `PROSODY_C2S_DIRECT_TLS_PORT` | XMPP Direct TLS | +| Prosody | 5270 | `PROSODY_S2S_DIRECT_TLS_PORT` | XMPP S2S Direct TLS | +| Prosody | 5280 | `PROSODY_HTTP_PORT` | BOSH / WebSocket | +| Prosody | 5000 | `PROSODY_PROXY65_PORT` | File transfer proxy | +| Nginx (XMPP) | 5281 | `PROSODY_HTTPS_PORT` | XMPP HTTPS proxy | +| The Lounge | 9000 | `THELOUNGE_PORT` | Web IRC client | +| Dozzle | 8082 | `DOZZLE_PORT` | Log viewer (dev profile only) | + +### Internal-only communication + +These connections happen over the `atl-chat` Docker network and are never exposed to the host: + +| From | To | Port | Purpose | +|---|---|---|---| +| Atheme | UnrealIRCd | 6901 | Services link (shared network namespace via `network_mode: service:atl-irc-server`) | +| Bridge | UnrealIRCd | 6697 | IRC connection | +| Bridge | Prosody | 5347 | XMPP component protocol | +| The Lounge | UnrealIRCd | 6697 | IRC connection (WebIRC) | +| WebPanel | UnrealIRCd | 8600 | JSON-RPC | +| cert-manager | — | — | Writes to shared `data/certs/` volume only | + +### Atheme network namespace sharing + +Atheme uses `network_mode: service:atl-irc-server`, which means it shares UnrealIRCd's network stack entirely. Atheme connects to UnrealIRCd on `127.0.0.1:6901` (loopback within the shared namespace). This is why the Atheme HTTP port (8081) appears as a published port on the UnrealIRCd container. + +### Production hardening + +In production, bind published ports to your Tailscale IP rather than `0.0.0.0`: + +```bash +# .env (production) +ATL_CHAT_IP=100.64.7.0 +ATL_GATEWAY_IP=100.64.1.0 +``` + +This ensures services are only reachable over the Tailscale mesh, not on the public internet. Place a reverse proxy (e.g. Caddy, nginx) in front of web-facing ports (8000, 8080, 9000, 5280, 5281) to add rate limiting and additional TLS termination. + +## TLS configuration best practices + +Detailed certificate management is covered in the [SSL/TLS page](/docs/operations/ssl-tls). This section +tore private keys in the repository. The `data/` directory is in `.gitignore`. +- In development, `just init` generates self-signed certificates. These are not suitable for production. + +### Post-renewal hooks + +After certificate renewal, services need to pick up the new certificates: + +```bash +# UnrealIRCd: reload config (picks up new certs) +docker compose kill -s HUP atl-irc-server + +# Prosody: reload +docker compose exec atl-xmpp-server prosodyctl reload +``` + +### STS configuration + +UnrealIRCd's Strict Transport Security is configured via environment variables: + +| Variable | Default | Description | +|---|---|---| +| `IRC_STS_DURATION` | `1m` | STS policy duration (use longer values like `365d` in production) | +| `IRC_STS_PRELOAD` | `no` | Whether to include in STS preload lists | + +## Gitleaks pre-commit hook + +The repository uses [Gitleaks](https://github.com/gitleaks/gitleaks) as a pre-commit hook to prevent accidental secret commits. It is configured in `.pre-commit-config.yaml`: + +```yaml +- repo: https://github.com/gitleaks/gitleaks + rev: v8.30.0 + hooks: + - id: gitleaks + args: [--config, .gitleaks.toml] +``` + +### Setup + +Install the pre-commit hooks: + +```bash +# If core.hooksPath is set by your environment, unset it first +git config --unset-all core.hooksPath + +# Install hooks +pre-commit install +``` + +Or run manually: + +```bash +# Scan all files +pre-commit run gitleaks --all-files + +# Equivalent via just +just lint +``` + +### How it works + +Gitleaks scans staged files against a comprehensive rule set defined in `.gitleaks.toml` (auto-generated from the upstream Gitleaks default config). It detects patterns for hundreds of secret types: API keys, tokens, passwords, private keys, and more. + +The `.gitleaks.toml` global allowlist excludes common false positives: + +- Binary and font files +- Lock files (`pnpm-lock.yaml`, `package-lock.json`, etc.) +- Vendored dependencies +- Template variables (`${VAR}`, `$VAR`, `{{ var }}`) +- Common file paths + +### Handling a blocked commit + +If Gitleaks blocks your commit, it means a potential secret was detected in your staged changes: + +1. Review the Gitleaks output to identify the flagged file and line +2. If it is a real secret: + - Remove the secret from the file + - Move it to `.env` (which is gitignored) + - Reference it via environment variable substitution +3. If it is a false positive: + - Add an inline comment `# gitleaks:allow` on the flagged line, or + - Add the specific pattern to the `[[rules.allowlists]]` section in `.gitleaks.toml` + +### Handling accidental secret commits + +If a secret was committed before the hook was installed: + +1. Rotate the compromised secret immediately using the generation methods above +2. Remove the secret from the repository history using `git filter-repo`: + ```bash + # Install git-filter-repo if needed + pip install git-filter-repo + + # Remove the file containing the secret from all history + git filter-repo --invert-paths --path + + # Or replace a specific string across all history + git filter-repo --replace-text <(echo 'OLD_SECRET==>REDACTED') + ``` +3. Force-push the cleaned history: + ```bash + git push --force --all + git push --force --tags + ``` +4. Notify all contributors to re-clone or rebase onto the cleaned history +5. If the secret was for an external service (Discord token, Cloudflare API token), revoke it at the provider and generate a new one + +> **Warning:** Force-pushing rewrites history for all collaborators. Coordinate with your team before doing this. In most cases, rotating the secret is sufficient — history rewriting is only necessary if the secret grants persistent access that cannot be revoked. + +## Verification checklist + +After configuring secrets for a new deployment, verify everything is in place: + +```bash +# Check .env exists and has restricted permissions +ls -la .env +# Expected: -rw------- 1 user user ... .env + +# Verify all placeholder values have been replaced +grep -c 'change_me' .env +# Expected: 0 + +# Verify Gitleaks hook is installed +pre-commit run gitleaks --all-files +# Expected: Passed (or specific findings to address) + +# Verify services start with the new secrets +docker compose up -d +docker compose ps +# Expected: all services show "Up" / "healthy" +``` + +## Related pages + +- [SSL/TLS](/docs/operations/ssl-tls) — certificate management, renewal, and per-service cert paths +- [Deployment](/docs/operations/deployment) — production deployment runbook +- [Environment Variables](/docs/reference/environment-variables) — complete variable reference with security warnings +- [Architecture](/docs/architecture) — system design and service connectivity +- [IRC Configuration](/docs/services/irc/configuration) — UnrealIRCd secrets, oper blocks, and cloak keys +- [XMPP Configuration](/docs/services/xmpp/configuration) — Prosody TLS and authentication settings +- [Atheme Configuration](/docs/services/atheme/configuration) — Atheme services link password and SASL setup +- [Bridge Configuration](/docs/services/bridge/configuration) — bridge credentials and Discord token diff --git a/apps/docs/content/docs/operations/ssl-tls.mdx b/apps/docs/content/docs/operations/ssl-tls.mdx new file mode 100644 index 00000000..1d0904d0 --- /dev/null +++ b/apps/docs/content/docs/operations/ssl-tls.mdx @@ -0,0 +1,263 @@ +--- +title: SSL/TLS +description: Certificate management with cert-manager (Lego), Let's Encrypt, DNS-01 challenges, and per-service certificate paths. +--- + +TLS certificates for all atl.chat services are managed centrally through the `cert-manager` compose service, which uses [Lego](https://go-acme.github.io/lego/) to obtain wildcard certificates from Let's Encrypt via DNS-01 challenges with Cloudflare. In development, `just init` generates self-signed certificates automatically. + +## How it works + +The certificate infrastructure follows a shared-volume pattern. A single `cert-manager` container obtains and renews certificates, storing them in `data/certs/` on the host. Each service container mounts this directory read-only and references certificates by domain path. + +```mermaid +graph TD + LE["Let's Encrypt"] -- "ACME DNS-01" --> CM["cert-manager
(Lego)"] + CM -- "writes certs" --> DC["data/certs/"] + DC -- "read-only mount" --> IRC["UnrealIRCd
/home/unrealircd/unrealircd/certs/"] + DC -- "read-only mount" --> XMPP["Prosody
/etc/prosody/certs/"] + DC -- "read-only mount" --> NX["Nginx (XMPP HTTPS)
/etc/nginx/certs/"] +``` + +## cert-manager service + +The `cert-manager` service is defined in `infra/compose/cert-manager.yaml` and runs the `goacme/lego:latest` image. On startup it requests a wildcard certificate covering `*.atl.chat` and the bare `atl.chat` domain, then enters a renewal loop that checks every 24 hours. + +### Configuration + +The service requires two environment variables in your `.env` file: + +| Variable | Description | Required | +|---|---|---| +| `CLOUDFLARE_DNS_API_TOKEN` | Cloudflare API token with DNS edit permissions for your zone | Yes (production) | +| `LETSENCRYPT_EMAIL` | Email for Let's Encrypt account registration and expiry notices | Yes | +| `IRC_ROOT_DOMAIN` | Root domain for certificates (default: `atl.chat`) | No | +| `SSL_DOMAIN` | Alternative domain override (fallback if `IRC_ROOT_DOMAIN` is unset) | No | + +If `CLOUDFLARE_DNS_API_TOKEN` is not set, the cert-manager logs a warning and sleeps indefinitely — this is normal in development where self-signed certificates are used instead. + +### Challenge type: DNS-01 + +The cert-manager uses the DNS-01 challenge exclusively via Cloudflare's DNS API. This is required because the stack obtains wildcard certificates (`*.atl.chat`), which cannot be validated with HTTP-01 challenges. + +DNS-01 works by creating a temporary `_acme-challenge` TXT record in your Cloudflare DNS zone. Lego handles this automatically when given a valid API token. + +To create a Cloudflare API token: + +1. Go to [Cloudflare Dashboard](https://dash.cloudflare.com/) → My Profile → API Tokens +2. Click "Create Token" +3. Use the "Edit zone DNS" template +4. Scope it to the specific zone (e.g., `atl.chat`) +5. Copy the token into `CLOUDFLARE_DNS_API_TOKEN` in your `.env` file + +### Volumes + +The cert-manager mounts a single volume: + +```yaml +volumes: + - ../../data/certs:/data +``` + +Lego writes certificates into `/data/certificates/` inside the container, which maps to `data/certs/certificates/` on the host. The certificate files follow Lego's naming convention for wildcard domains. + +## Certificate paths per service + +All services read certificates from the shared `data/certs/` directory, mounted at different container paths. Certificates follow a `live//` layout with `fullchain.pem` and `privkey.pem` files. + +### Host paths + +``` +data/certs/ +└── live/ + ├── / # e.g., irc.atl.chat + │ ├── fullchain.pem + │ └── privkey.pem + └── / # e.g., xmpp.atl.chat + ├── fullchain.pem + └── privkey.pem +``` + +### Per-service mount points + +| Service | Container mount | Certificate path inside container | +|---|---|---| +| UnrealIRCd | `data/certs` → `/home/unrealircd/unrealircd/certs` | `/home/unrealircd/unrealircd/certs/live/${IRC_DOMAIN}/fullchain.pem` | +| Prosody | `data/certs` → `/etc/prosody/certs` | `/etc/prosody/certs/live/${XMPP_DOMAIN}/fullchain.pem` | +| Nginx (XMPP HTTPS) | `data/certs` → `/etc/nginx/certs` (read-only) | `/etc/nginx/certs/live/${XMPP_DOMAIN}/fullchain.pem` | + +### Environment variable overrides + +You can override the default certificate paths using environment variables: + +| Variable | Default | Service | +|---|---|---| +| `IRC_SSL_CERT_PATH` | `/home/unrealircd/unrealircd/certs/live/${IRC_DOMAIN}/fullchain.pem` | UnrealIRCd | +| `IRC_SSL_KEY_PATH` | `/home/unrealircd/unrealircd/certs/live/${IRC_DOMAIN}/privkey.pem` | UnrealIRCd | +| `PROSODY_SSL_CERT` | `certs/live/${XMPP_DOMAIN}/fullchain.pem` | Prosody | +| `PROSODY_SSL_KEY` | `certs/live/${XMPP_DOMAIN}/privkey.pem` | Prosody | + +## TLS configuration + +### UnrealIRCd + +UnrealIRCd is configured with modern TLS settings in `apps/unrealircd/config/unrealircd.conf.template`: + +- Protocols: TLS 1.2 and TLS 1.3 only +- Ciphers: ECDHE suites with forward secrecy (CHACHA20-POLY1305, AES-GCM) +- ECDH groups: X25519MLKEM768 (post-quantum hybrid), X25519, secp521r1, secp384r1, prime256v1 +- STS (Strict Transport Security): redirects clients to port 6697 with configurable duration via `IRC_STS_DURATION` +- Certificate expiry notifications: enabled + +### Prosody + +Prosody's TLS is configured in `apps/prosody/config/prosody.cfg.lua`: + +- Protocols: TLS 1.2+ +- Ciphers: `ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS` +- Each VirtualHost and Component (MUC, upload, proxy) has its own `ssl` block pointing to the same certificate files +- The `certificates = "certs"` directive tells Prosody to look for certificates in its cert directory + +### Nginx (XMPP HTTPS proxy) + +The nginx proxy for Prosody HTTPS (port 5281) uses the same certificate files, configured via the `prosody-https.conf.template`: + +- Protocols: TLS 1.2 and TLS 1.3 +- Ciphers: same strong suite as Prosody + +## Renewal process + +### Automatic renewal + +The cert-manager container runs a continuous loop: + +1. On startup, it requests certificates from Let's Encrypt using `lego run` +2. It then sleeps for 24 hours +3. After waking, it runs `lego renew`, which only renews certificates within 30 days of expiry +4. The loop repeats indefinitely + +Let's Encrypt certificates are valid for 90 days. With daily renewal checks, certificates are renewed approximately 30 days before expiry. + +### Post-renewal hooks + +After certificates are renewed on disk, services that cache TLS state in memory need to reload. Currently, the cert-manager does not execute post-renewal hooks automatically. You need to signal services manually after a renewal: + +1. Reload UnrealIRCd TLS certificates (no downtime, no client disconnection): + + ```bash + # Rehash reloads config and TLS certificates without restarting + docker exec atl-irc-server /home/unrealircd/unrealircd/bin/unrealircd rehash + ``` + + Alternatively, from inside IRC as an operator: + + ``` + /REHASH -tls + ``` + +2. Reload Prosody to pick up new certificates: + + ```bash + docker exec atl-xmpp-server prosodyctl reload + ``` + +3. Reload nginx for the XMPP HTTPS proxy: + + ```bash + docker exec atl-xmpp-nginx nginx -s reload + ``` + +> **Tip:** You can automate post-renewal hooks by adding a cron job or a script that runs after `lego renew` completes. Check the cert-manager container logs to confirm renewal occurred before signalling services. + +## Manual renewal + +To force a certificate renewal outside the automatic cycle: + +```bash +# Run the cert-manager container with the renew command +docker compose -f compose.yaml run --rm cert-manager \ + lego --email "$LETSENCRYPT_EMAIL" \ + --dns cloudflare \ + --domains "*.$IRC_ROOT_DOMAIN" \ + --domains "$IRC_ROOT_DOMAIN" \ + --path /data \ + --accept-tos renew --days 90 +``` + +After manual renewal, reload all services: + +```bash +# Reload UnrealIRCd TLS +docker exec atl-irc-server /home/unrealircd/unrealircd/bin/unrealircd rehash + +# Reload Prosody +docker exec atl-xmpp-server prosodyctl reload + +# Reload nginx +docker exec atl-xmpp-nginx nginx -s reload +``` + +### Verification + +Confirm the new certificate is active: + +```bash +# Check UnrealIRCd certificate expiry +echo | openssl s_client -connect localhost:6697 -servername irc.atl.chat 2>/dev/null | openssl x509 -noout -dates + +# Check Prosody HTTPS certificate expiry +echo | openssl s_client -connect localhost:5281 -servername xmpp.atl.chat 2>/dev/null | openssl x509 -noout -dates +``` + +## Development certificates + +In development, `just init` (via `scripts/init.sh`) generates self-signed certificates automatically. No cert-manager or Cloudflare token is needed. + +The `generate_dev_certs` function creates certificates for each service domain: + +```bash +# Generated by scripts/init.sh +data/certs/live/irc.localhost/fullchain.pem +data/certs/live/irc.localhost/privkey.pem +data/certs/live/xmpp.localhost/fullchain.pem +data/certs/live/xmpp.localhost/privkey.pem +``` + +These self-signed certificates include SANs (Subject Alternative Names) for the main domain, wildcard, Prosody components (`muc.`, `upload.`, `proxy.`, `pubsub.`, `bridge.`), and `localhost`. They are valid for 365 days. + +> **Note:** Browsers and IRC clients will show certificate warnings for self-signed certificates. This is expected in development. Use `-k` or `--insecure` flags with `curl` and `openssl` commands when testing locally. + +## Troubleshooting + +### cert-manager sleeps immediately + +If the cert-manager container logs show "CLOUDFLARE_DNS_API_TOKEN is not set" and sleeps, set the token in your `.env` file. This is expected in development. + +### Certificate not found errors + +Verify the certificate files exist at the expected paths: + +```bash +ls -la data/certs/live/*/ +``` + +Check that the domain directories match your `IRC_DOMAIN` and `XMPP_DOMAIN` values in `.env`. + +### TLS handshake failures + +If clients report TLS errors after renewal: + +1. Confirm the certificate was renewed: check file modification times in `data/certs/live/` +2. Reload the affected service (see [Post-renewal hooks](#post-renewal-hooks) above) +3. Verify the certificate chain is complete: `openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt data/certs/live//fullchain.pem` + +### Permission errors + +Certificate files must be readable by the container user (UID set by `PUID` in `.env`, default `1000`). The `init.sh` script sets permissions to `644` for private keys in development. In production, ensure the cert-manager writes files with appropriate permissions. + +## Related pages + +- [Environment Variables](/docs/reference/environment-variables) — SSL/TLS-related variables +- [Deployment](/docs/operations/deployment) — production deployment including TLS setup +- [IRC Configuration](/docs/services/irc/configuration) — UnrealIRCd TLS settings +- [XMPP Configuration](/docs/services/xmpp/configuration) — Prosody TLS settings +- [Networking](/docs/architecture/networking) — port mappings and network architecture diff --git a/apps/docs/content/docs/operations/troubleshooting.mdx b/apps/docs/content/docs/operations/troubleshooting.mdx new file mode 100644 index 00000000..3e814409 --- /dev/null +++ b/apps/docs/content/docs/operations/troubleshooting.mdx @@ -0,0 +1,710 @@ +--- +title: Troubleshooting +description: Cross-service diagnostic commands, common issues, and resolution steps for the atl.chat stack. +--- + +This page is your starting point for diagnosing problems across the atl.chat stack. Issues are organised by service so you can jump directly to the relevant section. + +For IRC-specific troubleshooting with deeper detail, see [IRC Troubleshooting](/docs/services/irc/troubleshooting). + +## Quick stack health check + +Run these commands first to get a fast overview of the entire stack: + +```bash +# Show all container statuses and health +just status + +# Check health of every atl-* container in one pass +for c in atl-irc-server atl-irc-services atl-xmpp-server atl-xmpp-nginx atl-bridge atl-thelounge atl-irc-webpanel; do + status=$(docker inspect --format='{{.State.Health.Status}}' "$c" 2>/dev/null || echo "not found") + printf "%-22s %s\n" "$c" "$status" +done + +# View recent logs across all services +docker compose logs --tail=20 +``` + +If a specific service shows `unhealthy` or `not found`, jump to that service's section below. + +## General diagnostic commands + +These commands apply to any service in the stack: + +```bash +# View logs for a specific container (last 50 lines) +docker compose logs --tail=50 + +# Follow logs in real time +docker compose logs --follow + +# Filter logs by time range (last hour) +docker compose logs --since=1h + +# Search logs for errors +docker compose logs 2>&1 | grep -i error + +# Check container resource usage +docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}" | grep atl- + +# Inspect a container's full state +docker inspect | jq '.[0].State' + +# Check disk usage of data directories +du -sh data/*/ +``` + +## UnrealIRCd (IRC server) + +Container: `atl-irc-server` · Ports: 6697 (TLS), 8000 (WebSocket), 8600 (RPC) + +### Diagnostic commands + +```bash +# Container health and status +docker inspect --format='{{.State.Health.Status}}' atl-irc-server + +# Recent logs +docker compose logs --tail=50 atl-irc-server + +# Test TLS connectivity +echo "QUIT" | openssl s_client -connect localhost:6697 -servername irc.localhost 2>/dev/null | head -5 + +# Test WebSocket port +curl -s -o /dev/null -w "%{http_code}" http://localhost:8000 + +# Test JSON-RPC endpoint +curl -s http://localhost:8600/api/rpc.info | jq . + +# Check if ports are listening on the host +ss -tlnp | grep -E "(6697|8000|8600)" + +# Shell into the container +docker compose exec atl-irc-server bash +``` + +### Common issues + +#### 1. Container exits immediately on startup + +The most common cause is a configuration syntax error. + +```bash +# Check the exit code +docker compose ps -a | grep atl-irc-server + +# View startup logs for the error message +docker compose logs atl-irc-server | head -40 +``` + +Resolution: look for `error` or `fatal` in the output. Fix the config template in `apps/unrealircd/config/`, then regenerate and restart: + +```bash +scripts/prepare-config.sh +just dev +``` + +#### 2. TLS certificate errors + +Clients report certificate warnings or TLS handshake failures. + +```bash +# Check certificate expiry +openssl x509 -in data/certs/live/irc.localhost/fullchain.pem -noout -dates 2>/dev/null || \ + echo "Certificate file not found" + +# Verify the certificate matches the expected domain +openssl x509 -in data/certs/live/irc.localhost/fullchain.pem -noout -subject 2>/dev/null + +# Check cert files are readable inside the container +docker compose exec atl-irc-server ls -la /home/unrealircd/unrealircd/certs/live/ +``` + +Resolution: for dev environments, `just init` generates self-signed certificates. If certs are missing or expired, re-run `just init`. For production, see [SSL/TLS](/docs/operations/ssl-tls). + +#### 3. Port already in use + +Another process is binding to 6697, 8000, or 8600. + +```bash +# Find what's using the port +ss -tlnp | grep 6697 +``` + +Resolution: stop the conflicting process, or change the port in `.env`: + +```bash +# Override the port in .env +IRC_TLS_PORT=6698 +``` + +Then restart: `just dev` + +#### 4. Unsubstituted environment variables in config + +The generated config contains literal `${VARIABLE}` strings instead of values. + +```bash +# Check for unsubstituted variables +grep -n '${' apps/unrealircd/config/unrealircd.conf | head -20 +``` + +Resolution: ensure `.env` exists and contains all required variables, then regenerate: + +```bash +scripts/prepare-config.sh +just irc reload +``` + +#### 5. Cannot connect from external clients + +The server is running but clients outside localhost cannot reach it. + +```bash +# Check the bind address — 127.0.0.1 means localhost only +grep ATL_CHAT_IP .env + +# Check firewall rules +sudo ufw status 2>/dev/null || sudo iptables -L -n 2>/dev/null | head -20 +``` + +Resolution: to allow external connections, set `ATL_CHAT_IP=0.0.0.0` in `.env` and restart. Ensure your firewall allows the relevant ports. + +## Atheme (IRC services) + +Container: `atl-irc-services` · Shares network with `atl-irc-server` (port 8081 for HTTP API) + +### Diagnostic commands + +```bash +# Container health +docker inspect --format='{{.State.Health.Status}}' atl-irc-services + +# Recent logs +docker compose logs --tail=50 atl-irc-services + +# Check Atheme process is running +docker compose exec atl-irc-server pgrep -f atheme-services + +# Check Atheme HTTP API responds +curl -sf http://localhost:8081/ && echo "Atheme HTTP OK" || echo "Atheme HTTP not responding" + +# Check Atheme database file +ls -la data/atheme/data/services.db + +# Verify Atheme is linked to UnrealIRCd (from an IRC client as oper) +# /LINKS — should show services listed +``` + +### Common issues + +#### 1. Atheme won't start — waiting for UnrealIRCd + +Atheme depends on `atl-irc-server` being healthy before it starts. + +```bash +# Check if UnrealIRCd is healthy +docker inspect --format='{{.State.Health.Status}}' atl-irc-server +``` + +Resolution: fix UnrealIRCd first. Atheme starts automatically once UnrealIRCd is healthy. + +#### 2. Atheme starts but immediately exits + +Usually a config syntax error or missing database directory. + +```bash +docker compose logs atl-irc-services | head -30 +``` + +Resolution: check that `data/atheme/data/` exists and is writable. Re-run `just init` if directories are missing. + +#### 3. NickServ/ChanServ not responding + +Atheme is running but service bots do not respond to commands. + +```bash +# Check if Atheme is linked to UnrealIRCd +docker compose logs atl-irc-services 2>&1 | grep -i "link\|connect" +``` + +Resolution: Atheme connects to UnrealIRCd via a server link. Check that the link block credentials in the Atheme config match the UnrealIRCd config. Restart Atheme after fixing: + +```bash +docker compose restart atl-irc-services +``` + +#### 4. Database corruption + +Atheme refuses to load `services.db` on startup. + +```bash +docker compose logs atl-irc-services 2>&1 | grep -iE "database|corrupt|error" +``` + +Resolution: Atheme creates periodic `.bak` files in `data/atheme/data/`. Restore from the most recent backup: + +```bash +docker compose stop atl-irc-services +cp data/atheme/data/services.db.bak data/atheme/data/services.db +docker compose start atl-irc-services +``` + +#### 5. Permission denied on data directory + +```bash +ls -la data/atheme/data/ +``` + +Resolution: fix ownership to match your host user: + +```bash +sudo chown -R "$(id -u):$(id -g)" data/atheme/ +``` + +## Prosody (XMPP server) + +Container: `atl-xmpp-server` · Ports: 5222 (C2S), 5280 (HTTP/BOSH), 5281 (HTTPS via nginx) + +### Diagnostic commands + +```bash +# Container health +docker inspect --format='{{.State.Health.Status}}' atl-xmpp-server + +# Recent logs +docker compose logs --tail=50 atl-xmpp-server + +# HTTP status endpoint +curl -sf http://localhost:5280/status && echo " — Prosody OK" + +# Test XMPP C2S port +nc -zv localhost 5222 + +# Test HTTPS proxy (nginx) +curl -sk https://localhost:5281/health + +# Check nginx proxy health +docker inspect --format='{{.State.Health.Status}}' atl-xmpp-nginx + +# Shell into the container +docker compose exec atl-xmpp-server bash +``` + +### Common issues + +#### 1. Prosody fails to start + +```bash +docker compose logs atl-xmpp-server | head -40 +``` + +Common causes: missing or invalid TLS certificates, config syntax error, or port conflict. + +Resolution: check that `data/certs/` contains valid certificates for your XMPP domain. Re-run `just init` for dev certs. Check for config errors in `apps/prosody/config/prosody.cfg.lua`. + +#### 2. XMPP clients cannot authenticate + +Users get "authentication failed" errors. + +```bash +# Check auth-related log entries +docker compose logs atl-xmpp-server 2>&1 | grep -iE "auth.*fail|sasl|credential" +``` + +Resolution: verify the user account exists. Create or reset a user: + +```bash +just xmpp adduser +``` + +#### 3. BOSH/WebSocket endpoint not responding + +Web clients cannot connect via `http://localhost:5280/http-bind`. + +```bash +curl -sf http://localhost:5280/http-bind && echo "BOSH OK" || echo "BOSH not responding" +``` + +Resolution: ensure the `bosh` and `websocket` modules are enabled in the Prosody config. Check that port 5280 is mapped correctly in `.env`. + +#### 4. HTTPS proxy (nginx) returning 502 + +The nginx sidecar at port 5281 returns a bad gateway error. + +```bash +docker compose logs --tail=30 atl-xmpp-nginx +``` + +Resolution: nginx proxies to `atl-xmpp-server:5280`. Ensure Prosody is running and healthy before checking nginx. Restart both if needed: + +```bash +docker compose restart atl-xmpp-server atl-xmpp-nginx +``` + +#### 5. Certificate mismatch for XMPP domain + +Clients report the certificate does not match the expected domain. + +```bash +# Check which domain the cert covers +openssl s_client -connect localhost:5222 -starttls xmpp /dev/null | \ + openssl x509 -noout -subject -dates +``` + +Resolution: ensure `XMPP_DOMAIN` in `.env` matches the domain in your TLS certificate. For dev, re-run `just init` to regenerate certs matching the configured domain. + +## Bridge (Discord↔IRC↔XMPP) + +Container: `atl-bridge` · No exposed ports (connects outbound to IRC, XMPP, and Discord) + +### Diagnostic commands + +```bash +# Container health +docker inspect --format='{{.State.Health.Status}}' atl-bridge + +# Recent logs +docker compose logs --tail=50 atl-bridge + +# Check bridge process is running +docker exec atl-bridge pgrep -f "bridge.__main__" && echo "Bridge running" + +# Search for connection errors +docker compose logs atl-bridge 2>&1 | grep -iE "error|disconnect|reconnect|refused" + +# Check bridge config is mounted +docker exec atl-bridge cat /app/config.yaml | head -10 +``` + +### Common issues + +#### 1. Bridge won't start — waiting for dependencies + +The bridge depends on both `atl-irc-server` and `atl-xmpp-server` being healthy. + +```bash +docker inspect --format='{{.State.Health.Status}}' atl-irc-server +docker inspect --format='{{.State.Health.Status}}' atl-xmpp-server +``` + +Resolution: fix the upstream service first. The bridge starts automatically once both are healthy. + +#### 2. Discord connection fails + +```bash +docker compose logs atl-bridge 2>&1 | grep -i discord +``` + +Resolution: verify `BRIDGE_DISCORD_TOKEN` is set correctly in `.env`. The token must be a valid Discord bot token with the required intents enabled. + +#### 3. IRC connection refused + +The bridge cannot connect to UnrealIRCd. + +```bash +docker compose logs atl-bridge 2>&1 | grep -iE "irc.*refused|irc.*error|irc.*timeout" +``` + +Resolution: check that `BRIDGE_IRC_NICK` and `BRIDGE_IRC_OPER_PASSWORD` are set in `.env` and match the UnrealIRCd oper block configuration. + +#### 4. XMPP component connection fails + +```bash +docker compose logs atl-bridge 2>&1 | grep -iE "xmpp.*component|xmpp.*error" +``` + +Resolution: verify these variables in `.env` match the Prosody component configuration: + +- `BRIDGE_XMPP_COMPONENT_JID` +- `BRIDGE_XMPP_COMPONENT_SECRET` +- `BRIDGE_XMPP_COMPONENT_SERVER` +- `BRIDGE_XMPP_COMPONENT_PORT` + +#### 5. Messages not relaying between protocols + +The bridge is running but messages do not appear on the other side. + +```bash +# Check for relay errors +docker compose logs atl-bridge 2>&1 | grep -iE "relay|forward|send.*fail" + +# Verify config.yaml channel mappings +docker exec atl-bridge cat /app/config.yaml +``` + +Resolution: check that channel mappings in `apps/bridge/config.yaml` are correct. Ensure the bridge bot has the necessary permissions in each channel (IRC oper, Discord channel access, XMPP MUC membership). + +## The Lounge (web IRC client) + +Container: `atl-thelounge` · Port: 9000 + +### Diagnostic commands + +```bash +# Check container is running (no built-in health check) +docker compose ps atl-thelounge + +# Recent logs +docker compose logs --tail=50 atl-thelounge + +# Test HTTP connectivity +curl -sf http://localhost:9000/ -o /dev/null && echo "The Lounge OK" || echo "The Lounge not responding" + +# List registered users +just lounge list + +# Check data directory +ls -la data/thelounge/ +``` + +### Common issues + +#### 1. Cannot access the web interface + +Browser shows connection refused at `http://localhost:9000`. + +```bash +docker compose ps atl-thelounge +docker compose logs --tail=20 atl-thelounge +``` + +Resolution: check the container is running. If it exited, check logs for errors. Common cause: `data/thelounge/` directory does not exist or has wrong permissions. + +```bash +# Ensure directory exists with correct ownership +mkdir -p data/thelounge +sudo chown -R "$(id -u):$(id -g)" data/thelounge/ +docker compose restart atl-thelounge +``` + +#### 2. Login page appears but no users exist + +The Lounge runs in private mode — you must create a user account first. + +```bash +just lounge add +``` + +Follow the prompts to set a username and password. + +#### 3. Cannot connect to IRC server from The Lounge + +You can log in but The Lounge cannot reach UnrealIRCd. + +```bash +# Check if UnrealIRCd is healthy +docker inspect --format='{{.State.Health.Status}}' atl-irc-server + +# Check The Lounge logs for connection errors +docker compose logs atl-thelounge 2>&1 | grep -iE "error|refused|timeout" +``` + +Resolution: The Lounge connects to UnrealIRCd over the Docker network. Ensure UnrealIRCd is running and the server address in The Lounge config points to `atl-irc-server` (the container hostname). + +#### 4. Plugin installation fails + +The janitor or giphy plugins fail to install on startup. + +```bash +docker compose logs atl-thelounge 2>&1 | grep -i plugin +``` + +Resolution: plugin installation runs at container startup and requires network access. If running in an air-gapped environment, pre-install plugins into the `data/thelounge/` volume. Failures are non-fatal — The Lounge still starts without the plugins. + +#### 5. Chat history missing after restart + +The Lounge stores history in `data/thelounge/`. If the volume mount is misconfigured, data is lost on restart. + +```bash +# Verify the volume mount +docker inspect atl-thelounge | jq '.[0].Mounts' +``` + +Resolution: ensure `data/thelounge/` is bind-mounted to `/var/opt/thelounge` in the container. Check that the directory was not accidentally deleted. See [Backups](/docs/operations/backups) for restore procedures. + +## WebPanel (UnrealIRCd admin) + +Container: `atl-irc-webpanel` · Port: 8080 + +### Diagnostic commands + +```bash +# Check container is running (no built-in health check) +docker compose ps atl-irc-webpanel + +# Recent logs +docker compose logs --tail=30 atl-irc-webpanel + +# Test HTTP connectivity +curl -sf http://localhost:8080/ -o /dev/null && echo "WebPanel OK" || echo "WebPanel not responding" + +# Check if the RPC connection to UnrealIRCd works +docker compose exec atl-irc-server ls -la /home/unrealircd/unrealircd/data/rpc.socket +``` + +### Common issues + +#### 1. WebPanel returns 502 or blank page + +```bash +docker compose logs --tail=30 atl-irc-webpanel +``` + +Resolution: the WebPanel connects to UnrealIRCd via JSON-RPC. Ensure UnrealIRCd is running and healthy: + +```bash +docker inspect --format='{{.State.Health.Status}}' atl-irc-server +``` + +If UnrealIRCd is healthy but WebPanel still fails, restart the WebPanel container: + +```bash +docker compose restart atl-irc-webpanel +``` + +#### 2. Cannot log in to WebPanel + +Authentication fails even with correct credentials. + +Resolution: WebPanel authenticates against UnrealIRCd's RPC interface. Verify the RPC credentials in `.env` match the UnrealIRCd configuration. Check that the RPC user block is configured in `apps/unrealircd/config/`. + +#### 3. WebPanel shows stale data + +The panel displays outdated server information. + +Resolution: WebPanel queries UnrealIRCd's RPC in real time. If data appears stale, the RPC connection may have dropped. Restart the WebPanel: + +```bash +docker compose restart atl-irc-webpanel +``` + +#### 4. Port conflict on 8080 + +Another service (commonly a local web server or proxy) is using port 8080. + +```bash +ss -tlnp | grep 8080 +``` + +Resolution: change the WebPanel port in `.env`: + +```bash +WEBPANEL_PORT=8083 +``` + +Then restart: `just dev` + +#### 5. WebPanel data directory permission errors + +```bash +ls -la data/irc/webpanel-data/ +``` + +Resolution: fix ownership: + +```bash +sudo chown -R "$(id -u):$(id -g)" data/irc/webpanel-data/ +docker compose restart atl-irc-webpanel +``` + +## Cross-service issues + +These problems affect multiple services or the stack as a whole. + +### Docker Compose fails to start + +```bash +# Validate compose configuration +docker compose config --quiet && echo "Config OK" || echo "Config has errors" + +# Check for missing .env file +test -f .env && echo ".env exists" || echo ".env missing — copy from .env.example" +``` + +Resolution: ensure `.env` exists. The `CLOUDFLARE_DNS_API_TOKEN` warning is safe to ignore in dev. + +### All services unhealthy after host reboot + +After a host reboot, Docker may not be running. + +```bash +# Check if Docker daemon is running +docker info >/dev/null 2>&1 && echo "Docker OK" || echo "Docker not running" +``` + +Resolution: start Docker and then the stack: + +```bash +sudo dockerd &>/tmp/dockerd.log & +just dev +``` + +### Network connectivity between containers + +Services cannot reach each other over the Docker network. + +```bash +# Check the atl-chat network exists +docker network ls | grep atl-chat + +# Inspect which containers are on the network +docker network inspect atl-chat | jq '.[0].Containers | to_entries[] | {name: .value.Name, ip: .value.IPv4Address}' +``` + +Resolution: if the network is missing, recreate it by restarting the stack: + +```bash +just down +just dev +``` + +### Data directory missing or empty + +Services fail because `data/` subdirectories do not exist. + +```bash +ls -la data/ +``` + +Resolution: re-run the init script to create all required directories: + +```bash +just init +``` + +### Config regeneration after .env changes + +After editing `.env`, services still use old values. + +Resolution: regenerate configs from templates and restart: + +```bash +scripts/prepare-config.sh +just down +just dev +``` + +## Recovery quick reference + +| Scenario | Command | +|---|---| +| Restart a single service | `docker compose restart ` | +| Restart the full stack | `just down && just dev` | +| Regenerate configs | `scripts/prepare-config.sh` | +| Recreate data directories | `just init` | +| Rebuild all containers | `just build` | +| Reset to clean state | `just down && rm -rf data/ && just init && just dev` | +| View real-time logs | `docker compose logs --follow ` | +| Check all container health | `just status` | + +> **Warning:** Resetting to clean state (`rm -rf data/`) destroys all persistent data including user registrations, chat history, and certificates. Back up first — see [Backups](/docs/operations/backups). + +## Related pages + +- [IRC Troubleshooting](/docs/services/irc/troubleshooting) — in-depth IRC-specific diagnostics +- [Monitoring](/docs/operations/monitoring) — health checks, metrics, and alerting +- [Backups](/docs/operations/backups) — backup and restore procedures +- [SSL/TLS](/docs/operations/ssl-tls) — certificate management and renewal +- [Deployment](/docs/operations/deployment) — production deployment runbook +- [Environment Variables](/docs/reference/environment-variables) — complete variable reference diff --git a/apps/docs/content/docs/reference/api.mdx b/apps/docs/content/docs/reference/api.mdx new file mode 100644 index 00000000..18db74c2 --- /dev/null +++ b/apps/docs/content/docs/reference/api.mdx @@ -0,0 +1,183 @@ +--- +title: API Reference +description: JSON-RPC and REST API reference for atl.chat services and the Portal integration. +--- + +The atl.chat stack exposes several internal APIs for server management, IRC services, XMPP user provisioning, and cross-protocol identity resolution. Most APIs are consumed internally by other services in the stack or by the external Portal application — they are not designed for direct end-user access. + +## API overview + +| API | Protocol | Port | Consumer | Purpose | +|---|---|---|---|---| +| [UnrealIRCd JSON-RPC](#unrealircd-json-rpc) | JSON-RPC 2.0 | 8600 | WebPanel, Portal | IRC server management | +| [Atheme HTTP API](#atheme-http-api) | JSON-RPC | 8081 | Portal | IRC services management (NickServ, ChanServ, etc.) | +| [Prosody REST API](#prosody-rest-api) | REST (HTTP) | 5280 | Portal | XMPP user account provisioning | +| [Portal Bridge API](#portal-bridge-api) | REST (HTTP) | — | Bridge | Cross-protocol identity resolution | +| [Fumadocs Search API](#fumadocs-search-api) | REST (HTTP) | 3000 | Docs UI | Documentation full-text search | + +## UnrealIRCd JSON-RPC + +UnrealIRCd 6.x exposes a [JSON-RPC 2.0](https://www.unrealircd.org/docs/JSON-RPC) interface for server management. The WebPanel and Portal use this API to query server state, manage users, and perform administrative actions. + +| Detail | Value | +|---|---| +| Endpoint | `https://:8600/api` | +| Protocol | JSON-RPC 2.0 over HTTPS | +| Authentication | HTTP Basic (`IRC_UNREAL_RPC_USER` / `IRC_UNREAL_RPC_PASSWORD`) | +| Internal socket | `/home/unrealircd/unrealircd/data/rpc.socket` (used by healthcheck) | + +The healthcheck verifies the RPC socket is responsive: + +```bash +echo '{"jsonrpc":"2.0","method":"rpc.info","params":{},"id":1}' \ + | nc -U /home/unrealircd/unrealircd/data/rpc.socket +``` + +Common methods include `rpc.info`, `user.list`, `channel.list`, and `server.rehash`. See the [UnrealIRCd JSON-RPC documentation](https://www.unrealircd.org/docs/JSON-RPC) for the full method reference. + +### Environment variables + +| Variable | Description | Default | +|---|---|---| +| `IRC_UNREAL_JSONRPC_URL` | Full RPC endpoint URL | `https://irc.atl.chat:8600/api` | +| `IRC_UNREAL_RPC_USER` | RPC username (matches `WEBPANEL_RPC_USER`) | `adminpanel` | +| `IRC_UNREAL_RPC_PASSWORD` | RPC password (matches `WEBPANEL_RPC_PASSWORD`) | `change_me_webpanel_password` | + +> **Warning:** Change `IRC_UNREAL_RPC_PASSWORD` before production deployment. This credential grants full administrative access to the IRC server. + +## Atheme HTTP API + +Atheme IRC services expose an HTTP JSON-RPC interface for programmatic access to NickServ, ChanServ, OperServ, and other service bots. The Portal uses this API to manage IRC registrations and query account information. + +| Detail | Value | +|---|---| +| Endpoint | `http://atl-irc-server:8081/jsonrpc` | +| Protocol | JSON-RPC over HTTP | +| Authentication | Atheme credentials (configured in Atheme's `httpd` block) | + +Atheme shares the network namespace with UnrealIRCd (`network_mode: service:atl-irc-server`), so port 8081 is exposed through the IRC server container. + +### Environment variables + +| Variable | Description | Default | +|---|---|---| +| `IRC_ATHEME_JSONRPC_URL` | Atheme JSON-RPC endpoint URL | `http://atl-irc-server:8081/jsonrpc` | + +## Prosody REST API + +Prosody exposes a REST API via [`mod_http_admin_api`](https://modules.prosody.im/mod_http_admin_api) for XMPP user account management. The Portal uses this API to provision and deprovision XMPP accounts. Self-registration is disabled — all user creation flows through this API. + +| Detail | Value | +|---|---| +| Base URL | `http://atl-xmpp-server:5280` | +| Protocol | REST over HTTP | +| Authentication | OAuth2 Bearer token (via `mod_http_oauth2`) or HTTP Basic | +| Module | `mod_http_admin_api` (loaded per-VirtualHost, not globally) | + +The `mod_http_oauth2` module generates Bearer tokens using the Resource Owner Password Grant. These tokens are also usable for OAUTHBEARER SASL authentication. + +### Environment variables + +| Variable | Description | Default | +|---|---|---| +| `PROSODY_REST_URL` | Prosody REST API base URL | `http://atl-xmpp-server:5280` | +| `PROSODY_REST_USERNAME` | REST admin username (JID) | `admin@atl.chat` | +| `PROSODY_REST_PASSWORD` | REST admin password | `change_me_prosody_rest_password` | + +> **Warning:** Change `PROSODY_REST_PASSWORD` before production deployment. This credential grants admin-level access to XMPP user management. + +## Portal Bridge API + +The bridge optionally integrates with the [Portal](https://github.com/allthingslinux/portal) application for unified user identity across protocols. The Portal is a separate Next.js project and is not part of this monorepo. When `BRIDGE_PORTAL_BASE_URL` is set, the bridge creates a `PortalClient` and `IdentityResolver` to map users between Discord, IRC, and XMPP. + +### Identity endpoint + +The bridge calls a single Portal endpoint to resolve cross-protocol identities: + +``` +GET {BRIDGE_PORTAL_BASE_URL}/api/bridge/identity +``` + +Authentication uses a Bearer token (`BRIDGE_PORTAL_TOKEN`) in the `Authorization` header. + +### Query parameters + +Look up a user identity by providing one of the following query parameters: + +| Parameter | Type | Description | +|---|---|---| +| `discordId` | string | Discord user ID | +| `ircNick` | string | IRC nickname | +| `ircServer` | string | IRC server hostname (optional, used with `ircNick`) | +| `xmppJid` | string | XMPP JID | + +### Response + +The endpoint returns a JSON object with the user's linked identities: + +```json +{ + "ok": true, + "identity": { + "user_id": "portal-user-id", + "discord_id": "123456789", + "irc_nick": "username", + "xmpp_jid": "username@atl.chat" + } +} +``` + +Returns `404` when no matching identity is found. The bridge's `IdentityResolver` caches results in a TTL cache (default: 1 hour, max 1024 entries) to reduce API calls. + +### Identity resolution flow + +The bridge uses the `IdentityResolver` to translate user identities when relaying messages: + +1. A message arrives on one protocol (e.g., Discord) +2. The resolver checks its TTL cache for a cached identity +3. On cache miss, it calls `GET /api/bridge/identity?discordId=` +4. The Portal returns the linked IRC nick and/or XMPP JID +5. The bridge uses the resolved identity for puppet nicks on the target protocol + +When `BRIDGE_PORTAL_BASE_URL` is not set, identity resolution is disabled and the bridge falls back to dev puppet nicks or generic relay formatting. + +### Environment variables + +| Variable | Description | Default | +|---|---|---| +| `BRIDGE_PORTAL_BASE_URL` | Portal API base URL (also accepts `BRIDGE_PORTAL_URL`) | `https://portal.atl.tools` | +| `BRIDGE_PORTAL_TOKEN` | Bearer token for Portal API (also accepts `BRIDGE_PORTAL_API_TOKEN`) | `change_me_bridge_portal_token` | + +> **Warning:** Change `BRIDGE_PORTAL_TOKEN` before production deployment. + +### Retry behaviour + +The `PortalClient` uses exponential backoff retries (up to 5 attempts, 2–30 second delays) for transient HTTP errors including connection timeouts, read/write errors, and HTTP status errors. + +## Fumadocs Search API + +The docs site exposes an [Orama](https://orama.com/) search endpoint used by the built-in search UI: + +``` +GET /api/search?query= +``` + +This is an internal endpoint powering the documentation search bar. It is not intended for external consumption and its schema may change between releases. + +## Future APIs + +The following APIs are planned or under consideration: + +- **Bridge metrics endpoint** — expose relay statistics (message counts, latency, error rates) for monitoring integration +- **Bridge admin API** — runtime configuration changes (reload config, pause/resume relays) without container restart +- **Portal webhook receiver** — allow the Portal to push identity updates to the bridge instead of polling + +These APIs will be documented here as they are implemented. Track progress in the [atl.chat GitHub repository](https://github.com/allthingslinux/atl.chat). + +## Related pages + +- [Environment Variables](/docs/reference/environment-variables) — full variable reference including all API credentials +- [Ports Reference](/docs/reference/ports) — complete port registry for all services +- [Bridge Configuration](/docs/services/bridge/configuration) — bridge setup and Portal integration +- [WebPanel Configuration](/docs/services/webpanel/configuration) — WebPanel RPC connection setup +- [Security](/docs/operations/security) — credential rotation and secret management diff --git a/apps/docs/content/docs/reference/environment-variables.mdx b/apps/docs/content/docs/reference/environment-variables.mdx new file mode 100644 index 00000000..a4fdce32 --- /dev/null +++ b/apps/docs/content/docs/reference/environment-variables.mdx @@ -0,0 +1,493 @@ +--- +title: Environment Variables +description: Complete reference for all environment variables used across atl.chat services, organised by section with defaults, required status, and security guidance. +--- + +Every configurable value in the atl.chat stack is driven by environment variables defined in `.env` (gitignored), with defaults documented in `.env.example`. This page is the canonical reference for every variable, organised by the same sections as `.env.example`: Core, IRC, XMPP, Bridge, Web, Docs, and Portal. + +## How environment files work + +The stack uses a layered environment file approach: + +1. **`.env`** — your primary configuration file, copied from `.env.example`. Contains all production values. +2. **`.env.dev`** — an optional overlay for local development, copied from `.env.dev.example`. Docker Compose loads `.env` first, then `.env.dev`, so any variable defined in both files is overridden by the `.env.dev` value. + +To set up: + +```bash +# First-time setup +cp .env.example .env +cp .env.dev.example .env.dev + +# Then start the dev stack +just dev +``` + +The `just dev` command passes both files to Docker Compose. The `just prod` command only loads `.env`. + +## Core + +Core variables control the overall environment, Docker user mapping, host networking, and certificate management. + +| Variable | Description | Required | Default | +|---|---|---|---| +| `ATL_ENVIRONMENT` | Environment mode (`dev` or `prod`) — controls TLS verification and log levels | Yes | `dev` | +| `PUID` | Docker container user ID (solves volume permission issues) | No | `1000` | +| `PGID` | Docker container group ID (solves volume permission issues) | No | `1000` | +| `TZ` | Timezone for all containers | No | `UTC` | +| `ATL_GATEWAY_IP` | Gateway IP address (Tailscale IP for prod, `127.0.0.1` for dev) | Yes | `100.64.1.0` | +| `ATL_CHAT_IP` | Chat server IP address (Tailscale IP for prod, `127.0.0.1` for dev) | Yes | `100.64.7.0` | +| `LETSENCRYPT_EMAIL` | Email address for Let's Encrypt certificate registration | Yes | `admin@allthingslinux.org` | +| `CLOUDFLARE_DNS_API_TOKEN` | Cloudflare API token for DNS-01 challenge (cert-manager) | Prod only | _(empty)_ | + +> **Warning:** `CLOUDFLARE_DNS_API_TOKEN` is a sensitive credential that grants DNS modification access. Store it securely and never commit it to version control. In dev, this variable can be left empty — Docker Compose emits a warning about it being unset, which is safe to ignore. + +## IRC Service (UnrealIRCd + Atheme) + +### Build versions + +| Variable | Description | Required | Default | +|---|---|---|---| +| `UNREALIRCD_VERSION` | UnrealIRCd Docker image git tag | Yes | `6.2.0.1` | +| `ATHEME_VERSION` | Atheme Docker image git tag | Yes | `master` | + +> **Warning:** `ATHEME_VERSION=master` is non-reproducible. Pin to a specific commit or tag for production deployments. + +### Network identity + +| Variable | Description | Required | Default | +|---|---|---|---| +| `IRC_DOMAIN` | Public hostname of the IRC server | Yes | `irc.atl.chat` | +| `IRC_ROOT_DOMAIN` | Root domain for the IRC network | Yes | `atl.chat` | +| `IRC_NETWORK_NAME` | Human-readable network name shown to clients | Yes | `All Things Linux IRC` | +| `IRC_CLOAK_PREFIX` | Prefix used in cloaked hostnames | Yes | `atl` | + +### Ports + +| Variable | Description | Required | Default | +|---|---|---|---| +| `IRC_TLS_PORT` | TLS client connection port | Yes | `6697` | +| `IRC_SERVER_PORT` | Server-to-server link port | Yes | `6900` | +| `IRC_RPC_PORT` | JSON-RPC API port | Yes | `8600` | +| `IRC_WEBSOCKET_PORT` | WebSocket client connection port | Yes | `8000` | + +### Security secrets + +| Variable | Description | Required | Default | +|---|---|---|---| +| `IRC_CLOAK_KEY_1` | First cloak key for IP masking (must be identical on all servers) | Yes | _(example key)_ | +| `IRC_CLOAK_KEY_2` | Second cloak key for IP masking | Yes | _(example key)_ | +| `IRC_CLOAK_KEY_3` | Third cloak key for IP masking | Yes | _(example key)_ | +| `IRC_OPER_PASSWORD` | IRC operator (oper) password | Yes | `change_me_irc_oper_password` | +| `IRC_DRPASS` | Die/restart password for UnrealIRCd | Yes | `change_me_drpass` | +| `IRC_SERVICES_PASSWORD` | Link password between UnrealIRCd and Atheme | Yes | `change_me_secure_services_pass` | +| `ATL_WEBIRC_PASSWORD` | WebIRC password for web client authentication | Yes | `change_me_webirc_password` | + +> **Warning:** All IRC security secrets must be changed before any public or production deployment. The example values in `.env.example` are placeholders only. Generate cloak keys with `just irc gencloak`. Generate hashed oper passwords with: +> ```bash +> docker run --rm ghcr.io/allthingslinux/unrealircd ./unrealircd mkpasswd argon2 +> ``` + +### Admin info + +| Variable | Description | Required | Default | +|---|---|---|---| +| `IRC_ADMIN_NAME` | Admin name shown in `/admin` | Yes | `All Things Linux` | +| `IRC_ADMIN_EMAIL` | Admin contact email | Yes | `admin@allthingslinux.org` | +| `IRC_STAFF_VHOST` | Virtual host assigned to staff members | No | `allthingslinux.org` | + +### Strict Transport Security (STS) + +| Variable | Description | Required | Default | +|---|---|---|---| +| `IRC_STS_DURATION` | STS policy duration | No | `1m` | +| `IRC_STS_PRELOAD` | Whether to include STS preload directive | No | `no` | + +### TLS certificate paths + +| Variable | Description | Required | Default | +|---|---|---|---| +| `IRC_SSL_CERT_PATH` | Path to TLS certificate inside the container | Yes | `/home/unrealircd/unrealircd/certs/live/irc.atl.chat/fullchain.pem` | +| `IRC_SSL_KEY_PATH` | Path to TLS private key inside the container | Yes | `/home/unrealircd/unrealircd/certs/live/irc.atl.chat/privkey.pem` | + +> **Warning:** `IRC_SSL_KEY_PATH` points to a TLS private key. Ensure the key file has restrictive permissions (readable only by the UnrealIRCd process) and is never committed to version control. + +### Atheme services link + +| Variable | Description | Required | Default | +|---|---|---|---| +| `IRC_SERVICES_SERVER` | Hostname of the Atheme services server | Yes | `services.atl.chat` | +| `ATHEME_SERVER_NAME` | Atheme server name on the IRC network | Yes | `services.atl.chat` | +| `ATHEME_SERVER_DESC` | Atheme server description | Yes | `All Things Linux IRC Services` | +| `ATHEME_UPLINK_HOST` | Host Atheme connects to (UnrealIRCd) | Yes | `127.0.0.1` | +| `ATHEME_UPLINK_PORT` | Port Atheme uses to connect to UnrealIRCd | Yes | `6901` | +| `ATHEME_NUMERIC` | Atheme server numeric identifier | Yes | `00A` | +| `ATHEME_RECONTIME` | Reconnection interval in seconds | No | `10` | +| `ATHEME_HTTPD_PORT` | Atheme HTTP API port | Yes | `8081` | + +### Atheme network info + +| Variable | Description | Required | Default | +|---|---|---|---| +| `ATHEME_NETNAME` | Network name used by Atheme | Yes | `atl.chat` | +| `ATHEME_ADMIN_NAME` | Atheme admin display name | Yes | `All Things Linux` | +| `ATHEME_ADMIN_EMAIL` | Atheme admin contact email | Yes | `admin@allthingslinux.org` | +| `ATHEME_REGISTER_EMAIL` | From address for registration emails | Yes | `noreply@allthingslinux.org` | +| `ATHEME_HIDEHOST_SUFFIX` | Suffix for hidden hostnames | Yes | `users.atl.chat` | +| `ATHEME_HELP_CHANNEL` | Default help channel | No | `#help` | +| `ATHEME_HELP_URL` | URL for additional help | No | `https://discord.gg/linux` | + + +### Atheme service bot identities + +Each Atheme service bot has four identity variables following the pattern `ATHEME__NICK`, `_USER`, `_HOST`, `_REAL`. The table below lists all service bots and their defaults. + +| Service | Nick | User | Host | Real Name | +|---|---|---|---|---| +| NickServ | `NickServ` | `NickServ` | `services.atl.chat` | Nickname Services | +| ChanServ | `ChanServ` | `ChanServ` | `services.atl.chat` | Channel Services | +| OperServ | `OperServ` | `OperServ` | `services.atl.chat` | Operator Services | +| MemoServ | `MemoServ` | `MemoServ` | `services.atl.chat` | Memo Services | +| SaslServ | `SaslServ` | `SaslServ` | `services.atl.chat` | SASL Authentication Agent | +| BotServ | `BotServ` | `BotServ` | `services.atl.chat` | Bot Services | +| GroupServ | `GroupServ` | `GroupServ` | `services.atl.chat` | Group Management Services | +| HostServ | `HostServ` | `HostServ` | `services.atl.chat` | Host Management Services | +| InfoServ | `InfoServ` | `InfoServ` | `services.atl.chat` | Information Service | +| HelpServ | `HelpServ` | `HelpServ` | `services.atl.chat` | Help Services | +| StatServ | `StatServ` | `StatServ` | `services.atl.chat` | Statistics Services | +| Global | `Global` | `Global` | `services.atl.chat` | Network Announcements | +| ALIS | `ALIS` | `alis` | `services.atl.chat` | Channel Directory | +| Proxyscan | `Proxyscan` | `dnsbl` | `services.atl.chat` | Proxyscan Service | +| GameServ | `GameServ` | `GameServ` | `services.atl.chat` | Game Services | +| RPGServ | `RPGServ` | `RPGServ` | `services.atl.chat` | RPG Finding Services | + +The full variable names follow the pattern: `ATHEME_NICKSERV_NICK=NickServ`, `ATHEME_NICKSERV_USER=NickServ`, `ATHEME_NICKSERV_HOST=services.atl.chat`, `ATHEME_NICKSERV_REAL="Nickname Services"`. Repeat for each service listed above. + +### WebPanel + +| Variable | Description | Required | Default | +|---|---|---|---| +| `WEBPANEL_PORT` | Port for the UnrealIRCd web admin panel | Yes | `8080` | +| `WEBPANEL_RPC_USER` | RPC username for WebPanel to authenticate with UnrealIRCd | Yes | `adminpanel` | +| `WEBPANEL_RPC_PASSWORD` | RPC password for WebPanel authentication | Yes | `change_me_webpanel_password` | + +> **Warning:** `WEBPANEL_RPC_PASSWORD` grants administrative access to UnrealIRCd via the JSON-RPC API. Change this to a strong, unique value before production deployment. + +### The Lounge + +| Variable | Description | Required | Default | +|---|---|---|---| +| `THELOUNGE_PORT` | Port for The Lounge web IRC client | Yes | `9000` | +| `THELOUNGE_WEBIRC_PASSWORD` | WebIRC password for The Lounge to authenticate with UnrealIRCd | Yes | `change_me_thelounge_webirc` | +| `THELOUNGE_DELETE_UPLOADS_AFTER_MINUTES` | Auto-delete uploaded files after this many minutes | No | `1440` | + +> **Warning:** `THELOUNGE_WEBIRC_PASSWORD` must match the WebIRC password configured in UnrealIRCd. Change this from the default before production deployment. + +## XMPP Service (Prosody) + +### Domain and admin + +| Variable | Description | Required | Default | +|---|---|---|---| +| `XMPP_DOMAIN` | Primary XMPP domain (Prosody VirtualHost) | Yes | `atl.chat` | +| `PROSODY_ADMIN_EMAIL` | Admin contact email for Prosody | Yes | `admin@allthingslinux.org` | + +### Storage + +| Variable | Description | Required | Default | +|---|---|---|---| +| `PROSODY_STORAGE` | Storage backend (`sqlite` for dev, `sql` for prod with PostgreSQL) | Yes | `sqlite` | + +### Database (only used when `PROSODY_STORAGE=sql`) + +| Variable | Description | Required | Default | +|---|---|---|---| +| `PROSODY_DB_DRIVER` | Database driver | Conditional | `PostgreSQL` | +| `PROSODY_DB_HOST` | Database hostname | Conditional | `xmpp-postgres-dev` | +| `PROSODY_DB_PORT` | Database port | Conditional | `5432` | +| `PROSODY_DB_NAME` | Database name | Conditional | `prosody` | +| `PROSODY_DB_USER` | Database username | Conditional | `prosody` | +| `PROSODY_DB_PASSWORD` | Database password | Conditional | `change_me_secure_db_pass` | + +> **Warning:** `PROSODY_DB_PASSWORD` is a database credential. Use a strong, unique password in production and restrict database network access to the Prosody container only. + +### Ports + +| Variable | Description | Required | Default | +|---|---|---|---| +| `PROSODY_C2S_PORT` | Client-to-server port | Yes | `5222` | +| `PROSODY_S2S_PORT` | Server-to-server port | Yes | `5269` | +| `PROSODY_HTTP_PORT` | HTTP port (BOSH/WebSocket) | Yes | `5280` | +| `PROSODY_HTTPS_PORT` | HTTPS port (via nginx) | Yes | `5281` | +| `PROSODY_C2S_DIRECT_TLS_PORT` | Direct TLS client port | No | `5223` | +| `PROSODY_S2S_DIRECT_TLS_PORT` | Direct TLS server port | No | `5270` | +| `PROSODY_PROXY65_PORT` | SOCKS5 bytestream proxy port | No | `5000` | + +### TURN/STUN + +| Variable | Description | Required | Default | +|---|---|---|---| +| `TURN_PORT` | TURN server port | No | `3478` | +| `TURNS_PORT` | TURN over TLS port | No | `5349` | +| `TURN_SECRET` | Shared secret for TURN authentication | Yes | `change_me_turn_secret` | +| `TURN_EXTERNAL_HOST` | External hostname for TURN server | Yes | `turn.atl.network` | + +> **Warning:** `TURN_SECRET` is a shared authentication secret. Generate a strong random value for production. + +### Security + +| Variable | Description | Required | Default | +|---|---|---|---| +| `PROSODY_OAUTH2_REGISTRATION_KEY` | OAuth2 registration key | Yes | `change_me_secure_oauth2_key` | +| `PROSODY_ALLOW_REGISTRATION` | Allow public account registration | No | `false` | +| `PROSODY_C2S_REQUIRE_ENCRYPTION` | Require TLS for client connections | No | `true` | +| `PROSODY_S2S_REQUIRE_ENCRYPTION` | Require TLS for server-to-server | No | `true` | +| `PROSODY_S2S_SECURE_AUTH` | Require verified TLS certificates for s2s | No | `true` | +| `PROSODY_ALLOW_UNENCRYPTED_PLAIN_AUTH` | Allow plaintext authentication without TLS | No | `false` | +| `PROSODY_MAX_CONNECTIONS_PER_IP` | Maximum concurrent connections per IP | No | `10` | +| `PROSODY_REGISTRATION_THROTTLE_MAX` | Maximum registrations per throttle period | No | `10` | +| `PROSODY_REGISTRATION_THROTTLE_PERIOD` | Throttle period in seconds | No | `60` | +| `PROSODY_BLOCK_REGISTRATIONS_REQUIRE` | Regex pattern usernames must match | No | `^[a-zA-Z0-9_.-]+$` | +| `PROSODY_TLS_CHANNEL_BINDING` | Enable TLS channel binding | No | `false` | + +> **Warning:** `PROSODY_OAUTH2_REGISTRATION_KEY` is a sensitive key used for OAuth2 registration. Change it from the default before production deployment. + + +### HTTP + +| Variable | Description | Required | Default | +|---|---|---|---| +| `PROSODY_HTTP_HOST` | HTTP host binding | No | `localhost` | +| `PROSODY_HTTP_SCHEME` | HTTP scheme (`http` or `https`) | No | `http` | +| `PROSODY_UPLOAD_EXTERNAL_URL` | External URL for HTTP file upload | No | `http://localhost:5280/upload/` | +| `PROSODY_PROXY_ADDRESS` | Proxy address for Prosody | No | `localhost` | + +### TLS certificates + +| Variable | Description | Required | Default | +|---|---|---|---| +| `PROSODY_SSL_KEY` | Path to TLS private key (relative to Prosody cert dir) | No | _(derived from `XMPP_DOMAIN`)_ | +| `PROSODY_SSL_CERT` | Path to TLS certificate (relative to Prosody cert dir) | No | _(derived from `XMPP_DOMAIN`)_ | + +> **Warning:** `PROSODY_SSL_KEY` points to a TLS private key. Ensure the key file has restrictive permissions and is never committed to version control. + +### Logging and statistics + +| Variable | Description | Required | Default | +|---|---|---|---| +| `PROSODY_LOG_LEVEL` | Log verbosity (`debug`, `info`, `warn`, `error`) | No | `info` | +| `PROSODY_STATISTICS` | Statistics backend (`internal` or `statsd`) | No | `internal` | +| `PROSODY_STATISTICS_INTERVAL` | Statistics collection interval | No | `manual` | +| `PROSODY_OPENMETRICS_IP` | IP address for OpenMetrics endpoint | No | `127.0.0.1` | +| `PROSODY_OPENMETRICS_CIDR` | CIDR range allowed to access OpenMetrics | No | `127.0.0.1/32` | + +### Message Archiving (MAM) + +| Variable | Description | Required | Default | +|---|---|---|---| +| `PROSODY_ARCHIVE_EXPIRES_AFTER` | Archive retention period | No | `30d` | +| `PROSODY_ARCHIVE_POLICY` | Enable message archiving | No | `true` | +| `PROSODY_ARCHIVE_COMPRESSION` | Enable archive compression | No | `true` | +| `PROSODY_ARCHIVE_STORE` | Archive storage backend name | No | `archive` | +| `PROSODY_ARCHIVE_MAX_QUERY_RESULTS` | Maximum results per MAM query | No | `250` | +| `PROSODY_MAM_SMART_ENABLE` | Enable smart MAM (archive only when needed) | No | `true` | + +### MUC (Multi-User Chat) + +| Variable | Description | Required | Default | +|---|---|---|---| +| `PROSODY_MUC_NOTIFICATIONS` | Enable MUC notifications | No | `true` | +| `PROSODY_MUC_OFFLINE_DELIVERY` | Deliver messages to offline MUC participants | No | `true` | +| `PROSODY_RESTRICT_ROOM_CREATION` | Restrict who can create rooms | No | `false` | +| `PROSODY_MUC_DEFAULT_PUBLIC` | New rooms are public by default | No | `true` | +| `PROSODY_MUC_DEFAULT_PERSISTENT` | New rooms are persistent by default | No | `true` | +| `PROSODY_MUC_DEFAULT_PUBLIC_JIDS` | Show participant JIDs by default | No | `true` | +| `PROSODY_MUC_LOCKING` | Lock rooms until configured | No | `false` | +| `PROSODY_MUC_LOG_BY_DEFAULT` | Log MUC messages by default | No | `true` | +| `PROSODY_MUC_LOG_EXPIRES_AFTER` | MUC log retention period | No | `1y` | +| `PROSODY_MUC_LOG_PRESENCES` | Log presence changes in MUC | No | `false` | +| `PROSODY_MUC_LOG_ALL_ROOMS` | Log all rooms regardless of room setting | No | `false` | +| `PROSODY_MUC_LOG_CLEANUP_INTERVAL` | Cleanup interval for expired logs (seconds) | No | `86400` | +| `PROSODY_MUC_MAX_ARCHIVE_QUERY_RESULTS` | Maximum MUC archive query results | No | `100` | +| `PROSODY_MUC_LOG_STORE` | MUC log storage backend name | No | `muc_log` | +| `PROSODY_MUC_LOG_COMPRESSION` | Enable MUC log compression | No | `true` | +| `PROSODY_MUC_MAM_SMART_ENABLE` | Enable smart MAM for MUC | No | `false` | + +### Rate limiting + +| Variable | Description | Required | Default | +|---|---|---|---| +| `PROSODY_C2S_RATE` | Client-to-server rate limit | No | `10kb/s` | +| `PROSODY_C2S_BURST` | Client-to-server burst allowance | No | `25kb` | +| `PROSODY_C2S_STANZA_SIZE` | Maximum c2s stanza size (bytes) | No | `262144` | +| `PROSODY_S2S_RATE` | Server-to-server rate limit | No | `30kb/s` | +| `PROSODY_S2S_BURST` | Server-to-server burst allowance | No | `100kb` | +| `PROSODY_S2S_STANZA_SIZE` | Maximum s2s stanza size (bytes) | No | `524288` | +| `PROSODY_HTTP_UPLOAD_RATE` | HTTP upload rate limit | No | `2mb/s` | +| `PROSODY_HTTP_UPLOAD_BURST` | HTTP upload burst allowance | No | `10mb` | + +### Push notifications + +| Variable | Description | Required | Default | +|---|---|---|---| +| `PROSODY_PUSH_IMPORTANT_BODY` | Body text for important push notifications | No | `New Message!` | +| `PROSODY_PUSH_MAX_ERRORS` | Maximum push errors before disabling | No | `16` | +| `PROSODY_PUSH_MAX_DEVICES` | Maximum push devices per user | No | `5` | +| `PROSODY_PUSH_MAX_HIBERNATION_TIMEOUT` | Maximum hibernation timeout (seconds) | No | `259200` | +| `PROSODY_PUSH_NOTIFICATION_WITH_BODY` | Include message body in push | No | `false` | +| `PROSODY_PUSH_NOTIFICATION_WITH_SENDER` | Include sender in push | No | `false` | + +### Account lifecycle + +| Variable | Description | Required | Default | +|---|---|---|---| +| `PROSODY_ACCOUNT_INACTIVE_PERIOD` | Seconds before an account is considered inactive | No | `31536000` | +| `PROSODY_ACCOUNT_GRACE_PERIOD` | Grace period before inactive account cleanup (seconds) | No | `2592000` | + +### Server info + +| Variable | Description | Required | Default | +|---|---|---|---| +| `PROSODY_SERVER_NAME` | Server display name | No | `localhost` | +| `PROSODY_SERVER_WEBSITE` | Server website URL | No | `http://localhost` | +| `PROSODY_SERVER_DESCRIPTION` | Server description | No | `XMPP Service` | + +### Performance tuning (Lua GC) + +| Variable | Description | Required | Default | +|---|---|---|---| +| `LUA_GC_STEP_SIZE` | Lua garbage collector step size | No | `13` | +| `LUA_GC_PAUSE` | Lua GC pause parameter | No | `110` | +| `LUA_GC_SPEED` | Lua GC speed parameter | No | `200` | +| `LUA_GC_THRESHOLD` | Lua GC threshold parameter | No | `120` | + +### PubSub feeds + +| Variable | Description | Required | Default | +|---|---|---|---| +| `PROSODY_FEED_URL` | Atom/RSS feed URL for PubSub | No | `https://allthingslinux.org/feed` | + +## Bridge Service (Discord↔IRC↔XMPP relay) + +> **Warning:** `BRIDGE_DISCORD_TOKEN` is a Discord bot token that grants full bot access. Never share it or commit it to version control. Regenerate immediately if exposed. `BRIDGE_PORTAL_TOKEN` is also a sensitive API credential — change it from the default before production. + +### Discord and Portal + +| Variable | Description | Required | Default | +|---|---|---|---| +| `BRIDGE_DISCORD_TOKEN` | Discord bot token for the bridge | Yes | `change_me_discord_bot_token` | +| `BRIDGE_DISCORD_CHANNEL_ID` | Discord channel ID to bridge | Yes | `REPLACE_WITH_DISCORD_CHANNEL_ID` | +| `BRIDGE_PORTAL_BASE_URL` | Base URL for the portal API | Yes | `https://portal.atl.tools` | +| `BRIDGE_PORTAL_TOKEN` | API token for portal authentication | Yes | `change_me_bridge_portal_token` | + +### XMPP component + +| Variable | Description | Required | Default | +|---|---|---|---| +| `BRIDGE_XMPP_COMPONENT_JID` | JID for the bridge XMPP component | Yes | `bridge.atl.chat` | +| `BRIDGE_XMPP_COMPONENT_SECRET` | Shared secret for XMPP component authentication | Yes | `change_me_xmpp_component_secret` | +| `BRIDGE_XMPP_COMPONENT_SERVER` | XMPP server hostname (Docker service name) | Yes | `atl-xmpp-server` | +| `BRIDGE_XMPP_COMPONENT_PORT` | XMPP component port | Yes | `5347` | + +> **Warning:** `BRIDGE_XMPP_COMPONENT_SECRET` is a shared secret between the bridge and Prosody. Change it from the default and keep it synchronised between both services. + +### IRC + +| Variable | Description | Required | Default | +|---|---|---|---| +| `BRIDGE_IRC_NICK` | IRC nickname used by the bridge bot | Yes | `bridge` | +| `BRIDGE_IRC_OPER_PASSWORD` | IRC oper password for the bridge bot | Yes | `change_me_bridge_oper` | +| `IRC_BRIDGE_SERVER` | IRC server hostname (Docker service name) | Yes | `atl-irc-server` | + +> **Warning:** `BRIDGE_IRC_OPER_PASSWORD` grants IRC operator privileges to the bridge bot. Use a strong, unique password. + +## Web Frontend (Next.js) + +| Variable | Description | Required | Default | +|---|---|---|---| +| `NEXT_PUBLIC_IRC_WS_URL` | WebSocket URL for the IRC client (browser-accessible) | Yes | `wss://irc.atl.chat/ws` | +| `NEXT_PUBLIC_XMPP_BOSH_URL` | BOSH URL for the XMPP client (browser-accessible) | Yes | `https://xmpp.atl.chat/http-bind` | + +These are `NEXT_PUBLIC_` prefixed variables, meaning they are embedded into the client-side JavaScript bundle at build time. Change them before building for your deployment domain. + +## Docs (Cloudflare Workers via Alchemy) + +| Variable | Description | Required | Default | +|---|---|---|---| +| `ALCHEMY_PASSWORD` | Password used by Alchemy to encrypt deployment state secrets | Yes | `change-me` | + +> **Warning:** `ALCHEMY_PASSWORD` encrypts secrets in the deployment state. Use a strong, unique value and do not share it. Required only when running `pnpm run deploy` or `pnpm run destroy` from `apps/docs`. + +## Portal Integration (external service) + +These variables are pre-defined for the external ATL Portal service that connects to IRC/XMPP services. They are **not consumed by any compose file or config template in this monorepo** — they exist in `.env.example` for documentation and convenience when the Portal service is co-located. + +| Variable | Description | Required | Default | +|---|---|---|---| +| `IRC_ATHEME_JSONRPC_URL` | Atheme JSON-RPC endpoint URL | No | `http://atl-irc-server:8081/jsonrpc` | +| `IRC_UNREAL_JSONRPC_URL` | UnrealIRCd JSON-RPC endpoint URL | No | `https://irc.atl.chat:8600/api` | +| `IRC_UNREAL_RPC_USER` | UnrealIRCd RPC username (matches `WEBPANEL_RPC_USER`) | No | `adminpanel` | +| `IRC_UNREAL_RPC_PASSWORD` | UnrealIRCd RPC password (matches `WEBPANEL_RPC_PASSWORD`) | No | `change_me_webpanel_password` | +| `PROSODY_REST_URL` | Prosody REST API URL | No | `http://atl-xmpp-server:5280` | +| `PROSODY_REST_USERNAME` | Prosody REST admin username | No | `admin@atl.chat` | +| `PROSODY_REST_PASSWORD` | Prosody REST admin password | No | `change_me_prosody_rest_password` | +| `IRC_SERVER` | IRC server hostname for Portal connections | No | `irc.atl.chat` | +| `IRC_PORT` | IRC TLS port for Portal connections | No | `6697` | + +> **Warning:** `IRC_UNREAL_RPC_PASSWORD`, `PROSODY_REST_PASSWORD`, and `IRC_UNREAL_RPC_USER` are administrative credentials. If you deploy the Portal alongside this stack, ensure these match the corresponding service passwords and are changed from defaults. + +## `.env.dev` overlay — variables overridden for local development + +When you run `just dev`, Docker Compose loads `.env` first, then `.env.dev`. Any variable defined in `.env.dev` overrides the value from `.env`. This lets you keep production values in `.env` while using localhost-friendly values during development. + +The `.env.dev.example` file overrides the following variables: + +| Variable | Production value (`.env`) | Dev override (`.env.dev`) | Purpose | +|---|---|---|---| +| `ATL_ENVIRONMENT` | `dev` or `prod` | `dev` | Force dev mode | +| `ATL_CHAT_IP` | `100.64.7.0` (Tailscale) | `127.0.0.1` | Bind ports to localhost | +| `IRC_DOMAIN` | `irc.atl.chat` | `irc.localhost` | Use localhost domain for IRC | +| `IRC_SSL_CERT_PATH` | `…/irc.atl.chat/fullchain.pem` | `…/irc.localhost/fullchain.pem` | Dev self-signed cert path | +| `IRC_SSL_KEY_PATH` | `…/irc.atl.chat/privkey.pem` | `…/irc.localhost/privkey.pem` | Dev self-signed key path | +| `XMPP_DOMAIN` | `atl.chat` | `xmpp.localhost` | Use localhost domain for XMPP | +| `PROSODY_DOMAIN` | _(not set)_ | `xmpp.localhost` | Prosody VirtualHost for dev | +| `PROSODY_STORAGE` | `sqlite` | `sqlite` | SQLite for dev (no PostgreSQL needed) | +| `PROSODY_UPLOAD_EXTERNAL_URL` | `http://localhost:5280/upload/` | `https://xmpp.localhost:5281/` | Dev upload URL | +| `PROSODY_HTTP_EXTERNAL_URL` | _(not set)_ | `http://xmpp.localhost:5280/` | BOSH/WebSocket base URL for dev | +| `PROSODY_PROXY_ADDRESS` | `localhost` | `xmpp.localhost` | Proxy address for dev | +| `PROSODY_SSL_KEY` | _(derived)_ | `certs/live/xmpp.localhost/privkey.pem` | Dev cert key path | +| `PROSODY_SSL_CERT` | _(derived)_ | `certs/live/xmpp.localhost/fullchain.pem` | Dev cert path | +| `BRIDGE_XMPP_COMPONENT_JID` | `bridge.atl.chat` | `bridge.xmpp.localhost` | Dev bridge JID | +| `BRIDGE_PORTAL_BASE_URL` | `https://portal.atl.tools` | _(empty)_ | Disable Portal in dev | +| `BRIDGE_IRC_TLS_VERIFY` | _(not set)_ | `false` | Skip TLS verification for self-signed certs | +| `PROSODY_C2S_REQUIRE_ENCRYPTION` | `true` | `false` | Relax TLS for dev | +| `PROSODY_S2S_REQUIRE_ENCRYPTION` | `true` | `false` | Relax TLS for dev | +| `PROSODY_S2S_SECURE_AUTH` | `true` | `false` | Relax s2s auth for self-signed certs | +| `PROSODY_ALLOW_UNENCRYPTED_PLAIN_AUTH` | `false` | `true` | Allow plaintext auth in dev | +| `BRIDGE_RELAYMSG_CLEAN_NICKS` | _(not set)_ | `true` | Clean relay nicks in dev | +| `PROSODY_REST_URL` | `http://atl-xmpp-server:5280` | `http://atl-xmpp-server:5280` | Internal hostname | +| `PROSODY_REST_USERNAME` | `admin@atl.chat` | `admin@xmpp.localhost` | Dev admin JID | +| `IRC_ATHEME_JSONRPC_URL` | `http://atl-irc-server:8081/jsonrpc` | `http://atl-irc-server:8081/jsonrpc` | Internal hostname | +| `IRC_UNREAL_JSONRPC_URL` | `https://irc.atl.chat:8600/api` | `https://irc.localhost:8600/api` | Dev RPC URL | +| `IRC_SERVER` | `irc.atl.chat` | `irc.localhost` | Dev IRC hostname | +| `NEXT_PUBLIC_IRC_WS_URL` | `wss://irc.atl.chat/ws` | `wss://irc.localhost/ws` | Dev WebSocket URL | +| `NEXT_PUBLIC_XMPP_BOSH_URL` | `https://xmpp.atl.chat/http-bind` | `https://xmpp.localhost/http-bind` | Dev BOSH URL | + +The dev overlay also adds variables not present in `.env.example`: + +| Variable | Value | Purpose | +|---|---|---| +| `XMPP_AVATAR_BASE_URL` | `http://atl-xmpp-server:5280` | Bridge avatar resolution (Docker internal) | +| `XMPP_UPLOAD_FETCH_URL` | `http://atl-xmpp-server:5280` | Bridge media fetch URL (Docker internal) | +| `BRIDGE_DEV_IRC_PUPPETS` | `true` (commented) | Enable per-user IRC connections in dev | +| `BRIDGE_DEV_IRC_NICK_MAP` | _(commented)_ | Map Discord user IDs to IRC nicks | + + +## Related pages + +- [Ports Reference](/docs/reference/ports) — complete port registry with all service ports +- [API Reference](/docs/reference/api) — Portal API and UnrealIRCd JSON-RPC endpoints +- [Glossary](/docs/reference/glossary) — definitions of project-specific terms and acronyms +- [FAQ](/docs/reference/faq) — frequently asked questions about atl.chat +- [Security](/docs/operations/security) — secret generation and credential rotation for sensitive variables +- [Deployment](/docs/operations/deployment) — production deployment using these variables diff --git a/apps/docs/content/docs/reference/faq.mdx b/apps/docs/content/docs/reference/faq.mdx new file mode 100644 index 00000000..86c963f6 --- /dev/null +++ b/apps/docs/content/docs/reference/faq.mdx @@ -0,0 +1,170 @@ +--- +title: FAQ +description: Frequently asked questions about atl.chat, connecting to services, contributing, and day-to-day operations. +--- + +This page answers the most common questions about atl.chat — what it is, how to connect, how to contribute, and how to handle everyday operational tasks. + +## General + +### What is atl.chat? + +atl.chat is the unified chat infrastructure for the [All Things Linux](https://allthingslinux.org) community. It provides IRC (via [UnrealIRCd](/docs/services/irc) and [Atheme](/docs/services/atheme)), XMPP (via [Prosody](/docs/services/xmpp)), a Discord bridge, and web-based clients — all running as a single Docker Compose stack in one monorepo. + +### How do the services relate to each other? + +UnrealIRCd handles IRC connections and Atheme provides nickname/channel registration on top of it (they share a Docker network namespace). Prosody runs the XMPP side independently. The [Bridge](/docs/services/bridge) relays messages between Discord, IRC, and XMPP so users on any platform see the same conversations. [The Lounge](/docs/services/thelounge) and [Gamja](/docs/reference/glossary) are web IRC clients that connect to UnrealIRCd, and the [WebPanel](/docs/services/webpanel) gives operators a browser-based admin interface via UnrealIRCd's JSON-RPC API. + +### Is atl.chat open source? + +Yes. The entire monorepo — server configs, bridge code, web apps, documentation, and infrastructure — is open source. You can fork it, run your own instance, or contribute back via pull requests. + +## Connecting + +### How do I connect to IRC? + +You have several options: + +1. **Desktop IRC client** (HexChat, irssi, WeeChat) — connect to `irc.atl.tools` on port `6697` with TLS enabled. +2. **The Lounge** — open the web client in your browser (port `9000` in dev, or the public URL in production). You need an account created by an operator (`just lounge add`). +3. **Gamja** — a lightweight web client (planned, not yet active). + +See the [IRC overview](/docs/services/irc) for full connection details. + +### How do I connect to XMPP? + +Use any XMPP client (Conversations on Android, Gajim on desktop, Moxxy on iOS) and connect to the Prosody server on port `5222` with TLS. Browser-based access is available via BOSH (port `5280`) or WebSocket. See the [XMPP overview](/docs/services/xmpp) for details. + +### Can I use Discord instead? + +Yes. The [Bridge](/docs/services/bridge) relays messages between Discord, IRC, and XMPP channels automatically. You chat on Discord as usual and your messages appear on IRC/XMPP (and vice versa). No extra setup is needed on the user side. + +### How do I register my IRC nickname? + +After connecting, register with NickServ: + +```text +/msg NickServ REGISTER +``` + +Then identify on future connections: + +```text +/msg NickServ IDENTIFY +``` + +Most modern clients support SASL authentication, which identifies you automatically at connect time. See the [Atheme operations page](/docs/services/atheme/operations) for more NickServ commands. + +## Contributing + +### How do I contribute to atl.chat? + +1. Fork the repository and clone it locally. +2. Follow the [local development guide](/docs/getting-started/local-development) to get the stack running. +3. Create a feature branch, make your changes, and open a pull request. + +The project uses [Conventional Commits](/docs/development/contributing), pre-commit hooks (`just lint`), and automated tests (`just test`). See the full [contributing guide](/docs/development/contributing) for branch naming, code style, and review expectations. + +### What languages and tools does the project use? + +| Area | Language / Tool | +|---|---| +| IRC server config | UnrealIRCd config format, Lua scripts | +| IRC services | Atheme (C, configured via template) | +| XMPP server | Prosody (Lua configuration) | +| Bridge | Python | +| Web app and docs | TypeScript, Next.js, MDX | +| Infrastructure | Docker Compose, bash scripts, `just` | +| Linting | ruff (Python), Biome (TypeScript), luacheck (Lua), shellcheck (bash) | + +See the [contributing page](/docs/development/contributing) for code style guides per language. + +### How do I run the tests? + +```bash +# Unit tests (fast, no Docker needed) +uv run pytest tests/unit/ + +# Bridge tests (fast, no Docker needed) +uv run pytest apps/bridge/tests/ + +# All tests +just test-all +``` + +See the [testing guide](/docs/development/testing) for the full test directory layout, fixtures, and CI integration. + +## Operations + +### How do I add a new user to The Lounge? + +```bash +just lounge add +``` + +You will be prompted for a password. The user can then log in to The Lounge web interface. See [The Lounge operations](/docs/services/thelounge/operations) for listing, resetting, and removing users. + +### How do I rotate secrets and passwords? + +Generate new values for sensitive environment variables (anything containing `PASSWORD`, `TOKEN`, `SECRET`, or `KEY` in `.env`), then restart the affected services: + +```bash +# Generate a secure random value +openssl rand -base64 32 + +# After updating .env, restart the stack +just prod +``` + +See the [security page](/docs/operations/security) for per-variable generation commands and the full credential rotation procedure. + +### How do I check if services are healthy? + +Quick health checks for each service: + +```bash +# IRC — verify TLS connection +openssl s_client -connect localhost:6697 -servername irc.localhost /dev/null | head -5 + +# XMPP — check BOSH endpoint +curl -sf http://localhost:5280/http-bind + +# The Lounge — HTTP health +curl -sf http://localhost:9000 + +# Container status +just status +``` + +See the [monitoring page](/docs/operations/monitoring) for comprehensive health checks, log inspection patterns, and alerting strategies. + +### How do I update or upgrade services? + +1. Back up persistent data (`data/` directory) — see the [backups guide](/docs/operations/backups). +2. Pull the latest changes: `git pull`. +3. Rebuild and restart: `just build` then `just prod`. +4. Verify services are running: `just status`. + +For TLS certificate renewal and post-renewal hooks (UnrealIRCd rehash, Prosody reload), see the [SSL/TLS page](/docs/operations/ssl-tls). + +### Where do I find logs? + +```bash +# Follow all service logs +just logs + +# Follow a specific service +just logs unrealircd +just logs prosody +just logs bridge +``` + +Docker Compose logs are the primary source. In dev mode, [Dozzle](http://localhost:8082) provides a browser-based log viewer. See [monitoring](/docs/operations/monitoring) for log inspection patterns and filtering tips. + + +## Related pages + +- [Glossary](/docs/reference/glossary) — definitions of project-specific terms and acronyms +- [Getting Started](/docs/getting-started) — prerequisites and local development setup +- [Environment Variables](/docs/reference/environment-variables) — complete variable reference +- [Troubleshooting](/docs/operations/troubleshooting) — cross-service diagnostic commands and common issues diff --git a/apps/docs/content/docs/reference/glossary.mdx b/apps/docs/content/docs/reference/glossary.mdx new file mode 100644 index 00000000..8be7cff8 --- /dev/null +++ b/apps/docs/content/docs/reference/glossary.mdx @@ -0,0 +1,103 @@ +--- +title: Glossary +description: Definitions of project-specific terms, acronyms, and service names used across the atl.chat documentation. +--- + +This glossary defines every project-specific term, acronym, and service name used across the atl.chat documentation, so you can look up unfamiliar concepts in one place. + +## Services and applications + +| Term | Definition | +|---|---| +| **UnrealIRCd** | The IRC server (version 6.x) that powers atl.chat's IRC network. Runs as a Docker container from `apps/unrealircd/` and handles client connections, channel management, and server-to-server linking. | +| **Atheme** | The IRC services daemon that provides nickname registration (NickServ), channel management (ChanServ), operator tools (OperServ), and other service bots. Shares a network namespace with UnrealIRCd. | +| **Prosody** | The XMPP server that provides real-time messaging alongside IRC. Configured via Lua (`prosody.cfg.lua`) and runs from `apps/prosody/`. | +| **The Lounge** | A self-hosted web IRC client running in private mode. Connects to UnrealIRCd via WebIRC and provides a persistent, browser-based chat experience. Managed from `apps/thelounge/`. | +| **WebPanel** | The UnrealIRCd administration web panel served by nginx. Connects to UnrealIRCd's JSON-RPC interface for server management. Runs from `apps/webpanel/`. | +| **Bridge** | A Python application (`apps/bridge/`) that relays messages between Discord, IRC, and XMPP using an event bus pattern. | +| **Gamja** | A lightweight, planned IRC web client. Directory exists at `apps/gamja/` but is not yet active. | +| **Portal** | The community web portal application, providing user-facing features beyond chat. Referenced in environment variable sections as the `PORTAL_*` variable group. | + +## IRC service bots (Atheme) + +| Term | Definition | +|---|---| +| **NickServ** | Atheme service bot that handles nickname registration, identification, and account management. | +| **ChanServ** | Atheme service bot that manages channel registration, access lists, and channel settings. | +| **OperServ** | Atheme service bot that provides IRC operator tools: akills (network bans), server management, and administrative commands. | +| **MemoServ** | Atheme service bot that provides offline messaging between registered users. | +| **HostServ** | Atheme service bot that manages virtual hostnames (vhosts) for registered users. | +| **BotServ** | Atheme service bot that assigns custom bot identities to channels. | + +## Protocols and standards + +| Term | Definition | +|---|---| +| **IRC** | Internet Relay Chat — a text-based real-time messaging protocol defined by RFCs 1459 and 2812 (with modern IRCv3 extensions). The primary chat protocol used by atl.chat. | +| **XMPP** | Extensible Messaging and Presence Protocol — an open XML-based messaging standard. atl.chat runs an XMPP server (Prosody) alongside IRC. | +| **MUC** | Multi-User Chat — the XMPP extension (XEP-0045) for group chat rooms, analogous to IRC channels. | +| **BOSH** | Bidirectional-streams Over Synchronous HTTP — an XMPP transport that tunnels XMPP stanzas over HTTP long-polling. Prosody exposes BOSH on port 5280. | +| **WebSocket** | A full-duplex communication protocol over a single TCP connection. Used by UnrealIRCd (port 8000) and Prosody for browser-based clients. | +| **SASL** | Simple Authentication and Security Layer — a framework for authentication in network protocols. Used by both IRC (IRCv3 SASL) and XMPP for client authentication. | +| **SCRAM** | Salted Challenge Response Authentication Mechanism — a SASL mechanism (SCRAM-SHA-256) used by Prosody for secure password-based authentication without transmitting plaintext passwords. | +| **TLS** | Transport Layer Security — the cryptographic protocol that encrypts connections. All atl.chat services use TLS for client and inter-service communication. | +| **STS** | Strict Transport Security — an IRCv3 capability that tells clients to always use TLS when connecting to the server. | +| **WebIRC** | An IRC protocol extension that allows trusted web clients (like The Lounge) to forward the real IP address of connecting users to the IRC server. | +| **XEP** | XMPP Extension Protocol — numbered specifications that extend the core XMPP standard. Examples include XEP-0045 (MUC), XEP-0313 (MAM), and XEP-0060 (PubSub). | +| **RFC** | Request for Comments — standards documents published by the IETF. IRC is defined by RFCs 1459/2812; TLS by RFC 8446; XMPP by RFCs 6120/6121. | +| **PubSub** | Publish-Subscribe — an XMPP extension (XEP-0060) for event-driven messaging where publishers send data to topics and subscribers receive updates. | +| **MAM** | Message Archive Management — an XMPP extension (XEP-0313) that stores message history server-side so clients can retrieve past messages. | +| **TURN/STUN** | Traversal Using Relays around NAT / Session Traversal Utilities for NAT — protocols that help establish peer-to-peer connections through firewalls and NAT devices. The `infra/turn-standalone/` directory contains configuration for a standalone TURN server. | +| **ACME** | Automatic Certificate Management Environment — the protocol used by Let's Encrypt (and other CAs) to automate TLS certificate issuance and renewal. | +| **DNS-01** | An ACME challenge type that proves domain ownership by creating a DNS TXT record. Used by the cert-manager service with Cloudflare DNS for wildcard certificates. | + +## IRC concepts + +| Term | Definition | +|---|---| +| **cloak** | A virtual hostname applied to a user's connection to hide their real IP address. Generated using cloak keys defined in the environment variables. | +| **oper** | An IRC operator — a privileged user with administrative access to the IRC server. Configured via oper blocks in UnrealIRCd's configuration. | +| **rehash** | An UnrealIRCd command that reloads the server configuration without disconnecting users. Used after config changes or certificate renewal. | +| **netsplit** | A temporary disconnection between IRC servers in a network, causing users on each side to appear to leave channels. | + +## Infrastructure and tooling + +| Term | Definition | +|---|---| +| **Docker Compose** | The container orchestration tool used to define and run the entire atl.chat stack. The root `compose.yaml` includes fragments from `infra/compose/*.yaml`. | +| **just** | A command runner (similar to `make`) used throughout the project. The root `justfile` defines commands like `just dev`, `just prod`, and `just test`. Per-app recipes are loaded via `mod`. | +| **envsubst** | A GNU gettext utility that substitutes environment variable references in template files. Used by `scripts/prepare-config.sh` to generate service configuration files from templates. | +| **cert-manager** | A Docker service defined in `infra/compose/cert-manager.yaml` that uses Lego to obtain and renew TLS certificates via ACME. | +| **Lego** | A Go-based ACME client used by the cert-manager service to request TLS certificates from Let's Encrypt. Supports DNS-01 challenges via Cloudflare. | +| **Tailscale** | A mesh VPN service. In production, atl.chat services bind to Tailscale IP addresses for secure inter-node communication. | +| **OpenMetrics** | A metrics exposition format (compatible with Prometheus) used for monitoring service health and performance. | + +## Development and CI/CD tools + +| Term | Definition | +|---|---| +| **Gitleaks** | A secret scanning tool configured as a pre-commit hook to prevent accidental commits of passwords, tokens, and API keys. | +| **ruff** | A fast Python linter and formatter used for all Python code in the monorepo (primarily the bridge application). | +| **Biome** | A JavaScript/TypeScript linter and formatter. Used via the `ultracite` wrapper for the web and docs applications. | +| **luacheck** | A static analysis tool for Lua code. Used to lint Prosody configuration and UnrealIRCd Lua scripts. | +| **shellcheck** | A static analysis tool for shell scripts. Used to lint bash scripts in `scripts/` and infrastructure directories. | +| **semantic-release** | An automated versioning and release tool configured via `.releaserc.json`. Generates changelogs and version bumps from Conventional Commits. | +| **Conventional Commits** | A commit message convention (`type(scope): description`) used by the project to enable automated changelogs and semantic versioning. | + +## Documentation and web + +| Term | Definition | +|---|---| +| **Fumadocs** | The documentation framework used by `apps/docs/`. Built on Next.js with file-system routing — the directory structure under `content/docs/` determines the navigation hierarchy. | +| **MDX** | Markdown with JSX — a file format that combines Markdown content with React components. All documentation pages are `.mdx` files. | +| **Cloudflare Pages** | The hosting platform where the documentation site and web application are deployed. The docs site builds via the OpenNext adapter for Cloudflare Workers. | +| **BLUF** | Bottom Line Up Front — a writing principle used throughout this documentation where the key takeaway appears in the first sentence of every page. | +| **Mermaid** | A diagramming language rendered in MDX via the `remarkMdxMermaid` plugin. Used for all architecture and flow diagrams instead of ASCII art. | + + +## Related pages + +- [FAQ](/docs/reference/faq) — frequently asked questions about atl.chat +- [Environment Variables](/docs/reference/environment-variables) — complete variable reference +- [Architecture Overview](/docs/architecture) — system diagram and design decisions +- [Getting Started](/docs/getting-started) — prerequisites and monorepo orientation diff --git a/apps/docs/content/docs/reference/meta.json b/apps/docs/content/docs/reference/meta.json new file mode 100644 index 00000000..cf9f1a1c --- /dev/null +++ b/apps/docs/content/docs/reference/meta.json @@ -0,0 +1,10 @@ +{ + "title": "Reference", + "pages": [ + "environment-variables", + "ports", + "api", + "glossary", + "faq" + ] +} diff --git a/apps/docs/content/docs/reference/ports.mdx b/apps/docs/content/docs/reference/ports.mdx new file mode 100644 index 00000000..46c1c241 --- /dev/null +++ b/apps/docs/content/docs/reference/ports.mdx @@ -0,0 +1,94 @@ +--- +title: Port Registry +description: Complete port registry for all atl.chat services, verified against compose files. +--- + +This page lists every port used by atl.chat services, verified against the current `compose.yaml` and `infra/compose/*.yaml` files. All host-side ports are configurable via environment variables in `.env`. + +## IRC stack + +Defined in `infra/compose/irc.yaml`. + +| Port | Env Variable | Service | Protocol | Bind address | Description | +|---|---|---|---|---|---| +| `6697` | `IRC_TLS_PORT` | UnrealIRCd | TCP | `ATL_CHAT_IP` | IRC over TLS — primary client connection | +| `8000` | `IRC_WEBSOCKET_PORT` | UnrealIRCd | TCP | `ATL_CHAT_IP` | IRC WebSocket for web clients | +| `8600` | `IRC_RPC_PORT` | UnrealIRCd | TCP | `ATL_CHAT_IP` | JSON-RPC API (used by WebPanel and tooling) | +| `6900` | `IRC_SERVER_PORT` | UnrealIRCd | TCP | `ATL_CHAT_IP` | Server-to-server links | +| `8081` | `ATHEME_HTTPD_PORT` | Atheme | TCP | `ATL_CHAT_IP` | Atheme HTTP API (Atheme shares the IRC server's network namespace) | +| `8080` | `WEBPANEL_PORT` | WebPanel | TCP | `0.0.0.0` | UnrealIRCd admin web panel | + +The five UnrealIRCd and Atheme ports bind to `ATL_CHAT_IP` (default `127.0.0.1`). The WebPanel port binds to all interfaces (`0.0.0.0`) and does not use `ATL_CHAT_IP`. + +Atheme uses `network_mode: service:atl-irc-server`, so it shares the IRC server's network stack. The Atheme uplink port (`6901`) is internal to that shared namespace and not exposed to the host. + +## XMPP stack + +Defined in `infra/compose/xmpp.yaml`. + +| Port | Env Variable | Service | Protocol | Description | +|---|---|---|---|---| +| `5222` | `PROSODY_C2S_PORT` | Prosody | TCP | XMPP client-to-server (STARTTLS) | +| `5223` | `PROSODY_C2S_DIRECT_TLS_PORT` | Prosody | TCP | XMPP client-to-server (Direct TLS) | +| `5269` | `PROSODY_S2S_PORT` | Prosody | TCP | XMPP server-to-server federation | +| `5270` | `PROSODY_S2S_DIRECT_TLS_PORT` | Prosody | TCP | XMPP server-to-server (Direct TLS) | +| `5280` | `PROSODY_HTTP_PORT` | Prosody | TCP | HTTP — BOSH and WebSocket endpoints | +| `5000` | `PROSODY_PROXY65_PORT` | Prosody | TCP | SOCKS5 bytestream proxy for file transfers | +| `5281` | `PROSODY_HTTPS_PORT` | Nginx (XMPP) | TCP | HTTPS reverse proxy for Prosody | + +## Web clients + +| Port | Env Variable | Service | Compose File | Description | +|---|---|---|---|---| +| `9000` | `THELOUNGE_PORT` | The Lounge | `infra/compose/thelounge.yaml` | Web IRC client (private mode, requires user account) | + +## Bridge + +Defined in `infra/compose/bridge.yaml`. The bridge service does not expose any host ports — it connects outbound to IRC, XMPP, and Discord over the `atl-chat` Docker network and the internet. + +## Dev-only services + +Available when running `just dev` (activates the `dev` Docker Compose profile). + +| Port | Env Variable | Service | Description | +|---|---|---|---| +| `8082` | `DOZZLE_PORT` | Dozzle | Docker log viewer (container port `8080` mapped to host `8082`) | + +## TURN/STUN (standalone) + +Deployed separately via `infra/turn-standalone/compose.yaml` using host networking. Not part of the main `compose.yaml` stack. + +| Port | Protocol | Service | Description | +|---|---|---|---| +| `3478` | TCP/UDP | Coturn | STUN/TURN relay | +| `5349` | TCP | Coturn | TURNS (TLS) | +| `49152–49202` | UDP | Coturn | Media relay port range | + +## Services not in Docker Compose + +These services run outside the Docker stack during development: + +| Port | Service | Description | +|---|---|---| +| `3000` | Next.js web app | Run locally with `pnpm dev` in `apps/web/` | + +## Customising ports + +Every host-side port is controlled by an environment variable in `.env`. To change a port, set the variable before starting the stack: + +```bash +# Example: move IRC TLS to port 6698 +IRC_TLS_PORT=6698 +``` + +The IRC stack ports also respect `ATL_CHAT_IP` (default `127.0.0.1`), which controls the bind address. Set it to `0.0.0.0` to listen on all interfaces in production. + +See the [environment variables reference](/docs/reference/environment-variables) for the full list. + + +## Related pages + +- [Environment Variables](/docs/reference/environment-variables) — port variables and bind address configuration +- [Networking](/docs/architecture/networking) — full network architecture, Tailscale overlay, and firewall rules +- [Deployment](/docs/operations/deployment) — production port binding and Tailscale setup +- [Monitoring](/docs/operations/monitoring) — health check endpoints per service diff --git a/apps/docs/content/docs/services/atheme/configuration.mdx b/apps/docs/content/docs/services/atheme/configuration.mdx new file mode 100644 index 00000000..4085b413 --- /dev/null +++ b/apps/docs/content/docs/services/atheme/configuration.mdx @@ -0,0 +1,347 @@ +--- +title: Atheme Configuration +description: Config template, environment variables, module system, SASL setup, service bot identities, and general settings for Atheme IRC services. +--- + +Atheme is configured through a template file (`apps/atheme/config/atheme.conf.template`) that gets processed by `scripts/prepare-config.sh` during `just init` — substituting `${VAR}` placeholders with values from your `.env` (and `.env.dev` in development) to produce the final `atheme.conf`. + +## Config template pipeline + +The configuration follows the same envsubst pattern used across the atl.chat stack: + +1. You set values in `.env` (copied from `.env.example`) +2. Running `just init` or `just dev` triggers `scripts/prepare-config.sh` +3. The script sources `.env`, then `.env.dev` if it exists (dev overrides) +4. `envsubst` replaces all `${VAR}` references in `atheme.conf.template` +5. The rendered `atheme.conf` is written to `apps/atheme/config/atheme.conf` +6. The config directory is mounted read-only into the container at `/usr/local/atheme/etc` + +> **Warning:** `prepare-config.sh` unconditionally sources `.env.dev` if the file exists, even when preparing production configs. Do not leave `.env.dev` on a production server, or your production Atheme config will contain development overrides. + +To regenerate the config after changing `.env` values: + +```bash +# Regenerate all configs (including Atheme) +just init + +# Or run prepare-config.sh directly +./scripts/prepare-config.sh +``` + +## Environment variables + +### Server identity + +These variables define how Atheme identifies itself to UnrealIRCd and the network: + +| Variable | Description | Default | +|---|---|---| +| `ATHEME_SERVER_NAME` | Server name announced to the IRC network | `services.atl.chat` | +| `ATHEME_SERVER_DESC` | Human-readable server description | `"All Things Linux IRC Services"` | +| `ATHEME_NUMERIC` | Unique server numeric identifier (must not conflict with other servers) | `00A` | +| `ATHEME_RECONTIME` | Seconds to wait before reconnecting to the uplink after a disconnect | `10` | +| `ATHEME_NETNAME` | Network name used in service messages | `atl.chat` | +| `ATHEME_HIDEHOST_SUFFIX` | Suffix appended to hidden hostnames (e.g., `user.users.atl.chat`) | `users.atl.chat` | + +### Network information + +| Variable | Description | Default | +|---|---|---| +| `ATHEME_ADMIN_NAME` | Administrator name shown in `/admin` queries | `"All Things Linux"` | +| `ATHEME_ADMIN_EMAIL` | Administrator email shown in `/admin` queries | `admin@allthingslinux.org` | +| `ATHEME_REGISTER_EMAIL` | Sender address for registration and verification emails | `noreply@allthingslinux.org` | +| `ATHEME_HELP_CHANNEL` | Channel shown to users when they use unknown commands | `#help` | +| `ATHEME_HELP_URL` | URL shown to users for additional help | `https://discord.gg/linux` | + +### Uplink connection + +These control how Atheme connects to UnrealIRCd. Because Atheme shares UnrealIRCd's network namespace (`network_mode: service:atl-irc-server`), the uplink host is always `127.0.0.1`: + +| Variable | Description | Default | +|---|---|---| +| `ATHEME_UPLINK_HOST` | Hostname or IP of the UnrealIRCd services port | `127.0.0.1` | +| `ATHEME_UPLINK_PORT` | Port for the services uplink (plaintext, localhost only) | `6901` | +| `IRC_SERVICES_PASSWORD` | Shared password for the UnrealIRCd ↔ Atheme link (used as both `send_password` and `receive_password`) | `change_me_secure_services_pass` | + +> **Warning:** `IRC_SERVICES_PASSWORD` must match the link password configured in UnrealIRCd. Change this from the default before any public deployment. The same variable is referenced in both `atheme.conf.template` and `unrealircd.conf.template`. + +### HTTP daemon + +| Variable | Description | Default | +|---|---|---| +| `ATHEME_HTTPD_PORT` | Port for the Atheme HTTP server (JSON-RPC API) | `8081` | + +The HTTP daemon binds to `0.0.0.0` inside the shared network namespace. Since UnrealIRCd's port mappings expose this port, the JSON-RPC API is reachable at `http://localhost:8081` in development. + +### Service bot identities + +Each service bot has four environment variables controlling its IRC presence: `_NICK`, `_USER`, `_HOST`, and `_REAL`. These are purely cosmetic — they control how the bot appears on IRC, not its functionality. + +| Service | Nick variable | Default nick | Default realname | +|---|---|---|---| +| NickServ | `ATHEME_NICKSERV_NICK` | `NickServ` | `"Nickname Services"` | +| ChanServ | `ATHEME_CHANSERV_NICK` | `ChanServ` | `"Channel Services"` | +| OperServ | `ATHEME_OPERSERV_NICK` | `OperServ` | `"Operator Services"` | +| MemoServ | `ATHEME_MEMOSERV_NICK` | `MemoServ` | `"Memo Services"` | +| SaslServ | `ATHEME_SASLSERV_NICK` | `SaslServ` | `"SASL Authentication Agent"` | +| BotServ | `ATHEME_BOTSERV_NICK` | `BotServ` | `"Bot Services"` | +| GroupServ | `ATHEME_GROUPSERV_NICK` | `GroupServ` | `"Group Management Services"` | +| HostServ | `ATHEME_HOSTSERV_NICK` | `HostServ` | `"Host Management Services"` | +| InfoServ | `ATHEME_INFOSERV_NICK` | `InfoServ` | `"Information Service"` | +| HelpServ | `ATHEME_HELPSERV_NICK` | `HelpServ` | `"Help Services"` | +| StatServ | `ATHEME_STATSERV_NICK` | `StatServ` | `"Statistics Services"` | +| Global | `ATHEME_GLOBAL_NICK` | `Global` | `"Network Announcements"` | +| ALIS | `ATHEME_ALIS_NICK` | `ALIS` | `"Channel Directory"` | +| Proxyscan | `ATHEME_PROXYSCAN_NICK` | `Proxyscan` | `"Proxyscan Service"` | +| GameServ | `ATHEME_GAMESERV_NICK` | `GameServ` | `"Game Services"` | +| RPGServ | `ATHEME_RPGSERV_NICK` | `RPGServ` | `"RPG Finding Services"` | + +All bots default to `services.atl.chat` as their host. The full set of variables for each bot follows the pattern `ATHEME__NICK`, `ATHEME__USER`, `ATHEME__HOST`, `ATHEME__REAL`. + +> **Note:** ChanFix variables (`ATHEME_CHANFIX_*`) are defined in `.env.example` but only consumed in a commented-out block in the config template. The ChanFix module is disabled by default. + +## Module system + +Atheme's functionality is entirely module-driven. The config template loads modules via `loadmodule` directives, organized by category. Modules that are commented out (`#loadmodule`) are available but disabled by default. + +### Core modules + +These three modules are required and must be loaded first: + +```c +loadmodule "protocol/unreal4"; /* UnrealIRCd server-to-server protocol */ +loadmodule "backend/opensex"; /* Atheme's native flat-file database */ +loadmodule "crypto/pbkdf2v2"; /* Password hashing (must load before other crypto) */ +``` + +- `protocol/unreal4` — speaks UnrealIRCd's S2S protocol. This is the only protocol module you should load for this stack. +- `backend/opensex` — Atheme's flat-file database format. The database is stored at `data/atheme/data/services.db`. This is not SQLite — it is Atheme's own serialization format. +- `crypto/pbkdf2v2` — PBKDF2 v2 password hashing. Must be loaded before any other crypto module to ensure SCRAM-SHA support works correctly. + +### Service modules + +Each service bot loads a `main` module plus individual command modules. The template loads a comprehensive set for each service. Here is a summary of the module categories and approximate counts: + +| Category | Modules loaded | Key capabilities | +|---|---|---| +| NickServ | 50 | Registration, identification, ghost, group, vhost, cert fingerprints, enforcement | +| ChanServ | 51 | Registration, access/flags, akick, ban, topic, mode lock, antiflood, templates | +| OperServ | 42 | AKILL, clones, jupe, mode, rehash, restart, shutdown, soper | +| MemoServ | 10 | Send, list, read, forward, delete, ignore | +| GroupServ | 22 | Register, join, flags, set properties | +| HostServ | 9 | Vhost requests, offers, on/off | +| HelpServ | 4 | Help tickets, service directory | +| SASL | 3 | PLAIN, SCRAM-SHA, AUTHCOOKIE | +| Global / InfoServ | 2 | Network announcements, login info | +| StatServ | 4 | Server, channel, netsplit statistics | +| ALIS | 1 | Advanced channel list search | + +### Disabled modules + +The following modules are available in the template but disabled by default: + +- **SASL EXTERNAL** — certificate-based authentication +- **SASL ECDH/ECDSA** — elliptic curve challenge mechanisms + +To enable a disabled module, uncomment the relevant `loadmodule` line in `atheme.conf.template` and regenerate the config with `just init`. + +## SASL and password hashing + +Atheme handles SASL authentication for the IRC network, allowing clients to authenticate before completing the IRC connection handshake. This is the recommended authentication method. + +### SASL mechanisms + +Three SASL mechanisms are loaded: + +| Mechanism | Module | Security | Notes | +|---|---|---|---| +| PLAIN | `saslserv/plain` | Relies on TLS for transport security | Widely supported by all IRC clients | +| SCRAM-SHA | `saslserv/scram` | Challenge-response, no plaintext password sent | More secure; requires client support | +| AUTHCOOKIE | `saslserv/authcookie` | Cookie-based | Used by IRIS web interface | + +### PBKDF2v2 configuration + +The `crypto{}` block configures password hashing. The current settings are optimized for SCRAM-SHA-256 support: + +```c +crypto { + pbkdf2v2_digest = "SCRAM-SHA-256"; /* Enables SCRAM-SHA-256 SASL mechanism */ + pbkdf2v2_rounds = 64000; /* Iteration count (10000–65536 for SCRAM compat) */ + pbkdf2v2_saltlen = 32; /* Salt length in bytes */ +}; +``` + +Key requirements for SCRAM-SHA to work: + +1. Atheme must be built with `--with-libidn` (the Containerfile includes this) +2. `crypto/pbkdf2v2` must be loaded before any other crypto module +3. The digest must be set to a `SCRAM-*` variant (`SCRAM-SHA-256` is recommended) +4. Rounds must be between 10,000 and 65,536 for Cyrus SASL compatibility +5. The `saslserv/scram` module must be loaded + +> **Note:** When you change the `pbkdf2v2_digest` setting, existing passwords are not automatically re-hashed. Users need to change their password (or re-identify) for the new hash format to take effect. + +## Service bot configuration blocks + +Beyond the identity variables, each service bot has a configuration block in the template with service-specific settings. Here are the notable ones: + +### NickServ + +```c +nickserv { + /* ... identity vars ... */ + aliases { + "ID" = "IDENTIFY"; + "MYACCESS" = "LISTCHANS"; + }; + spam; /* Send spam warnings to users */ + maxnicks = 5; /* Max nicknames per account */ + expire = 30; /* Days before unused nicknames expire */ + enforce_expire = 14; /* Days before enforced nicknames expire */ + enforce_delay = 30; /* Seconds before nickname enforcement kicks in */ + enforce_prefix = "Guest"; /* Prefix for enforced nickname changes */ + waitreg_time = 30; /* Seconds a user must wait before registering */ + show_custom_metadata; /* Show custom metadata in INFO */ +}; +``` + +### ChanServ + +```c +chanserv { + /* ... identity vars ... */ + maxchans = 5; /* Max channels per account */ + fantasy; /* Enable fantasy commands (e.g., !op, !kick) */ + trigger = "!"; /* Fantasy command prefix */ + expire = 30; /* Days before unused channels expire */ + maxchanacs = 0; /* Max access entries per channel (0 = unlimited) */ + maxfounders = 4; /* Max founders per channel */ + changets; /* Sync channel TS on registration */ + antiflood_enforce_method = quiet; /* Flood response: quiet offenders */ + templates { + vop = "+AV"; + hop = "+AHehitrv"; + aop = "+AOehiortv"; + sop = "+AOaefhiorstv"; + founder = "+AFORaefhioqrstv"; + member = "+Ai"; + op = "+AOiortv"; + }; + deftemplates = "MEMBER=+Ai OP=+AOiortv"; +}; +``` + +### General settings + +The `general{}` block controls network-wide service behavior: + +```c +general { + helpchan = "${ATHEME_HELP_CHANNEL}"; + helpurl = "${ATHEME_HELP_URL}"; + join_chans; /* ChanServ joins registered channels */ + leave_chans; /* ChanServ leaves when channel empties */ + uflags = { hidemail; }; /* Default user flags */ + cflags = { guard; verbose; }; /* Default channel flags */ + flood_msgs = 7; /* Messages before flood protection triggers */ + flood_time = 10; /* Flood detection window (seconds) */ + ratelimit_uses = 5; /* Command uses before rate limiting */ + ratelimit_period = 60; /* Rate limit window (seconds) */ + kline_time = 7; /* Default KLINE duration (days, 0 = permanent) */ + commit_interval = 5; /* Database save interval (minutes) */ + default_clone_allowed = 5; + default_clone_warn = 4; + clone_identified_increase_limit; + uplink_sendq_limit = 1048576; /* 1 MB send queue */ + language = "en"; + immune_level = immune; + show_entity_id; + load_database_mdeps; + match_masks_through_vhost; +}; +``` + +Notable defaults: ChanServ guards (joins) all registered channels, user emails are hidden by default, and the database is saved every 5 minutes. + +## Logging + +Atheme uses a single comprehensive log file that captures all log levels: + +```c +logfile "logs/atheme.log" { debug; }; +``` + +The log file is written to `data/atheme/logs/atheme.log` on the host (mounted as `/usr/local/atheme/logs/` in the container). The `debug` level captures everything — admin actions, errors, info, network events, wallops, and registrations. + +> **Note:** The `ATHEME_LOG_LEVEL` variable is defined in `.env.example` but is not actually consumed by the config template. The log level is hardcoded to `debug` in the template. To change the log level, you need to edit the `logfile` directive in `atheme.conf.template` directly. + +Optional IRC channel logging is available but commented out: + +```c +/* Uncomment to log to IRC channels */ +#logfile "#services" { debug; }; +#logfile "!snotices" { debug; }; +``` + +## Operator classes + +Atheme defines two operator classes that control what services operators can do: + +### ircop + +The base operator class with standard privileges: + +- User management: auspex (see hidden info), admin, sendpass, vhost, mark +- Channel management: auspex, admin, cmodes, joinstaffonly +- General: auspex, helper, viewprivs, flood bypass +- OperServ: omode, akill, jupe, global +- Group: auspex, admin + +### sra (Services Root Administrator) + +Extends `ircop` with elevated privileges: + +- User: exceedlimits, hold, regnolimit +- General: metadata, admin +- OperServ: noop, grant +- Requires IRC operator status (`needoper`) + +SRA operators are managed at runtime via the OperServ SOPER command rather than static config blocks: + +```text +/msg OperServ SOPER ADD sra +``` + +## DNSBL configuration + +The Proxyscan service checks connecting users against DNS-based blocklists: + +```c +proxyscan { + blacklists { + "dnsbl.dronebl.org"; + "rbl.efnetrbl.org"; + "tor.efnet.org"; + "dnsbl.tornevall.org"; + "bl.spamcop.net"; + }; + dnsbl_action = kline; /* Automatically KLINE users matching a DNSBL */ +}; +``` + +## Known issues and audit notes + +Based on the [environment variable audit](/docs/reference/environment-variables), several Atheme-related variables have known quirks: + +- `ATHEME_UPLINK_SSL_PORT` — defined in `.env.example` as `6900` but never consumed by any template or compose file. The uplink uses plaintext on port 6901 (safe because it is localhost-only within the shared network namespace). +- `ATHEME_LOG_LEVEL` — defined in `.env.example` as `all` but not referenced in the config template. Log level is hardcoded. +- `ATHEME_CHANFIX_*` — four variables defined for ChanFix but only used in a commented-out config block. ChanFix is disabled by default. +- `ATHEME_VERSION` — set to `master` in `.env.example`. Consider pinning to a specific commit or tag for production stability. + +## Related pages + +- [Atheme Overview](/docs/services/atheme) — architecture, service bots, and how Atheme fits in the stack +- [Atheme Operations](/docs/services/atheme/operations) — service commands for NickServ, ChanServ, OperServ, and database management +- [IRC Configuration](/docs/services/irc/configuration) — UnrealIRCd config including the services uplink and link password +- [Environment Variables Reference](/docs/reference/environment-variables) — complete variable reference including all `ATHEME_*` variables +- [Security](/docs/operations/security) — secret management and credential rotation for `IRC_SERVICES_PASSWORD` diff --git a/apps/docs/content/docs/services/atheme/index.mdx b/apps/docs/content/docs/services/atheme/index.mdx new file mode 100644 index 00000000..f86664c3 --- /dev/null +++ b/apps/docs/content/docs/services/atheme/index.mdx @@ -0,0 +1,98 @@ +--- +title: Atheme IRC Services +description: Overview of Atheme IRC services — NickServ, ChanServ, OperServ, MemoServ, HostServ, and other service bots in the atl.chat stack. +--- + +Atheme provides IRC services for the atl.chat stack, running service bots like NickServ (nickname registration), ChanServ (channel management), OperServ (network administration), MemoServ (user messaging), HostServ (virtual hosts), and more — all connecting to UnrealIRCd via a server-to-server uplink in a shared network namespace. + +## How Atheme fits in the stack + +Atheme runs as the `atl-irc-services` container and shares UnrealIRCd's network namespace via `network_mode: service:atl-irc-server` in Docker Compose. This means Atheme connects to UnrealIRCd over `127.0.0.1:6901` (the services uplink port) without crossing any Docker network boundaries. Atheme waits for UnrealIRCd's health check to pass before starting. + +Atheme's HTTP API (JSON-RPC) listens on port 8081 inside the shared namespace. Since UnrealIRCd's port bindings expose 8081 to the host, you can reach the Atheme API at `http://localhost:8081` in development. + +```mermaid +graph LR + subgraph SharedNamespace["Shared network namespace (atl-irc-server)"] + IRCd["UnrealIRCd
IRC daemon"] + Atheme["Atheme
IRC services"] + end + + IRCd -- "localhost:6901
(services uplink)" --> Atheme + Atheme -- "localhost:8081
(JSON-RPC API)" --> HTTP["Host / external"] + Users["IRC clients"] -- "6697 TLS / 8000 WS" --> IRCd +``` + +## Core service bots + +Atheme runs multiple service bots, each handling a specific domain. The primary bots you interact with daily are: + +| Bot | Purpose | +|---|---| +| NickServ | Nickname registration, identification, password management, and account settings | +| ChanServ | Channel registration, access control lists, mode locks, and topic management | +| OperServ | Network administration — AKILLs, clones monitoring, server jupes, and runtime settings | +| MemoServ | Private messaging between registered users and channels | +| HostServ | Virtual host (vhost) requests, offers, and management | +| GroupServ | Account grouping for shared channel access and permissions | + +Additional service bots are also active: + +| Bot | Purpose | +|---|---| +| SaslServ | SASL authentication (PLAIN and SCRAM-SHA mechanisms) | +| HelpServ | Help ticket system and service directory | +| InfoServ | Network announcements displayed at login | +| Global | Network-wide broadcast messages from operators | +| StatServ | Network statistics — server, channel, and netsplit tracking | +| ALIS | Channel directory and search (Advanced List Service) | +| Proxyscan | DNSBL-based proxy and open relay detection | + +> **Note:** BotServ, GameServ, and RPGServ modules are present in the config template but disabled (commented out) by default. You can enable them by uncommenting the relevant `loadmodule` lines in `atheme.conf.template` and regenerating the config. + +## Architecture + +Atheme is built from source in a multi-stage Docker build (`apps/atheme/Containerfile`). The build stage compiles Atheme with `--enable-contrib`, `--with-libidn` (for SCRAM-SHA support), and `--enable-large-net`. The runtime stage uses a minimal Alpine image with `tini` as the init system. + +Configuration lives in `apps/atheme/config/atheme.conf.template` and is processed by `scripts/prepare-config.sh` during `just init`. The script substitutes `${VAR}` placeholders with values from your `.env` file to produce the final `atheme.conf`. + +Key architectural details: + +- **Protocol module:** `protocol/unreal4` — speaks UnrealIRCd's server-to-server protocol +- **Database backend:** `backend/opensex` — Atheme's native flat-file database format (stored at `data/atheme/data/services.db`) +- **Password hashing:** `crypto/pbkdf2v2` configured for SCRAM-SHA-256 with 64,000 iterations +- **SASL support:** PLAIN and SCRAM-SHA mechanisms via `saslserv/plain` and `saslserv/scram` +- **HTTP API:** JSON-RPC via `misc/httpd` and `transport/jsonrpc` on port 8081 +- **Health check:** `pgrep -f atheme-services` every 30 seconds + +## Technology + +| Component | Technology | +|---|---| +| Software | Atheme IRC Services (built from `master` branch) | +| Base image | Alpine Linux 3.23 | +| Init system | tini | +| Config management | Template substitution via `scripts/prepare-config.sh` | +| Database | opensex flat-file backend | +| Password hashing | PBKDF2v2 (SCRAM-SHA-256, 64k rounds) | +| Container name | `atl-irc-services` | +| Network mode | Shares UnrealIRCd namespace (`network_mode: service:atl-irc-server`) | +| Uplink | `127.0.0.1:6901` | +| HTTP API | JSON-RPC on port `8081` | + +## Data and volumes + +The compose configuration mounts three volumes into the Atheme container: + +| Host path | Container path | Purpose | +|---|---|---| +| `apps/atheme/config/` | `/usr/local/atheme/etc` | Configuration (read-only at runtime after generation) | +| `data/atheme/data/` | `/usr/local/atheme/data` | Database file (`services.db`) — persistent | +| `data/atheme/logs/` | `/usr/local/atheme/logs` | Log files (`atheme.log`) — persistent | + +## Related pages + +- [Configuration](/docs/services/atheme/configuration) — config template, modules, SASL setup, service bot identities +- [Operations](/docs/services/atheme/operations) — service commands for NickServ, ChanServ, OperServ, and database management +- [IRC Stack Overview](/docs/services/irc) — UnrealIRCd and the broader IRC stack +- [IRC Configuration](/docs/services/irc/configuration) — UnrealIRCd config including the services uplink diff --git a/apps/docs/content/docs/services/atheme/meta.json b/apps/docs/content/docs/services/atheme/meta.json new file mode 100644 index 00000000..535155e0 --- /dev/null +++ b/apps/docs/content/docs/services/atheme/meta.json @@ -0,0 +1,8 @@ +{ + "title": "Atheme", + "pages": [ + "index", + "configuration", + "operations" + ] +} diff --git a/apps/docs/content/docs/services/atheme/operations.mdx b/apps/docs/content/docs/services/atheme/operations.mdx new file mode 100644 index 00000000..95f0ff21 --- /dev/null +++ b/apps/docs/content/docs/services/atheme/operations.mdx @@ -0,0 +1,459 @@ +--- +title: Atheme Operations +description: Service commands for NickServ, ChanServ, OperServ, MemoServ, HostServ, and BotServ, plus database management and container operations for Atheme IRC services. +--- + +This page covers day-to-day Atheme operations: interacting with service bots via IRC commands, managing the opensex flat-file database, and running container-level maintenance tasks. + +## Interacting with Atheme + +You interact with Atheme service bots by sending IRC messages. All commands follow the same pattern: + +```text +/msg [arguments] +``` + +Every service bot supports a `HELP` command that lists available commands: + +```text +/msg NickServ HELP +/msg ChanServ HELP +/msg OperServ HELP +``` + +> **Note:** Atheme shares UnrealIRCd's network namespace, so there is no separate container to connect to. You send commands through your normal IRC connection to the network. + +## NickServ — nickname management + +NickServ handles nickname registration, identification, and account settings. + +### Registration and identification + +```text +/msg NickServ REGISTER +/msg NickServ IDENTIFY +/msg NickServ IDENTIFY +``` + +After registering, you can configure your client to auto-identify via SASL (recommended) or send `IDENTIFY` on connect. + +### Account settings + +```text +/msg NickServ SET PASSWORD +/msg NickServ SET EMAIL +/msg NickServ SET HIDEMAIL ON +/msg NickServ SET PRIVATE ON +/msg NickServ SET ENFORCE ON +``` + +`ENFORCE` enables nickname protection — if someone uses your nick without identifying, they are renamed to `Guest*` after 30 seconds (configurable via `enforce_delay` in the config). + +### Nickname grouping + +You can group multiple nicknames under one account (up to 5 by default): + +```text +/nick AltNick +/msg NickServ GROUP +``` + +### Other useful commands + +```text +/msg NickServ INFO +/msg NickServ GHOST +/msg NickServ LISTCHANS +``` + +- `INFO` — view registration details for a nickname +- `GHOST` — disconnect a session using your nickname (useful after a disconnection) +- `LISTCHANS` — list channels where your account has access + +## ChanServ — channel management + +ChanServ handles channel registration, access control, mode locks, and topic management. + +### Channel registration + +You must be in the channel and have operator status to register it: + +```text +/join #mychannel +/msg ChanServ REGISTER #mychannel +``` + +Each account can register up to 5 channels by default. + +### Access control + +ChanServ uses a flags-based access system. You can use named templates or raw flags: + +```text +/msg ChanServ FLAGS #mychannel +``` + +Built-in templates and their flags: + +| Template | Flags | Typical role | +|---|---|---| +| `MEMBER` | `+Ai` | Basic member (auto-invite) | +| `VOP` | `+AV` | Auto-voice | +| `HOP` | `+AHehitrv` | Half-op | +| `AOP` | `+AOehiortv` | Auto-op | +| `SOP` | `+AOaefhiorstv` | Super-op | +| `FOUNDER` | `+AFORaefhioqrstv` | Full control | + +Examples: + +```text +/msg ChanServ FLAGS #mychannel friend AOP +/msg ChanServ FLAGS #mychannel friend -AOP +/msg ChanServ FLAGS #mychannel LIST +``` + +### Channel settings + +```text +/msg ChanServ SET #mychannel GUARD ON +/msg ChanServ SET #mychannel MLOCK +nt +/msg ChanServ SET #mychannel TOPICLOCK ON +/msg ChanServ TOPIC #mychannel New topic text here +``` + +- `GUARD` — ChanServ joins the channel permanently (enabled by default for new registrations) +- `MLOCK` — locks channel modes so they persist across restarts +- `TOPICLOCK` — only users with the `+t` flag can change the topic via ChanServ + +### Fantasy commands + +ChanServ supports in-channel "fantasy" commands prefixed with `!` (the default trigger): + +```text +!op nickname +!deop nickname +!kick nickname reason +!ban nickname +!topic New topic +``` + +Fantasy commands are enabled by default in the config (`fantasy;` and `trigger = "!";` in the `chanserv{}` block). + +### Other useful commands + +```text +/msg ChanServ INFO #channel +/msg ChanServ AKICK #channel ADD +/msg ChanServ AKICK #channel DEL +/msg ChanServ AKICK #channel LIST +``` + +## OperServ — network administration + +OperServ provides administrative commands for IRC operators. You must have IRC operator status and be added as a services operator to use most commands. + +### Becoming a services operator + +After you have IRC oper status (via `/oper` in UnrealIRCd), a services root administrator (SRA) can grant you services operator privileges: + +```text +/msg OperServ SOPER ADD sra +``` + +The `sra` operclass has full privileges. The `ircop` operclass provides standard operator access. See the [configuration page](/docs/services/atheme/configuration) for details on operator classes. + +### AKILL management + +AKILLs are network-wide bans managed by Atheme (similar to K-lines but persistent across restarts): + +```text +/msg OperServ AKILL ADD +/msg OperServ AKILL ADD !T 7d +/msg OperServ AKILL DEL +/msg OperServ AKILL LIST +``` + +The `!T 7d` syntax sets a 7-day duration. Without `!T`, the default duration is 7 days (configured via `kline_time` in the `general{}` block). Use `!T 0` for permanent AKILLs. + +### Service control + +```text +/msg OperServ REHASH +/msg OperServ RESTART +/msg OperServ SHUTDOWN +``` + +- `REHASH` — reloads the Atheme configuration file without restarting +- `RESTART` — restarts Atheme services (brief disconnection from the network) +- `SHUTDOWN` — stops Atheme services entirely + +> **Warning:** `RESTART` and `SHUTDOWN` affect all service bots. Users lose access to NickServ, ChanServ, and all other services until Atheme reconnects. Prefer `REHASH` for config changes when possible. + +### Other OperServ commands + +```text +/msg OperServ JUPE +/msg OperServ MODE +/msg OperServ CLONES LIST +/msg OperServ UPTIME +/msg OperServ SPECS +``` + +- `JUPE` — prevents a server from connecting to the network +- `MODE` — sets channel modes as OperServ (bypasses access checks) +- `CLONES` — monitors users connecting from the same host (default limit: 5 per host) + +## MemoServ — private messaging + +MemoServ provides offline messaging between registered users. + +### Sending and reading memos + +```text +/msg MemoServ SEND +/msg MemoServ SEND #channel +/msg MemoServ LIST +/msg MemoServ READ +/msg MemoServ DELETE +``` + +Channel memos are sent to all users with sufficient access in the channel. + +### Forwarding and ignoring + +```text +/msg MemoServ FORWARD +/msg MemoServ IGNORE ADD +/msg MemoServ IGNORE DEL +/msg MemoServ IGNORE LIST +``` + +## HostServ — virtual hosts + +HostServ manages virtual hosts (vhosts) that replace your real hostname on IRC. + +### Requesting and using vhosts + +```text +/msg HostServ REQUEST +/msg HostServ ON +/msg HostServ OFF +``` + +Vhost requests require operator approval unless an operator has pre-configured offered vhosts. + +### Operator vhost management + +Operators can offer vhosts to groups of users or manage individual requests: + +```text +/msg HostServ OFFER +/msg HostServ ACTIVATE +/msg HostServ REJECT +/msg HostServ VHOST + ``` + +2. The first registered account does not automatically get operator privileges. You need to add yourself as an SRA from the Atheme console or by editing the database. The simplest approach is to use the OperServ `SOPER` command after gaining IRC oper status: + + ```text + /oper + /msg OperServ SOPER ADD sra + ``` + + This requires that your IRC oper block in UnrealIRCd is already configured. See [IRC Operations](/docs/services/irc/operations) for oper setup. + +3. Verify your services operator status: + + ```text + /msg OperServ SPECS + ``` + +## Database management + +Atheme uses the `backend/opensex` flat-file database format. The database file is stored at `data/atheme/data/services.db` on the host, mounted into the container at `/usr/local/atheme/data/services.db`. + +> **Warning:** This is not SQLite. The opensex format is Atheme's own serialization format. Do not attempt to query it with `sqlite3` or other SQL tools. + +### Automatic saves + +Atheme saves the database to disk every 5 minutes (`commit_interval = 5` in the `general{}` config block). The database is also saved on a clean shutdown. + +### Backing up the database + +```bash +# Stop writes by shutting down Atheme (optional but safest) +docker compose stop atl-irc-services + +# Copy the database file +cp data/atheme/data/services.db data/atheme/data/services.db.bak + +# Restart Atheme +docker compose start atl-irc-services +``` + +For a live backup without stopping the service, copy the file between save intervals. Since Atheme writes the database atomically, a simple `cp` is usually safe: + +```bash +cp data/atheme/data/services.db "backups/atheme-$(date +%Y%m%d-%H%M%S).db" +``` + +### Restoring from backup + +1. Stop Atheme: + + ```bash + docker compose stop atl-irc-services + ``` + +2. Replace the database file: + + ```bash + cp backups/atheme-YYYYMMDD-HHMMSS.db data/atheme/data/services.db + ``` + +3. Start Atheme: + + ```bash + docker compose start atl-irc-services + ``` + +4. Verify services loaded correctly: + + ```bash + docker compose logs atl-irc-services --tail 50 + ``` + + Look for a line indicating the database was loaded successfully and the number of accounts/channels registered. + +### Database location summary + +| Host path | Container path | Contents | +|---|---|---| +| `data/atheme/data/services.db` | `/usr/local/atheme/data/services.db` | All registered nicknames, channels, access lists, memos, and settings | +| `data/atheme/logs/atheme.log` | `/usr/local/atheme/logs/atheme.log` | Service log (all levels, including debug) | + +## Container operations + +Atheme does not have its own `just` module. Use `docker compose` commands directly, referencing the service name `atl-irc-services`. + +### Viewing logs + +```bash +# Follow logs in real time +docker compose logs -f atl-irc-services + +# Last 100 lines +docker compose logs --tail 100 atl-irc-services + +# Or read the log file directly +tail -f data/atheme/logs/atheme.log +``` + +### Restarting the service + +```bash +# Restart the container +docker compose restart atl-irc-services + +# Or stop and start separately +docker compose stop atl-irc-services +docker compose start atl-irc-services +``` + +> **Note:** Restarting Atheme causes a brief disconnection from the IRC network. All service bots disappear and reconnect. Prefer `REHASH` via OperServ for configuration changes that do not require a restart. + +### Health check + +The container health check runs `pgrep -f atheme-services` every 30 seconds: + +```bash +# Check container health status +docker inspect --format='{{.State.Health.Status}}' atl-irc-services + +# Check if the process is running +docker compose exec atl-irc-services pgrep -f atheme-services +``` + +### Checking connectivity to UnrealIRCd + +Since Atheme shares UnrealIRCd's network namespace, the uplink is on `127.0.0.1:6901`: + +```bash +docker compose exec atl-irc-services nc -z 127.0.0.1 6901 && echo "Uplink reachable" || echo "Uplink down" +``` + +## Troubleshooting + +### Atheme not connecting to UnrealIRCd + +Check that UnrealIRCd is healthy and the link password matches: + +```bash +# Check UnrealIRCd health +docker inspect --format='{{.State.Health.Status}}' atl-irc-server + +# Check Atheme logs for link errors +docker compose logs atl-irc-services | grep -i "link\|password\|uplink" +``` + +The `IRC_SERVICES_PASSWORD` variable must be identical in both the UnrealIRCd and Atheme configs. If you changed it, regenerate both configs with `just init` and restart both services. + +### Services not responding to commands + +If service bots are visible on IRC but not responding: + +```bash +# Check if the Atheme process is running +docker compose exec atl-irc-services pgrep -f atheme-services + +# Check for errors in the log +grep -i "error\|fatal\|warning" data/atheme/logs/atheme.log | tail -20 +``` + +### Database corruption + +If Atheme fails to start with database errors: + +1. Check the logs for the specific error: + + ```bash + docker compose logs atl-irc-services | grep -i "database\|opensex\|corrupt" + ``` + +2. Restore from the most recent backup (see [Restoring from backup](#restoring-from-backup) above). + +3. If no backup is available, you can try starting with an empty database by moving the corrupted file: + + ```bash + docker compose stop atl-irc-services + mv data/atheme/data/services.db data/atheme/data/services.db.corrupt + docker compose start atl-irc-services + ``` + + This loses all registrations and you will need to re-bootstrap from scratch. + +## Related pages + +- [Atheme Overview](/docs/services/atheme) — architecture, service bots, and how Atheme fits in the stack +- [Atheme Configuration](/docs/services/atheme/configuration) — config template, modules, SASL setup, service bot identities +- [IRC Operations](/docs/services/irc/operations) — UnrealIRCd oper setup, config reload, and certificate rotation +- [Backups](/docs/operations/backups) — cross-service backup and restore procedures +- [Security](/docs/operations/security) — credential rotation for `IRC_SERVICES_PASSWORD` diff --git a/apps/docs/content/docs/services/bridge/configuration.mdx b/apps/docs/content/docs/services/bridge/configuration.mdx new file mode 100644 index 00000000..b81d2ce7 --- /dev/null +++ b/apps/docs/content/docs/services/bridge/configuration.mdx @@ -0,0 +1,151 @@ +--- +title: Bridge Configuration +description: config.yaml format and channel mapping for the atl.chat bridge. +--- + +The bridge is configured via `apps/bridge/config.yaml`, which defines channel mappings and operational settings. Sensitive values (tokens, passwords) are injected via environment variables — they do not appear in `config.yaml`. + +`config.yaml` is generated from `apps/bridge/config.template.yaml` by `scripts/prepare-config.sh` (via `envsubst`) during `just init` or `just dev`/`just prod`. + +## config.yaml structure + +```yaml +mappings: + - discord_channel_id: ${BRIDGE_DISCORD_CHANNEL_ID} + irc: + server: ${IRC_BRIDGE_SERVER} + port: ${IRC_TLS_PORT} + tls: true + channel: '#general' + xmpp: + muc_jid: general@muc.${PROSODY_DOMAIN} + +announce_joins_and_quits: true +announce_extras: false + +identity_cache_ttl_seconds: 3600 +avatar_cache_ttl_seconds: 86400 +xmpp_avatar_base_url: ${XMPP_AVATAR_BASE_URL} +irc_puppet_idle_timeout_hours: 24 +irc_puppet_postfix: '' + +irc_throttle_limit: 10 +irc_message_queue: 30 +irc_rejoin_delay: 5 +irc_auto_rejoin: true + +# Set false for dev with self-signed certs; true for production +irc_tls_verify: ${IRC_TLS_VERIFY} + +irc_relaymsg_clean_nicks: false +irc_redact_enabled: false + +irc_use_sasl: false +irc_sasl_user: '' +irc_sasl_password: '' +``` + +> **Warning:** `irc_tls_verify` defaults to `false` in development (self-signed certificates). You must set `BRIDGE_IRC_TLS_VERIFY=true` in `.env` before any public deployment. + +## Environment variables + +| Variable | Description | Required | +|---|---|---| +| `BRIDGE_DISCORD_TOKEN` | Discord bot token | Yes (if Discord adapter enabled) | +| `BRIDGE_DISCORD_CHANNEL_ID` | Discord channel snowflake ID to bridge | Yes | +| `IRC_BRIDGE_SERVER` | IRC server hostname (use Docker service name internally) | Yes | +| `IRC_TLS_PORT` | IRC server port (default: `6697`) | No | +| `BRIDGE_IRC_NICK` | IRC nick for the bridge bot | No | +| `BRIDGE_IRC_OPER_PASSWORD` | IRC oper password for the bridge | No | +| `BRIDGE_IRC_TLS_VERIFY` | Override `irc_tls_verify` (`true`/`false`) | No | +| `BRIDGE_XMPP_COMPONENT_JID` | XMPP component JID (for component protocol) | Yes (if XMPP enabled) | +| `BRIDGE_XMPP_COMPONENT_SECRET` | XMPP component secret | Yes (if XMPP enabled) | +| `BRIDGE_XMPP_COMPONENT_SERVER` | XMPP server hostname for component connection | No | +| `BRIDGE_XMPP_COMPONENT_PORT` | XMPP component port (default: `5347`) | No | +| `BRIDGE_PORTAL_BASE_URL` | Portal API base URL for identity resolution | No | +| `BRIDGE_PORTAL_TOKEN` | Portal API authentication token | No | + +## Portal integration + +The bridge supports an optional Portal identity service for unified user profiles across platforms. When configured, the bridge resolves user identities across Discord, IRC, and XMPP so that bridged messages display consistent nicknames. + +To enable Portal integration, set both environment variables: + +```bash +BRIDGE_PORTAL_BASE_URL=https://portal.atl.chat/api +BRIDGE_PORTAL_TOKEN=your-portal-api-token +``` + +When Portal is not configured, the bridge runs in dev mode with fallback nick resolution — bridged messages use the originating platform's display name with a platform suffix. + +The Portal client includes: +- Automatic retries with exponential backoff (via tenacity) +- TTL-based identity cache (default: 1024 entries) to reduce API calls +- Lookup methods: `discord_to_irc`, `discord_to_xmpp`, `irc_to_discord`, `xmpp_to_discord` + +## Channel mapping format + +Channel mappings connect a Discord channel to an IRC channel and/or an XMPP MUC room. Each mapping is defined in the `mappings` list within the config: + +```yaml +mappings: + - discord_channel_id: "123456789012345678" # Discord channel snowflake ID + irc: + server: "irc.atl.chat" # IRC server hostname + port: 6697 # IRC server port + tls: true # Use TLS connection + channel: "#general" # IRC channel name + xmpp: + muc_jid: "general@muc.atl.chat" # XMPP MUC room JID +``` + +Each mapping requires a `discord_channel_id`. The `irc` and `xmpp` sections are optional — you can bridge Discord to IRC only, Discord to XMPP only, or all three platforms together. + +### IRC target fields + +| Field | Type | Description | +|---|---|---| +| `server` | string | IRC server hostname | +| `port` | integer | IRC server port (default: `6667`) | +| `tls` | boolean | Use TLS for the connection | +| `channel` | string | IRC channel name (including `#` prefix) | + +### XMPP target fields + +| Field | Type | Description | +|---|---|---| +| `muc_jid` | string | Full JID of the XMPP MUC room | + +### Multiple mappings + +You can define multiple mappings to bridge several channels: + +```yaml +mappings: + - discord_channel_id: "111111111111111111" + irc: + server: "irc.atl.chat" + port: 6697 + tls: true + channel: "#general" + xmpp: + muc_jid: "general@muc.atl.chat" + + - discord_channel_id: "222222222222222222" + irc: + server: "irc.atl.chat" + port: 6697 + tls: true + channel: "#support" + # No XMPP target — this channel bridges Discord <-> IRC only +``` + + +## Related pages + +- [Bridge Overview](/docs/services/bridge) — architecture, event bus, and adapter design +- [Bridge Operations](/docs/services/bridge/operations) — health monitoring, adapter debugging, and Portal identity +- [IRC Configuration](/docs/services/irc/configuration) — UnrealIRCd config including oper blocks for the bridge +- [XMPP Configuration](/docs/services/xmpp/configuration) — Prosody component configuration for the bridge +- [Environment Variables](/docs/reference/environment-variables) — complete variable reference including all `BRIDGE_*` variables +- [Security](/docs/operations/security) — secret management for `BRIDGE_DISCORD_TOKEN` and other bridge credentials diff --git a/apps/docs/content/docs/services/bridge/index.mdx b/apps/docs/content/docs/services/bridge/index.mdx new file mode 100644 index 00000000..5b765206 --- /dev/null +++ b/apps/docs/content/docs/services/bridge/index.mdx @@ -0,0 +1,117 @@ +--- +title: Bridge Overview +description: Event bus and adapter architecture of the atl.chat Discord↔IRC↔XMPP bridge. +--- + +The atl.chat bridge is a Python application that connects Discord, IRC, and XMPP into a unified message bus. Each protocol is implemented as an independent adapter that emits and consumes normalised internal events. + +## Architecture + +``` +Discord Adapter ←→ Internal Event Bus ←→ IRC Adapter + ↕ + XMPP Adapter +``` + +The event bus decouples adapters from each other: a message arriving on Discord is converted to an internal event and forwarded to IRC and XMPP adapters, which translate it into their respective protocol messages. + +## Technology + +| Component | Library | +|---|---| +| Discord adapter | `discord.py` | +| IRC adapter | `pydle` | +| XMPP adapter | `slixmpp` | +| Entry point | `apps/bridge/src/bridge/__main__.py` | + +## Configuration schema + +The bridge reads its configuration from `apps/bridge/config.yaml`, generated at init time from `apps/bridge/config.template.yaml` via `envsubst`. Sensitive values (tokens, passwords) come from environment variables defined in `.env`. The `Config` class in `src/bridge/config/schema.py` provides typed property access with defaults and environment variable overrides. + +### Channel mappings + +The `mappings` list connects a Discord channel ID to an IRC channel and/or an XMPP MUC room JID. Each entry requires a `discord_channel_id` and optional `irc` and `xmpp` blocks: + +```yaml +mappings: + - discord_channel_id: "123456789012345678" + irc: + server: irc.example.com + port: 6697 + tls: true + channel: "#general" + xmpp: + muc_jid: general@muc.xmpp.example.com +``` + +Validation runs on load and on SIGHUP reload — every mapping must be a dict with a non-empty `discord_channel_id`. + +### Relay toggles + +| Key | Type | Default | Description | +|---|---|---|---| +| `announce_joins_and_quits` | bool | `true` | Relay join/part/quit events to other protocols | +| `announce_extras` | bool | `false` | Relay topic and mode changes | +| `content_filter_regex` | list | `[]` | Messages matching any regex pattern are not bridged | + +### Identity and caching + +| Key | Type | Default | Description | +|---|---|---|---| +| `identity_cache_ttl_seconds` | int | `3600` | TTL for Portal identity cache | +| `avatar_cache_ttl_seconds` | int | `86400` | TTL for avatar URL cache | +| `xmpp_avatar_base_url` | string | — | Base URL for XMPP avatar HEAD checks (use internal Docker hostname) | + +### IRC adapter settings + +| Key | Type | Default | Description | +|---|---|---|---| +| `irc_puppet_idle_timeout_hours` | int | `24` | Disconnect idle puppet connections after this many hours | +| `irc_puppet_ping_interval` | int | `120` | Keep-alive PING interval in seconds | +| `irc_puppet_prejoin_commands` | list | `[]` | Commands sent after puppet connects (supports `{nick}` placeholder) | +| `irc_puppet_postfix` | string | `""` | Suffix appended to puppet nicknames | +| `irc_throttle_limit` | int | `10` | IRC messages per second (token bucket) | +| `irc_message_queue` | int | `30` | Maximum IRC outbound queue size | +| `irc_rejoin_delay` | float | `5` | Seconds before rejoin after KICK or disconnect | +| `irc_auto_rejoin` | bool | `true` | Auto-rejoin channels after KICK or disconnect | + +### IRC TLS and authentication + +| Key | Type | Default | Description | +|---|---|---|---| +| `irc_tls_verify` | bool | `true` (prod) / `false` (dev) | Verify IRC TLS certificates. Overridden by `BRIDGE_IRC_TLS_VERIFY` env var, or set to `false` when `ATL_ENVIRONMENT=dev` | +| `irc_use_sasl` | bool | `false` | Use SASL PLAIN for IRC authentication | +| `irc_sasl_user` | string | `""` | SASL username | +| `irc_sasl_password` | string | `""` | SASL password | + +### IRC protocol features + +| Key | Type | Default | Description | +|---|---|---|---| +| `irc_relaymsg_clean_nicks` | bool | `false` | Use `/d` suffix for RELAYMSG. Enable only if UnrealIRCd has `allow-clean-nicks yes` | +| `irc_redact_enabled` | bool | `false` | Send REDACT when a Discord message is deleted. Disabled by default due to a known UnrealIRCd `third/redact` crash (exit 139) | + +### Environment variable overrides + +Several config keys can be overridden at runtime via environment variables without editing `config.yaml`: + +| Environment variable | Overrides | Notes | +|---|---|---| +| `BRIDGE_IRC_TLS_VERIFY` | `irc_tls_verify` | `true`/`false` | +| `BRIDGE_IRC_REDACT_ENABLED` | `irc_redact_enabled` | `true`/`false` | +| `BRIDGE_RELAYMSG_CLEAN_NICKS` | `irc_relaymsg_clean_nicks` | `true`/`false` | +| `ATL_ENVIRONMENT` | `irc_tls_verify` | When set to `dev`, TLS verify defaults to `false` | + +### Config reload + +The bridge supports live config reload via `SIGHUP`. On reload, the YAML file is re-read, env overrides are re-loaded, and validation runs. A `ConfigReload` event is dispatched to all adapters. + +```bash +# Reload config without restarting the container +docker kill --signal=SIGHUP atl-bridge +``` + +## Related pages + +- [Configuration](/docs/services/bridge/configuration) — config.yaml format, channel mapping +- [Operations](/docs/services/bridge/operations) — Portal identity, adapter debugging diff --git a/apps/docs/content/docs/services/bridge/meta.json b/apps/docs/content/docs/services/bridge/meta.json new file mode 100644 index 00000000..c3e66ea9 --- /dev/null +++ b/apps/docs/content/docs/services/bridge/meta.json @@ -0,0 +1,8 @@ +{ + "title": "Bridge", + "pages": [ + "index", + "configuration", + "operations" + ] +} diff --git a/apps/docs/content/docs/services/bridge/operations.mdx b/apps/docs/content/docs/services/bridge/operations.mdx new file mode 100644 index 00000000..a8e72a1a --- /dev/null +++ b/apps/docs/content/docs/services/bridge/operations.mdx @@ -0,0 +1,92 @@ +--- +title: Bridge Operations +description: Health monitoring, adapter debugging, and Portal identity management for the bridge. +--- + +This page covers operational tasks for the bridge: checking health, debugging adapter connections, and managing Portal identity integration. + +## Health check + +The bridge container uses `pgrep` to verify the Python process is running. Check the health status: + +```bash +docker inspect --format='{{.State.Health.Status}}' atl-bridge +# Expected: healthy +``` + +The underlying health check command is `pgrep -f "bridge.__main__"`. + +## Restarting the bridge + +```bash +docker compose restart atl-bridge +``` + +After a restart, verify all adapters reconnect by inspecting logs: + +```bash +docker logs --tail 50 atl-bridge +``` + +Look for connection confirmation messages from each adapter. + +## Debugging adapters + +Each adapter logs connection events and errors. Key log patterns to watch for: + +| Pattern | Meaning | +|---|---| +| `Discord: connected` | Discord adapter connected successfully | +| `IRC: connected to ` | IRC adapter connected to the server | +| `XMPP: session started` | XMPP adapter established a session | + +### Common error patterns + +| Pattern | Meaning | Resolution | +|---|---|---| +| `Discord: authentication failed` | Invalid or expired Discord token | Regenerate `DISCORD_TOKEN` in the Discord Developer Portal | +| `IRC: connection refused` | IRC server unreachable | Verify UnrealIRCd is running with `just status` | +| `XMPP: auth failed` | Wrong XMPP password | Check `XMPP_BRIDGE_PASSWORD` matches Prosody config | +| `Router: no mapping for ` | Message from an unmapped channel | Add the channel to the `mappings` list in config | + +### Viewing full logs + +```bash +# Follow logs in real time +docker logs --follow atl-bridge + +# Show last 200 lines +docker logs --tail 200 atl-bridge + +# Filter for a specific adapter +docker logs atl-bridge 2>&1 | grep "IRC:" +``` + +## Portal integration + +The bridge supports an optional Portal identity service for unified user profiles. When Portal is configured, the bridge resolves consistent nicknames across platforms. + +### Checking Portal connectivity + +If `BRIDGE_PORTAL_BASE_URL` and `BRIDGE_PORTAL_TOKEN` are set, the bridge logs Portal status at startup: + +```bash +docker logs atl-bridge 2>&1 | grep -i "portal" +``` + +When Portal is not configured, you see: + +```text +XMPP adapter running without Portal (dev mode): using fallback nicks +``` + +### Identity cache + +The Portal client caches identity lookups (default: 1024 entries with TTL expiry) to reduce API calls. The cache is in-memory and resets on bridge restart. + +## Related pages + +- [Configuration](/docs/services/bridge/configuration) — config.yaml format, channel mappings, environment variables +- [Bridge Overview](/docs/services/bridge) — architecture and adapter design +- [Troubleshooting](/docs/operations/troubleshooting) — cross-service diagnostic commands +- [Monitoring](/docs/operations/monitoring) — health checks and log inspection across all services diff --git a/apps/docs/content/docs/services/irc/configuration.mdx b/apps/docs/content/docs/services/irc/configuration.mdx new file mode 100644 index 00000000..b5679275 --- /dev/null +++ b/apps/docs/content/docs/services/irc/configuration.mdx @@ -0,0 +1,204 @@ +--- +title: IRC Configuration +description: Environment variables, config templates, and secrets for the UnrealIRCd and Atheme IRC stack. +--- + +The IRC stack is configured entirely through environment variables in `.env` and config templates processed by `scripts/prepare-config.sh` at startup — you never edit the generated config files directly. + +## How config templating works + +UnrealIRCd and Atheme both use template files that contain `${VARIABLE}` placeholders. When you run `just init` or `just dev`, the `scripts/prepare-config.sh` script runs `envsubst` to substitute your `.env` values into the templates, producing the final config files. + +| Template | Output | Service | +|---|---|---| +| `apps/unrealircd/config/unrealircd.conf.template` | `apps/unrealircd/config/unrealircd.conf` | UnrealIRCd | +| `apps/atheme/config/atheme.conf.template` | `apps/atheme/config/atheme.conf` | Atheme | + +To add a new config block, edit the `.template` file (not the generated `.conf`), add any new `${VARIABLES}` to `.env.example`, and re-run `scripts/prepare-config.sh` (or `just init`). + +> **Warning:** Never edit the generated `.conf` files directly. They are overwritten on every `just init` or `just dev` run. + +## Environment variables + +All IRC-related variables are defined in the `IRC SERVICE` section of `.env.example`. Copy `.env.example` to `.env` and customise before first run. + +### Network identity + +| Variable | Description | Required | Default | +|---|---|---|---| +| `IRC_DOMAIN` | Public hostname of the IRC server | Yes | `irc.atl.chat` | +| `IRC_ROOT_DOMAIN` | Root domain for the network | Yes | `atl.chat` | +| `IRC_NETWORK_NAME` | Human-readable network name shown to clients | Yes | `All Things Linux IRC` | +| `IRC_CLOAK_PREFIX` | Prefix for cloaked hostnames (e.g. `atl-ABC123.users.atl.chat`) | Yes | `atl` | + +### Build versions + +| Variable | Description | Required | Default | +|---|---|---|---| +| `UNREALIRCD_VERSION` | UnrealIRCd Docker image git tag | Yes | `6.2.0.1` | +| `ATHEME_VERSION` | Atheme Docker image git tag | Yes | `master` | + +### Ports + +| Variable | Description | Required | Default | +|---|---|---|---| +| `IRC_TLS_PORT` | Client TLS port | Yes | `6697` | +| `IRC_SERVER_PORT` | Server-to-server TLS port | Yes | `6900` | +| `IRC_RPC_PORT` | JSON-RPC API port (WebPanel) | Yes | `8600` | +| `IRC_WEBSOCKET_PORT` | WebSocket port for web clients | Yes | `8000` | + +### Security secrets + +| Variable | Description | Required | Default | +|---|---|---|---| +| `IRC_CLOAK_KEY_1` | First cloak key for IP masking | Yes | Example key (change for prod) | +| `IRC_CLOAK_KEY_2` | Second cloak key | Yes | Example key (change for prod) | +| `IRC_CLOAK_KEY_3` | Third cloak key | Yes | Example key (change for prod) | +| `IRC_OPER_PASSWORD` | IRC operator password (argon2id hash) | Yes | `change_me_irc_oper_password` | +| `IRC_DRPASS` | Password for `/DIE` and `/RESTART` commands | Yes | `change_me_drpass` | +| `IRC_SERVICES_PASSWORD` | Atheme ↔ UnrealIRCd link password | Yes | `change_me_secure_services_pass` | +| `ATL_WEBIRC_PASSWORD` | WEBIRC password for gateway proxy | Yes | `change_me_webirc_password` | + +> **Warning:** All `change_me_*` values must be replaced before any public deployment. See [Security](/docs/operations/security) for generation instructions. + +### Admin info + +| Variable | Description | Required | Default | +|---|---|---|---| +| `IRC_ADMIN_NAME` | Admin name shown in `/ADMIN` | Yes | `All Things Linux` | +| `IRC_ADMIN_EMAIL` | Admin email shown in ban messages | Yes | `admin@allthingslinux.org` | +| `IRC_STAFF_VHOST` | Virtual host for IRC operators | Yes | `allthingslinux.org` | + +### STS (Strict Transport Security) + +| Variable | Description | Required | Default | +|---|---|---|---| +| `IRC_STS_DURATION` | STS cache duration (start low: `1m`, increase to `180d`) | Yes | `1m` | +| `IRC_STS_PRELOAD` | Enable STS preload in clients | Yes | `no` | + +### TLS certificate paths + +| Variable | Description | Required | Default | +|---|---|---|---| +| `IRC_SSL_CERT_PATH` | Path to fullchain.pem inside container | Yes | `/home/unrealircd/unrealircd/certs/live/irc.atl.chat/fullchain.pem` | +| `IRC_SSL_KEY_PATH` | Path to privkey.pem inside container | Yes | `/home/unrealircd/unrealircd/certs/live/irc.atl.chat/privkey.pem` | + +### Atheme services link + +| Variable | Description | Required | Default | +|---|---|---|---| +| `IRC_SERVICES_SERVER` | Atheme server name in the IRC network | Yes | `services.atl.chat` | +| `ATHEME_SERVER_NAME` | Atheme's own server identity | Yes | `services.atl.chat` | +| `ATHEME_SERVER_DESC` | Atheme server description | Yes | `All Things Linux IRC Services` | +| `ATHEME_UPLINK_HOST` | Host Atheme connects to (localhost via network namespace) | Yes | `127.0.0.1` | +| `ATHEME_UPLINK_PORT` | Port Atheme connects to on UnrealIRCd | Yes | `6901` | +| `ATHEME_NUMERIC` | Atheme server numeric identifier | Yes | `00A` | +| `ATHEME_RECONTIME` | Reconnect interval in seconds | Yes | `10` | +| `ATHEME_HTTPD_PORT` | Atheme JSON-RPC HTTP port | Yes | `8081` | + +### WebPanel + +| Variable | Description | Required | Default | +|---|---|---|---| +| `WEBPANEL_PORT` | WebPanel HTTP port | Yes | `8080` | +| `WEBPANEL_RPC_USER` | RPC username for WebPanel → UnrealIRCd | Yes | `adminpanel` | +| `WEBPANEL_RPC_PASSWORD` | RPC password | Yes | `change_me_webpanel_password` | + +### The Lounge + +| Variable | Description | Required | Default | +|---|---|---|---| +| `THELOUNGE_PORT` | The Lounge web client port | Yes | `9000` | +| `THELOUNGE_WEBIRC_PASSWORD` | WEBIRC password for The Lounge → UnrealIRCd | Yes | `change_me_thelounge_webirc` | +| `THELOUNGE_DELETE_UPLOADS_AFTER_MINUTES` | Auto-delete uploaded files after N minutes | No | `1440` | + +## Cloak keys + +Cloak keys mask user IP addresses so that real IPs are never visible to other users. Three keys are required and must be identical across all servers in the network. + +### Generating cloak keys + +Use the built-in generation command: + +```bash +just irc gencloak +``` + +This runs `scripts/gencloak-update-env.sh`, which: + +1. Spins up a temporary UnrealIRCd container +2. Runs the `gencloak` command to generate three cryptographically random keys +3. Updates `IRC_CLOAK_KEY_1`, `IRC_CLOAK_KEY_2`, and `IRC_CLOAK_KEY_3` in your `.env` file +4. Re-runs `scripts/prepare-config.sh` to apply the new keys to the config + +After generating new keys, reload the config: + +```bash +just irc reload +``` + +> **Warning:** Changing cloak keys changes every user's cloaked hostname. Existing channel bans based on cloaked hosts will stop matching. Coordinate key changes with your operator team. + +## Config template structure + +The main template at `apps/unrealircd/config/unrealircd.conf.template` is organized into the following sections: + +| Section | Lines | Contents | +| --- | --- | --- | +| Third-party modules | 320–370 | cloak_sha256, metadata, react, redact, relaymsg-atl | +| Server identity (`me {}`) | 370–380 | Server name, SID, network info | +| Proxy/WEBIRC blocks | 380–420 | NPM gateway, The Lounge, X-Forwarded-For trust | +| Listen blocks | 420–460 | Ports 6697, 6900, 6901, 8600, 8000, Unix sockets | +| TLS configuration | 460–540 | Protocols, ciphers, STS, plaintext/outdated policies | +| Link and allow blocks | 540–570 | Atheme services link, client allow rules | +| Ban nicks | 570–690 | Reserved nicknames (services, system names) | +| Blacklists (DNSBL) | 690–730 | DroneBL, EFnetRBL, Tornevall | +| Network `set {}` block | 730–760 | Network name, cloak keys, hidden host prefix | +| Oper blocks | 760–800 | Admin oper, bridge oper, RPC user | +| Logging | 800–840 | Text log (100M), JSON log (250M) | +| Server `set {}` block | 840–end | Modes, anti-flood, connthrottle, restrict-commands, history | + +## Audit findings and recommendations + +The following findings from a configuration audit remain relevant to the current setup. They are informational — the defaults work for development, but you should address them before production deployment. + +### TLS configuration + +The TLS setup is strong: TLS 1.2+ only, ECDHE-only ciphers with forward secrecy, and post-quantum hybrid key exchange (`X25519MLKEM768`) when OpenSSL 3.5.0+ is available. STS is enabled with a configurable phased rollout (`IRC_STS_DURATION`). + +### Oper block security + +The admin oper block restricts connections to the Docker network (`172.16.0.0/12`) and requires a TLS connection (`require-modes "z"`). The oper password must be an argon2id hash — generate one with: + +```bash +docker run --rm ghcr.io/allthingslinux/unrealircd \ + ./unrealircd mkpasswd argon2 'your-password-here' +``` + +### Connection throttling + +The `connthrottle` module is loaded and configured with exemptions for identified users and those with established reputation scores. For production, review the `connect-flood` rate (currently `5:60` — 5 connections per 60 seconds per IP). + +### DNSBL configuration + +Three DNS blacklists are configured: DroneBL, EFnetRBL, and Tornevall. The Tornevall DNSBL has had historical reliability issues and uses a broad reply code range (1–16). Consider removing it or narrowing the reply codes if you experience false positives. + +### Missing spamfilter rules + +The included `spamfilter.conf` contains rules from 2005 targeting obsolete malware. For production, either write modern rules targeting current spam patterns (crypto scams, phishing, mass-highlight floods) or rely on dynamic spamfilters via the `/SPAMFILTER` command. + +### Recommended third-party modules + +Two additional modules from the `third-party-modules.list` are worth enabling for production: + +- `third/block_masshighlight` — blocks mass-highlighting (mentioning many nicks at once), a common harassment tactic +- `third/repeatprot` — catches repeated messages that slip through rate limits + +## Related pages + +- [Operations](/docs/services/irc/operations) — oper setup, config reload, cert rotation +- [Modules](/docs/services/irc/modules) — third-party module management +- [Troubleshooting](/docs/services/irc/troubleshooting) — common issues and diagnostics +- [User Modes](/docs/services/irc/user-modes) — user mode reference +- [Environment Variables](/docs/reference/environment-variables) — full variable reference +- [Security](/docs/operations/security) — secret generation and rotation diff --git a/apps/docs/content/docs/services/irc/dns.mdx b/apps/docs/content/docs/services/irc/dns.mdx new file mode 100644 index 00000000..d4d6e401 --- /dev/null +++ b/apps/docs/content/docs/services/irc/dns.mdx @@ -0,0 +1,107 @@ +--- +title: IRC DNS & Ports +description: Port assignments, A records, STS policy, and WebIRC configuration for IRC. +--- + +The IRC server requires a public A record, open firewall ports for client and TLS connections, an STS policy for automatic TLS upgrades, and WebIRC configuration for web clients. + +## Ports + +These port assignments are defined in `apps/unrealircd/config/unrealircd.conf.template`: + +| Port | Purpose | Options | +|---|---|---| +| `6697` | IRC over TLS (client connections) | `tls` | +| `6900` | Server-to-server links (TLS) | `tls; serversonly` | +| `6901` | Atheme services uplink (internal) | `serversonly` | +| `8000` | WebSocket (plain, TLS terminated at reverse proxy) | `websocket { type text; }` | +| `8600` | JSON-RPC API (TLS) | `rpc; tls` | + +UnrealIRCd also listens on two Unix sockets for local IPC: +- `/home/unrealircd/unrealircd/data/rpc.socket` — RPC socket for the WebPanel +- `/home/unrealircd/unrealircd/data/services.sock` — Atheme services socket + +## DNS records + +Create an A record pointing your IRC domain to the server IP: + +```text +irc.atl.chat. IN A +``` + +If your server has an IPv6 address, add an AAAA record as well: + +```text +irc.atl.chat. IN AAAA +``` + +Most IRC clients resolve both A and AAAA records automatically. Providing both ensures connectivity for IPv4-only and IPv6-capable clients. + +## STS (Strict Transport Security) + +UnrealIRCd advertises an STS policy to IRC clients, automatically redirecting capable clients from plaintext to TLS. The `sts` module is loaded by default in the atl.chat configuration. + +The STS policy is configured inside the `set::tls` block (not inside `set::plaintext-policy`): + +```c +set { + tls { + /* STS policy — redirects clients to TLS port 6697 */ + sts-policy { + port 6697; /* Redirect target */ + duration ${IRC_STS_DURATION}; /* How long clients cache the policy */ + preload ${IRC_STS_PRELOAD}; /* Whether to preload in client */ + } + } +} +``` + +You control STS behaviour through two environment variables: + +| Variable | Description | Recommended production value | +|---|---|---| +| `IRC_STS_DURATION` | Cache duration for the STS policy | `180d` (6 months) | +| `IRC_STS_PRELOAD` | Enable STS preloading in clients | `yes` | + +Roll out STS gradually — start with a short duration (e.g., `1m`) and increase over time as you confirm clients connect successfully over TLS. + +## WebIRC + +WebIRC allows The Lounge and other web clients to pass the real client IP to UnrealIRCd instead of appearing as the Docker gateway IP. The configuration uses `proxy {}` blocks: + +```c +/* The Lounge web IRC client: passes real user IP via WEBIRC */ +proxy thelounge { + type webirc; + match { ip 172.16.0.0/12; } /* Docker network range */ + password "${THELOUNGE_WEBIRC_PASSWORD}"; +} + +/* NPM/gateway WEBIRC: passes real client IP from reverse proxy */ +proxy npm-webirc { + type webirc; + match { ip ${ATL_GATEWAY_IP}/32; } + password "${ATL_WEBIRC_PASSWORD}"; +} +``` + +The `THELOUNGE_WEBIRC_PASSWORD` must match the password configured in The Lounge's config. The Docker network range `172.16.0.0/12` covers all default Docker bridge subnets. + +For WebSocket connections through a reverse proxy (e.g., Nginx Proxy Manager), an additional `proxy` block trusts `X-Forwarded-For` headers: + +```c +proxy npm-x-forwarded { + type x-forwarded; + match { ip ${ATL_GATEWAY_IP}/32; ip 100.64.0.0/10; } +} +``` + +The `100.64.0.0/10` range covers Tailscale addresses for overlay network deployments. + +## Related pages + +- [IRC Stack Overview](/docs/services/irc) — UnrealIRCd and Atheme overview +- [IRC Configuration](/docs/services/irc/configuration) — env vars, config templates, secrets +- [XMPP DNS](/docs/services/xmpp/dns) — XMPP SRV records and federation DNS +- [Networking](/docs/architecture/networking) — full port registry and firewall rules +- [SSL/TLS](/docs/operations/ssl-tls) — certificate management for TLS connections diff --git a/apps/docs/content/docs/services/irc/index.mdx b/apps/docs/content/docs/services/irc/index.mdx new file mode 100644 index 00000000..559172bf --- /dev/null +++ b/apps/docs/content/docs/services/irc/index.mdx @@ -0,0 +1,25 @@ +--- +title: IRC Stack Overview +description: Overview of the atl.chat IRC stack — UnrealIRCd server and Atheme IRC services. +--- + +The atl.chat IRC stack consists of UnrealIRCd (the IRC server daemon) and Atheme (IRC services providing NickServ, ChanServ, and friends). Both run as Docker containers and are configured entirely via environment variables and templated config files. + +## Components + +### UnrealIRCd + +UnrealIRCd is the IRC server. The `apps/unrealircd/` image uses a multi-stage Alpine build: a build stage compiles UnrealIRCd from source, and a minimal runtime stage contains only the compiled binary and its config. + +### Atheme + +Atheme provides IRC services (NickServ, ChanServ, HostServ, etc.). It runs in the same network namespace as UnrealIRCd and connects to it via the Atheme uplink on `127.0.0.1:6901`. + +## Related pages + +- [Configuration](/docs/services/irc/configuration) — env vars, config templates, secrets +- [DNS](/docs/services/irc/dns) — ports, A records, STS, WebIRC +- [Modules](/docs/services/irc/modules) — third-party module management +- [Operations](/docs/services/irc/operations) — oper setup, Atheme bootstrap, SSL reload +- [Troubleshooting](/docs/services/irc/troubleshooting) — common issues and diagnostics +- [User Modes](/docs/services/irc/user-modes) — user mode reference diff --git a/apps/docs/content/docs/services/irc/meta.json b/apps/docs/content/docs/services/irc/meta.json new file mode 100644 index 00000000..6883e728 --- /dev/null +++ b/apps/docs/content/docs/services/irc/meta.json @@ -0,0 +1,12 @@ +{ + "title": "IRC", + "pages": [ + "index", + "configuration", + "dns", + "modules", + "operations", + "troubleshooting", + "user-modes" + ] +} diff --git a/apps/docs/content/docs/services/irc/modules.mdx b/apps/docs/content/docs/services/irc/modules.mdx new file mode 100644 index 00000000..a52bae61 --- /dev/null +++ b/apps/docs/content/docs/services/irc/modules.mdx @@ -0,0 +1,121 @@ +--- +title: IRC Modules +description: Managing third-party UnrealIRCd modules with third-party-modules.list and manage-modules.sh. +--- + +UnrealIRCd supports third-party modules for extended functionality. atl.chat manages these via a declarative list file and an automated install process during the Docker image build. + +## Module list + +Third-party modules are declared in `apps/unrealircd/third-party-modules.list`. The file format is: + +- One module name per line (e.g., `third/showwebirc`) +- Lines starting with `#` are comments +- Empty lines are ignored + +The current modules installed by atl.chat: + +| Module | Purpose | +|---|---| +| `third/showwebirc` | Shows WebIRC/WebSocket information in WHOIS replies | +| `third/metadata` | IRCv3 `draft/metadata` — avatars, message colouring, status texts | +| `third/react` | IRCv3 `draft/react` — message reactions | +| `third/redact` | IRCv3 `draft/message-redaction` — REDACT command for message deletion | +| `third/relaymsg-atl` | atl.chat fork of RELAYMSG for stateless bridging | + +## Installing modules + +Modules are installed during the Docker image build. The `Containerfile` reads `third-party-modules.list` and runs `unrealircd module install` for each entry: + +```bash +# The build process runs this for each module: +./unrealircd module install third/module-name +``` + +To add a new module: + +1. Find the module at [modules.unrealircd.org](https://modules.unrealircd.org/) +2. Add a line to `apps/unrealircd/third-party-modules.list`: + + ```text + # Description of what the module does + third/module-name + ``` + +3. Rebuild the Docker image: + + ```bash + docker compose build atl-irc-server + ``` + +4. Reference the module in `unrealircd.conf`: + + ```c + loadmodule "third/module-name"; + ``` + +5. Restart or rehash the server: + + ```bash + # Rehash loads new config without disconnecting users + just irc reload + + # Full restart if the module requires it + docker compose restart atl-irc-server + ``` + +## Verifying modules + +To check which modules are loaded on a running server: + +```bash +# List all installed modules +docker compose exec atl-irc-server unrealircd module list + +# Get info about a specific module +docker compose exec atl-irc-server unrealircd module info third/module-name +``` + +To upgrade all third-party modules to their latest versions: + +```bash +docker compose exec atl-irc-server unrealircd module upgrade +``` + +## Enabling modules + +After adding a module to the list and rebuilding, reference it in `unrealircd.conf` with a `loadmodule` directive: + +```c +loadmodule "third/showwebirc"; +loadmodule "third/metadata"; +loadmodule "third/react"; +loadmodule "third/redact"; +loadmodule "third/relaymsg-atl"; +``` + +Some modules require additional configuration blocks. For example, the `metadata` module: + +```c +metadata { + max-user-metadata 10; + max-channel-metadata 10; + max-subscriptions 10; +} +``` + +And the `relaymsg-atl` module for bridge integration: + +```c +relaymsg { + hostmask "bridge@${IRC_DOMAIN}"; + require-separator no; /* Allow clean nicks without / separator */ +} +``` + +## Related pages + +- [IRC Stack Overview](/docs/services/irc) — UnrealIRCd and Atheme overview +- [IRC Configuration](/docs/services/irc/configuration) — env vars, config templates, and module loading +- [IRC Operations](/docs/services/irc/operations) — rehash vs restart for module changes +- [Bridge Overview](/docs/services/bridge) — the relaymsg-atl module powers bridge integration diff --git a/apps/docs/content/docs/services/irc/operations.mdx b/apps/docs/content/docs/services/irc/operations.mdx new file mode 100644 index 00000000..14257d51 --- /dev/null +++ b/apps/docs/content/docs/services/irc/operations.mdx @@ -0,0 +1,255 @@ +--- +title: IRC Operations +description: Oper setup, Atheme bootstrap, config reload, and SSL certificate management for the IRC stack. +--- + +This page covers day-to-day and first-time operational tasks for the IRC stack: granting IRC operator access, initialising Atheme services, reloading configuration, and rotating SSL certificates. + +## Oper setup + +IRC operators are configured in the UnrealIRCd config template via `oper {}` blocks. The default admin oper block uses environment variables for the password and vhost. + +### Generating an oper password + +Oper passwords must be stored as argon2id hashes. Generate one using the UnrealIRCd container: + +```bash +docker run --rm ghcr.io/allthingslinux/unrealircd \ + ./unrealircd mkpasswd argon2 'your-secure-password-here' +``` + +This outputs a hash like `$argon2id$v=19$m=6144,t=2,p=2$...`. Set it in your `.env`: + +```bash +IRC_OPER_PASSWORD='$argon2id$v=19$m=6144,t=2,p=2$...' +``` + +Then regenerate the config and reload: + +```bash +scripts/prepare-config.sh +just irc reload +``` + +### Default oper block + +The template defines an `admin` oper with these properties: + +| Property | Value | Notes | +|---|---|---| +| `class` | `opers` | Higher sendq/recvq limits | +| `mask` | `*@172.16.0.0/12` | Restricted to Docker network | +| `password` | `${IRC_OPER_PASSWORD}` | Argon2id hash from `.env` | +| `operclass` | `netadmin-with-override` | Full admin privileges | +| `vhost` | `${IRC_STAFF_VHOST}` | Custom hostname for opers | +| `require-modes` | `z` | Must connect via TLS | + +### Becoming an operator + +Once connected to the IRC server via a TLS connection, authenticate with: + +``` +/OPER admin your-plaintext-password +``` + +You will receive modes `+xwsH` and be auto-joined to `#mod-chat`. + +### Bridge oper + +A separate `bridge` oper exists for the Discord-IRC bridge with minimal permissions (channel override and relaymsg only). Its password is set via `BRIDGE_IRC_OPER_PASSWORD` in `.env`. + +## Atheme bootstrap + +On first start, Atheme's database is empty. You need to register the first account and claim founder status. + +### First-run sequence + +1. Connect to the IRC server and register a nickname with NickServ: + + ``` + /MSG NickServ REGISTER your-password your-email@example.com + ``` + +2. Check your email and confirm the registration (if email verification is enabled): + + ``` + /MSG NickServ VERIFY REGISTER yournick confirmation-code + ``` + +3. Identify with your registered account: + + ``` + /MSG NickServ IDENTIFY your-password + ``` + +4. OPER up to gain admin privileges: + + ``` + /OPER admin your-oper-password + ``` + +5. Register the primary channel: + + ``` + /JOIN #your-channel + /MSG ChanServ REGISTER #your-channel + ``` + +6. Set yourself as network administrator in Atheme (requires oper): + + ``` + /MSG OperServ SOPER yournick add SRA + ``` + + `SRA` (Services Root Administrator) gives full control over all Atheme services. + +### Atheme database + +Atheme stores its database in `data/atheme/data/`. The database is written periodically and on clean shutdown. If Atheme crashes, you may lose recent registrations since the last write. + +## Configuration reload + +UnrealIRCd supports live config reloads without disconnecting users. Atheme does not support live reloads and requires a restart. + +### Rehash vs restart + +| Action | Command | Effect | When to use | +|---|---|---|---| +| Rehash (reload) | `just irc reload` | Re-reads config without disconnecting users | Most config changes: set blocks, oper blocks, listen options, anti-flood, spamfilters | +| Restart | `docker compose restart atl-irc-server` | Full process restart, disconnects all users | Module changes (loadmodule/unloadmodule), TLS library updates, binary upgrades | + +### What requires a restart + +These changes cannot be applied via rehash: + +- Adding or removing `loadmodule` directives +- Changing the `me {}` block (server name, SID) +- Upgrading the UnrealIRCd binary +- Changes to listen block socket types (adding/removing Unix sockets) + +### What can be rehashed + +Most configuration changes take effect immediately after rehash: + +- `set {}` block changes (modes, anti-flood, connthrottle, etc.) +- `oper {}` block changes (passwords, masks, permissions) +- `allow {}` and `deny {}` blocks +- `ban nick {}` and `except ban {}` blocks +- `blacklist {}` (DNSBL) changes +- `link {}` block changes +- Spamfilter rules +- TLS certificate paths (triggers automatic cert reload) + +### Performing a rehash + +```bash +# Via just recipe (sends HUP signal) +just irc reload + +# Or directly via docker +docker compose -f compose.yaml -p atl-chat kill -s HUP atl-irc-server +``` + +Verify the rehash succeeded by checking logs: + +```bash +docker compose -f compose.yaml -p atl-chat logs --tail=20 atl-irc-server +``` + +Look for `Configuration loaded` in the output. + +## SSL certificate rotation + +Certificates are managed by the cert-manager service (Lego/Let's Encrypt). When a certificate is renewed, UnrealIRCd needs to pick up the new cert. + +### How cert renewal works + +1. The cert-manager container runs Lego to obtain/renew certificates via DNS-01 challenge (Cloudflare) +2. Certificates are written to `data/certs/live//` (Let's Encrypt directory layout) +3. UnrealIRCd mounts `data/certs/` at `/home/unrealircd/unrealircd/certs/` + +### Triggering a TLS reload + +After certificate renewal, signal UnrealIRCd to reload the new certificate: + +```bash +# Rehash picks up new cert files automatically +just irc reload +``` + +UnrealIRCd re-reads the certificate and key files on every rehash. No restart is needed. + +### Manual certificate check + +Verify the certificate currently in use: + +```bash +# Check cert expiry from outside +echo | openssl s_client -connect localhost:6697 -servername irc.atl.chat 2>/dev/null | \ + openssl x509 -noout -dates + +# Check cert files on disk +openssl x509 -in data/certs/live/irc.atl.chat/fullchain.pem -noout -dates +``` + +### Certificate expiry notifications + +UnrealIRCd has built-in certificate expiry monitoring (`certificate-expiry-notification yes` in the config). It warns IRC operators when the certificate is approaching expiry. + +## Health checks + +The UnrealIRCd container includes a health check that verifies the process is running. + +### Container health + +```bash +# Check container health status +docker inspect --format='{{.State.Health.Status}}' atl-irc-server + +# Check all IRC stack containers +docker compose -f compose.yaml -p atl-chat ps +``` + +### Log inspection + +```bash +# Follow UnrealIRCd logs +docker compose -f compose.yaml -p atl-chat logs -f atl-irc-server + +# Follow Atheme logs +docker compose -f compose.yaml -p atl-chat logs -f atl-irc-services + +# Check for errors in the last 50 lines +docker compose -f compose.yaml -p atl-chat logs --tail=50 atl-irc-server | grep -i error +``` + +### Connectivity test + +```bash +# Test TLS connection to IRC +echo "QUIT" | openssl s_client -connect localhost:6697 -servername irc.atl.chat 2>/dev/null + +# Test WebSocket port +curl -s -o /dev/null -w "%{http_code}" http://localhost:8000 + +# Test RPC port +curl -sk https://localhost:8600/api/server.list +``` + +### Common failure modes + +| Symptom | Likely cause | Resolution | +|---|---|---| +| Container exits immediately | Config syntax error | Check logs: `docker compose logs atl-irc-server` | +| TLS handshake fails | Missing or expired certificate | Verify cert paths and expiry dates | +| Atheme won't link | Wrong `IRC_SERVICES_PASSWORD` | Ensure password matches in both configs | +| WebSocket 502 | Port 8000 not listening | Check UnrealIRCd started with websocket module loaded | +| RPC auth fails | Wrong `WEBPANEL_RPC_PASSWORD` | Ensure password matches between WebPanel and UnrealIRCd | + +## Related pages + +- [Configuration](/docs/services/irc/configuration) — env vars, config templates, secrets +- [Troubleshooting](/docs/services/irc/troubleshooting) — detailed diagnostic procedures +- [Atheme Operations](/docs/services/atheme/operations) — NickServ, ChanServ, OperServ commands +- [SSL/TLS](/docs/operations/ssl-tls) — cert-manager setup and renewal +- [Monitoring](/docs/operations/monitoring) — health checks across all services diff --git a/apps/docs/content/docs/services/irc/troubleshooting.mdx b/apps/docs/content/docs/services/irc/troubleshooting.mdx new file mode 100644 index 00000000..d026434f --- /dev/null +++ b/apps/docs/content/docs/services/irc/troubleshooting.mdx @@ -0,0 +1,224 @@ +--- +title: IRC Troubleshooting +description: Diagnostic commands and solutions for common UnrealIRCd and Atheme issues. +--- + +This page covers common issues and diagnostic procedures for the IRC stack (UnrealIRCd and Atheme). For cross-service troubleshooting, see [Operations Troubleshooting](/docs/operations/troubleshooting). + +## Quick diagnosis + +Run these commands to get a fast overview of the IRC stack health: + +```bash +# Check container status +docker compose -f compose.yaml -p atl-chat ps + +# View recent UnrealIRCd logs +docker compose -f compose.yaml -p atl-chat logs --tail=30 atl-irc-server + +# View recent Atheme logs +docker compose -f compose.yaml -p atl-chat logs --tail=30 atl-irc-services + +# Check container health +docker inspect --format='{{.State.Health.Status}}' atl-irc-server +``` + +## Common issues + +### Services won't start + +Containers fail to start or exit immediately after launch. + +```bash +# Check exit code and status +docker compose -f compose.yaml -p atl-chat ps -a + +# View startup logs for UnrealIRCd +docker compose -f compose.yaml -p atl-chat logs atl-irc-server + +# View startup logs for Atheme +docker compose -f compose.yaml -p atl-chat logs atl-irc-services +``` + +Common causes: +- Config syntax error — look for `error` or `fatal` in the log output +- Missing environment variables — ensure `.env` exists and has all required values +- Port conflict — another process is using port 6697, 8000, or 8600 + +To fix config issues, edit the template and regenerate: + +```bash +# Regenerate config from templates +scripts/prepare-config.sh + +# Restart the stack +just dev +``` + +### SSL/TLS certificate issues + +TLS setup fails, clients get certificate errors, or certificates expire. + +```bash +# Check certificate expiry on disk +openssl x509 -in data/certs/live/irc.atl.chat/fullchain.pem -noout -dates 2>/dev/null || \ + echo "No certificate found at expected path" + +# Test TLS connection +echo "QUIT" | openssl s_client -connect localhost:6697 -servername irc.atl.chat 2>/dev/null | \ + openssl x509 -noout -subject -dates + +# Check if cert files are readable by the container +docker compose -f compose.yaml -p atl-chat exec atl-irc-server \ + ls -la /home/unrealircd/unrealircd/certs/live/ +``` + +For dev environments, `just init` generates self-signed certificates. For production, see [SSL/TLS](/docs/operations/ssl-tls). + +### Configuration issues + +Services start but don't behave as expected. + +```bash +# Check the generated config (not the template) +# Look for unsubstituted ${VARIABLES} which indicate missing env vars +grep -n '${' apps/unrealircd/config/unrealircd.conf | head -20 + +# Regenerate config +scripts/prepare-config.sh + +# Reload without restart +just irc reload +``` + +### Network connectivity issues + +Cannot connect to the IRC server from a client. + +```bash +# Check if ports are listening +ss -tlnp | grep -E "(6697|8000|8600)" + +# Test IRC TLS port +echo "QUIT" | openssl s_client -connect localhost:6697 2>/dev/null + +# Test WebSocket port +curl -s -o /dev/null -w "%{http_code}" http://localhost:8000 + +# Check firewall (if applicable) +sudo ufw status 2>/dev/null || sudo iptables -L -n 2>/dev/null | head -20 +``` + +### Permission denied errors + +File permission issues with data directories or config files. + +```bash +# Fix data directory ownership +sudo chown -R "$(id -u):$(id -g)" data/irc/ data/atheme/ + +# Check PUID/PGID match your host user +echo "Host user: $(id -u):$(id -g)" +grep -E "^PUID=|^PGID=" .env + +# Fix .env file permissions (contains secrets) +chmod 600 .env +``` + +## Service-specific diagnostics + +### UnrealIRCd + +```bash +# Access the UnrealIRCd container shell +docker compose -f compose.yaml -p atl-chat exec atl-irc-server bash + +# Check UnrealIRCd process inside container +docker compose -f compose.yaml -p atl-chat exec atl-irc-server pgrep unrealircd + +# View the active config (inside container) +docker compose -f compose.yaml -p atl-chat exec atl-irc-server \ + head -50 /home/unrealircd/unrealircd/config/unrealircd.conf + +# Check loaded modules (connect as oper, then) +# /STATS m — shows loaded modules +# /STATS u — shows server uptime +``` + +### Atheme + +```bash +# Check Atheme process +docker compose -f compose.yaml -p atl-chat exec atl-irc-services pgrep atheme-services + +# Check Atheme database +ls -la data/atheme/data/ + +# View Atheme logs +docker compose -f compose.yaml -p atl-chat logs --tail=50 atl-irc-services + +# Check if Atheme is linked to UnrealIRCd (from an IRC client as oper) +# /LINKS — should show services.atl.chat +``` + +### WebPanel + +```bash +# Check WebPanel container +docker compose -f compose.yaml -p atl-chat logs --tail=20 atl-webpanel + +# Test WebPanel HTTP access +curl -sI http://localhost:8080 + +# Test RPC connectivity from WebPanel to UnrealIRCd +docker compose -f compose.yaml -p atl-chat exec atl-irc-server \ + ls -la /home/unrealircd/unrealircd/data/rpc.socket +``` + +## Recovery procedures + +### Restart the IRC stack + +```bash +# Graceful restart (preserves other services) +docker compose -f compose.yaml -p atl-chat restart atl-irc-server atl-irc-services + +# Full stack restart +just down +just dev +``` + +### Reset to clean state + +> **Warning:** This destroys all IRC data including registered nicknames, channels, and Atheme database. + +```bash +# Stop everything +just down + +# Remove IRC data +rm -rf data/irc/ data/atheme/ + +# Reinitialise +just init +just dev +``` + +### Rebuild containers + +If you suspect a corrupted image or need to pick up Dockerfile changes: + +```bash +# Rebuild IRC containers +docker compose -f compose.yaml -p atl-chat build atl-irc-server atl-irc-services + +# Restart with rebuilt images +docker compose -f compose.yaml -p atl-chat up -d atl-irc-server atl-irc-services +``` + +## Related pages + +- [Operations](/docs/services/irc/operations) — oper setup, config reload, cert rotation +- [Configuration](/docs/services/irc/configuration) — env vars and config templates +- [Operations Troubleshooting](/docs/operations/troubleshooting) — cross-service troubleshooting +- [Monitoring](/docs/operations/monitoring) — health checks for all services diff --git a/apps/docs/content/docs/services/irc/user-modes.mdx b/apps/docs/content/docs/services/irc/user-modes.mdx new file mode 100644 index 00000000..9f4a78f0 --- /dev/null +++ b/apps/docs/content/docs/services/irc/user-modes.mdx @@ -0,0 +1,112 @@ +--- +title: User Modes Reference +description: Complete reference of UnrealIRCd user modes available on the atl.chat IRC network. +--- + +User modes control privacy, visibility, and behaviour for individual IRC connections. You can only set modes on yourself (not other users) using `/MODE yournick +modes`. Some modes are set automatically by the server or services. + +## Default modes + +New connections receive `+ixw` automatically (configured via `set::modes-on-connect`): + +- `+i` — invisible (hidden from `/WHO` and `/NAMES` for users outside your channels) +- `+x` — cloaked hostname (real IP/hostname is hidden) +- `+w` — receive wallops (network-wide operator announcements) + +Users cannot remove `+x` (cloaking) — this is enforced by `restrict-usermodes "x"` in the server config. + +## Mode reference + +The following modes are confirmed available on the current UnrealIRCd 6.x configuration. Modes are grouped by who can set them. + +### User-settable modes + +These modes can be set by any user on themselves. + +| Mode | Module | Description | +|---|---|---| +| `B` | `usermodes/bot` | Marks you as a bot. Adds a line to `/WHOIS` so others can identify bots. | +| `I` | built-in | Hides your idle time in `/WHOIS`. Configure `set::hide-idle-time` to control server-side defaults. | +| `d` | built-in | Deaf to channel messages except those prefixed with a command character (e.g. `!`). Useful for command-only bots. | +| `D` | `usermodes/privdeaf` | Blocks private messages from everyone except IRC operators, servers, and services. | +| `G` | `usermodes/censor` | Applies the server's bad-word filter to messages you receive. | +| `i` | built-in | Invisible — hidden from `/WHO` and `/NAMES` queries by users outside your channels. Set by default. | +| `p` | `usermodes/privacy` | Hides your channel list from `/WHOIS`. | +| `R` | `usermodes/regonlymsg` | Only receive private messages from registered (identified) users. Effective anti-spam measure. | +| `T` | `usermodes/noctcp` | Blocks incoming CTCP requests (VERSION, TIME, etc.). Recommended for bots or during CTCP floods. | +| `w` | `usermodes/wallops` | Receive wallops messages (network announcements from operators). Set by default. | +| `x` | built-in | Cloaked hostname — hides your real IP/hostname. Set by default and cannot be removed. | +| `Z` | `usermodes/secureonlymsg` | Only communicate with users on TLS connections. You also cannot message non-TLS users. | + +### Server/services-set modes + +These modes are set automatically and cannot be changed manually. + +| Mode | Module | Description | +|---|---|---| +| `r` | built-in | Registered nick — set by NickServ when you identify. Used by `+R` filtering. | +| `S` | `usermodes/servicebot` | Services bot — set on Atheme service bots (NickServ, ChanServ, etc.). Provides extra protection. | +| `t` | built-in | Using a virtual host — set when you have an active `/VHOST` or oper vhost. | +| `z` | built-in | Connected via TLS — set automatically when you connect on port 6697. | + +### IRC operator modes + +These modes require IRC operator status (`+o`). + +| Mode | Module | Description | +|---|---|---| +| `H` | built-in | Hide oper status — regular users cannot see you are an operator in `/WHOIS`. | +| `o` | built-in | IRC operator — granted via the `/OPER` command with valid credentials. | +| `q` | `usermodes/nokick` | Unkickable — only services (U-lines) can kick you. Use sparingly. | +| `s` | built-in | Server notices — enables snomask-based server monitoring. Use `/MODE yournick +s +snomask`. | +| `W` | `usermodes/showwhois` | Shows you when someone performs a `/WHOIS` on you. | + +## Operator modes on oper-up + +When you `/OPER` successfully, you receive `+xwsH` automatically (configured via `set::modes-on-oper`), plus snomasks `+bBcdfkqsSo` (configured via `set::snomask-on-oper`). + +## Recommended configurations + +### For regular users + +The defaults (`+ixw`) provide good privacy. Consider adding: + +- `+R` if you receive spam from unregistered users +- `+T` if you experience CTCP floods + +``` +/MODE yournick +RT +``` + +### For bots + +``` +/MODE botname +Bd +``` + +- `+B` marks the connection as a bot (visible in `/WHOIS`) +- `+d` reduces traffic by ignoring non-command channel messages + +### For staff accounts + +``` +/MODE staffnick +DRp +``` + +- `+D` blocks unsolicited private messages +- `+R` allows PMs only from registered users +- `+p` hides channel list from `/WHOIS` + +## Cloaking + +Cloaking (`+x`) replaces your real hostname with a hashed version like `atl-ABC123.users.atl.chat`. The `atl` prefix is set by `IRC_CLOAK_PREFIX` in `.env`. + +Cloaking uses SHA-256 with three secret cloak keys (`IRC_CLOAK_KEY_1/2/3`). Changing cloak keys changes all cloaked hostnames network-wide, which breaks existing bans based on cloaked hosts. + +All identified users also receive an automatic vhost matching the IRC domain (e.g. `irc.atl.chat`), which provides a clean hostname in `/WHOIS`. + +## Related pages + +- [Configuration](/docs/services/irc/configuration) — cloak keys, default modes, env vars +- [Modules](/docs/services/irc/modules) — third-party module management +- [Operations](/docs/services/irc/operations) — oper setup and authentication diff --git a/apps/docs/content/docs/services/meta.json b/apps/docs/content/docs/services/meta.json new file mode 100644 index 00000000..1ea89cc2 --- /dev/null +++ b/apps/docs/content/docs/services/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Services", + "pages": [ + "irc", + "atheme", + "xmpp", + "bridge", + "web", + "thelounge", + "webpanel" + ] +} diff --git a/apps/docs/content/docs/services/thelounge/configuration.mdx b/apps/docs/content/docs/services/thelounge/configuration.mdx new file mode 100644 index 00000000..a468ea29 --- /dev/null +++ b/apps/docs/content/docs/services/thelounge/configuration.mdx @@ -0,0 +1,225 @@ +--- +title: The Lounge Configuration +description: Config template, environment variables, plugins (janitor, giphy), WebIRC setup, and user management settings for The Lounge web IRC client. +--- + +The Lounge is configured through a template file (`apps/thelounge/config.js.template`) that gets processed by `scripts/prepare-config.sh` during `just init` — substituting `${VAR}` placeholders with values from your `.env` (and `.env.dev` in development) to produce the final `config.js`. + +## Config template pipeline + +The configuration follows the same envsubst pattern used across the atl.chat stack: + +1. You set values in `.env` (copied from `.env.example`) +2. Running `just init` or `just dev` triggers `scripts/prepare-config.sh` +3. The script sources `.env`, then `.env.dev` if it exists (dev overrides) +4. `envsubst` replaces all `${VAR}` references in `config.js.template` +5. The rendered `config.js` is written to `data/thelounge/config.js` +6. The config file is mounted into the container at `/var/opt/thelounge/config.js` + +To regenerate the config after changing `.env` values: + +```bash +# Regenerate all configs (including The Lounge) +just init + +# Or run prepare-config.sh directly +./scripts/prepare-config.sh +``` + +## Environment variables + +The Lounge uses a small set of environment variables. Some are defined in `.env.example` and some are derived by `prepare-config.sh` at init time. + +### Variables from `.env.example` + +| Variable | Description | Default | +|---|---|---| +| `THELOUNGE_PORT` | Host port mapped to The Lounge's web interface (container always listens on 9000) | `9000` | +| `THELOUNGE_WEBIRC_PASSWORD` | Shared secret for the WebIRC handshake between The Lounge and UnrealIRCd | `change_me_thelounge_webirc` | +| `THELOUNGE_DELETE_UPLOADS_AFTER_MINUTES` | Minutes before the janitor plugin deletes uploaded files (`0` disables cleanup) | `1440` (24 hours) | +| `IRC_NETWORK_NAME` | Network name shown in The Lounge's server list | `"All Things Linux IRC"` | + +> **Warning:** `THELOUNGE_WEBIRC_PASSWORD` must match the WebIRC password configured in UnrealIRCd. Change this from the default before any public deployment. If the passwords do not match, UnrealIRCd will reject The Lounge's connections and users will see a "WebIRC authentication failed" error. + +### Derived variables + +These variables are not in `.env.example` — they are computed by `prepare-config.sh`: + +| Variable | Description | Logic | +|---|---|---| +| `IRC_LOUNGE_REJECT_UNAUTHORIZED` | Whether The Lounge rejects self-signed TLS certificates from UnrealIRCd | `false` when `ATL_ENVIRONMENT=dev`, `true` otherwise | + +In development, self-signed certificates are used for the IRC server, so TLS verification is disabled. In production, this defaults to `true` so that The Lounge only connects to UnrealIRCd if the TLS certificate is valid. + +## Core configuration options + +The config template (`apps/thelounge/config.js.template`) produces a Node.js module that The Lounge reads at startup. Here are the key settings: + +### Mode and proxy + +```js +module.exports = { + public: false, // Private mode — admin must create user accounts + reverseProxy: true, // Trust X-Forwarded-For headers for real client IPs + theme: "default", +}; +``` + +- `public: false` — disables self-registration. Users can only be created by an admin via `just lounge add `. +- `reverseProxy: true` — required when The Lounge sits behind a reverse proxy or load balancer. Ensures client IP addresses are read from forwarded headers rather than the proxy's IP. + +### Default server connection + +Every new user account gets these default IRC connection settings: + +```js +defaults: { + name: "${IRC_NETWORK_NAME}", // Display name in the server list + host: "atl-irc-server", // Docker service name on the atl-chat network + port: 6697, // TLS-encrypted IRC + tls: true, // Always use TLS + rejectUnauthorized: ${IRC_LOUNGE_REJECT_UNAUTHORIZED}, + nick: "atl%%", // %% is replaced with a random number + username: "atl", + realname: "The Lounge User", + join: "#help", // Auto-join channel on first connect +}, +``` + +Users can change these settings in their client after logging in, but the defaults ensure every new account connects to the atl.chat IRC server out of the box. + +### WebIRC + +The `webirc` block maps IRC server hostnames to the shared WebIRC password: + +```js +webirc: { + "atl-irc-server": "${THELOUNGE_WEBIRC_PASSWORD}", + "irc.atl.chat": "${THELOUNGE_WEBIRC_PASSWORD}", + "irc.localhost": "${THELOUNGE_WEBIRC_PASSWORD}", +}, +``` + +When The Lounge connects to any of these hostnames, it sends a `WEBIRC` command with the password and the user's real IP address. UnrealIRCd verifies the password and applies the real IP for cloaking, bans, and connection limits. + +Three hostnames are listed to cover all connection scenarios: + +- `atl-irc-server` — Docker internal hostname (used by the default connection) +- `irc.atl.chat` — production domain +- `irc.localhost` — local development domain + +### File uploads + +```js +fileUpload: { + enable: true, +}, +``` + +File uploads are enabled by default. Users can share files through The Lounge's web interface, and uploaded files are stored in `data/thelounge/uploads/`. The janitor plugin handles cleanup (see below). + +## Plugins + +The Lounge container installs two plugins at startup before launching the main process. The install commands are part of the container's entrypoint: + +```bash +thelounge install thelounge-plugin-janitor 2>/dev/null || true +thelounge install thelounge-plugin-giphy 2>/dev/null || true +exec thelounge start +``` + +The `2>/dev/null || true` pattern suppresses errors if the plugin is already installed, making the startup idempotent. + +### thelounge-plugin-janitor + +The janitor plugin automatically deletes uploaded files after a configurable retention period. This keeps disk usage under control without manual intervention. + +| Setting | Variable | Default | Notes | +|---|---|---|---| +| Delete uploads after | `THELOUNGE_DELETE_UPLOADS_AFTER_MINUTES` | `1440` (24 hours) | Set to `0` to disable automatic cleanup | + +The config template maps this to the `deleteUploadsAfter` key: + +```js +deleteUploadsAfter: ${THELOUNGE_DELETE_UPLOADS_AFTER_MINUTES}, +``` + +> **Note:** The Lounge core does not recognize the `deleteUploadsAfter` key and logs an "Unknown key" warning at startup. This is expected — the janitor plugin reads the key directly from the config object. + +### thelounge-plugin-giphy + +The giphy plugin adds a `/giphy` command to The Lounge's chat interface for searching and posting GIFs. + +| Command | Description | +|---|---| +| `/giphy search ` | Search for a GIF matching the term and post it in the channel | +| `/giphy random` | Post a random GIF in the channel | + +The plugin requires no additional configuration — it works out of the box after installation. + +## User management + +Because The Lounge runs in private mode, all user accounts are managed by an administrator. User data is stored in `data/thelounge/users/` as individual JSON files (one per user). + +### Creating users + +```bash +# Create a new user (prompts for password interactively) +just lounge add +``` + +This runs `thelounge add ` inside the container. You will be prompted to set a password. The user can then log in at `http://localhost:9000` (or your configured domain) with those credentials. + +### Listing users + +```bash +# List all registered users +just lounge list +``` + +### Resetting passwords + +```bash +# Reset a user's password (prompts for new password) +just lounge reset +``` + +### Removing users + +To remove a user, delete their JSON file from the data directory: + +```bash +# Remove a user manually +rm data/thelounge/users/.json +``` + +Then restart The Lounge for the change to take effect: + +```bash +docker compose restart atl-thelounge +``` + +## Container configuration + +The compose file (`infra/compose/thelounge.yaml`) defines the container setup: + +| Setting | Value | +|---|---| +| Image | `ghcr.io/thelounge/thelounge:latest` | +| Container name | `atl-thelounge` | +| Restart policy | `unless-stopped` | +| User/group | `${PUID:-1000}:${PGID:-1000}` | +| Port mapping | `${THELOUNGE_PORT:-9000}:9000` | +| Volume | `data/thelounge:/var/opt/thelounge` | +| Network | `atl-chat` | +| Depends on | `atl-irc-server` (healthy) | + +The `THELOUNGE_HOME` environment variable is set to `/var/opt/thelounge` inside the container, which is where The Lounge looks for its config, user data, and uploads. + +## Related pages + +- [The Lounge Overview](/docs/services/thelounge) — architecture, private mode, and how The Lounge fits in the stack +- [The Lounge Operations](/docs/services/thelounge/operations) — user CRUD via `just lounge` commands, log management +- [IRC Configuration](/docs/services/irc/configuration) — UnrealIRCd config including WebIRC password settings +- [Environment Variables Reference](/docs/reference/environment-variables) — complete variable reference including all `THELOUNGE_*` variables +- [Security](/docs/operations/security) — secret management and credential rotation for `THELOUNGE_WEBIRC_PASSWORD` diff --git a/apps/docs/content/docs/services/thelounge/index.mdx b/apps/docs/content/docs/services/thelounge/index.mdx new file mode 100644 index 00000000..8bc3872a --- /dev/null +++ b/apps/docs/content/docs/services/thelounge/index.mdx @@ -0,0 +1,90 @@ +--- +title: The Lounge +description: Overview of The Lounge web IRC client — private mode, WebIRC integration, and always-on connectivity in the atl.chat stack. +--- + +The Lounge is the web-based IRC client for atl.chat, running in private mode with WebIRC support so that users can connect to UnrealIRCd through a browser with persistent sessions and always-on connectivity. + +## How The Lounge fits in the stack + +The Lounge runs as the `atl-thelounge` container and connects to UnrealIRCd over TLS on port 6697 using the internal Docker network (`atl-chat`). It depends on the `atl-irc-server` health check passing before it starts, ensuring UnrealIRCd is ready to accept connections. + +The Lounge operates in **private mode** — there is no public registration. An administrator must create each user account via the `just lounge add` command. Once logged in, users get always-on IRC connectivity: The Lounge maintains the connection to UnrealIRCd even when the browser is closed, and messages are buffered for the next session. + +**WebIRC** is configured so that UnrealIRCd sees each user's real IP address instead of the container's internal IP. The Lounge sends a WebIRC handshake (authenticated by a shared password from `THELOUNGE_WEBIRC_PASSWORD`) for every client connection. This means bans, cloaks, and connection limits apply per-user rather than per-container. + +```mermaid +graph LR + Browser["Browser
port 9000"] --> Lounge["The Lounge
atl-thelounge"] + Lounge -- "TLS 6697
(WebIRC)" --> IRCd["UnrealIRCd
atl-irc-server"] + Admin["Admin"] -- "just lounge add" --> Lounge +``` + +## Private mode + +The Lounge is configured with `public: false` in its config, which means: + +- Users cannot self-register — an admin creates accounts with `just lounge add ` +- Each user has a persistent session tied to their account +- The Lounge stays connected to IRC on behalf of the user, even when the browser tab is closed +- Message history, highlights, and unread markers are preserved across sessions + +This model is ideal for a community like atl.chat where you want controlled access and always-on presence without requiring users to run a local IRC client or bouncer. + +## Default connection settings + +The Lounge's config template (`apps/thelounge/config.js.template`) sets these defaults for every new user: + +| Setting | Value | Notes | +|---|---|---| +| Server host | `atl-irc-server` | Docker service name, resolved via the `atl-chat` network | +| Port | `6697` | TLS-encrypted IRC | +| TLS | Enabled | Always on | +| Reject unauthorized certs | Configurable | Set via `IRC_LOUNGE_REJECT_UNAUTHORIZED` (typically `false` in dev for self-signed certs) | +| Default nick | `atl%%` | `%%` is replaced with a random number by The Lounge | +| Default channel | `#help` | Users auto-join this channel on first connect | + +## Plugins + +The Lounge container installs two plugins at startup before launching the main process: + +| Plugin | Purpose | Usage | +|---|---|---| +| `thelounge-plugin-janitor` | Automatically deletes uploaded files after a configurable interval | Controlled by `THELOUNGE_DELETE_UPLOADS_AFTER_MINUTES` (set in `.env`) | +| `thelounge-plugin-giphy` | Adds a `/giphy` command for searching and posting GIFs | `/giphy search ` or `/giphy random` | + +File uploads are enabled by default. The janitor plugin keeps disk usage in check by cleaning up old uploads based on the configured retention period. + +## Data and volumes + +The compose configuration mounts a single volume into The Lounge container: + +| Host path | Container path | Purpose | +|---|---|---| +| `data/thelounge/` | `/var/opt/thelounge` | All persistent data — config, user accounts, message logs, uploads | + +The `data/thelounge/` directory is created by `scripts/init.sh` during `just init`. The Lounge's generated config file, user database, and uploaded files all live here. + +## Technology + +| Component | Technology | +|---|---| +| Software | [The Lounge](https://thelounge.chat/) (latest) | +| Base image | `ghcr.io/thelounge/thelounge:latest` | +| Container name | `atl-thelounge` | +| Mode | Private (admin-managed accounts) | +| WebIRC | Enabled (shared password with UnrealIRCd) | +| IRC connection | TLS on port 6697 to `atl-irc-server` | +| Web interface | Port `9000` | +| Plugins | janitor (upload cleanup), giphy (GIF search) | +| Config management | Template substitution via `scripts/prepare-config.sh` | +| Network | `atl-chat` Docker network | +| User/group | Runs as `${PUID}:${PGID}` (default `1000:1000`) | + +## Related pages + +- [Configuration](/docs/services/thelounge/configuration) — config template, plugins, environment variables +- [Operations](/docs/services/thelounge/operations) — user management via `just lounge` commands, log management +- [IRC Stack Overview](/docs/services/irc) — UnrealIRCd and the broader IRC stack +- [IRC Configuration](/docs/services/irc/configuration) — UnrealIRCd config including WebIRC settings +- [Environment Variables](/docs/reference/environment-variables) — all configurable variables including The Lounge settings diff --git a/apps/docs/content/docs/services/thelounge/meta.json b/apps/docs/content/docs/services/thelounge/meta.json new file mode 100644 index 00000000..dc766f91 --- /dev/null +++ b/apps/docs/content/docs/services/thelounge/meta.json @@ -0,0 +1,8 @@ +{ + "title": "The Lounge", + "pages": [ + "index", + "configuration", + "operations" + ] +} diff --git a/apps/docs/content/docs/services/thelounge/operations.mdx b/apps/docs/content/docs/services/thelounge/operations.mdx new file mode 100644 index 00000000..09e51975 --- /dev/null +++ b/apps/docs/content/docs/services/thelounge/operations.mdx @@ -0,0 +1,253 @@ +--- +title: The Lounge Operations +description: User management via just lounge commands, log management, health checks, restart procedures, and troubleshooting for The Lounge web IRC client. +--- + +This page covers day-to-day operations for The Lounge: managing user accounts with `just lounge` recipes, inspecting logs, checking container health, and troubleshooting common issues. + +## User management + +The Lounge runs in private mode, so all user accounts are created and managed by an administrator. User data is stored as individual JSON files in `data/thelounge/users/` on the host (mounted at `/var/opt/thelounge/users/` inside the container). + +### Creating a user + +```bash +# Create a new user (prompts for password interactively) +just lounge add +``` + +This executes `thelounge add ` inside the `atl-thelounge` container. You are prompted to set a password. Once created, the user can log in at `http://localhost:9000` (or your configured domain) with those credentials. + +### Listing users + +```bash +# List all registered users +just lounge list +``` + +This shows every user account that has a JSON file in the users directory. + +### Resetting a password + +```bash +# Reset a user's password (prompts for new password) +just lounge reset +``` + +The user's existing sessions are not terminated immediately — they remain connected until the next login attempt, at which point the new password is required. + +### Removing a user + +There is no `just lounge` recipe for user removal. Delete the user's JSON file manually and restart the container: + +```bash +# Remove the user's data file +rm data/thelounge/users/.json + +# Restart The Lounge to pick up the change +docker compose restart atl-thelounge +``` + +### User data structure + +Each user account is a JSON file at `data/thelounge/users/.json`. The file contains the user's hashed password, client settings, saved networks, and channel preferences. The Lounge reads these files at startup and watches for changes at runtime. + +| Host path | Container path | Contents | +|---|---|---| +| `data/thelounge/users/.json` | `/var/opt/thelounge/users/.json` | Hashed password, networks, channels, client preferences | +| `data/thelounge/users/` | `/var/opt/thelounge/users/` | All user account files | + +## Log management + +### Container logs + +The primary way to inspect The Lounge logs is through Docker: + +```bash +# Follow logs in real time +docker compose logs -f atl-thelounge + +# Last 100 lines +docker compose logs --tail 100 atl-thelounge + +# Logs since a specific time +docker compose logs --since 1h atl-thelounge +``` + +### Internal logs + +The Lounge also writes logs inside its data directory. These include chat message logs when logging is enabled per-user: + +| Host path | Container path | Contents | +|---|---|---| +| `data/thelounge/logs/` | `/var/opt/thelounge/logs/` | Per-user, per-channel message logs | +| `data/thelounge/logs///` | `/var/opt/thelounge/logs///` | Channel-specific log files | + +Message logging is a per-user setting controlled from The Lounge's web interface under Settings. When enabled, logs are stored as plain text files organised by user, network, and channel. + +To inspect a specific user's logs: + +```bash +# List log directories for a user +ls data/thelounge/logs// + +# Tail a specific channel log +tail -f data/thelounge/logs///#channel.log +``` + +## Health check and status + +The Lounge container does not define a custom health check in the compose file. You can verify the service is running by checking the container status and testing the web interface: + +```bash +# Check container status +docker compose ps atl-thelounge + +# Verify the web interface is responding +curl -s -o /dev/null -w "%{http_code}" http://localhost:9000 +# Expected: 200 +``` + +### Checking the IRC connection + +The Lounge connects to UnrealIRCd over TLS on port 6697 via the Docker network. If users report connection issues, verify that UnrealIRCd is healthy: + +```bash +# Check UnrealIRCd health +docker inspect --format='{{.State.Health.Status}}' atl-irc-server + +# Check The Lounge logs for connection errors +docker compose logs atl-thelounge | grep -i "error\|disconnect\|refused" +``` + +## Restart procedures + +### Graceful restart + +```bash +# Restart the container +docker compose restart atl-thelounge +``` + +A restart disconnects all active user sessions from IRC temporarily. The Lounge reconnects to UnrealIRCd automatically after the container starts, and users see their sessions resume when they refresh the browser. + +### Full stop and start + +```bash +# Stop The Lounge +docker compose stop atl-thelounge + +# Start The Lounge +docker compose start atl-thelounge +``` + +### When to restart + +You need to restart The Lounge when: + +- You regenerate the config file (after running `just init` or modifying `.env` values) +- You manually remove a user JSON file +- You update the container image + +You do not need to restart for: + +- Creating new users (`just lounge add` takes effect immediately) +- Resetting passwords (`just lounge reset` takes effect immediately) +- User-side settings changes (saved to JSON files in real time) + +## Troubleshooting + +### Users cannot log in + +If a user cannot authenticate: + +1. Verify the user account exists: + + ```bash + just lounge list + ``` + +2. Reset the password: + + ```bash + just lounge reset + ``` + +3. Check The Lounge logs for authentication errors: + + ```bash + docker compose logs atl-thelounge | grep -i "auth\|login\|password" + ``` + +### The Lounge cannot connect to UnrealIRCd + +If users see "Disconnected" or connection errors in the web interface: + +1. Confirm UnrealIRCd is healthy: + + ```bash + docker inspect --format='{{.State.Health.Status}}' atl-irc-server + ``` + +2. Check that the WebIRC password matches between The Lounge config and UnrealIRCd config. Both must use the same `THELOUNGE_WEBIRC_PASSWORD` value from `.env`: + + ```bash + # Check the rendered config + grep -i webirc data/thelounge/config.js + ``` + +3. If the password was changed, regenerate configs and restart both services: + + ```bash + just init + docker compose restart atl-irc-server atl-thelounge + ``` + +### Web interface not loading + +If `http://localhost:9000` is not responding: + +1. Check the container is running: + + ```bash + docker compose ps atl-thelounge + ``` + +2. Check for port conflicts: + + ```bash + # See what is using port 9000 + ss -tlnp | grep 9000 + ``` + +3. Check container logs for startup errors: + + ```bash + docker compose logs --tail 50 atl-thelounge + ``` + +### Plugin installation failures + +The Lounge installs plugins (janitor, giphy) at every container start. If plugins fail to install, you see warnings in the logs but The Lounge still starts. The `2>/dev/null || true` pattern in the entrypoint suppresses these errors. + +To manually reinstall a plugin: + +```bash +docker compose exec atl-thelounge thelounge install thelounge-plugin-janitor +docker compose exec atl-thelounge thelounge install thelounge-plugin-giphy +``` + +Then restart the container for the plugins to load: + +```bash +docker compose restart atl-thelounge +``` + +## Related pages + +- [The Lounge Overview](/docs/services/thelounge) — architecture, private mode, and how The Lounge fits in the stack +- [The Lounge Configuration](/docs/services/thelounge/configuration) — config template, plugins, environment variables +- [IRC Operations](/docs/services/irc/operations) — UnrealIRCd oper setup, config reload, and certificate rotation +- [Monitoring](/docs/operations/monitoring) — health checks and log inspection across all services +- [Backups](/docs/operations/backups) — backup and restore procedures for The Lounge user data +- [Security](/docs/operations/security) — credential rotation for `THELOUNGE_WEBIRC_PASSWORD` diff --git a/apps/docs/content/docs/services/web/development.mdx b/apps/docs/content/docs/services/web/development.mdx new file mode 100644 index 00000000..1cece4fb --- /dev/null +++ b/apps/docs/content/docs/services/web/development.mdx @@ -0,0 +1,112 @@ +--- +title: Web Development +description: Running the dev server, building for Cloudflare Pages, environment variables, and deployment for the atl.chat web app. +--- + +The web app runs as a standard Next.js dev server locally and builds to a Cloudflare Pages-compatible output via `@cloudflare/next-on-pages` for production deployment. + +## Dev server + +Start the development server using the `just` recipe, which automatically sets localhost-appropriate environment variables: + +```bash +just web dev +``` + +This runs `pnpm dev` with `NEXT_PUBLIC_IRC_WS_URL=ws://localhost:8000` and `NEXT_PUBLIC_XMPP_BOSH_URL=http://localhost:5280/http-bind`, so the app points at your local IRC and XMPP services. The dev server starts at `http://localhost:3000`. + +Alternatively, run directly with pnpm: + +```bash +# From apps/web/ +NEXT_PUBLIC_IRC_WS_URL="ws://localhost:8000" \ +NEXT_PUBLIC_XMPP_BOSH_URL="http://localhost:5280/http-bind" \ +pnpm dev +``` + +> **Note:** The web app runs outside Docker as a local Node.js process. The rest of the stack (IRC, XMPP, etc.) runs in Docker via `just dev`. + +## Build + +Build the app for production: + +```bash +just web build +``` + +This runs `next build`, which uses `@cloudflare/next-on-pages` (configured in `next.config.mjs`) to produce output compatible with Cloudflare Pages. The build output goes to `.vercel/output/static` as specified in `wrangler.toml`. + +To preview the production build locally with Wrangler: + +```bash +# From apps/web/ +npx wrangler pages dev .vercel/output/static +``` + +This starts a local Cloudflare Pages preview server so you can verify the production build before deploying. + +## Cloudflare Pages deployment + +Deployment is configured in `apps/web/wrangler.toml`: + +```toml +compatibility_date = "2024-07-29" +compatibility_flags = ["nodejs_compat"] +name = "atl-chat" +pages_build_output_dir = ".vercel/output/static" + +[vars] +NODE_VERSION = "18.18.0" +``` + +Key settings: + +| Setting | Value | Purpose | +|---|---|---| +| `name` | `atl-chat` | Cloudflare Pages project name | +| `pages_build_output_dir` | `.vercel/output/static` | Build output directory for `@cloudflare/next-on-pages` | +| `compatibility_flags` | `nodejs_compat` | Enables Node.js API compatibility in Workers runtime | +| `NODE_VERSION` | `18.18.0` | Node.js version used in the Cloudflare build environment | + +Pushes to the main branch trigger an automatic Cloudflare Pages deployment. Environment variables for production (such as `NEXT_PUBLIC_IRC_WS_URL`) are managed in the Cloudflare dashboard under the project's environment settings. + +## Environment variables + +The web app reads environment variables at build time (since they use the `NEXT_PUBLIC_` prefix, they are inlined into the client bundle during build). + +For local development, create `apps/web/.env.local` by copying the example: + +```bash +cp apps/web/.env.example apps/web/.env.local +``` + +See the [Web App Overview](/docs/services/web) for the full variable reference table. + +For production, set these variables in the Cloudflare Pages dashboard: + +1. Go to your Cloudflare Pages project settings +2. Navigate to **Settings → Environment variables** +3. Add each `NEXT_PUBLIC_*` variable with production values (e.g., `NEXT_PUBLIC_IRC_WS_URL=wss://irc.atl.chat/ws`) + +> **Note:** Since `NEXT_PUBLIC_` variables are embedded at build time, you need to trigger a new deployment after changing them in the Cloudflare dashboard. + +## Linting and type-checking + +The web app uses [Biome](https://biomejs.dev/) via the `ultracite` wrapper for linting and formatting: + +```bash +just web lint # Run Ultracite/Biome check +just web fix # Auto-fix lint issues +just web type-check # Run TypeScript type checking (tsc --noEmit) +``` + +The Biome configuration in `biome.jsonc` extends the `ultracite/biome/core` and `ultracite/biome/next` presets. + +> **Note:** `ultracite check` currently fails due to a pre-existing issue where the Biome config expects a `.gitignore` in `apps/web/`. The Next.js build (`just web build`) works fine regardless. + + +## Related pages + +- [Web App Overview](/docs/services/web) — architecture, routes, and environment variables +- [Contributing](/docs/development/contributing) — PR workflow, commit conventions, and code style guides +- [Testing](/docs/development/testing) — test directory layout and running tests diff --git a/apps/docs/content/docs/services/web/index.mdx b/apps/docs/content/docs/services/web/index.mdx new file mode 100644 index 00000000..a06bb60d --- /dev/null +++ b/apps/docs/content/docs/services/web/index.mdx @@ -0,0 +1,76 @@ +--- +title: Web App Overview +description: Overview of the atl.chat public-facing Next.js landing site deployed to Cloudflare Pages. +--- + +The web app (`apps/web`) is the public-facing atl.chat landing site — a Next.js application that lists all community chat platforms (IRC, XMPP, Discord, Signal) and links visitors to connection instructions. It deploys to Cloudflare Pages via `@cloudflare/next-on-pages`. + +## What it is (and isn't) + +- **Is:** The `atl.chat` public homepage — a single-page site showing how to connect via IRC, XMPP, Discord, Signal, and other platforms. +- **Is not:** The [Portal](https://github.com/allthingslinux/portal) app, which is a separate Next.js project in its own repository handling user identity, profiles, and the Portal API consumed by the bridge. + +## Technology stack + +| Layer | Technology | +|---|---| +| Framework | Next.js (App Router) | +| Language | TypeScript | +| Styling | Tailwind CSS + shadcn/ui | +| UI components | Button, Card, Badge (from shadcn/ui via Radix UI) | +| Icons | Lucide React | +| Linting | Biome (via ultracite) | +| Deployment target | Cloudflare Pages (`@cloudflare/next-on-pages`) | +| Package manager | pnpm (workspace member `atl.chat`) | + +## Architecture + +The web app follows a minimal Next.js App Router structure: + +``` +apps/web/ +├── app/ +│ ├── layout.tsx # Root layout with Geist font loading +│ ├── page.tsx # Landing page — the only route +│ ├── globals.css # Tailwind CSS imports +│ ├── favicon.ico +│ └── fonts/ # Geist Sans and Geist Mono (local fonts) +├── components/ +│ └── ui/ # shadcn/ui components (button, card, badge) +├── lib/ +│ └── utils.ts # Tailwind merge utility (cn helper) +├── next.config.mjs # Cloudflare dev platform setup +├── wrangler.toml # Cloudflare Pages deployment config +├── biome.jsonc # Extends ultracite/biome presets +└── package.json # Package name: "atl.chat" +``` + +## Routes + +The site currently has a single route: + +| Route | Component | Description | +|---|---|---| +| `/` | `app/page.tsx` | Landing page with platform cards for IRC, XMPP, Signal, and a Discord join button. Also shows "Someday Maybe?" badges for Mastodon and Matrix. | + +The app uses Next.js App Router with `layout.tsx` providing the root HTML structure, Geist font variables, and global styles. There are no additional routes, API routes, or dynamic segments at this time — the site is a static single-page application. + +## Environment variables + +The web app uses client-side environment variables (prefixed with `NEXT_PUBLIC_`) to configure service connection URLs: + +| Variable | Description | Default | +|---|---|---| +| `NEXT_PUBLIC_ATL_BASE_DOMAIN` | Base domain for the atl.chat project | `atl.chat` | +| `NEXT_PUBLIC_ATL_ENVIRONMENT` | Environment identifier (`development` or `production`) | `development` | +| `NEXT_PUBLIC_IRC_WS_URL` | IRC WebSocket URL for browser-based IRC clients | `wss://irc.atl.chat/ws` | +| `NEXT_PUBLIC_XMPP_BOSH_URL` | XMPP BOSH endpoint for browser-based XMPP clients | `https://xmpp.atl.chat/http-bind` | +| `NEXT_PUBLIC_SENTRY_DSN` | Sentry DSN for error tracking (optional) | _(empty)_ | + +See `apps/web/.env.example` for the full list. Copy it to `apps/web/.env.local` for local overrides. + +> **Note:** During local development, `just web dev` automatically overrides `NEXT_PUBLIC_IRC_WS_URL` and `NEXT_PUBLIC_XMPP_BOSH_URL` to point at `localhost` ports. + +## Related pages + +- [Development](/docs/services/web/development) — dev server, build, deployment, and linting diff --git a/apps/docs/content/docs/services/web/meta.json b/apps/docs/content/docs/services/web/meta.json new file mode 100644 index 00000000..24e28fb7 --- /dev/null +++ b/apps/docs/content/docs/services/web/meta.json @@ -0,0 +1,7 @@ +{ + "title": "Web", + "pages": [ + "index", + "development" + ] +} diff --git a/apps/docs/content/docs/services/webpanel/configuration.mdx b/apps/docs/content/docs/services/webpanel/configuration.mdx new file mode 100644 index 00000000..ff8f7fdf --- /dev/null +++ b/apps/docs/content/docs/services/webpanel/configuration.mdx @@ -0,0 +1,203 @@ +--- +title: WebPanel Configuration +description: Port configuration, RPC credentials, authentication setup, and Nginx security headers for the UnrealIRCd WebPanel. +--- + +The WebPanel connects to UnrealIRCd via JSON-RPC on port 8600 using credentials you define in `.env` — this page covers the environment variables, RPC authentication chain, Nginx security layer, and authentication backend options. + +## Environment variables + +The WebPanel uses three variables from `.env.example`, all in the IRC service section under `--- WebPanel ---`: + +| Variable | Description | Default | Sensitive | +|---|---|---|---| +| `WEBPANEL_PORT` | Host port mapped to the WebPanel container (container always listens on 8080 internally) | `8080` | No | +| `WEBPANEL_RPC_USER` | Username for JSON-RPC authentication against UnrealIRCd | `adminpanel` | No | +| `WEBPANEL_RPC_PASSWORD` | Password for JSON-RPC authentication against UnrealIRCd | `change_me_webpanel_password` | Yes | + +> **Warning:** Change `WEBPANEL_RPC_PASSWORD` from the default before any public deployment. This password grants full administrative RPC access to UnrealIRCd. The same credential is also referenced by the external Portal integration as `IRC_UNREAL_RPC_PASSWORD`. + +To apply changes after editing `.env`: + +```bash +# Regenerate configs and restart the stack +just init +just dev +``` + +The WebPanel does not use the `scripts/prepare-config.sh` envsubst pipeline. Instead, the RPC credentials are consumed on the UnrealIRCd side through the config template, and the WebPanel reads them during its first-run setup wizard or from its own stored configuration in `data/irc/webpanel-data/`. + +## RPC connection + +The WebPanel communicates with UnrealIRCd through the JSON-RPC API. The connection chain works like this: + +```mermaid +sequenceDiagram + participant Admin as Admin Browser + participant WP as WebPanel (port 8080) + participant IRCd as UnrealIRCd (port 8600) + + Admin->>WP: HTTP request + WP->>IRCd: JSON-RPC over TLS (port 8600) + IRCd-->>WP: JSON-RPC response + WP-->>Admin: Rendered HTML +``` + +### UnrealIRCd RPC configuration + +On the UnrealIRCd side, the RPC listener and user are defined in `apps/unrealircd/config/unrealircd.conf.template`: + +1. A TLS-enabled RPC listener on port 8600: + + ``` + listen { + ip *; + port 8600; + options { rpc; tls; } + tls-options { + certificate "...fullchain.pem"; + key "...privkey.pem"; + } + } + ``` + +2. An `rpc-user` block that authorises the WebPanel: + + ``` + rpc-user "${WEBPANEL_RPC_USER}" { + match { ip 172.16.0.0/12; ip 127.0.0.0/8; } + rpc-class full; + password "${WEBPANEL_RPC_PASSWORD}"; + } + ``` + +The `match` block restricts RPC access to Docker network IPs (`172.16.0.0/12`) and localhost (`127.0.0.0/8`). The `rpc-class full` grants all permissions — user management, channel administration, server bans, spamfilters, and log access. + +### RPC classes + +UnrealIRCd defines two RPC permission classes: + +| Class | Permissions | Use case | +|---|---|---| +| `full` | All RPC operations | WebPanel (default) | +| `read-only` | Stats, logs, user/server/channel listing, bans listing | Monitoring tools, read-only dashboards | + +The WebPanel uses `full` because it needs write access for ban management, spamfilter editing, and other administrative actions. If you want a restricted monitoring-only integration, create a separate `rpc-user` with `rpc-class read-only`. + +## Port configuration + +The compose service definition in `infra/compose/irc.yaml` maps the WebPanel port: + +```yaml +ports: + - '${WEBPANEL_PORT:-8080}:8080' +``` + +The container always listens on port 8080 internally (Nginx inside the container). The `WEBPANEL_PORT` variable only controls the host-side mapping. To change the access port: + +```bash +# In .env +WEBPANEL_PORT=9090 +``` + +Then restart the stack: + +```bash +docker compose down atl-irc-webpanel +just dev +``` + +Access the WebPanel at `http://localhost:` — by default `http://localhost:8080`. + +## Authentication + +The WebPanel supports two authentication backends for admin login. + +### File-based authentication (default) + +On first access, the WebPanel runs a setup wizard that creates an admin account stored in `data/irc/webpanel-data/`. This is the default and simplest option — no external database is required. + +User credentials are stored as files within the data volume: + +| Host path | Container path | Contents | +|---|---|---| +| `data/irc/webpanel-data/` | `/var/www/html/unrealircd-webpanel/data` | Config state, session data, user accounts | + +The `data/irc/webpanel-data/` directory is created by `scripts/init.sh` during `just init`. + +### SQL authentication + +The WebPanel also supports SQL-backed authentication (SQLite or MySQL). To switch from file-based to SQL, configure the database connection through the WebPanel's admin settings page after initial setup. This is useful for larger deployments where you want centralised user management. + +## Nginx configuration + +The WebPanel container uses `trafex/php-nginx` as its base image, which bundles PHP-FPM and Nginx. A custom Nginx config at `apps/webpanel/nginx.conf` adds security hardening: + +### Security headers + +```nginx +add_header X-Frame-Options "SAMEORIGIN" always; +add_header X-Content-Type-Options "nosniff" always; +add_header X-XSS-Protection "1; mode=block" always; +``` + +| Header | Effect | +|---|---| +| `X-Frame-Options: SAMEORIGIN` | Prevents the WebPanel from being embedded in iframes on other domains (clickjacking protection) | +| `X-Content-Type-Options: nosniff` | Prevents browsers from MIME-sniffing responses away from the declared content type | +| `X-XSS-Protection: 1; mode=block` | Enables the browser's built-in XSS filter and blocks the page if an attack is detected | + +### Access restrictions + +The Nginx config denies direct access to sensitive directories: + +```nginx +# Deny access to dotfiles +location ~ /\. { + deny all; +} + +# Deny access to data and config directories +location ~ /(data|config) { + deny all; +} +``` + +This prevents browsers from directly accessing the WebPanel's stored configuration, session data, or internal config files — even if someone guesses the URL path. + +### PHP and static file handling + +| Setting | Value | Notes | +|---|---|---| +| PHP-FPM | `127.0.0.1:9000` (inside container) | FastCGI to the local PHP-FPM process | +| `fastcgi_read_timeout` | `300s` | Allows long-running RPC operations to complete | +| Static file caching | `1 year`, `Cache-Control: public, immutable` | JS, CSS, images are aggressively cached | +| URL rewriting | `try_files $uri $uri/ /index.php?$query_string` | All routes fall through to PHP | + +## Container setup + +The WebPanel container is built from `apps/webpanel/Containerfile` using a multi-stage build: + +1. **Builder stage** (`composer/composer`): clones the [upstream WebPanel repository](https://github.com/unrealircd/unrealircd-webpanel) and runs `composer install --no-dev --optimize-autoloader` +2. **Runtime stage** (`trafex/php-nginx`): copies the built application, installs `php84-sodium` for cryptographic operations, and sets file permissions + +The compose definition in `infra/compose/irc.yaml`: + +| Setting | Value | +|---|---| +| Container name | `atl-irc-webpanel` | +| Restart policy | `unless-stopped` | +| Depends on | `atl-irc-server` (healthy) | +| Volume | `data/irc/webpanel-data` → `/var/www/html/unrealircd-webpanel/data` | +| Port | `${WEBPANEL_PORT:-8080}:8080` | +| Network | `atl-chat` | + +The `depends_on` with `condition: service_healthy` ensures UnrealIRCd's JSON-RPC API is ready before the WebPanel starts. UnrealIRCd's health check verifies the RPC socket is responding. + +## Related pages + +- [WebPanel Overview](/docs/services/webpanel) — features, architecture, and troubleshooting +- [IRC Configuration](/docs/services/irc/configuration) — UnrealIRCd config including the RPC listener and module loading +- [Environment Variables Reference](/docs/reference/environment-variables) — complete variable reference including `WEBPANEL_*` variables +- [Security](/docs/operations/security) — credential rotation for `WEBPANEL_RPC_PASSWORD` +- [Ports Reference](/docs/reference/ports) — complete port registry diff --git a/apps/docs/content/docs/services/webpanel/index.mdx b/apps/docs/content/docs/services/webpanel/index.mdx new file mode 100644 index 00000000..37cda65f --- /dev/null +++ b/apps/docs/content/docs/services/webpanel/index.mdx @@ -0,0 +1,126 @@ +--- +title: WebPanel +description: Overview of the UnrealIRCd web administration panel for managing the atl.chat IRC server. +--- + +The WebPanel is a web-based administration interface for UnrealIRCd, connecting via JSON-RPC to provide server management, user oversight, and channel administration without direct shell access. + +## How the WebPanel fits in the stack + +The WebPanel runs as the `atl-irc-webpanel` container and connects to UnrealIRCd's JSON-RPC API on port 8600 over the internal `atl-chat` Docker network. It depends on the `atl-irc-server` health check passing before it starts, ensuring UnrealIRCd is ready to accept RPC requests. + +The WebPanel is the [official UnrealIRCd web admin panel](https://github.com/unrealircd/unrealircd-webpanel), built with PHP and served by Nginx. It provides a browser-based interface for common IRC administration tasks that would otherwise require an IRC operator connection or direct config file editing. + +```mermaid +graph LR + Admin["Admin Browser
port 8080"] --> WebPanel["WebPanel
atl-irc-webpanel"] + WebPanel -- "JSON-RPC 8600" --> IRCd["UnrealIRCd
atl-irc-server"] +``` + +## Features + +The WebPanel provides these administration capabilities through the browser: + +### Dashboard + +- Server status and uptime monitoring +- Online user count and channel statistics +- Recent activity overview + +### User management + +- View currently connected users +- Manage server bans (K-line, Z-line, G-line) +- Search and inspect user details + +### Channel administration + +- Browse the channel list with details +- Manage channel modes +- Edit channel topics + +### Server administration + +- View and manage loaded modules +- Browse server logs +- Manage spamfilters and other server-level settings + +## Data and volumes + +The compose configuration mounts a single bind-mount volume into the WebPanel container: + +| Host path | Container path | Purpose | +|---|---|---| +| `data/irc/webpanel-data/` | `/var/www/html/unrealircd-webpanel/data` | Persistent data — configuration state, session data, SQLite database | + +The `data/irc/webpanel-data/` directory is created by `scripts/init.sh` during `just init`. The WebPanel stores its runtime configuration and any file-based authentication data here. + +## Technology + +| Component | Technology | +|---|---| +| Software | [UnrealIRCd WebPanel](https://github.com/unrealircd/unrealircd-webpanel) (latest) | +| Base image | `trafex/php-nginx` (PHP 8.4 + Nginx) | +| Container name | `atl-irc-webpanel` | +| Build | Multi-stage — Composer dependency install, then PHP-Nginx runtime | +| Web interface | Port `8080` (configurable via `WEBPANEL_PORT`) | +| RPC connection | JSON-RPC to UnrealIRCd on port `8600` | +| Authentication | File-based (default) or SQL | +| Network | `atl-chat` Docker network | +| Security headers | `X-Frame-Options`, `X-Content-Type-Options`, `X-XSS-Protection` via Nginx | + +## Troubleshooting + +### WebPanel shows "Connection failed" + +If the WebPanel cannot reach UnrealIRCd's JSON-RPC API: + +1. Verify UnrealIRCd is running and healthy: + + ```bash + docker compose ps atl-irc-server + ``` + +2. Check RPC connectivity from the WebPanel container: + + ```bash + docker exec atl-irc-webpanel nc -z atl-irc-server 8600 + ``` + +3. Check UnrealIRCd logs for RPC-related errors: + + ```bash + docker compose logs atl-irc-server | grep -i rpc + ``` + +4. Verify the RPC credentials in your `.env` file match what UnrealIRCd expects (`WEBPANEL_RPC_USER` and `WEBPANEL_RPC_PASSWORD`). + +### WebPanel not loading + +If the web interface is not responding on port 8080: + +1. Check the container is running: + + ```bash + docker compose ps atl-irc-webpanel + ``` + +2. Check container logs for Nginx or PHP errors: + + ```bash + docker compose logs atl-irc-webpanel + ``` + +3. Restart the WebPanel container: + + ```bash + docker compose restart atl-irc-webpanel + ``` + +## Related pages + +- [Configuration](/docs/services/webpanel/configuration) — port settings, RPC credentials, authentication setup +- [IRC Stack Overview](/docs/services/irc) — UnrealIRCd and the broader IRC stack +- [IRC Configuration](/docs/services/irc/configuration) — UnrealIRCd config including JSON-RPC settings +- [Environment Variables](/docs/reference/environment-variables) — all configurable variables including WebPanel settings +- [Ports Reference](/docs/reference/ports) — complete port registry diff --git a/apps/docs/content/docs/services/webpanel/meta.json b/apps/docs/content/docs/services/webpanel/meta.json new file mode 100644 index 00000000..e8f97840 --- /dev/null +++ b/apps/docs/content/docs/services/webpanel/meta.json @@ -0,0 +1,7 @@ +{ + "title": "WebPanel", + "pages": [ + "index", + "configuration" + ] +} diff --git a/apps/docs/content/docs/services/xmpp/configuration.mdx b/apps/docs/content/docs/services/xmpp/configuration.mdx new file mode 100644 index 00000000..8d5523a6 --- /dev/null +++ b/apps/docs/content/docs/services/xmpp/configuration.mdx @@ -0,0 +1,435 @@ +--- +title: XMPP Configuration +description: "Complete guide to Prosody's prosody.cfg.lua structure, environment variables, VirtualHost blocks, components, and security settings." +--- + +Prosody is configured through a single Lua file (`prosody.cfg.lua`) that uses environment variable substitution at container startup, giving you full control over domains, security, storage, and components without editing Lua directly. + +## Configuration file structure + +The main configuration lives at `apps/prosody/config/prosody.cfg.lua`. It is a monolithic Lua file organised into clearly labelled sections. Environment variables are read via `os.getenv()` with sensible fallback defaults. + +```mermaid +graph TD + A["prosody.cfg.lua"] --> B["Global settings"] + A --> C["modules_enabled / modules_disabled"] + A --> D["Storage & archiving"] + A --> E["Networking & ports"] + A --> F["HTTP services"] + A --> G["TLS / security"] + A --> H["Authentication & OAuth2"] + A --> I["VirtualHost block"] + A --> J["Component blocks"] + A --> K["Contact info & account lifecycle"] + + J --> J1["MUC component"] + J --> J2["HTTP file upload"] + J --> J3["SOCKS5 proxy"] + J --> J4["PubSub"] + J --> J5["Bridge component"] +``` + +### Section overview + +| Section | Purpose | +|---|---| +| Plugin paths | Community and custom module directories | +| `modules_enabled` | All globally loaded modules, grouped by function | +| `modules_disabled` | Explicitly disabled auto-loaded modules | +| Core server settings | PID file, user/group, admin JIDs | +| Data storage | SQLite/PostgreSQL backend, per-store assignments | +| Message archiving (MAM) | Retention, compression, query limits | +| Networking | Ports, interfaces, IPv6, backend tuning | +| HTTP services | BOSH, WebSocket, file upload, CORS, security headers | +| TURN/STUN | External TURN server for audio/video calls | +| Logging | Console and file log levels, OpenMetrics | +| Security | Rate limits, registration throttling, anti-spam | +| TLS/SSL | Protocol version, ciphers, certificate paths | +| Authentication | SASL mechanisms, hashed storage, blocked usernames | +| OAuth2 | Bearer token generation for Portal integration | +| Push notifications | Cloud notify settings for mobile clients | +| VirtualHost | Primary domain with per-host modules and SSL | +| Components | MUC, file upload, proxy65, PubSub, bridge | +| Contact info | Admin contacts, server info, account cleanup | + +## Environment variables + +All Prosody settings can be tuned via environment variables defined in `.env`. The config reads them with `os.getenv()` and falls back to defaults when unset. + +### Core XMPP variables + +| Variable | Description | Default | +|---|---|---| +| `XMPP_DOMAIN` | Primary XMPP domain (sets `PROSODY_DOMAIN` in compose) | `atl.chat` | +| `PROSODY_ADMIN_EMAIL` | Admin email for contact info | `admin@allthingslinux.org` | +| `PROSODY_ADMIN_JID` | Admin JID for prosodyctl and MUC ownership | `admin@` | +| `PROSODY_ALLOW_REGISTRATION` | Enable in-band registration (`true`/`false`) | `false` | +| `PROSODY_LOG_LEVEL` | Minimum log level (`debug`, `info`, `warn`, `error`) | `info` | + +### Port variables + +| Variable | Description | Default | +|---|---|---| +| `PROSODY_C2S_PORT` | Client-to-server (STARTTLS) | `5222` | +| `PROSODY_S2S_PORT` | Server-to-server (federation) | `5269` | +| `PROSODY_HTTP_PORT` | HTTP (BOSH/WebSocket) | `5280` | +| `PROSODY_HTTPS_PORT` | HTTPS (via nginx proxy) | `5281` | +| `PROSODY_C2S_DIRECT_TLS_PORT` | Client-to-server direct TLS | `5223` | +| `PROSODY_S2S_DIRECT_TLS_PORT` | Server-to-server direct TLS | `5270` | +| `PROSODY_PROXY65_PORT` | SOCKS5 file transfer proxy | `5000` | + +### Security variables + +| Variable | Description | Default | +|---|---|---| +| `PROSODY_C2S_REQUIRE_ENCRYPTION` | Require TLS for client connections | `true` | +| `PROSODY_S2S_REQUIRE_ENCRYPTION` | Require TLS for server federation | `true` | +| `PROSODY_S2S_SECURE_AUTH` | Require certificate-based s2s auth | `true` | +| `PROSODY_ALLOW_UNENCRYPTED_PLAIN_AUTH` | Allow plaintext passwords without TLS | `false` | +| `PROSODY_TLS_CHANNEL_BINDING` | Enable TLS channel binding for SASL | `true` | +| `PROSODY_OAUTH2_REGISTRATION_KEY` | OAuth2 dynamic client registration key | *(must be set)* | +| `PROSODY_MAX_CONNECTIONS_PER_IP` | Max concurrent connections per IP | `5` | +| `PROSODY_REGISTRATION_THROTTLE_MAX` | Max registrations per throttle period | `3` | +| `PROSODY_REGISTRATION_THROTTLE_PERIOD` | Throttle window for registration rate limiting (seconds) | `3600` | + +### Database variables + +| Variable | Description | Default | +|---|---|---| +| `PROSODY_DB_PORT` | Database port | `5432` | +| `PROSODY_DB_NAME` | Database name | `prosody` | +| `PROSODY_DB_USER` | Database user | `prosody` | +| `PROSODY_DB_PASSWORD` | Database password | *(change before production)* | + +### MAM (message archiving) variables + +| Variable | Description | Default | +|---|---|---| +| `PROSODY_ARCHIVE_EXPIRES_AFTER` | Archive retention period | `1y` | +| `PROSODY_ARCHIVE_POLICY` | Archive all conversations by default | `true` | +| `PROSODY_ARCHIVE_COMPRESSION` | Compress archived messages | `true` | +| `PROSODY_ARCHIVE_MAX_QUERY_RESULTS` | Max results per MAM query | `250` | + +### MUC variables + +| Variable | Description | Default | +|---|---|---| +| `PROSODY_MUC_DEFAULT_PUBLIC` | New rooms are public by default | `true` | +| `PROSODY_MUC_DEFAULT_PERSISTENT` | New rooms persist by default | `true` | +| `PROSODY_MUC_LOCKING` | Lock rooms until configured | `false` | +| `PROSODY_MUC_LOG_BY_DEFAULT` | Archive MUC messages by default | `true` | +| `PROSODY_MUC_LOG_EXPIRES_AFTER` | MUC archive retention | `1y` | +| `PROSODY_RESTRICT_ROOM_CREATION` | Restrict who can create rooms | `false` | + +### TURN/STUN variables + +| Variable | Description | Default | +|---|---|---| +| `TURN_SECRET` | Shared secret with TURN server | *(change before production)* | +| `TURN_EXTERNAL_HOST` | TURN server hostname | `turn.atl.network` | +| `TURN_PORT` | TURN/STUN UDP port | `3478` | +| `TURNS_PORT` | TURN over TLS port | `5349` | + +### Rate limiting variables + +| Variable | Description | Default | +|---|---|---| +| `PROSODY_C2S_RATE` | Client connection rate limit | `10kb/s` | +| `PROSODY_C2S_BURST` | Client burst allowance | `25kb` | +| `PROSODY_C2S_STANZA_SIZE` | Max client stanza size (bytes) | `262144` | +| `PROSODY_S2S_RATE` | Server federation rate limit | `30kb/s` | +| `PROSODY_S2S_BURST` | Server burst allowance | `100kb` | +| `PROSODY_S2S_STANZA_SIZE` | Max server stanza size (bytes) | `524288` | + +For the complete list of all environment variables, see the [Environment Variables reference](/docs/reference/environment-variables). + +## VirtualHost configuration + +Prosody uses a single `VirtualHost` block for the primary domain. The domain is set dynamically from the `XMPP_DOMAIN` environment variable (mapped to `PROSODY_DOMAIN` in compose). + +```lua +-- Domain from environment +local domain = os.getenv("PROSODY_DOMAIN") or "atl.chat" +allow_registration = os.getenv("PROSODY_ALLOW_REGISTRATION") == "true" + +VirtualHost(domain) +http_host = __http_host -- maps HTTP Host header to this VirtualHost + +ssl = { + key = os.getenv("PROSODY_SSL_KEY") or + ("certs/live/" .. domain .. "/privkey.pem"), + certificate = os.getenv("PROSODY_SSL_CERT") or + ("certs/live/" .. domain .. "/fullchain.pem") +} + +-- VirtualHost-scoped modules (not loaded globally) +modules_enabled = { + "http_admin_api", -- REST API for user account management + "default_bookmarks", -- Default bookmarks when user has none +} + +-- Default MUC bookmark for new users +default_bookmarks = { + { jid = "general@muc." .. domain, name = "General", autojoin = true }, +} +``` + +Key points about the VirtualHost: + +- `http_host` maps incoming HTTP requests to this VirtualHost, which is important when Prosody sits behind a reverse proxy +- `http_admin_api` is loaded only on the VirtualHost (not globally) so the REST API is scoped to this domain +- `default_bookmarks` auto-joins new users to the `#general` MUC room +- SSL certificates follow the Let's Encrypt directory layout (`certs/live//`) +- Converse.js BOSH/WebSocket URLs are dynamically derived from `PROSODY_HTTP_EXTERNAL_URL` when set + +## Component blocks + +Components extend Prosody with additional services. Each component gets its own subdomain and SSL certificate. + +### MUC (multi-user chat) + +```lua +Component("muc." .. domain) "muc" + +modules_enabled = { + "muc_notifications", -- Push notifications for MUC events + "muc_offline_delivery", -- Offline delivery for MUC events + "muc_thread_polyfill", -- Infer thread from XEP-0461 reply + "pastebin", -- Intercept large messages, replace with paste URL + "muc_limits", -- Rate-limit room events to prevent floods + "muc_moderation", -- XEP-0425: Message moderation + "muc_mam_hints", -- XEP-0334: Respect store/no-store hints + "muc_mam_markers", -- XEP-0333: Archive chat markers in MAM + "muc_markers", -- Rewrite message id to stanza-id for reactions + "muc_defaults", -- Create default MUCs on startup + "muc_slow_mode", -- Per-user rate limit set by room owners +} +``` + +The MUC component creates a `general` room on startup with the admin JID as owner. Room defaults (public, persistent, logged) are controlled via `PROSODY_MUC_*` environment variables. + +MUC rate limiting is configured globally: + +| Setting | Value | Description | +|---|---|---| +| `muc_event_rate` | `0.5` | Max events/second (one every 2 seconds) | +| `muc_burst_factor` | `6` | Allow 6× burst for 6 seconds | +| `muc_max_nick_length` | `23` | Max nickname length | +| `muc_max_char_count` | `5664` | Max bytes per message | +| `muc_max_line_count` | `23` | Max lines per message | + +### HTTP file upload + +```lua +Component("upload." .. domain) "http_file_share" +http_host = __http_host +http_external_url = os.getenv("PROSODY_UPLOAD_EXTERNAL_URL") + or ("https://upload." .. domain .. "/") +``` + +| Setting | Value | +|---|---| +| Max file size | 100 MB | +| Daily quota per user | 1 GB | +| Global quota | 10 GB | +| File expiration | 30 days | + +### SOCKS5 proxy (XEP-0065) + +```lua +Component("proxy." .. domain) "proxy65" +proxy65_address = os.getenv("PROSODY_PROXY_ADDRESS") or ("proxy." .. domain) +``` + +Provides peer-to-peer file transfer proxying. The proxy listens on port 5000 (configurable via `PROSODY_PROXY65_PORT`). + +### PubSub with RSS feeds + +```lua +Component("pubsub." .. domain) "pubsub" +modules_enabled = { "pubsub_feeds" } +feeds = { + feed = os.getenv("PROSODY_FEED_URL") or "https://allthingslinux.org/feed", +} +``` + +The PubSub component pulls RSS/Atom feeds and publishes them as XMPP PubSub nodes. Registered users can create their own PubSub nodes. + +### Bridge component (XEP-0114) + +```lua +Component("bridge." .. domain) "component" +component_secret = os.getenv("BRIDGE_XMPP_COMPONENT_SECRET") + or os.getenv("XMPP_COMPONENT_SECRET") + or "change_me_xmpp_component_secret" +``` + +The bridge service connects as an external XMPP component on port 5347. The component interface listens on all interfaces (`component_interfaces = { "*" }`) so the bridge container can reach it across the Docker network. + +> **Warning:** Change `BRIDGE_XMPP_COMPONENT_SECRET` from the default before production deployment. + +## Storage configuration + +Prosody uses SQLite by default for development and can be switched to PostgreSQL for production via `PROSODY_STORAGE=sql`. + +```lua +default_storage = "sql" + +sql = { + driver = "SQLite3", + database = "data/prosody.sqlite", +} +``` + +Storage is assigned per data type: + +| Data type | Backend | Notes | +|---|---|---| +| accounts, roster, vcard, private, blocklist | `sql` | User data | +| archive, muc_log, offline | `sql` | Message archives | +| pubsub_nodes, pubsub_data, pep | `sql` | PubSub and PEP | +| http_file_share | `sql` | File upload metadata | +| caps, carbons | `memory` | Ephemeral (not persisted) | + +Data is stored in Docker volumes mapped to `data/xmpp/data/` on the host. + +## TLS and security + +### Global TLS settings + +```lua +ssl = { + protocol = "tlsv1_2+", + ciphers = "ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS", + curve = "secp384r1", + options = { "cipher_server_preference", "single_dh_use", "single_ecdh_use" }, +} +``` + +This enforces TLS 1.2+ with modern AEAD ciphers and ECDHE key exchange. The certificate directory follows the Let's Encrypt layout: + +``` +/etc/prosody/certs/live// +├── fullchain.pem +└── privkey.pem +``` + +### Encryption enforcement + +| Setting | Default | Recommendation | +|---|---|---| +| `c2s_require_encryption` | `true` | Always `true` in production | +| `s2s_require_encryption` | `true` | Always `true` in production | +| `s2s_secure_auth` | `true` | `true` for certificate-based federation auth | +| `allow_unencrypted_plain_auth` | `false` | Never `true` in production | + +### Authentication + +Prosody uses hashed password storage with SCRAM-SHA-256 as the primary SASL mechanism: + +```lua +authentication = "internal_hashed" +sasl_mechanisms = { + "SCRAM-SHA-256", + "SCRAM-SHA-1", +} +``` + +SCRAM-SHA-1 is kept for compatibility with older clients. In-band registration is disabled — users are provisioned via the Portal through `mod_http_admin_api`. + +### Rate limiting + +Three rate limit tiers protect against abuse: + +| Tier | Rate | Burst | Max stanza | +|---|---|---|---| +| Client (c2s) | 10 KB/s | 25 KB | 256 KB | +| Server (s2s) | 30 KB/s | 100 KB | 512 KB | +| HTTP upload | 2 MB/s | 10 MB | — | + +Additional protections: + +- `max_connections_per_ip`: 5 (increase if bridge shares an IP) +- `anti_spam_services`: subscribes to `xmppbl.org` real-time block lists +- `block_registrations_users`: blocks common abusive usernames (admin, root, postmaster, etc.) +- `block_registrations_require`: enforces `^[a-zA-Z0-9_.-]+$` pattern for usernames + +### HTTP security headers + +Prosody serves HTTP responses with hardened headers: + +| Header | Value | +|---|---| +| `Strict-Transport-Security` | `max-age=31536000; includeSubDomains; preload` | +| `X-Frame-Options` | `DENY` | +| `X-Content-Type-Options` | `nosniff` | +| `X-XSS-Protection` | `1; mode=block` | +| `Referrer-Policy` | `strict-origin-when-cross-origin` | +| `Content-Security-Policy` | Allows `self`, Converse.js CDN, and XMPP endpoints | + +## Audit findings and recommendations + +The following findings are from a configuration audit and remain relevant to the current setup. + +### Resolved issues + +These critical issues from the original audit have been fixed in the current configuration: + +- **TLS enforcement defaults**: `.env.example` now ships with `PROSODY_C2S_REQUIRE_ENCRYPTION=true`, `PROSODY_S2S_REQUIRE_ENCRYPTION=true`, `PROSODY_S2S_SECURE_AUTH=true`, and `PROSODY_ALLOW_UNENCRYPTED_PLAIN_AUTH=false`. +- **Global SSL block**: The `ssl` block is now active (not commented out) with TLS 1.2+ and modern ciphers. +- **Legacy auth removed**: `legacyauth` is no longer in `modules_enabled` — only SASL (SCRAM-SHA-256/SHA-1) is used. +- **HTTP status endpoint**: `http_status_allow_cidr` is restricted to `172.16.0.0/12` and `127.0.0.0/8` (Docker networks and localhost), not world-open. + +### Active considerations + +- **Archive retention**: `PROSODY_ARCHIVE_EXPIRES_AFTER` defaults to `1y`. Monitor SQLite database size over time, especially with many active users. +- **Connections per IP**: `PROSODY_MAX_CONNECTIONS_PER_IP` defaults to `5`. In Docker environments where the bridge and other services connect from the same network IP, you may need to increase this value. +- **`mod_register` for password changes**: Registration is disabled (Portal provisions users), but `mod_register` is also needed for password changes by existing users. Consider enabling it with `allow_registration = false` if users need self-service password changes. +- **Namespace exclusions**: `dont_archive_namespaces` excludes typing indicators (`chatstates`) and Jingle call signaling from archives, reducing storage usage. + +### Positive findings + +The configuration exceeds typical Prosody deployments in several areas: + +- Comprehensive module selection covering core protocol, modern messaging (MAM, carbons, smacks), mobile optimization (CSI, cloud_notify), and spam prevention (anti_spam, blocklist, report_forward) +- Push notification privacy: `push_notification_with_body` and `push_notification_with_sender` are both `false` by default +- Anti-spam with xmppbl.org RTBL subscription +- Thorough HTTP security headers including HSTS, CSP, and X-Frame-Options +- Proper TURN/STUN external configuration for audio/video calls +- Certificate handling with Let's Encrypt layout, legacy fallback, and self-signed generation + +## Customising the configuration + +To modify Prosody settings: + +1. Set environment variables in `.env` (preferred for most settings) +2. Edit `apps/prosody/config/prosody.cfg.lua` directly for structural changes +3. Reload the configuration without restarting: + ```bash + just xmpp reload + ``` +4. Verify the configuration is valid: + ```bash + just xmpp check-config + ``` + +For TLS certificate changes, Prosody needs a reload: + +```bash +just xmpp reload +``` + +To verify certificates are correctly configured: + +```bash +just xmpp check-certs +``` + +## Related pages + +- [XMPP Overview](/docs/services/xmpp) — architecture and technology stack +- [XMPP DNS](/docs/services/xmpp/dns) — SRV records and DNS setup +- [XMPP Modules](/docs/services/xmpp/modules) — module reference +- [XMPP Operations](/docs/services/xmpp/operations) — operational commands and management +- [Environment Variables](/docs/reference/environment-variables) — complete variable reference +- [SSL/TLS](/docs/operations/ssl-tls) — certificate management across all services diff --git a/apps/docs/content/docs/services/xmpp/dns.mdx b/apps/docs/content/docs/services/xmpp/dns.mdx new file mode 100644 index 00000000..08909c1b --- /dev/null +++ b/apps/docs/content/docs/services/xmpp/dns.mdx @@ -0,0 +1,73 @@ +--- +title: XMPP DNS +description: SRV records, subdomain layout, and federation DNS configuration for XMPP. +--- + +XMPP requires specific DNS SRV records for clients and federated servers to discover the service. The XMPP domain (realm) is `atl.chat` — this is what appears in JIDs (`user@atl.chat`) and what clients use for SRV lookups. `xmpp.atl.chat` is a public hostname for the BOSH/WebSocket HTTPS endpoint, not the realm. + +## Subdomain layout + +| Subdomain | Purpose | +|---|---| +| `atl.chat` | Primary XMPP domain (realm — used in JIDs) | +| `xmpp.atl.chat` | Public hostname for BOSH/WebSocket HTTPS endpoint | +| `muc.atl.chat` | Multi-User Chat (MUC) rooms | +| `bridge.atl.chat` | Bridge component | + +## Client SRV records + +SRV records must be published under the XMPP domain (`atl.chat`), not the HTTPS hostname: + +```text +_xmpp-client._tcp.atl.chat. IN SRV 5 0 5222 xmpp.atl.chat. +``` + +## Server federation SRV records + +```text +_xmpp-server._tcp.atl.chat. IN SRV 5 0 5269 xmpp.atl.chat. +``` + +## XEP-0368: XMPP over TLS (Direct TLS) + +XEP-0368 allows clients and servers to connect directly over TLS without STARTTLS negotiation. Both client and server direct TLS listeners are enabled by default: + +```text +_xmpps-client._tcp.atl.chat. IN SRV 5 0 5223 xmpp.atl.chat. +_xmpps-server._tcp.atl.chat. IN SRV 5 0 5270 xmpp.atl.chat. +``` + +> **Note:** Direct TLS (ports 5223 for C2S and 5270 for S2S) is enabled in the default atl.chat Prosody configuration. Add both SRV records to allow clients and servers to discover and use direct TLS connections. + +## A and AAAA records + +Create A records for the service hostname and component subdomains: + +```text +xmpp.atl.chat. IN A +muc.atl.chat. IN A +``` + +If your server has an IPv6 address, add AAAA records: + +```text +xmpp.atl.chat. IN AAAA +muc.atl.chat. IN AAAA +``` + +Alternatively, use CNAME records if all subdomains resolve to the same server: + +```text +muc.atl.chat. IN CNAME xmpp.atl.chat. +``` + +## DNSSEC + +If your DNS provider supports DNSSEC, enable it for the `atl.chat` zone. DNSSEC protects against DNS spoofing attacks, which is particularly important for XMPP federation where servers discover each other via SRV records. Consult your DNS provider's documentation for enabling DNSSEC signing on your zone. + +## Related pages + +- [XMPP Overview](/docs/services/xmpp) — Prosody architecture and components +- [XMPP Configuration](/docs/services/xmpp/configuration) — prosody.cfg.lua, VirtualHost blocks, and port settings +- [IRC DNS](/docs/services/irc/dns) — IRC DNS records and STS configuration +- [Networking](/docs/architecture/networking) — full port registry and DNS zone layout diff --git a/apps/docs/content/docs/services/xmpp/index.mdx b/apps/docs/content/docs/services/xmpp/index.mdx new file mode 100644 index 00000000..e97cb9cd --- /dev/null +++ b/apps/docs/content/docs/services/xmpp/index.mdx @@ -0,0 +1,25 @@ +--- +title: XMPP Overview +description: Overview of the atl.chat XMPP stack powered by Prosody. +--- + +atl.chat's XMPP service is provided by Prosody, a lightweight, extensible XMPP server written in Lua. It supports standard XMPP clients via STARTTLS/TLS, server-to-server federation, and BOSH/WebSocket transports for web clients. + +## Components + +### Prosody + +Prosody handles all XMPP traffic. It is configured via `prosody.cfg.lua` and environment variables, with modules managed through a `source/` directory (for compiled modules) and an `enabled/` directory (for active modules). + +### Bridge integration + +The bridge connects to Prosody via an XMPP component connection. The component interface allows the bridge to appear as a separate subdomain (e.g., `bridge.atl.chat`) within the XMPP network. + +> **Note:** The `component_interfaces` configuration may need adjustment if the bridge container cannot connect. If you experience connectivity issues, verify that the component interface binds to `0.0.0.0` (or the Docker network interface) rather than `127.0.0.1` in `prosody.cfg.lua`. + +## Related pages + +- [Configuration](/docs/services/xmpp/configuration) — prosody.cfg.lua, env vars, VirtualHosts +- [DNS](/docs/services/xmpp/dns) — SRV records, subdomain layout, federation +- [Modules](/docs/services/xmpp/modules) — source vs enabled dirs, community modules +- [Operations](/docs/services/xmpp/operations) — prosodyctl, adduser, db-backup, health checks diff --git a/apps/docs/content/docs/services/xmpp/meta.json b/apps/docs/content/docs/services/xmpp/meta.json new file mode 100644 index 00000000..90c368a0 --- /dev/null +++ b/apps/docs/content/docs/services/xmpp/meta.json @@ -0,0 +1,10 @@ +{ + "title": "XMPP", + "pages": [ + "index", + "configuration", + "dns", + "modules", + "operations" + ] +} diff --git a/apps/docs/content/docs/services/xmpp/modules.mdx b/apps/docs/content/docs/services/xmpp/modules.mdx new file mode 100644 index 00000000..188d48a1 --- /dev/null +++ b/apps/docs/content/docs/services/xmpp/modules.mdx @@ -0,0 +1,131 @@ +--- +title: XMPP Modules +description: Managing Prosody modules — source directory, enabled directory, and community modules. +--- + +Prosody modules extend the server with additional functionality. atl.chat manages community modules via a `modules.list` file that is processed during the Docker image build. + +## Module directories + +The Prosody container uses two directories for community modules: + +| Directory | Purpose | +|---|---| +| `/usr/local/lib/prosody/prosody-modules/` | Full community modules repository (cloned from `hg.prosody.im`) | +| `/usr/local/lib/prosody/prosody-modules-enabled/` | Symlinks to active modules from the repository | + +During the Docker build, the `Containerfile` clones the [prosody-modules](https://modules.prosody.im/) Mercurial repository, then creates symlinks in the `enabled` directory for each module listed in `modules.list`. Modules not in the list are removed to reduce image size. + +At container startup, `docker-entrypoint.sh` runs `setup_community_modules()` which verifies the enabled directory exists and logs the count of active modules. + +## Built-in modules + +Prosody ships with a comprehensive set of built-in modules. You enable them in `prosody.cfg.lua`: + +```lua +modules_enabled = { + "roster", + "saslauth", + "tls", + "dialback", + "disco", + "carbons", + "pep", + "private", + "blocklist", + "vcard4", + "version", + "uptime", + "time", + "ping", + "register", + "admin_adhoc", +} +``` + +## Community modules + +Community modules are declared in `apps/prosody/modules.list`, one module name per line. The current list includes: + +| Module | Purpose | +|---|---| +| `mod_cloud_notify_extensions` | Push notification extensions | +| `mod_cloud_notify_filters` | Push notification filtering | +| `mod_cloud_notify_encrypted` | Encrypted push notifications | +| `mod_cloud_notify_priority_tag` | Priority tagging for push notifications | +| `mod_compliance_2023` | XMPP compliance suite 2023 | +| `mod_compliance_latest` | Latest XMPP compliance checks | +| `mod_muc_notifications` | MUC room notifications | +| `mod_muc_offline_delivery` | Offline message delivery for MUC | +| `mod_muc_limits` | Rate limiting for MUC rooms | +| `mod_muc_moderation` | Message moderation in MUC | +| `mod_muc_mam_hints` | MAM hints for MUC | +| `mod_muc_mam_markers` | Read markers for MUC MAM | +| `mod_muc_markers` | Chat markers for MUC | +| `mod_muc_defaults` | Default MUC room settings | +| `mod_muc_slow_mode` | Slow mode for MUC rooms | +| `mod_muc_thread_polyfill` | Thread support polyfill for MUC | +| `mod_default_bookmarks` | Default bookmark entries for new users | +| `mod_conversejs` | Built-in Converse.js web client | +| `mod_http_avatar` | Serves vCard avatars at `/avatar/` | +| `mod_http_status` | HTTP status endpoint | +| `mod_http_admin_api` | HTTP admin API | +| `mod_http_oauth2` | OAuth2 authentication | +| `mod_http_pep_avatar` | PEP avatar HTTP access | +| `mod_measure_modules` | Module performance metrics | +| `mod_anti_spam` | Spam detection and filtering | +| `mod_admin_blocklist` | Server-wide blocklist management | +| `mod_spam_reporting` | XEP-0377 spam reporting | +| `mod_report_forward` | Forward spam reports to admins | +| `mod_csi_battery_saver` | Client State Indication battery optimisation | +| `mod_invites` | Invitation-based registration | +| `mod_s2s_status` | Server-to-server connection status | +| `mod_s2s_keepalive` | Keep S2S connections alive | +| `mod_log_slow_events` | Log slow event processing | +| `mod_pastebin` | Auto-paste long messages | +| `mod_reload_modules` | Reload modules without restart | +| `mod_pubsub_subscription` | PubSub subscription management | +| `mod_pubsub_feeds` | PubSub Atom/RSS feeds | +| `mod_groups_internal` | Internal contact groups | +| `mod_support_contact` | Support contact information | +| `mod_idlecompat` | Idle time compatibility | + +## Adding a community module + +1. Find the module in the [prosody-modules repository](https://modules.prosody.im/) +2. Add the module name to `apps/prosody/modules.list`: + + ```text + mod_new_module + ``` + +3. Rebuild the Docker image: + + ```bash + docker compose build atl-xmpp-server + ``` + +4. Enable the module in `prosody.cfg.lua` if it is not auto-loaded: + + ```lua + modules_enabled = { + -- existing modules... + "new_module", + } + ``` + +5. Restart or reload Prosody: + + ```bash + # Reload config without restart + just xmpp reload + + # Full restart if needed + docker compose restart atl-xmpp-server + ``` + +## Related pages + +- [XMPP Overview](/docs/services/xmpp) — Prosody architecture and components +- [XMPP Configuration](/docs/services/xmpp/configuration) — prosody.cfg.lua and modules_enabled list +- [XMPP Operations](/docs/services/xmpp/operations) — prosodyctl commands and service management diff --git a/apps/docs/content/docs/services/xmpp/operations.mdx b/apps/docs/content/docs/services/xmpp/operations.mdx new file mode 100644 index 00000000..910913b9 --- /dev/null +++ b/apps/docs/content/docs/services/xmpp/operations.mdx @@ -0,0 +1,129 @@ +--- +title: XMPP Operations +description: prosodyctl commands, user management, database backup, and health checks for Prosody. +--- + +Day-to-day Prosody operations are performed via `prosodyctl` inside the container. This page covers user management, configuration reload, database backup, and health monitoring. + +## prosodyctl basics + +Open a shell in the Prosody container and run commands directly: + +```bash +# Open a shell in the Prosody container +docker exec -it atl-xmpp-server bash + +# Check Prosody status +prosodyctl status + +# Reload configuration without restarting +prosodyctl reload +``` + +You can also run commands without entering the container: + +```bash +# Run prosodyctl from outside the container +docker exec atl-xmpp-server prosodyctl status +``` + +Common `prosodyctl` commands: + +| Command | Description | +|---|---| +| `prosodyctl status` | Show whether Prosody is running | +| `prosodyctl reload` | Reload configuration files | +| `prosodyctl restart` | Restart the Prosody process | +| `prosodyctl about` | Show version, paths, and loaded modules | +| `prosodyctl check` | Run configuration and DNS checks | +| `prosodyctl cert generate ` | Generate a self-signed certificate | + +The `just xmpp` recipes provide shortcuts: + +```bash +# Open a shell +just xmpp shell + +# Reload configuration +just xmpp reload + +# Add a user +just xmpp adduser +``` + +## User management + +```bash +# Add a user (prompts for password) +prosodyctl adduser user@atl.chat + +# Change a password +prosodyctl passwd user@atl.chat + +# Delete a user +prosodyctl deluser user@atl.chat +``` + +## Database backup + +Prosody stores data in `/var/lib/prosody` inside the container, which is mapped as a Docker volume to `data/xmpp/` on the host. Back up this directory: + +```bash +# Create a backup archive +docker exec atl-xmpp-server prosodyctl backup /backup/prosody-$(date +%Y%m%d).tar.gz +``` + +You can also back up the host-side data directory directly: + +```bash +# Stop Prosody before backing up for consistency +docker compose stop atl-xmpp-server +tar czf backup-xmpp-$(date +%Y%m%d).tar.gz data/xmpp/ +docker compose start atl-xmpp-server +``` + +For automated backups, see the [Backups](/docs/operations/backups) page for retention policies and scheduling recommendations. + +## Health checks + +The Prosody container health check verifies the service is responsive: + +```bash +docker inspect --format='{{.State.Health.Status}}' atl-xmpp-server +# Expected: healthy +``` + +## Logs + +Prosody logs are written to `/var/lib/prosody/logs/` inside the container (mapped to `data/xmpp/logs/` on the host). View them with: + +```bash +# Follow Prosody container logs +docker logs --follow atl-xmpp-server + +# View Prosody's own log file +docker exec atl-xmpp-server cat /var/lib/prosody/logs/prosody.log + +# Show recent error-level entries +docker exec atl-xmpp-server grep "error" /var/lib/prosody/logs/prosody.log +``` + +Common failure modes to watch for: + +| Log pattern | Meaning | Resolution | +|---|---|---| +| `error: Failed to open` | Certificate file missing or unreadable | Check cert paths and permissions | +| `warn: No certificate` | TLS certificate not configured for a VirtualHost | Add certificate config to `prosody.cfg.lua` | +| `error: component connection refused` | Bridge cannot connect as a component | Verify `component_interfaces` in Prosody config | +| `warn: DNS resolution failed` | Cannot resolve federation targets | Check DNS records and network connectivity | + + + +## Related pages + +- [XMPP Overview](/docs/services/xmpp) — Prosody architecture and components +- [XMPP Configuration](/docs/services/xmpp/configuration) — prosody.cfg.lua, env vars, VirtualHosts +- [XMPP Modules](/docs/services/xmpp/modules) — community module management +- [Backups](/docs/operations/backups) — cross-service backup and restore procedures +- [Monitoring](/docs/operations/monitoring) — health checks and log inspection across all services +- [Troubleshooting](/docs/operations/troubleshooting) — cross-service diagnostic commands diff --git a/apps/docs/lib/cn.ts b/apps/docs/lib/cn.ts new file mode 100644 index 00000000..ba66fd25 --- /dev/null +++ b/apps/docs/lib/cn.ts @@ -0,0 +1 @@ +export { twMerge as cn } from 'tailwind-merge'; diff --git a/apps/docs/lib/layout.shared.tsx b/apps/docs/lib/layout.shared.tsx new file mode 100644 index 00000000..1f9a6b41 --- /dev/null +++ b/apps/docs/lib/layout.shared.tsx @@ -0,0 +1,16 @@ +import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared'; + +export function baseOptions(): BaseLayoutProps { + return { + nav: { + title: 'atl.chat docs', + }, + links: [ + { + text: 'Documentation', + url: '/', + active: 'nested-url', + }, + ], + }; +} diff --git a/apps/docs/lib/source.ts b/apps/docs/lib/source.ts new file mode 100644 index 00000000..37214593 --- /dev/null +++ b/apps/docs/lib/source.ts @@ -0,0 +1,26 @@ +import { docs } from 'fumadocs-mdx:collections/server'; +import { type InferPageType, loader } from 'fumadocs-core/source'; +import { lucideIconsPlugin } from 'fumadocs-core/source/lucide-icons'; + +export const source = loader({ + baseUrl: '/', + source: docs.toFumadocsSource(), + plugins: [lucideIconsPlugin()], +}); + +export function getPageImage(page: InferPageType) { + const segments = [...page.slugs, 'image.png']; + + return { + segments, + url: undefined, // OG images disabled for Cloudflare deployment + }; +} + +export async function getLLMText(page: InferPageType) { + const processed = await page.data.getText('processed'); + + return `# ${page.data.title} + +${processed}`; +} diff --git a/apps/docs/mdx-components.tsx b/apps/docs/mdx-components.tsx new file mode 100644 index 00000000..afe901a8 --- /dev/null +++ b/apps/docs/mdx-components.tsx @@ -0,0 +1,15 @@ +import defaultMdxComponents from 'fumadocs-ui/mdx'; +import { Mermaid } from '@/components/mdx/mermaid'; +import type { MDXComponents } from 'mdx/types'; + +export function getMDXComponents(components?: MDXComponents): MDXComponents { + return { + ...defaultMdxComponents, + Mermaid, + ...components, + }; +} + +export function useMDXComponents(components?: MDXComponents): MDXComponents { + return getMDXComponents(components); +} diff --git a/apps/docs/next.config.mjs b/apps/docs/next.config.mjs new file mode 100644 index 00000000..d31d68fc --- /dev/null +++ b/apps/docs/next.config.mjs @@ -0,0 +1,33 @@ +import { createMDX } from 'fumadocs-mdx/next'; +import { initOpenNextCloudflareForDev } from '@opennextjs/cloudflare'; + +const withMDX = createMDX(); + +/** @type {import('next').NextConfig} */ +const config = { + reactStrictMode: true, + logging: { + fetches: { + fullUrl: true, + hmrRefreshes: false, + }, + }, + experimental: { + turbopackFileSystemCacheForBuild: true, + browserDebugInfoInTerminal: { + showSourceLocation: true, + }, + }, + async rewrites() { + return [ + { + source: '/docs/:path*.mdx', + destination: '/llms.mdx/docs/:path*', + }, + ]; + }, +}; + +export default withMDX(config); + +initOpenNextCloudflareForDev(); diff --git a/apps/docs/open-next.config.ts b/apps/docs/open-next.config.ts new file mode 100644 index 00000000..1a57e31c --- /dev/null +++ b/apps/docs/open-next.config.ts @@ -0,0 +1,3 @@ +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; + +export default defineCloudflareConfig({}); diff --git a/apps/docs/package.json b/apps/docs/package.json new file mode 100644 index 00000000..4140e3d6 --- /dev/null +++ b/apps/docs/package.json @@ -0,0 +1,47 @@ +{ + "name": "@atl.chat/docs", + "private": true, + "type": "module", + "scripts": { + "dev": "next dev --port 3001", + "build": "next build", + "start": "next start", + "deploy": "opennextjs-cloudflare build && tsx alchemy.run.ts", + "deploy:prod": "opennextjs-cloudflare build && tsx alchemy.run.ts --stage prod", + "preview": "opennextjs-cloudflare preview", + "destroy": "tsx alchemy.run.ts --destroy", + "destroy:prod": "tsx alchemy.run.ts --destroy --stage prod", + "check-types": "fumadocs-mdx && next typegen && tsc --noEmit", + "typecheck": "fumadocs-mdx && tsc --noEmit", + "lint:links": "tsx scripts/lint.ts", + "postinstall": "fumadocs-mdx" + }, + "dependencies": { + "@opennextjs/cloudflare": "latest", + "@types/mdx": "^2.0.13", + "alchemy": "latest", + "fumadocs-core": "latest", + "fumadocs-mdx": "latest", + "fumadocs-ui": "npm:@fumadocs/base-ui@latest", + "lucide-react": "^0.564.0", + "mermaid": "^11.12.3", + "next": "16.1.6", + "next-themes": "^0.4.6", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "tailwind-merge": "^3.4.1" + }, + "devDependencies": { + "@cloudflare/workers-types": "^4.20260305.0", + "@tailwindcss/postcss": "^4.1.18", + "@types/node": "^25.2.3", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "next-validate-link": "^1.6.4", + "postcss": "^8.5.6", + "tailwindcss": "^4.1.18", + "tsx": "^4", + "typescript": "^5.9.3", + "wrangler": "^4.69.0" + } +} diff --git a/apps/docs/postcss.config.mjs b/apps/docs/postcss.config.mjs new file mode 100644 index 00000000..5d6d8457 --- /dev/null +++ b/apps/docs/postcss.config.mjs @@ -0,0 +1,8 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { + '@tailwindcss/postcss': {}, + }, +}; + +export default config; diff --git a/apps/docs/scripts/lint.ts b/apps/docs/scripts/lint.ts new file mode 100644 index 00000000..8854a043 --- /dev/null +++ b/apps/docs/scripts/lint.ts @@ -0,0 +1,51 @@ +import { type FileObject, printErrors, scanURLs, validateFiles } from 'next-validate-link'; +import type { InferPageType } from 'fumadocs-core/source'; +import { source } from '@/lib/source'; + +async function checkLinks() { + const scanned = await scanURLs({ + preset: 'next', + populate: { + '[[...slug]]': source.getPages().map((page) => { + return { + value: { + slug: page.slugs, + }, + hashes: getHeadings(page), + }; + }), + }, + }); + + printErrors( + await validateFiles(await getFiles(), { + scanned, + markdown: { + components: { + Card: { attributes: ['href'] }, + }, + }, + checkRelativePaths: 'as-url', + }), + true, + ); +} + +function getHeadings({ data }: InferPageType): string[] { + return data.toc.map((item) => item.url.slice(1)); +} + +function getFiles() { + const promises = source.getPages().map( + async (page): Promise => ({ + path: page.absolutePath ?? '', + content: await page.data.getText('raw'), + url: page.url, + data: page.data, + }), + ); + + return Promise.all(promises); +} + +void checkLinks(); diff --git a/apps/docs/source.config.ts b/apps/docs/source.config.ts new file mode 100644 index 00000000..b251c46a --- /dev/null +++ b/apps/docs/source.config.ts @@ -0,0 +1,26 @@ +import { + defineConfig, + defineDocs, + frontmatterSchema, + metaSchema, +} from 'fumadocs-mdx/config'; +import { remarkMdxMermaid } from 'fumadocs-core/mdx-plugins'; + +export const docs = defineDocs({ + dir: 'content/docs', + docs: { + schema: frontmatterSchema, + postprocess: { + includeProcessedMarkdown: true, + }, + }, + meta: { + schema: metaSchema, + }, +}); + +export default defineConfig({ + mdxOptions: { + remarkPlugins: [remarkMdxMermaid], + }, +}); diff --git a/apps/docs/tsconfig.json b/apps/docs/tsconfig.json new file mode 100644 index 00000000..58614212 --- /dev/null +++ b/apps/docs/tsconfig.json @@ -0,0 +1,39 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "target": "ESNext", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "tsBuildInfoFile": ".next/cache/tsconfig.tsbuildinfo", + "paths": { + "@/*": ["./*"], + "fumadocs-mdx:collections/*": [".source/*"] + }, + "types": ["@cloudflare/workers-types", "./types/env.d.ts"], + "plugins": [ + { + "name": "next" + } + ] + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + "alchemy.run.ts" + ], + "exclude": ["node_modules"] +} diff --git a/apps/docs/types/env.d.ts b/apps/docs/types/env.d.ts new file mode 100644 index 00000000..d33c388b --- /dev/null +++ b/apps/docs/types/env.d.ts @@ -0,0 +1,16 @@ +// Auto-generated Cloudflare binding types. +// @see https://alchemy.run/concepts/bindings/#type-safe-bindings + +import type { website } from "../alchemy.run.ts"; + +export type CloudflareEnv = typeof website.Env; + +declare global { + type Env = CloudflareEnv; +} + +declare module "cloudflare:workers" { + namespace Cloudflare { + export interface Env extends CloudflareEnv {} + } +} diff --git a/docs/AGENTS.md b/docs/AGENTS.md deleted file mode 100644 index 73e13b88..00000000 --- a/docs/AGENTS.md +++ /dev/null @@ -1,29 +0,0 @@ -# Documentation - -> Scope: `docs/` — inherits [AGENTS.md](../AGENTS.md). - -Documentation hub for atl.chat. Human-readable guides; not code. - -## Structure - -| Dir | Purpose | -|-----|---------| -| `architecture/` | CI/CD, new-service guide, architecture overview | -| `audits/` | Config audits (env vars, dev-prod lifecycle, prosody, unrealircd) | -| `bridges/` | Bridge infrastructure overview | -| `examples/` | Example configs (nginx, prometheus, unrealircd) | -| `infra/` | Containerization, data layout, networking, SSL | -| `onboarding/` | Local setup, just, pre-commit | -| `services/` | Per-service docs (irc, xmpp, web) | - -## Key Files - -- [README.md](README.md) — Hub index -- [architecture/new-service.md](architecture/new-service.md) — Adding new services -- [infra/data-structure.md](infra/data-structure.md) — Data layout -- [services/irc/README.md](services/irc/README.md) — IRC service docs - -## Related - -- [Monorepo AGENTS.md](../AGENTS.md) -- [apps/bridge/AGENTS.md](../apps/bridge/AGENTS.md) diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 0e7c4742..00000000 --- a/docs/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# Documentation Hub: atl.chat Monorepo - -Welcome to the centralized documentation hub for the `atl.chat` ecosystem. This monorepo manages multiple communication protocols (IRC, XMPP, Bridges) using a unified infrastructure. - -## 🚀 Getting Started - -- **[Onboarding Guide](./onboarding/README.md)** - Getting your local environment set up with `just` and `pre-commit`. -- **[Architecture Overview](./architecture/README.md)** - How the different services interact. -- **[Networking & Port Registry](./infra/networking.md)** - Tailscale VPC topology and port allocations. - -## 🏗️ Infrastructure Standards - -- **[Containerization](./infra/containerization.md)** - Standardized Docker patterns and orchestration. -- **[SSL/TLS Termination](./infra/ssl.md)** - Centralized security via the `atl.network` gateway. -- **[CI/CD Pipeline](./architecture/ci-cd.md)** - Automated testing and deployment workflows. - -## 🔌 Services & Protocols - -Learn more about the specific applications in this monorepo: - -### Core Services - -- **[IRC (UnrealIRCd)](./services/irc/README.md)** - high-performance, secure IRC server. -- **[XMPP (Prosody)](./services/xmpp/README.md)** - Modern, extensible XMPP server. -- **[Web Client](./services/web/README.md)** - Next.js frontend for the chat ecosystem. - -### Bridging & Interoperability - -- **[Bridge Infrastructure](./bridges/README.md)** - Overview of protocol bridging logic. -- **[Bridge (apps/bridge)](../apps/bridge/README.md)** - Discord↔IRC↔XMPP bridge. - ---- -> [!NOTE] -> If you are adding a new service, please follow our [Service Integration Guide](./architecture/new-service.md). diff --git a/docs/architecture/README.md b/docs/architecture/README.md deleted file mode 100644 index ed64a309..00000000 --- a/docs/architecture/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Architecture Overview - -How the atl.chat services interact and communicate. - -## Stack - -- **IRC** (UnrealIRCd + Atheme) – Chat server and services -- **XMPP** (Prosody) – Modern messaging -- **Bridges** (apps/bridge) – Discord↔IRC↔XMPP protocol bridging -- **Web** (Next.js) – Landing and clients - -## Compose Layout - -All orchestration lives under `infra/compose/`. The root `compose.yaml` includes: - -- `networks.yaml` – Shared `atl-chat` network -- `irc.yaml` – UnrealIRCd, Atheme, WebPanel -- `xmpp.yaml` – Prosody -- `cert-manager.yaml` – Let's Encrypt (Lego) -- `bridge.yaml` – Discord↔IRC↔XMPP bridge - -## See Also - -- [CI/CD Pipeline](./ci-cd.md) -- [New Service Guide](./new-service.md) -- [Networking](../../docs/infra/networking.md) diff --git a/docs/architecture/ci-cd.md b/docs/architecture/ci-cd.md deleted file mode 100644 index 5a2a6a0f..00000000 --- a/docs/architecture/ci-cd.md +++ /dev/null @@ -1,23 +0,0 @@ -# CI/CD Pipeline - -Automated testing and deployment for atl.chat. - -## Workflows - -See `.github/workflows/`: - -- **ci.yml** – Lint, security scan, Docker builds (per app) -- **docker.yml** – Reusable Docker image build -- **security.yml** – Trivy/Gitleaks scans - -## Triggers - -- `main`, `develop` – Push and PR -- Path filters: changes in `apps/unrealircd/**`, `apps/prosody/**`, `tests/**`, etc. run only relevant jobs - -## Local Checks - -```bash -just lint # Lefthook pre-commit -just irc test # IRC pytest -``` diff --git a/docs/architecture/new-service.md b/docs/architecture/new-service.md deleted file mode 100644 index 88ad4a64..00000000 --- a/docs/architecture/new-service.md +++ /dev/null @@ -1,45 +0,0 @@ -# New Service Integration Guide - -Adding a new service to the atl.chat monorepo. - -## 1. App Structure - -``` -apps/my-service/ -├── services/ # Container builds -│ └── mysvc/ -│ ├── Containerfile -│ └── config/ -├── scripts/ # Init, health checks -└── tests/ -``` - -## 2. Compose Fragment - -Add `infra/compose/my-service.yaml`: - -```yaml -name: atl-my-service -include: - - networks.yaml -services: - atl-my-service: - build: - context: ../../apps/my-service/services/mysvc - dockerfile: Containerfile - networks: - - atl-chat -``` - -## 3. Root Compose - -Add to `compose.yaml`: - -```yaml -include: - - infra/compose/my-service.yaml -``` - -## 4. Init - -Update `scripts/init.sh` (or create `apps/my-service/scripts/init.sh`) for data dirs and config. diff --git a/docs/audits/dev-prod-lifecycle-audit.md b/docs/audits/dev-prod-lifecycle-audit.md deleted file mode 100644 index 5048f4bb..00000000 --- a/docs/audits/dev-prod-lifecycle-audit.md +++ /dev/null @@ -1,283 +0,0 @@ -# Dev vs Prod Lifecycle Audit - -**Date:** 2026-02-26 -**Scope:** Full trace of dev and prod setup flows — env, scripts, Docker, TLS, DNS, config templates - ---- - -## Executive Summary - -The dev story is **solid and works well**. The prod story has **real gaps** that would block a production deployment. The main issues are: `just prod` doesn't run `init.sh` or load env files; the cert-manager is a placeholder that doesn't actually issue certs; the prod profiles don't actually differentiate services; and there's no documentation for the prod deployment path. - -**Verdict:** Dev is ~90% clean. Prod is ~40% ready. - ---- - -## Dev Flow Trace (`just dev`) - -### What happens - -``` -just dev - → sources .env.dev (sets ATL_ENVIRONMENT=dev, IRC_DOMAIN=irc.localhost, etc.) - → runs scripts/init.sh: - 1. Creates data/ directories (irc, atheme, xmpp, thelounge, certs) - 2. Sets permissions (chown/chmod) - 3. Copies system CA bundle to apps/unrealircd/config/tls/ - 4. Generates self-signed certs for irc.localhost and xmpp.localhost - 5. Copies .env.example → .env (if .env missing) - 6. Runs prepare-config.sh: - - Sources .env then .env.dev - - envsubst on unrealircd.conf.template → unrealircd.conf - - envsubst on atheme.conf.template → atheme.conf - - envsubst on bridge config.template.yaml → config.yaml - - envsubst on thelounge config.js.template → config.js - → docker compose --env-file .env --env-file .env.dev --profile dev up -d -``` - -### Dev Flow Verdict: 🟢 Works well - -| Aspect | Status | Notes | -|--------|--------|-------| -| Env loading | 🟢 | `.env` base + `.env.dev` overlay, compose loads both via `--env-file` | -| Init script | 🟢 | Creates dirs, generates self-signed certs, generates configs | -| Config generation | 🟢 | `prepare-config.sh` sources both env files, envsubst works correctly | -| TLS (dev) | 🟢 | Self-signed certs auto-generated for `irc.localhost` and `xmpp.localhost` | -| Docker profile | 🟢 | `--profile dev` starts Dozzle log viewer | -| Port binding | 🟢 | `ATL_CHAT_IP=127.0.0.1` binds IRC ports to localhost only | -| Bridge TLS verify | 🟢 | `BRIDGE_IRC_TLS_VERIFY=false` skips cert verification for self-signed | -| Prosody TLS | 🟢 | `.env.dev` relaxes c2s/s2s encryption requirements | -| Storage | 🟢 | `PROSODY_STORAGE=sqlite` — no external DB needed | - ---- - -## Prod Flow Trace (`just prod`) - -### What happens - -``` -just prod - → export ATL_ENVIRONMENT=prod (explicitly in justfile) - → docker compose --profile prod up -d -``` - -That's it. **No init.sh. No env file loading. No config generation.** - -### Prod Flow Verdict: 🔴 Broken - -| Issue | Severity | Details | -|-------|----------|---------| -| **No init.sh** | 🔴 | `just prod` doesn't run `init.sh`. No `data/` dirs created, no configs generated from templates. If deploying to a fresh server, nothing works. | -| **No `--env-file`** | 🔴 | Unlike `just dev` which passes `--env-file .env --env-file .env.dev`, `just prod` passes nothing. Docker Compose will auto-load `.env` but there's no explicit prod overlay. All prod-specific overrides must be in `.env` itself. | -| **No prod profile differentiation** | 🟡 | The `profiles: ["dev"]` on Dozzle means it only starts with `--profile dev`. But no services have `profiles: ["prod"]`. So `just prod` starts the exact same services as `docker compose up -d` (everything except Dozzle). The profiles are meaningless for prod. | -| **Cert-manager is a placeholder** | 🔴 | The cert-manager service uses `goacme/lego:latest` with a custom `run.sh`, but it runs on every `docker compose up` — including dev where it's not needed. There's no `CLOUDFLARE_DNS_API_TOKEN` set in dev, so it likely errors silently. In prod, someone would need to set this token, but there's no documentation for the cert issuance flow. | -| **No prod documentation** | 🟡 | README says `just prod` starts production stack, but there's no guide for: initial prod setup, cert issuance, DNS records needed, secrets management, backup strategy. | - ---- - -## TLS/Certificate Audit - -### Dev TLS - -``` -init.sh → generate_dev_certs() - → openssl req -x509 -nodes -days 365 -newkey rsa:2048 - → SANs: domain, *.domain, muc/upload/proxy/pubsub/bridge subdomains, localhost, 127.0.0.1 - → Stored in: data/certs/live/{irc.localhost,xmpp.localhost}/ -``` - -| Aspect | Status | Notes | -|--------|--------|-------| -| Self-signed generation | 🟢 | Good SANs, RSA 2048, 365 days | -| IRC reads from | 🟢 | `data/certs` mounted as `/home/unrealircd/unrealircd/certs` | -| Prosody reads from | 🟢 | `data/certs` mounted as `/etc/prosody/certs` | -| Prosody entrypoint | 🟢 | Creates `https` symlinks for automatic HTTPS cert discovery | -| Bridge skips verify | 🟢 | `BRIDGE_IRC_TLS_VERIFY=false` in `.env.dev` | -| Prosody skips s2s auth | 🟢 | `PROSODY_S2S_SECURE_AUTH=false` in `.env.dev` | - -### Prod TLS - -| Aspect | Status | Notes | -|--------|--------|-------| -| Cert source | 🟡 | cert-manager (Lego/Cloudflare DNS) exists but is untested/undocumented | -| Cert path convention | 🟢 | Let's Encrypt layout: `data/certs/live//fullchain.pem + privkey.pem` — matches both IRC and Prosody mount points | -| IRC cert paths | 🟢 | `IRC_SSL_CERT_PATH` in `.env.example` correctly points to `/home/unrealircd/unrealircd/certs/live/irc.atl.chat/fullchain.pem` | -| Prosody cert paths | 🟢 | `PROSODY_SSL_KEY/CERT` in `.env.example` are relative (`certs/live/localhost/...`) — **should be `certs/live/atl.chat/...` for prod** | -| XMPP nginx certs | 🟢 | `data/certs` mounted to nginx as `/etc/nginx/certs` | -| Cert renewal | 🔴 | No cron/timer for cert renewal. Lego container runs once and exits. No reload mechanism for IRC/Prosody after renewal. | -| Cert permissions | 🟢 | `init.sh` sets `chmod 644` on privkey.pem for container user access | - -### TLS Config Issues - -1. **Prosody `.env.example` cert paths say `localhost`:** - - ``` - PROSODY_SSL_KEY=certs/live/localhost/privkey.pem - PROSODY_SSL_CERT=certs/live/localhost/fullchain.pem - ``` - - This is the **dev default baked into the "production" example file**. Should be the prod domain. The `.env.dev` override correctly switches to `xmpp.localhost`. But if someone copies `.env.example` for prod and forgets to change these, Prosody won't find its certs. - -2. **No cert rotation/reload:** - When certs renew (every 60-90 days with Let's Encrypt), the running daemons need to be told: - - UnrealIRCd: `unrealircdctl rehash` or `kill -HUP` - - Prosody: `prosodyctl reload` - - The Lounge: restart container - - There's no hook for this. - ---- - -## DNS Audit - -### Dev DNS - -Dev uses `.localhost` TLD — `irc.localhost`, `xmpp.localhost`. These resolve to `127.0.0.1` on most systems (RFC 6761). No DNS setup needed. - -**However:** Inside Docker containers, `irc.localhost` does NOT resolve to the host — containers use Docker's internal DNS which only resolves container/service names. This is why the bridge uses Docker hostnames (`atl-irc-server`, `atl-xmpp-server`) instead of `irc.localhost`. - -| Aspect | Status | Notes | -|--------|--------|-------| -| Host access | 🟢 | `irc.localhost:6697` works from host machine | -| Container-to-container | 🟢 | Uses Docker service names (`atl-irc-server`) | -| Browser access | 🟢 | `http://localhost:3000` (web), `http://localhost:9000` (Lounge) | -| XMPP client access | 🟡 | `xmpp.localhost` resolves on host but clients may not accept `.localhost` as a valid domain | - -### Prod DNS - -**No DNS setup documentation exists.** For production, operators would need: - -| Record | Type | Target | Purpose | -|--------|------|--------|---------| -| `irc.atl.chat` | A/AAAA | server IP | IRC server | -| `xmpp.atl.chat` | A/AAAA | server IP | XMPP BOSH/WebSocket (via nginx) | -| `_xmpp-client._tcp.atl.chat` | SRV | `xmpp.atl.chat:5222` | XMPP C2S discovery | -| `_xmpps-client._tcp.atl.chat` | SRV | `xmpp.atl.chat:5223` | XMPP Direct TLS discovery | -| `_xmpp-server._tcp.atl.chat` | SRV | `xmpp.atl.chat:5269` | XMPP S2S federation | -| `_xmpps-server._tcp.atl.chat` | SRV | `xmpp.atl.chat:5270` | XMPP S2S Direct TLS | -| `muc.atl.chat` | CNAME | `xmpp.atl.chat` | MUC component | -| `upload.atl.chat` | CNAME | `xmpp.atl.chat` | HTTP file upload | -| `proxy.atl.chat` | CNAME | `xmpp.atl.chat` | SOCKS5 proxy | -| `pubsub.atl.chat` | CNAME | `xmpp.atl.chat` | PubSub | - ---- - -## Docker Audit - -### Profiles - -| Profile | What it activates | Command | -|---------|-------------------|---------| -| `dev` | Dozzle only | `just dev` | -| `prod` | Nothing — no services use this profile | `just prod` | -| (none) | All services except Dozzle | `docker compose up -d` | - -**Issue:** Prod profiles are empty. `just prod` is functionally identical to `docker compose up -d`. The profiles add no value. - -### Image Strategy - -| Service | Build | Image | -|---------|-------|-------| -| UnrealIRCd | `apps/unrealircd/Containerfile` | (local build) | -| Atheme | `apps/atheme/Containerfile` | (local build) | -| WebPanel | `apps/webpanel/Containerfile` | (local build) | -| Prosody | `apps/prosody/Containerfile` | `allthingslinux/prosody:latest` | -| XMPP Nginx | `infra/nginx/Dockerfile` | `allthingslinux/prosody-nginx:latest` | -| Bridge | `apps/bridge/Containerfile` | `ghcr.io/allthingslinux/bridge:latest` | -| The Lounge | (none) | `ghcr.io/thelounge/thelounge:latest` | -| Cert Manager | (none) | `goacme/lego:latest` | -| Dozzle | (none) | `amir20/dozzle:v8` | - -**Issue:** Services with both `build:` and `image:` will use the pulled image if it exists, not the local build. This caused the bridge disconnect bug during dev setup. For dev, you want local builds. For prod, you want published images. Currently there's no way to switch — you'd need to `docker compose build` explicitly. - -### Networking - -All services are on a single `atl-chat` bridge network. Atheme uses `network_mode: service:atl-irc-server` (shares network namespace with UnrealIRCd). This is correct — Atheme connects to UnrealIRCd on `127.0.0.1:6901`. - -### Port Binding - -Dev: `ATL_CHAT_IP=127.0.0.1` binds IRC ports to localhost only. -Prod: `ATL_CHAT_IP=100.64.7.0` (Tailscale IP). XMPP, WebPanel, Lounge bind to `0.0.0.0` (all interfaces). - -**Issue:** XMPP ports bind to `0.0.0.0` in both dev and prod (the xmpp.yaml doesn't use `ATL_CHAT_IP`). This means XMPP is publicly accessible even in dev. Not a security issue for localhost but inconsistent with IRC's approach. - ---- - -## Script Audit - -### `just dev` vs `just prod` asymmetry - -``` -just dev: - 1. Sources .env.dev ← yes - 2. Runs init.sh ← yes (creates dirs, generates certs, generates configs) - 3. Passes --env-file .env --env-file .env.dev ← yes - 4. Uses --profile dev ← yes - -just prod: - 1. Sources nothing ← no - 2. Runs nothing ← no init.sh - 3. Passes nothing ← no --env-file (relies on auto .env loading) - 4. Uses --profile prod ← meaningless (no services use this profile) -``` - -This is the biggest structural problem. A first-time prod deployment would: - -1. Clone repo -2. Create `.env` from `.env.example` -3. Run `just prod` -4. **Fail** because: - - No `data/` directories exist - - No configs generated from templates - - No certs generated or obtained - - Templates still have `${VAR}` placeholders - -### `prepare-config.sh` always sources `.env.dev` - -Line 57-63: The script unconditionally sources `.env.dev` if it exists. This means even if you're preparing prod configs, dev overrides would apply if `.env.dev` exists on the prod server. This is fine if `.env.dev` doesn't exist in prod, but it's a landmine. - ---- - -## Findings Summary - -### 🔴 CRITICAL - -1. **`just prod` doesn't run `init.sh`** — no dirs, no certs, no config generation -2. **No cert renewal mechanism** — certs will expire with no reload -3. **Prosody `.env.example` cert paths default to `localhost`** — must be manually changed for prod - -### 🟡 WARNING - -1. **Prod profiles are empty** — `just prod` is identical to bare `docker compose up` -2. **No prod deployment documentation** — DNS records, cert issuance, secrets, backups -3. **`prepare-config.sh` always loads `.env.dev`** if present — prod configs could get dev overrides -4. **XMPP ports bind to 0.0.0.0** — inconsistent with IRC's `ATL_CHAT_IP` binding -5. **Docker images: local build vs pulled image ambiguity** — no clear strategy - -### 🟢 OK - -1. Dev flow works smoothly end-to-end -2. Env overlay pattern (.env + .env.dev) is clean -3. Self-signed cert generation covers all needed SANs -4. Config templates use envsubst consistently -5. Docker networking (shared namespace for Atheme, single bridge network) is correct -6. Data directory structure is well-organized - -### 💡 SUGGESTIONS - -1. **Create `just prod` parity with `just dev`**: Run `init.sh`, pass `--env-file` -2. **Add `.env.prod.example`** for production-specific overrides -3. **Add cert renewal cron** in cert-manager + post-renewal reload hooks -4. **Add `docs/deployment.md`** with prod setup guide -5. **Add XMPP SRV record documentation** -6. **Consider `docker compose build --no-cache` step for prod image freshness** - ---- - -## Recommended Fix Priority - -1. Fix `just prod` to run init and load env files -2. Fix Prosody `.env.example` cert paths to use `${XMPP_DOMAIN}` not `localhost` -3. Add prod deployment docs (DNS, certs, secrets) -4. Add cert renewal mechanism -5. Clean up meaningless profiles diff --git a/docs/audits/env-var-audit.md b/docs/audits/env-var-audit.md deleted file mode 100644 index 2150fe16..00000000 --- a/docs/audits/env-var-audit.md +++ /dev/null @@ -1,513 +0,0 @@ -# Environment Variable Audit — atl.chat Monorepo - -> Generated: 2026-02-26 | Scope: Full 12-factor env var cleanup - ---- - -## Comprehensive Variable Table - -### 1. CORE PROJECT & ENVIRONMENT - -| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | -|----------|---------------|-------------------|---------------|-----------------|---------|------| -| `ATL_PROJECT_NAME` | ✅ `atl-chat` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `ATL_BASE_DOMAIN` | ✅ `atl.chat` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `ATL_ENVIRONMENT` | ✅ `dev` | ✅ `dev` | bridge.yaml | ❌ | prepare-config.sh | schema.py (`_ENV_OVERRIDE_KEYS`) | -| `PUID` | ✅ `1000` | ❌ | irc.yaml (build args, env), thelounge.yaml (user) | ❌ | ❌ | ❌ | -| `PGID` | ✅ `1000` | ❌ | irc.yaml (build args, env), thelounge.yaml (user) | ❌ | ❌ | ❌ | -| `TZ` | ✅ `UTC` | ❌ | irc.yaml (env) | ❌ | ❌ | ❌ | - -### 2. GLOBAL NETWORKING - -| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | -|----------|---------------|-------------------|---------------|-----------------|---------|------| -| `ATL_GATEWAY_IP` | ✅ `100.64.1.0` | ❌ | irc.yaml (env) | unrealircd.conf.template (`webirc mask`) | ❌ | ❌ | -| `ATL_CHAT_IP` | ✅ `100.64.7.0` | ✅ `127.0.0.1` | irc.yaml (ports), xmpp.yaml (indirectly via port bindings using defaults) | ❌ | ❌ | ❌ | -| `IRC_TLS_PORT` | ✅ `6697` | ❌ | irc.yaml (ports) | bridge config.template.yaml | ❌ | ❌ | -| `IRC_SERVER_PORT` | ✅ `6900` | ❌ | irc.yaml (ports) | ❌ | ❌ | ❌ | -| `IRC_RPC_PORT` | ✅ `8600` | ❌ | irc.yaml (ports) | ❌ | ❌ | ❌ | -| `IRC_WEBSOCKET_PORT` | ✅ `8000` | ❌ | irc.yaml (ports) | ❌ | ❌ | ❌ | -| `XMPP_C2S_PORT` | ✅ `5222` | ❌ | xmpp.yaml (ports, as `PROSODY_C2S_PORT` fallback) | ❌ | ❌ | ❌ | -| `XMPP_S2S_PORT` | ✅ `5269` | ❌ | xmpp.yaml (ports, as `PROSODY_S2S_PORT` fallback) | ❌ | ❌ | ❌ | -| `XMPP_HTTP_PORT` | ✅ `5280` | ❌ | xmpp.yaml (ports, as `PROSODY_HTTP_PORT` fallback) | ❌ | ❌ | ❌ | -| `XMPP_HTTPS_PORT` | ✅ `5281` | ❌ | xmpp.yaml (ports, as `PROSODY_HTTPS_PORT` fallback) | ❌ | ❌ | ❌ | -| `TURN_PORT` | ✅ `3478` | ❌ | ❌ | prosody.cfg.lua (`turn_external_port`) | ❌ | ❌ | -| `TURNS_PORT` | ✅ `5349` | ❌ | ❌ | prosody.cfg.lua (`turn_external_tls_port`) | ❌ | ❌ | - -### 3. IRC SERVICE (UnrealIRCd & Atheme) - -| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | -|----------|---------------|-------------------|---------------|-----------------|---------|------| -| `UNREALIRCD_VERSION` | ✅ `6.2.0.1` | ❌ | irc.yaml (build arg) | ❌ | ❌ | ❌ | -| `ATHEME_VERSION` | ✅ `master` | ❌ | irc.yaml (build arg) | ❌ | ❌ | ❌ | -| `IRC_DOMAIN` | ✅ `irc.atl.chat` | ✅ `irc.localhost` | irc.yaml (env) | unrealircd.conf.template (many), atheme.conf.template (uplink) | init.sh, prepare-config.sh | ❌ | -| `IRC_ROOT_DOMAIN` | ✅ `atl.chat` | ❌ | cert-manager.yaml (env) | ❌ | cert-manager/run.sh | ❌ | -| `IRC_NETWORK_NAME` | ✅ `"All Things Linux IRC"` | ❌ | ❌ | unrealircd.conf.template (`me`, `set`) | ❌ | thelounge config.js.template | -| `IRC_CLOAK_PREFIX` | ✅ `atl` | ❌ | ❌ | unrealircd.conf.template (`hiddenhost-prefix`) | prepare-config.sh | ❌ | -| `IRC_CLOAK_KEY_1` | ✅ (hash) | ❌ | ❌ | unrealircd.conf.template (`cloak-keys`) | prepare-config.sh | ❌ | -| `IRC_CLOAK_KEY_2` | ✅ (hash) | ❌ | ❌ | unrealircd.conf.template (`cloak-keys`) | prepare-config.sh | ❌ | -| `IRC_CLOAK_KEY_3` | ✅ (hash) | ❌ | ❌ | unrealircd.conf.template (`cloak-keys`) | prepare-config.sh | ❌ | -| `IRC_ADMIN_NAME` | ✅ `"All Things Linux"` | ❌ | ❌ | unrealircd.conf.template (`admin`) | init.sh, prepare-config.sh | ❌ | -| `IRC_ADMIN_EMAIL` | ✅ `admin@allthingslinux.org` | ❌ | ❌ | unrealircd.conf.template (`admin`, `kline-address`, `gline-address`) | init.sh, prepare-config.sh | ❌ | -| `IRC_STAFF_VHOST` | ✅ `allthingslinux.org` | ❌ | ❌ | unrealircd.conf.template (`oper admin vhost`) | ❌ | ❌ | -| `IRC_OPER_PASSWORD` | ✅ (argon2 hash) | ❌ | ❌ | unrealircd.conf.template (`oper admin password`) | prepare-config.sh | ❌ | -| `IRC_DRPASS` | ✅ `change_me_drpass` | ❌ | ❌ | unrealircd.conf.template (`drpass`) | ❌ | ❌ | -| `ATL_WEBIRC_PASSWORD` | ✅ `change_me_webirc_password` | ❌ | ❌ | unrealircd.conf.template (`webirc password`) | ❌ | ❌ | -| `IRC_STS_DURATION` | ✅ `1m` | ❌ | ❌ | unrealircd.conf.template (`sts-policy duration`) | ❌ | ❌ | -| `IRC_STS_PRELOAD` | ✅ `no` | ❌ | ❌ | unrealircd.conf.template (`sts-policy preload`) | ❌ | ❌ | -| `IRC_SSL_CERT_PATH` | ✅ (path) | ✅ (path) | irc.yaml (env) | ❌ | init.sh, prepare-config.sh | ❌ | -| `IRC_SSL_KEY_PATH` | ✅ (path) | ✅ (path) | irc.yaml (env) | ❌ | init.sh, prepare-config.sh | ❌ | -| `IRC_SERVICES_SERVER` | ✅ `services.atl.chat` | ❌ | ❌ | unrealircd.conf.template (`link`, `ulines`, `set services-server`, `sasl-server`) | prepare-config.sh | ❌ | -| `IRC_SERVICES_PASSWORD` | ✅ `change_me_secure_services_pass` | ❌ | ❌ | unrealircd.conf.template (`link password`), atheme.conf.template (`uplink send/receive_password`) | prepare-config.sh | ❌ | -| `ATHEME_SERVER_NAME` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template (`serverinfo name`) | init.sh, prepare-config.sh | ❌ | -| `ATHEME_SERVER_DESC` | ✅ `"All Things Linux IRC Services"` | ❌ | ❌ | atheme.conf.template (`serverinfo desc`) | ❌ | ❌ | -| `ATHEME_UPLINK_HOST` | ✅ `127.0.0.1` | ❌ | ❌ | atheme.conf.template (`uplink host`) | prepare-config.sh | ❌ | -| `ATHEME_UPLINK_PORT` | ✅ `6901` | ❌ | ❌ | atheme.conf.template (`uplink port`) | prepare-config.sh | ❌ | -| `ATHEME_UPLINK_SSL_PORT` | ✅ `6900` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `ATHEME_NUMERIC` | ✅ `00A` | ❌ | ❌ | atheme.conf.template (`serverinfo numeric`) | ❌ | ❌ | -| `ATHEME_RECONTIME` | ✅ `10` | ❌ | ❌ | atheme.conf.template (`serverinfo recontime`) | ❌ | ❌ | -| `ATHEME_LOG_LEVEL` | ✅ `all` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `ATHEME_HTTPD_PORT` | ✅ `8081` | ❌ | irc.yaml (ports) | atheme.conf.template (`httpd port`) | init.sh, prepare-config.sh | ❌ | -| `ATHEME_NETNAME` | ✅ `atl.chat` | ❌ | ❌ | atheme.conf.template (`serverinfo netname`) | init.sh | ❌ | -| `ATHEME_ADMIN_NAME` | ✅ `"All Things Linux"` | ❌ | ❌ | atheme.conf.template (`serverinfo adminname`) | init.sh | ❌ | -| `ATHEME_ADMIN_EMAIL` | ✅ `admin@allthingslinux.org` | ❌ | ❌ | atheme.conf.template (`serverinfo adminemail`) | init.sh | ❌ | -| `ATHEME_REGISTER_EMAIL` | ✅ `noreply@allthingslinux.org` | ❌ | ❌ | atheme.conf.template (`serverinfo registeremail`) | ❌ | ❌ | -| `ATHEME_HIDEHOST_SUFFIX` | ✅ `users.atl.chat` | ❌ | ❌ | atheme.conf.template (`serverinfo hidehostsuffix`) | ❌ | ❌ | -| `ATHEME_HELP_CHANNEL` | ✅ `#help` | ❌ | ❌ | atheme.conf.template (`general helpchan`) | ❌ | ❌ | -| `ATHEME_HELP_URL` | ✅ `https://discord.gg/linux` | ❌ | ❌ | atheme.conf.template (`general helpurl`) | ❌ | ❌ | -| `ATHEME_NICKSERV_NICK` | ✅ `NickServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_NICKSERV_USER` | ✅ `NickServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_NICKSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_NICKSERV_REAL` | ✅ `"Nickname Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_CHANSERV_NICK` | ✅ `ChanServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_CHANSERV_USER` | ✅ `ChanServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_CHANSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_CHANSERV_REAL` | ✅ `"Channel Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_OPERSERV_NICK` | ✅ `OperServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_OPERSERV_USER` | ✅ `OperServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_OPERSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_OPERSERV_REAL` | ✅ `"Operator Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_MEMOSERV_NICK` | ✅ `MemoServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_MEMOSERV_USER` | ✅ `MemoServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_MEMOSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_MEMOSERV_REAL` | ✅ `"Memo Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_SASLSERV_NICK` | ✅ `SaslServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_SASLSERV_USER` | ✅ `SaslServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_SASLSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_SASLSERV_REAL` | ✅ `"SASL Authentication Agent"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_BOTSERV_NICK` | ✅ `BotServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_BOTSERV_USER` | ✅ `BotServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_BOTSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_BOTSERV_REAL` | ✅ `"Bot Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_GROUPSERV_NICK` | ✅ `GroupServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_GROUPSERV_USER` | ✅ `GroupServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_GROUPSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_GROUPSERV_REAL` | ✅ `"Group Management Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_HOSTSERV_NICK` | ✅ `HostServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_HOSTSERV_USER` | ✅ `HostServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_HOSTSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_HOSTSERV_REAL` | ✅ `"Host Management Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_INFOSERV_NICK` | ✅ `InfoServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_INFOSERV_USER` | ✅ `InfoServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_INFOSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_INFOSERV_REAL` | ✅ `"Information Service"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_HELPSERV_NICK` | ✅ `HelpServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_HELPSERV_USER` | ✅ `HelpServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_HELPSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_HELPSERV_REAL` | ✅ `"Help Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_STATSERV_NICK` | ✅ `StatServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_STATSERV_USER` | ✅ `StatServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_STATSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_STATSERV_REAL` | ✅ `"Statistics Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_CHANFIX_NICK` | ✅ `ChanFix` | ❌ | ❌ | atheme.conf.template (commented out block) | ❌ | ❌ | -| `ATHEME_CHANFIX_USER` | ✅ `ChanFix` | ❌ | ❌ | atheme.conf.template (commented out block) | ❌ | ❌ | -| `ATHEME_CHANFIX_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template (commented out block) | ❌ | ❌ | -| `ATHEME_CHANFIX_REAL` | ✅ `"Channel Fixing Service"` | ❌ | ❌ | atheme.conf.template (commented out block) | ❌ | ❌ | -| `ATHEME_GLOBAL_NICK` | ✅ `Global` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_GLOBAL_USER` | ✅ `Global` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_GLOBAL_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_GLOBAL_REAL` | ✅ `"Network Announcements"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_ALIS_NICK` | ✅ `ALIS` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_ALIS_USER` | ✅ `alis` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_ALIS_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_ALIS_REAL` | ✅ `"Channel Directory"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_PROXYSCAN_NICK` | ✅ `Proxyscan` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_PROXYSCAN_USER` | ✅ `dnsbl` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_PROXYSCAN_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_PROXYSCAN_REAL` | ✅ `"Proxyscan Service"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_GAMESERV_NICK` | ✅ `GameServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_GAMESERV_USER` | ✅ `GameServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_GAMESERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_GAMESERV_REAL` | ✅ `"Game Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_RPGSERV_NICK` | ✅ `RPGServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_RPGSERV_USER` | ✅ `RPGServ` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_RPGSERV_HOST` | ✅ `services.atl.chat` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | -| `ATHEME_RPGSERV_REAL` | ✅ `"RPG Finding Services"` | ❌ | ❌ | atheme.conf.template | ❌ | ❌ | - -### 4. WEBPANEL & THE LOUNGE - -| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | -|----------|---------------|-------------------|---------------|-----------------|---------|------| -| `WEBPANEL_PORT` | ✅ `8080` | ❌ | irc.yaml (ports) | ❌ | ❌ | ❌ | -| `WEBPANEL_RPC_USER` | ✅ `adminpanel` | ❌ | ❌ | unrealircd.conf.template (`rpc-user`) | ❌ | ❌ | -| `WEBPANEL_RPC_PASSWORD` | ✅ `change_me_webpanel_password` | ❌ | ❌ | unrealircd.conf.template (`rpc-user password`) | ❌ | ❌ | -| `THELOUNGE_PORT` | ✅ `9000` | ✅ `9000` | thelounge.yaml (ports) | ❌ | ❌ | ❌ | -| `THELOUNGE_DOMAIN` | ✅ `webirc.atl.chat` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `THELOUNGE_WEBIRC_PASSWORD` | ✅ `change_me_thelounge_webirc` | ❌ | ❌ | unrealircd.conf.template (`proxy thelounge password`), config.js.template (`webirc`) | prepare-config.sh | ❌ | -| `THELOUNGE_DELETE_UPLOADS_AFTER_MINUTES` | ✅ `1440` | ❌ | ❌ | config.js.template (`deleteUploadsAfter`) | prepare-config.sh | ❌ | - -### 5. XMPP SERVICE (Prosody) - -| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | -|----------|---------------|-------------------|---------------|-----------------|---------|------| -| `XMPP_DOMAIN` | ✅ `atl.chat` | ✅ `xmpp.localhost` | xmpp.yaml (env: `PROSODY_DOMAIN=${XMPP_DOMAIN}`, `XMPP_DOMAIN` for nginx) | ❌ (mapped to PROSODY_DOMAIN) | prepare-config.sh | ❌ | -| `PROSODY_ADMIN_EMAIL` | ✅ `admin@allthingslinux.org` | ❌ | ❌ | prosody.cfg.lua (`contact_info`) | ❌ | ❌ | -| `PROSODY_ENV` | ✅ `development` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `PROSODY_DB_DRIVER` | ✅ `PostgreSQL` | ❌ | ❌ | ❌ | docker-entrypoint.sh (validation) | ❌ | -| `PROSODY_DB_HOST` | ✅ `xmpp-postgres-dev` | ❌ | ❌ | ❌ | docker-entrypoint.sh (pg_isready) | ❌ | -| `PROSODY_DB_PORT` | ✅ `5432` | ❌ | ❌ | ❌ | docker-entrypoint.sh | ❌ | -| `PROSODY_DB_NAME` | ✅ `prosody` | ❌ | ❌ | ❌ | docker-entrypoint.sh | ❌ | -| `PROSODY_DB_USER` | ✅ `prosody` | ❌ | ❌ | ❌ | docker-entrypoint.sh | ❌ | -| `PROSODY_DB_PASSWORD` | ✅ `change_me_secure_db_pass` | ❌ | ❌ | ❌ | docker-entrypoint.sh | ❌ | -| `PROSODY_ALLOW_REGISTRATION` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_C2S_REQUIRE_ENCRYPTION` | ✅ `true` | ✅ `false` | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_S2S_REQUIRE_ENCRYPTION` | ✅ `true` | ✅ `false` | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_S2S_SECURE_AUTH` | ✅ `true` | ✅ `false` | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_MAX_CONNECTIONS_PER_IP` | ✅ `10` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_REGISTRATION_THROTTLE_MAX` | ✅ `10` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_REGISTRATION_THROTTLE_PERIOD` | ✅ `60` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_BLOCK_REGISTRATIONS_REQUIRE` | ✅ `^[a-zA-Z0-9_.-]+$` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_TLS_CHANNEL_BINDING` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_ALLOW_UNENCRYPTED_PLAIN_AUTH` | ✅ `false` | ✅ `true` | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_HTTP_HOST` | ✅ `localhost` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_HTTP_SCHEME` | ✅ `http` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_SSL_KEY` | ✅ (path) | ✅ (path) | xmpp.yaml (env) | prosody.cfg.lua (multiple) | ❌ | ❌ | -| `PROSODY_SSL_CERT` | ✅ (path) | ✅ (path) | xmpp.yaml (env) | prosody.cfg.lua (multiple) | ❌ | ❌ | -| `PROSODY_LOG_LEVEL` | ✅ `info` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_STATISTICS` | ✅ `internal` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_STATISTICS_INTERVAL` | ✅ `manual` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_OPENMETRICS_IP` | ✅ `127.0.0.1` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_OPENMETRICS_CIDR` | ✅ `127.0.0.1/32` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_ARCHIVE_EXPIRES_AFTER` | ✅ `30d` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_ARCHIVE_POLICY` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_ARCHIVE_COMPRESSION` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_ARCHIVE_STORE` | ✅ `archive` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_ARCHIVE_MAX_QUERY_RESULTS` | ✅ `250` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_MAM_SMART_ENABLE` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_MUC_NOTIFICATIONS` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_MUC_OFFLINE_DELIVERY` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_RESTRICT_ROOM_CREATION` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_MUC_DEFAULT_PUBLIC` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_MUC_DEFAULT_PERSISTENT` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_MUC_DEFAULT_PUBLIC_JIDS` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_MUC_LOCKING` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_MUC_LOG_BY_DEFAULT` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_MUC_LOG_EXPIRES_AFTER` | ✅ `1y` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_MUC_LOG_PRESENCES` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_MUC_LOG_ALL_ROOMS` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_MUC_LOG_CLEANUP_INTERVAL` | ✅ `86400` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_MUC_MAX_ARCHIVE_QUERY_RESULTS` | ✅ `100` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_MUC_LOG_STORE` | ✅ `muc_log` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_MUC_LOG_COMPRESSION` | ✅ `true` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_MUC_MAM_SMART_ENABLE` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_C2S_RATE` | ✅ `10kb/s` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_C2S_BURST` | ✅ `25kb` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_C2S_STANZA_SIZE` | ✅ `262144` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_S2S_RATE` | ✅ `30kb/s` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_S2S_BURST` | ✅ `100kb` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_S2S_STANZA_SIZE` | ✅ `524288` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_HTTP_UPLOAD_RATE` | ✅ `2mb/s` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_HTTP_UPLOAD_BURST` | ✅ `10mb` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_PUSH_IMPORTANT_BODY` | ✅ `"New Message!"` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_PUSH_MAX_ERRORS` | ✅ `16` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_PUSH_MAX_DEVICES` | ✅ `5` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_PUSH_MAX_HIBERNATION_TIMEOUT` | ✅ `259200` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_PUSH_NOTIFICATION_WITH_BODY` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_PUSH_NOTIFICATION_WITH_SENDER` | ✅ `false` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_ACCOUNT_INACTIVE_PERIOD` | ✅ `31536000` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_ACCOUNT_GRACE_PERIOD` | ✅ `2592000` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_ACCOUNT_DELETION_CONFIRMATION` | ✅ `true` | ❌ | ❌ | ❌ (commented out in lua) | ❌ | ❌ | -| `PROSODY_SERVER_NAME` | ✅ `localhost` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_SERVER_WEBSITE` | ✅ `http://localhost` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_SERVER_DESCRIPTION` | ✅ `"XMPP Service"` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `LUA_GC_STEP_SIZE` | ✅ `13` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `LUA_GC_PAUSE` | ✅ `110` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `LUA_GC_SPEED` | ✅ `200` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `LUA_GC_THRESHOLD` | ✅ `120` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_UPLOAD_EXTERNAL_URL` | ✅ `http://localhost:5280/upload/` | ✅ `https://xmpp.localhost:5281/` | xmpp.yaml (env) | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_PROXY_ADDRESS` | ✅ `localhost` | ✅ `xmpp.localhost` | xmpp.yaml (env) | prosody.cfg.lua | ❌ | ❌ | -| `PROSODY_FEED_URL` | ✅ `https://allthingslinux.org/feed` | ❌ | ❌ | prosody.cfg.lua | ❌ | ❌ | - -### 6. DATABASE (PostgreSQL) - -| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | -|----------|---------------|-------------------|---------------|-----------------|---------|------| -| `POSTGRES_USER` | ✅ `prosody` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `POSTGRES_DB` | ✅ `prosody` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `POSTGRES_PASSWORD` | ✅ `change_me_secure_db_pass` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `POSTGRES_SHARED_BUFFERS` | ✅ `32MB` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `POSTGRES_EFFECTIVE_CACHE_SIZE` | ✅ `128MB` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `POSTGRES_WORK_MEM` | ✅ `1MB` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `POSTGRES_MAINTENANCE_WORK_MEM` | ✅ `16MB` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `POSTGRES_CHECKPOINT_COMPLETION_TARGET` | ✅ `0.9` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `POSTGRES_WAL_BUFFERS` | ✅ `4MB` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `POSTGRES_DEFAULT_STATISTICS_TARGET` | ✅ `50` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `POSTGRES_RANDOM_PAGE_COST` | ✅ `1.1` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `POSTGRES_EFFECTIVE_IO_CONCURRENCY` | ✅ `100` | ❌ | ❌ | ❌ | ❌ | ❌ | - -### 7. ADMINER - -| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | -|----------|---------------|-------------------|---------------|-----------------|---------|------| -| `ADMINER_PORT` | ✅ `8080` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `ADMINER_AUTO_LOGIN` | ✅ `false` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `ADMINER_DEFAULT_DRIVER` | ✅ `pgsql` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `ADMINER_DEFAULT_SERVER` | ✅ `xmpp-postgres` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `ADMINER_DEFAULT_DB` | ✅ `prosody` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `ADMINER_DEFAULT_USERNAME` | ✅ `prosody` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `ADMINER_DEFAULT_PASSWORD` | ✅ `change_me_secure_db_pass` | ❌ | ❌ | ❌ | ❌ | ❌ | - -### 8. NGINX REVERSE PROXY - -| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | -|----------|---------------|-------------------|---------------|-----------------|---------|------| -| `NGINX_HTTP_PORT` | ✅ `80` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `NGINX_HTTPS_PORT` | ✅ `443` | ❌ | ❌ | ❌ | ❌ | ❌ | - -### 9. EXTERNAL INTEGRATIONS - -| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | -|----------|---------------|-------------------|---------------|-----------------|---------|------| -| `ATL_SENTRY_DSN` | ✅ (empty) | ❌ | ❌ | ❌ | ❌ | ❌ | -| `ATL_INTERNAL_SECRET_IRC` | ✅ (empty) | ❌ | ❌ | ❌ | ❌ | ❌ | -| `ATL_INTERNAL_SECRET_XMPP` | ✅ (empty) | ❌ | ❌ | ❌ | ❌ | ❌ | -| `LETSENCRYPT_EMAIL` | ✅ `admin@allthingslinux.org` | ❌ | cert-manager.yaml (env) | ❌ | cert-manager/run.sh | ❌ | - -### 10. BRIDGE SERVICE - -| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | -|----------|---------------|-------------------|---------------|-----------------|---------|------| -| `BRIDGE_DISCORD_TOKEN` | ✅ `change_me_discord_bot_token` | ❌ | bridge.yaml (env) | ❌ | ❌ | `__main__.py`, `discord/adapter.py` | -| `BRIDGE_DISCORD_CHANNEL_ID` | ✅ `REPLACE_WITH_DISCORD_CHANNEL_ID` | ❌ | ❌ | config.template.yaml | prepare-config.sh | ❌ | -| `BRIDGE_PORTAL_BASE_URL` | ✅ `https://portal.atl.tools` | ✅ (empty) | bridge.yaml (env) | ❌ | ❌ | `__main__.py` | -| `BRIDGE_PORTAL_TOKEN` | ✅ `change_me_bridge_portal_token` | ❌ | bridge.yaml (env) | ❌ | ❌ | `__main__.py` | -| `BRIDGE_XMPP_COMPONENT_JID` | ✅ `bridge.atl.chat` | ✅ `bridge.xmpp.localhost` | bridge.yaml (env) | ❌ | prepare-config.sh | `xmpp/adapter.py` | -| `BRIDGE_XMPP_COMPONENT_SECRET` | ✅ `change_me_xmpp_component_secret` | ❌ | bridge.yaml (env) | prosody.cfg.lua | ❌ | `xmpp/adapter.py` | -| `BRIDGE_XMPP_COMPONENT_SERVER` | ✅ `atl-xmpp-server` | ✅ `atl-xmpp-server` | bridge.yaml (env) | ❌ | ❌ | `xmpp/adapter.py` | -| `BRIDGE_XMPP_COMPONENT_PORT` | ✅ `5347` | ✅ `5347` | bridge.yaml (env) | ❌ | ❌ | `xmpp/adapter.py` | -| `BRIDGE_IRC_NICK` | ✅ `bridge` | ❌ | bridge.yaml (env) | ❌ | ❌ | `irc/adapter.py` | -| `BRIDGE_IRC_OPER_PASSWORD` | ✅ `change_me_bridge_oper` | ❌ | bridge.yaml (env) | unrealircd.conf.template (`oper bridge`) | prepare-config.sh | `irc/client.py` | -| `IRC_BRIDGE_SERVER` | ✅ `atl-irc-server` | ✅ `atl-irc-server` | ❌ | config.template.yaml | prepare-config.sh | ❌ | -| `BRIDGE_IRC_TLS_VERIFY` | ❌ | ✅ `false` | bridge.yaml (env) | ❌ | prepare-config.sh | schema.py (`_ENV_OVERRIDE_KEYS`) | -| `BRIDGE_RELAYMSG_CLEAN_NICKS` | ❌ | ✅ `true` | bridge.yaml (env) | ❌ | ❌ | schema.py (`_ENV_OVERRIDE_KEYS`) | -| `LOG_LEVEL` | ❌ (commented) | ❌ (commented) | bridge.yaml (env) | ❌ | ❌ | `__main__.py` | - -### 11. PORTAL INTEGRATION - -| Variable | `.env.example` | `.env.dev.example` | Compose files | Config templates | Scripts | Code | -|----------|---------------|-------------------|---------------|-----------------|---------|------| -| `IRC_ATHEME_JSONRPC_URL` | ✅ `http://atl-irc-server:8081/jsonrpc` | ✅ (same) | ❌ | ❌ | ❌ | ❌ | -| `IRC_UNREAL_JSONRPC_URL` | ✅ `https://irc.atl.chat:8600/api` | ✅ `https://irc.localhost:8600/api` | ❌ | ❌ | ❌ | ❌ | -| `IRC_UNREAL_RPC_USER` | ✅ `${WEBPANEL_RPC_USER}` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `IRC_UNREAL_RPC_PASSWORD` | ✅ `${WEBPANEL_RPC_PASSWORD}` | ❌ | ❌ | ❌ | ❌ | ❌ | -| `PROSODY_REST_URL` | ✅ `http://atl-xmpp-server:5280` | ✅ (same) | ❌ | ❌ | ❌ | ❌ | -| `PROSODY_REST_USERNAME` | ✅ `admin@atl.chat` | ✅ `admin@xmpp.localhost` | ❌ | ❌ | ❌ | ❌ | -| `PROSODY_REST_PASSWORD` | ✅ `change_me_prosody_rest_password` | ✅ (same) | ❌ | ❌ | ❌ | ❌ | -| `IRC_SERVER` | ✅ `irc.atl.chat` | ✅ `irc.localhost` | ❌ | ❌ | ❌ | ❌ | -| `IRC_PORT` | ✅ `6697` | ✅ `6697` | ❌ | ❌ | ❌ | ❌ | - -### 12. WEB FRONTEND (Next.js) - -| Variable | `.env.example` (root) | `.env.dev.example` | `apps/web/.env.example` | Compose files | Code | -|----------|----------------------|-------------------|------------------------|---------------|------| -| `NEXT_PUBLIC_IRC_WS_URL` | ✅ `wss://irc.atl.chat/ws` | ✅ `wss://irc.localhost/ws` | ✅ `wss://irc.atl.chat/ws` | ❌ | justfile (hardcoded override) | -| `NEXT_PUBLIC_XMPP_BOSH_URL` | ✅ `https://xmpp.atl.chat/http-bind` | ✅ `https://xmpp.localhost/http-bind` | ✅ `https://xmpp.atl.chat/http-bind` | ❌ | justfile (hardcoded override) | -| `NEXT_PUBLIC_ATL_BASE_DOMAIN` | ❌ | ❌ | ✅ `atl.chat` | ❌ | ❌ | -| `NEXT_PUBLIC_ATL_ENVIRONMENT` | ❌ | ❌ | ✅ `development` | ❌ | ❌ | -| `NEXT_PUBLIC_SENTRY_DSN` | ❌ | ❌ | ✅ (empty) | ❌ | ❌ | - ---- - -## Categorized Findings - -### Category A: ORPHANED — Defined in `.env.example` but never consumed - -| Variable | Notes | -|----------|-------| -| `ATL_PROJECT_NAME` | Defined as `atl-chat`. Not referenced in any compose, config template, script, or code. Docker Compose `name: atl-chat` is hardcoded in compose.yaml. | -| `ATL_BASE_DOMAIN` | Defined as `atl.chat`. Not referenced anywhere. Individual domains (`IRC_DOMAIN`, `XMPP_DOMAIN`) are used instead. | -| `PROSODY_ENV` | Defined as `development`. Not referenced in any compose file, Lua config, script, or code. `ATL_ENVIRONMENT` is the actual env discriminator. | -| `ATHEME_UPLINK_SSL_PORT` | Defined as `6900`. Never consumed in any template or compose file. The atheme.conf.template uses `ATHEME_UPLINK_PORT` (6901, plaintext). | -| `ATHEME_LOG_LEVEL` | Defined as `all`. Not referenced in atheme.conf.template (log level is hardcoded: `logfile "logs/atheme.log" { debug; };`). | -| `POSTGRES_USER` | Defined as `prosody`. No PostgreSQL service exists in compose (removed; dev uses SQLite). Not consumed by any running service. | -| `POSTGRES_DB` | Same as above. | -| `POSTGRES_PASSWORD` | Same as above. | -| `POSTGRES_SHARED_BUFFERS` | No PostgreSQL compose service exists. | -| `POSTGRES_EFFECTIVE_CACHE_SIZE` | No PostgreSQL compose service exists. | -| `POSTGRES_WORK_MEM` | No PostgreSQL compose service exists. | -| `POSTGRES_MAINTENANCE_WORK_MEM` | No PostgreSQL compose service exists. | -| `POSTGRES_CHECKPOINT_COMPLETION_TARGET` | No PostgreSQL compose service exists. | -| `POSTGRES_WAL_BUFFERS` | No PostgreSQL compose service exists. | -| `POSTGRES_DEFAULT_STATISTICS_TARGET` | No PostgreSQL compose service exists. | -| `POSTGRES_RANDOM_PAGE_COST` | No PostgreSQL compose service exists. | -| `POSTGRES_EFFECTIVE_IO_CONCURRENCY` | No PostgreSQL compose service exists. | -| `ADMINER_PORT` | No Adminer service in compose files. | -| `ADMINER_AUTO_LOGIN` | No Adminer service in compose files. | -| `ADMINER_DEFAULT_DRIVER` | No Adminer service in compose files. | -| `ADMINER_DEFAULT_SERVER` | No Adminer service in compose files. | -| `ADMINER_DEFAULT_DB` | No Adminer service in compose files. | -| `ADMINER_DEFAULT_USERNAME` | No Adminer service in compose files. | -| `ADMINER_DEFAULT_PASSWORD` | No Adminer service in compose files. | -| `NGINX_HTTP_PORT` | No nginx reverse proxy service in compose files (only xmpp-nginx exists, which uses `PROSODY_HTTPS_PORT`). | -| `NGINX_HTTPS_PORT` | Same as above. | -| `ATL_SENTRY_DSN` | Defined (empty) but not referenced in any compose, config, script, or code in this monorepo. | -| `ATL_INTERNAL_SECRET_IRC` | Defined (empty) but not referenced anywhere. Intended for future Portal integration. | -| `ATL_INTERNAL_SECRET_XMPP` | Defined (empty) but not referenced anywhere. Intended for future Portal integration. | -| `THELOUNGE_DOMAIN` | Defined as `webirc.atl.chat`. Not referenced in any compose file, config template, script, or code. | -| `ATHEME_CHANFIX_NICK/USER/HOST/REAL` | Defined in `.env.example` but only consumed in a **commented-out** block in atheme.conf.template. The ChanFix module is disabled. | -| `IRC_ATHEME_JSONRPC_URL` | Defined in `.env.example` and `.env.dev.example`. Not consumed by any service in this monorepo — intended for an external Portal service. | -| `IRC_UNREAL_JSONRPC_URL` | Same — intended for external Portal. | -| `IRC_UNREAL_RPC_USER` | Same — intended for external Portal. Uses `${WEBPANEL_RPC_USER}` interpolation in `.env.example` which is unusual. | -| `IRC_UNREAL_RPC_PASSWORD` | Same — intended for external Portal. | -| `PROSODY_REST_URL` | Same — intended for external Portal. | -| `PROSODY_REST_USERNAME` | Same — intended for external Portal. | -| `PROSODY_REST_PASSWORD` | Same — intended for external Portal. | -| `IRC_SERVER` | Same — intended for external Portal. | -| `IRC_PORT` | Same — intended for external Portal. | -| `PROSODY_ACCOUNT_DELETION_CONFIRMATION` | Defined as `true`. The Lua code block that consumes it is **commented out** in prosody.cfg.lua. | - -**Total: 37 orphaned variables** (though ~11 Portal vars are intentionally pre-defined for external consumers) - -### Category B: UNDEFINED — Consumed in config/compose but NOT defined in `.env.example` - -| Variable | Where consumed | Notes | -|----------|---------------|-------| -| `PROSODY_DOMAIN` | prosody.cfg.lua, docker-entrypoint.sh, config.template.yaml, init.sh, prepare-config.sh | **Critical.** Not in `.env.example`. Derived in prepare-config.sh as `${PROSODY_DOMAIN:-${XMPP_DOMAIN:-xmpp.localhost}}`. Set in `.env.dev.example` as `xmpp.localhost`. Compose sets it from `XMPP_DOMAIN`. This is intentional (derived var) but confusing. | -| `PROSODY_HTTPS_VIA_PROXY` | xmpp.yaml (env), prosody.cfg.lua | Not in `.env.example`. Compose sets default `false`. Controls whether nginx handles HTTPS. | -| `PROSODY_HTTP_EXTERNAL_URL` | xmpp.yaml (env), prosody.cfg.lua | Not in `.env.example`. Set in `.env.dev.example`. Important for Converse.js dev access. | -| `PROSODY_C2S_DIRECT_TLS_PORT` | xmpp.yaml (ports, with default `5223`) | Not in `.env.example`. Port 5223 (direct TLS). | -| `PROSODY_S2S_DIRECT_TLS_PORT` | xmpp.yaml (ports, with default `5270`) | Not in `.env.example`. Port 5270 (direct s2s TLS). | -| `PROSODY_PROXY65_PORT` | xmpp.yaml (ports, with default `5000`) | Not in `.env.example`. SOCKS5 proxy port. | -| `PROSODY_C2S_PORT` | xmpp.yaml (ports, with default `5222`) | Not in `.env.example` as `PROSODY_C2S_PORT`. `XMPP_C2S_PORT` exists but compose uses `PROSODY_C2S_PORT`. **Name mismatch.** | -| `PROSODY_S2S_PORT` | xmpp.yaml (ports, with default `5269`) | Same issue. `XMPP_S2S_PORT` defined, compose uses `PROSODY_S2S_PORT`. | -| `PROSODY_HTTP_PORT` | xmpp.yaml (ports, with default `5280`) | Same. `XMPP_HTTP_PORT` defined, compose uses `PROSODY_HTTP_PORT`. | -| `PROSODY_HTTPS_PORT` | xmpp.yaml (ports, with default `5281`) | Same. `XMPP_HTTPS_PORT` defined, compose uses `PROSODY_HTTPS_PORT`. | -| `PROSODY_STORAGE` | docker-entrypoint.sh | Not in `.env.example`. Set in `.env.dev.example` as `sqlite`. Controls Prosody storage backend. | -| `LOG_MAX_SIZE` | xmpp.yaml (logging) | Not in `.env.example`. Defaults to `50m`. | -| `LOG_MAX_FILES` | xmpp.yaml (logging) | Not in `.env.example`. Defaults to `5`. | -| `DOZZLE_PORT` | compose.yaml | Not in `.env.example`. Defaults to `8082`. Dev-only (Dozzle log viewer). | -| `CLOUDFLARE_DNS_API_TOKEN` | cert-manager.yaml (env), cert-manager/run.sh | Not in `.env.example`. Required for Let's Encrypt cert issuance. | -| `SSL_DOMAIN` | cert-manager.yaml (env), cert-manager/run.sh | Not in `.env.example`. Optional override for cert domain. | -| `IRC_LOG_PATH` | unrealircd.conf.template (log destination), prepare-config.sh | Not in `.env.example`. Defaults to `/home/unrealircd/unrealircd/logs` in prepare-config.sh. | -| `IRC_TLS_VERIFY` | config.template.yaml (`irc_tls_verify`) | Not in `.env.example`. Derived in prepare-config.sh from `BRIDGE_IRC_TLS_VERIFY` / `ATL_ENVIRONMENT`. | -| `IRC_LOUNGE_REJECT_UNAUTHORIZED` | config.js.template (`rejectUnauthorized`) | Not in `.env.example`. Derived in prepare-config.sh from `ATL_ENVIRONMENT`. | -| `TURN_SECRET` | prosody.cfg.lua (`turn_external_secret`) | Not in `.env.example`. Defaults to `devsecret`. Required for production TURN server auth. | -| `TURN_EXTERNAL_HOST` | prosody.cfg.lua (`turn_external_host`) | Not in `.env.example`. Defaults to `turn.atl.network`. | -| `PROSODY_ADMIN_JID` | prosody.cfg.lua, docker-entrypoint.sh | Not in `.env.example`. Derived from `PROSODY_DOMAIN` in entrypoint. | -| `PROSODY_SUPPORT_CONTACT` | prosody.cfg.lua | Not in `.env.example`. Defaults to `support@{domain}`. | -| `PROSODY_SUPPORT_CONTACT_NICK` | prosody.cfg.lua | Not in `.env.example`. Defaults to `"Support"`. | -| `XMPP_AVATAR_BASE_URL` | bridge.yaml (env) | Not in `.env.example`. Set in `.env.dev.example`. Bridge needs internal URL to HEAD-check avatars. | -| `XMPP_UPLOAD_FETCH_URL` | bridge.yaml (env) | Not in `.env.example`. Set in `.env.dev.example`. Bridge rewrites XMPP upload URLs. | -| `BRIDGE_IRC_REDACT_ENABLED` | schema.py (`_ENV_OVERRIDE_KEYS`) | Not in `.env.example`. Env override for IRC REDACT feature toggle. | -| `BRIDGE_DEV_IRC_PUPPETS` | `__main__.py` | Not in `.env.example`. Mentioned (commented) in `.env.dev.example`. Enables IRC puppets without Portal. | -| `BRIDGE_DEV_IRC_NICK_MAP` | `identity/dev.py` | Not in `.env.example`. Mentioned (commented) in `.env.dev.example`. Maps Discord IDs to IRC nicks for dev. | -| `BRIDGE_PORTAL_URL` | `__main__.py` (legacy alias) | Not in `.env.example`. Legacy alias for `BRIDGE_PORTAL_BASE_URL`. | -| `BRIDGE_PORTAL_API_TOKEN` | `__main__.py` (legacy alias) | Not in `.env.example`. Legacy alias for `BRIDGE_PORTAL_TOKEN`. | -| `XMPP_COMPONENT_SECRET` | prosody.cfg.lua (fallback) | Not in `.env.example`. Legacy alias for `BRIDGE_XMPP_COMPONENT_SECRET`. | -| `IRC_PUPPET_IDLE_TIMEOUT_HOURS` | `irc/adapter.py` | Not in `.env.example`. Env override; defaults to `24`. Config YAML property `irc_puppet_idle_timeout_hours` is the primary. | -| `CERT_DIR` | xmpp-nginx docker-entrypoint.sh, prosody-https.conf.template | Not in `.env.example`. Hardcoded as env in xmpp.yaml nginx: `CERT_DIR=/etc/nginx/certs`. | - -### Category C: INCONSISTENT — Same variable referenced differently - -| Issue | Details | -|-------|---------| -| **XMPP port naming mismatch** | `.env.example` defines `XMPP_C2S_PORT`, `XMPP_S2S_PORT`, `XMPP_HTTP_PORT`, `XMPP_HTTPS_PORT`. But `xmpp.yaml` uses `PROSODY_C2S_PORT`, `PROSODY_S2S_PORT`, `PROSODY_HTTP_PORT`, `PROSODY_HTTPS_PORT` with fallback defaults. The `XMPP_*_PORT` vars are defined but **never consumed** by compose — the `PROSODY_*_PORT` names are what actually work. | -| **XMPP_DOMAIN vs PROSODY_DOMAIN** | `.env.example` defines `XMPP_DOMAIN`. Compose passes it as `PROSODY_DOMAIN=${XMPP_DOMAIN}`. Scripts derive `PROSODY_DOMAIN` from `XMPP_DOMAIN`. Prosody code only reads `PROSODY_DOMAIN`. Two names for one value; `.env.dev.example` defines **both** separately (`XMPP_DOMAIN=xmpp.localhost` and `PROSODY_DOMAIN=xmpp.localhost`). | -| **BRIDGE_PORTAL_URL vs BRIDGE_PORTAL_BASE_URL** | Code checks both `BRIDGE_PORTAL_BASE_URL` and `BRIDGE_PORTAL_URL` (legacy). Only `BRIDGE_PORTAL_BASE_URL` is in `.env.example`. | -| **BRIDGE_PORTAL_TOKEN vs BRIDGE_PORTAL_API_TOKEN** | Code checks both `BRIDGE_PORTAL_TOKEN` and `BRIDGE_PORTAL_API_TOKEN` (legacy). Only `BRIDGE_PORTAL_TOKEN` is in `.env.example`. | -| **BRIDGE_XMPP_COMPONENT_SECRET vs XMPP_COMPONENT_SECRET** | prosody.cfg.lua checks both `BRIDGE_XMPP_COMPONENT_SECRET` and `XMPP_COMPONENT_SECRET` (legacy fallback). Only the `BRIDGE_` prefixed version is in `.env.example`. | -| **IRC_UNREAL_RPC_USER/PASSWORD uses var interpolation in .env.example** | `IRC_UNREAL_RPC_USER=${WEBPANEL_RPC_USER}` — this relies on shell expansion when `.env` is sourced, but Docker Compose does NOT expand `${VAR}` references inside `.env` files. This means these vars will literally be set to the string `${WEBPANEL_RPC_USER}` when loaded by Compose. | - -### Category D: HARDCODED — Values that should be env vars but are hardcoded - -| Location | Hardcoded value | Should be | -|----------|----------------|-----------| -| `unrealircd.conf.template` | `me { sid "001"; }` | Consider `IRC_SERVER_SID` env var for multi-server setups | -| `unrealircd.conf.template` | `help-channel "#support"` | Could use `IRC_HELP_CHANNEL` | -| `unrealircd.conf.template` | `oper-auto-join "#mod-chat"` | Could use `IRC_OPER_CHANNEL` | -| `unrealircd.conf.template` | `maxchannelsperuser 10` | Could use `IRC_MAX_CHANNELS_PER_USER` | -| `unrealircd.conf.template` | `maxperip 5` (allow block) | Could use `IRC_MAX_PER_IP` | -| `config.js.template` | `host: "atl-irc-server"` | Could use `IRC_BRIDGE_SERVER` or a `THELOUNGE_IRC_HOST` var | -| `config.js.template` | `port: 6697` | Could use `IRC_TLS_PORT` | -| `config.js.template` | `join: "#help"` | Could use `THELOUNGE_DEFAULT_CHANNEL` | -| `prosody.cfg.lua` | `default_storage = "sql"` + SQLite3 config | Hardcoded to SQLite; the `PROSODY_DB_*` vars in `.env.example` suggest PostgreSQL should be configurable via env. The entrypoint switches based on `PROSODY_STORAGE` but the Lua config itself doesn't read it. | -| `compose.yaml` | `name: atl-chat` | Could use `ATL_PROJECT_NAME` | -| `xmpp.yaml` | `image: allthingslinux/prosody:latest` | Could use a versioned tag env var | -| Various | Docker hostnames like `atl-irc-server`, `atl-xmpp-server` | Hardcoded in multiple config templates; should be consistent env vars if they ever need to change | - -### Category E: DEAD — Defined and maybe partially referenced but serve no actual purpose - -| Variable | Reason | -|----------|--------| -| `PROSODY_ENV` | Defined as `development`. Nothing reads it. `ATL_ENVIRONMENT` is the actual environment discriminator used by bridge and scripts. | -| `ATHEME_LOG_LEVEL` | Defined as `all`. Atheme config hardcodes `logfile "logs/atheme.log" { debug; };` — doesn't read this env var. | -| `ATHEME_UPLINK_SSL_PORT` | Defined as `6900`. Atheme connects via plaintext port 6901 (`ATHEME_UPLINK_PORT`); the SSL variant is never used since Atheme connects via localhost/Docker network. | -| `ATHEME_CHANFIX_*` (4 vars) | ChanFix module is commented out in atheme.conf.template. These vars are substituted into a dead code block. | -| `PROSODY_ACCOUNT_DELETION_CONFIRMATION` | The Lua code block using it is commented out. | -| All `POSTGRES_*` vars (12 vars) | No PostgreSQL service exists in any compose file. Dev uses SQLite (`PROSODY_STORAGE=sqlite`). The `PROSODY_DB_*` vars exist for when Postgres is enabled, but the `POSTGRES_*` vars (standard Docker image vars) have no compose service to consume them. | -| All `ADMINER_*` vars (7 vars) | No Adminer service in compose files. | -| `NGINX_HTTP_PORT`, `NGINX_HTTPS_PORT` | No main nginx reverse proxy in compose. Only xmpp-nginx exists (different service). | -| `XMPP_C2S_PORT` | Name defined in `.env.example`, but compose uses `PROSODY_C2S_PORT` instead. | -| `XMPP_S2S_PORT` | Same mismatch. | -| `XMPP_HTTP_PORT` | Same mismatch. | -| `XMPP_HTTPS_PORT` | Same mismatch. | - -### Category F: DUPLICATE — Same logical setting under multiple variable names - -| Logical Setting | Variable Names | Notes | -|----------------|----------------|-------| -| XMPP domain | `XMPP_DOMAIN`, `PROSODY_DOMAIN` | `XMPP_DOMAIN` in `.env.example`, compose maps to `PROSODY_DOMAIN`. Both defined separately in `.env.dev.example`. | -| XMPP C2S port | `XMPP_C2S_PORT`, `PROSODY_C2S_PORT` | `.env.example` defines `XMPP_C2S_PORT`; compose uses `PROSODY_C2S_PORT`. | -| XMPP S2S port | `XMPP_S2S_PORT`, `PROSODY_S2S_PORT` | Same pattern. | -| XMPP HTTP port | `XMPP_HTTP_PORT`, `PROSODY_HTTP_PORT` | Same pattern. | -| XMPP HTTPS port | `XMPP_HTTPS_PORT`, `PROSODY_HTTPS_PORT` | Same pattern. | -| Portal API URL | `BRIDGE_PORTAL_BASE_URL`, `BRIDGE_PORTAL_URL` | Code accepts both (legacy compat). | -| Portal API token | `BRIDGE_PORTAL_TOKEN`, `BRIDGE_PORTAL_API_TOKEN` | Code accepts both (legacy compat). | -| XMPP component secret | `BRIDGE_XMPP_COMPONENT_SECRET`, `XMPP_COMPONENT_SECRET` | Prosody Lua accepts both (legacy fallback). | -| RPC credentials | `WEBPANEL_RPC_USER` / `IRC_UNREAL_RPC_USER` | Same value, two names. | -| RPC credentials | `WEBPANEL_RPC_PASSWORD` / `IRC_UNREAL_RPC_PASSWORD` | Same value, two names. | -| IRC TLS port | `IRC_TLS_PORT` / `IRC_PORT` | Both `6697`. `IRC_TLS_PORT` used by compose ports; `IRC_PORT` used by Portal section. | -| DB password | `PROSODY_DB_PASSWORD` / `POSTGRES_PASSWORD` / `ADMINER_DEFAULT_PASSWORD` | All default to `change_me_secure_db_pass`. | -| DB user | `PROSODY_DB_USER` / `POSTGRES_USER` / `ADMINER_DEFAULT_USERNAME` | All `prosody`. | -| DB name | `PROSODY_DB_NAME` / `POSTGRES_DB` / `ADMINER_DEFAULT_DB` | All `prosody`. | - ---- - -## Summary Statistics - -| Category | Count | Severity | -|----------|-------|----------| -| **A: Orphaned** | 37 vars | Medium — clutters .env.example, confuses operators | -| **B: Undefined** | 33 vars | High — some are critical (CLOUDFLARE_DNS_API_TOKEN, TURN_SECRET, PROSODY_DOMAIN) | -| **C: Inconsistent** | 6 issues | High — port naming mismatch causes vars to be silently ignored | -| **D: Hardcoded** | 12 items | Low-Medium — reduces configurability | -| **E: Dead** | ~30 vars | Medium — waste of .env.example real estate | -| **F: Duplicate** | 14 pairs | Medium — confusing, error-prone | - -## Recommended Actions (Priority Order) - -1. **Fix port naming mismatch (C):** Rename `XMPP_C2S_PORT` → `PROSODY_C2S_PORT` (etc.) in `.env.example`, OR update `xmpp.yaml` to use `XMPP_*` names. Currently the defined vars are **silently ignored**. - -2. **Add critical undefined vars to `.env.example` (B):** `CLOUDFLARE_DNS_API_TOKEN`, `TURN_SECRET`, `TURN_EXTERNAL_HOST`, `PROSODY_HTTPS_VIA_PROXY`, `PROSODY_HTTP_EXTERNAL_URL`, `PROSODY_STORAGE`, `DOZZLE_PORT`, `LOG_MAX_SIZE`, `LOG_MAX_FILES`, `XMPP_AVATAR_BASE_URL`, `XMPP_UPLOAD_FETCH_URL`, `IRC_LOG_PATH`, `BRIDGE_IRC_TLS_VERIFY`, `BRIDGE_RELAYMSG_CLEAN_NICKS`. - -3. **Remove dead PostgreSQL/Adminer/Nginx sections (A+E):** 21 vars for services with no compose definition. Move to a `compose.postgres.yaml` fragment or remove entirely. - -4. **Fix `.env` variable interpolation (C):** `IRC_UNREAL_RPC_USER=${WEBPANEL_RPC_USER}` won't expand in Docker Compose `.env` loading. Either hardcode the values or remove the indirection. - -5. **Consolidate XMPP_DOMAIN/PROSODY_DOMAIN (F):** Pick one canonical name. Recommendation: use `XMPP_DOMAIN` in `.env.example` and let compose/scripts derive `PROSODY_DOMAIN` internally. - -6. **Remove legacy alias support (F):** `BRIDGE_PORTAL_URL`, `BRIDGE_PORTAL_API_TOKEN`, `XMPP_COMPONENT_SECRET` — if no live deployment uses these, remove the fallbacks. - -7. **Remove dead ChanFix vars (E):** 4 vars for a commented-out module. - -8. **Document intentional Portal vars (A):** Add a clear comment that `IRC_ATHEME_JSONRPC_URL`, `IRC_SERVER`, `IRC_PORT`, `PROSODY_REST_*`, etc. are consumed by the external Portal service, not this monorepo. diff --git a/docs/audits/prosody-config-audit.md b/docs/audits/prosody-config-audit.md deleted file mode 100644 index 64a19e04..00000000 --- a/docs/audits/prosody-config-audit.md +++ /dev/null @@ -1,217 +0,0 @@ -# Prosody XMPP Configuration Audit Report - -**Date:** 2026-02-26 -**Auditor:** Cloud Agent (manual wiki + modules.prosody.im review) -**Local config:** `apps/prosody/config/prosody.cfg.lua` -**References:** prosody.im/doc (configure, security, certificates, ports, modules, storage, mod_mam, mod_http_file_share, mod_muc) + modules.prosody.im (mod_anti_spam, mod_cloud_notify, mod_muc_limits, mod_http_admin_api, mod_firewall) - ---- - -## Findings - -### 🔴 CRITICAL — Insecure `.env.example` defaults for TLS/auth - -The `.env.example` ships with: - -``` -PROSODY_C2S_REQUIRE_ENCRYPTION=false -PROSODY_S2S_REQUIRE_ENCRYPTION=false -PROSODY_S2S_SECURE_AUTH=false -PROSODY_ALLOW_UNENCRYPTED_PLAIN_AUTH=true -``` - -The Prosody Security docs state: - -- `c2s_require_encryption` should be **true** (default in Prosody). "Almost all clients will use SSL/TLS out of the box." -- `s2s_require_encryption` should be **true** (default in Prosody). "By default Prosody requires encrypted server-to-server connections." -- `s2s_secure_auth` should be **true** for strong authentication. With `false`, Prosody falls back to DNS dialback which is weaker. -- `allow_unencrypted_plain_auth = true` allows plaintext passwords over unencrypted connections — a serious security risk. - -**Fix:** Change `.env.example` defaults to secure values. Move insecure overrides to `.env.dev.example` for local development only. - -### 🔴 CRITICAL — Global `ssl` block is commented out - -Lines 531-536 have the TLS configuration entirely commented out: - -```lua --- ssl = { --- protocol = "tlsv1_2+", --- ciphers = "ECDHE+AESGCM:ECDHE+CHACHA20:...", --- curve = "secp384r1", --- options = { "cipher_server_preference", "single_dh_use", "single_ecdh_use" }, --- } -``` - -This means Prosody uses its built-in defaults. While Prosody's defaults are reasonable, the Security docs recommend explicitly configuring TLS for production. The commented config shows good settings — they should be enabled. - -**Fix:** Uncomment the global `ssl` block. - -### 🟡 WARNING — `legacyauth` module enabled - -Line 18 loads `legacyauth`: - -```lua -"legacyauth", -- Legacy authentication. Only used by some old clients and bots. -``` - -The Prosody docs describe this as supporting only "some old clients and bots." For a modern XMPP deployment with SCRAM-SHA-256 SASL auth, legacy auth is unnecessary and widens the attack surface. - -**Fix:** Comment out or remove `legacyauth` unless specific legacy clients require it. - -### 🟡 WARNING — `http_status_allow_cidr = "0.0.0.0/0"` (world-open) - -Line 446: - -```lua -http_status_allow_cidr = "0.0.0.0/0" -``` - -This exposes the HTTP status monitoring endpoint to the entire internet. While the status page itself may not contain sensitive data, it leaks server operational information to potential attackers. - -**Fix:** Restrict to Docker network and localhost: - -```lua -http_status_allow_cidr = "172.16.0.0/12" -``` - -### 🟡 WARNING — `archive_expires_after` defaults differ from upstream - -The config defaults to `"1y"` (1 year) if the env var is not set: - -```lua -archive_expires_after = ... or "1y" -``` - -The Prosody MAM docs state the default is `"1w"` (1 week). While 1 year is a valid choice for a community server, it should be a deliberate decision, and the storage implications should be considered (SQLite with 1 year of archives for many users will grow large). - -**Status:** Acknowledged as intentional, but ensure monitoring is in place for database size. - -### 🟡 WARNING — `max_connections_per_ip` may be too restrictive - -Line 517: - -```lua -max_connections_per_ip = ... or 5 -``` - -The default of 5 per IP is fine for individual users, but in a Docker environment where the bridge and other services connect from the same IP, this could be restrictive. The bridge container shares the Docker network. - -**Fix:** Consider increasing for Docker IPs or ensure the bridge connects from a recognizable IP that's exempted. - -### 🟢 OK — Module selection comprehensive and modern - -The config loads an excellent set of modules covering: - -- Core protocol (roster, saslauth, tls, dialback, disco, presence, message, iq) -- Modern messaging (mam, carbons, smacks, offline) -- Mobile optimization (csi, csi_battery_saver, cloud_notify) -- Security (blocklist, anti_spam, spam_reporting, report_forward, admin_blocklist, mimicking, tombstones) -- XMPP compliance (server_contact_info, server_info, compliance_latest) -- Web services (http, bosh, websocket, conversejs, http_files) -- S2S enhancements (s2s_bidi, s2s_keepalive, s2s_status) - -This exceeds typical Prosody deployments and covers XEP compliance well. - -### 🟢 OK — Authentication properly configured - -```lua -authentication = "internal_hashed" -sasl_mechanisms = { "SCRAM-SHA-256", "SCRAM-SHA-1" } -``` - -Uses hashed storage (as recommended by Security docs) with modern SCRAM-SHA-256 as primary mechanism. SCRAM-SHA-1 kept for compatibility. - -### 🟢 OK — Anti-spam with xmppbl.org RTBL - -```lua -anti_spam_services = { "xmppbl.org" } -``` - -Matches the mod_anti_spam docs recommendation for subscribing to shared block lists. - -### 🟢 OK — Push notification privacy settings - -```lua -push_notification_with_body = false -push_notification_with_sender = false -``` - -Matches mod_cloud_notify docs: "Not recommended" to enable these due to privacy implications. The config correctly keeps them disabled. - -### 🟢 OK — Rate limiting properly configured - -The `limits` block with c2s, s2s, and http_upload rate limits is well-structured and uses env vars for customization. - -### 🟢 OK — MUC limits match upstream defaults - -All `muc_limits` settings match the module defaults exactly (muc_event_rate=0.5, muc_burst_factor=6, etc.). - -### 🟢 OK — HTTP security headers - -The `http_headers` block includes HSTS, X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, Referrer-Policy, and Content-Security-Policy. This is excellent and exceeds typical XMPP deployments. - -### 🟢 OK — TURN/STUN external configuration - -Properly configured with shared secret, hostname, ports, TTL, and TCP support. - -### 🟢 OK — Storage backend assignments - -Comprehensive storage mapping with SQL for persistent data and memory for ephemeral data (caps, carbons). Correct pattern. - -### 🟢 OK — Certificate handling in docker-entrypoint.sh - -Thorough certificate setup with Let's Encrypt layout, legacy fallback, and self-signed generation. HTTPS service discovery symlinks properly created. - -### 🟢 OK — Trusted proxies - -```lua -trusted_proxies = { "127.0.0.1", "172.16.0.0/12", "10.0.0.0/8" } -``` - -Covers localhost and Docker networks. - -### 💡 SUGGESTION — Consider enabling `mod_register` for password changes - -Line 75 disables register entirely: - -```lua --- "register", -- Password changes (XEP-0077); registration disabled -``` - -While self-registration should stay disabled (Portal provisions), `mod_register` is also needed for **password changes** by existing users. Consider enabling it with `allow_registration = false` to allow password changes without self-registration. - -### 💡 SUGGESTION — Consider `dont_archive_namespaces` - -Lines 200-203 have sensible namespace exclusions commented out: - -```lua --- dont_archive_namespaces = { --- "http://jabber.org/protocol/chatstates", --- "urn:xmpp:jingle-message:0", --- } -``` - -Enabling these would reduce archive storage by excluding chat state notifications (typing indicators) and Jingle call signaling. - -### 💡 SUGGESTION — Enable `mod_log_slow_events` - -The module is listed in `modules.list` (line 30) but not in `modules_enabled`. This module helps identify performance bottlenecks. - -### 💡 SUGGESTION — Enable `mod_reload_modules` - -Listed in `modules.list` (line 32) but not enabled. Allows modules to be reloaded on config change without full restart. - ---- - -## Summary - -| Severity | Count | Key Items | -|----------|-------|-----------| -| 🔴 CRITICAL | 2 | Insecure `.env.example` TLS/auth defaults; global ssl block commented out | -| 🟡 WARNING | 3 | legacyauth enabled; http_status world-open; MAM retention vs upstream | -| 🟢 OK | 12+ | Module selection, auth, anti-spam, push privacy, rate limits, MUC limits, HTTP headers, TURN, storage, certs, proxies | -| 💡 SUGGESTION | 4 | mod_register for password changes, dont_archive_namespaces, mod_log_slow_events, mod_reload_modules | - -### Overall Assessment - -The Prosody configuration is **excellent** for a Docker-deployed XMPP server. The module selection is comprehensive and modern, covering XMPP compliance, mobile optimization, spam prevention, and web services. The main issues are the insecure `.env.example` defaults and the commented-out global TLS block — both straightforward to fix. The HTTP security headers, rate limiting, and storage configuration all follow best practices. diff --git a/docs/audits/unrealircd-config-audit.md b/docs/audits/unrealircd-config-audit.md deleted file mode 100644 index 9d4a58c3..00000000 --- a/docs/audits/unrealircd-config-audit.md +++ /dev/null @@ -1,666 +0,0 @@ -# UnrealIRCd Configuration Audit Report - -**Date:** 2026-02-26 -**Auditor:** Cloud Agent (automated cross-reference) -**Local config:** `apps/unrealircd/config/unrealircd.conf.template` (UnrealIRCd 6.2.0.1) -**Reference:** UnrealIRCd 6.1.10 `modules.default.conf` and 6.x `example.conf` from repo docs - ---- - -## A. Loaded Modules - -### Comparison: Local vs `modules.default.conf` (6.1.10) - -All **229** upstream default modules are present in the local config. The local config loads **13 additional** modules beyond the defaults: - -| Extra Module | Purpose | Verdict | -|---|---|---| -| `cloak_sha256` | SHA-256 cloaking (required, upstream example loads it separately) | 🟢 Correct | -| `webserver` | HTTP server for RPC/WebSocket | 🟢 Required for WebSocket + RPC | -| `websocket` | WebSocket protocol support | 🟢 Required for web clients | -| `antirandom` | Block random-looking user/nick/ident | 🟢 Good security hardening | -| `antimixedutf8` | Block mixed-script spam | 🟢 Good anti-spam measure | -| `ircops` | `/IRCOPS` command to list online opers | 🟢 Useful for community | -| `staff` | `/STAFF` command | 🟢 Useful for community | -| `nocodes` | Strip mIRC color codes from certain channels | 🟢 Nice to have | -| `maxperip` | Per-IP connection limiting | 🟢 Good security hardening | -| `utf8functions` | UTF-8 nick/channel support | 🟢 Modern best practice | -| `third/showwebirc` | Show WebIRC info in WHOIS | 🟢 Good for transparency | -| `third/metadata` | IRCv3 draft/metadata | 🟢 Modern feature | -| `third/react` | IRCv3 draft/react (reactions) | 🟢 Modern feature | -| `third/redact` | IRCv3 draft/message-redaction | 🟢 Modern feature | -| `third/relaymsg-atl` | Stateless bridging (atl.chat fork) | 🟢 Required for bridge | - -### Findings - -- 🟢 **OK** — All upstream default modules are loaded. No important modules are missing. -- 🟢 **OK** — Extra modules are all justified and well-documented with `@if module-loaded()` guards where appropriate. -- 💡 **SUGGESTION** — The local `modules.default.conf` reference is version 6.1.10 while the server runs 6.2.0.1. Consider checking the 6.2.0.1 `modules.default.conf` for any newly added modules. Specifically, the `maxperip` module was added as a standalone module in newer versions (it's loaded locally but not in the 6.1.10 reference, which is correct behavior). - ---- - -## B. TLS/SSL Configuration - -**Config lines:** `set { tls { ... } }` (lines 465–513) - -### Findings - -- 🟢 **OK** — **TLS Protocols**: `"TLSv1.2,+TLSv1.3"` — correctly enforces TLS 1.2+ only. No SSLv3, TLS 1.0, or TLS 1.1. - -- 🟢 **OK** — **TLS 1.2 Ciphers**: Strong ECDHE-only cipher suite with Forward Secrecy. All ciphers use AEAD (GCM or ChaCha20-Poly1305). No weak ciphers (RC4, DES, 3DES, CBC). - - ``` - ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384: - ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256 - ``` - -- 🟢 **OK** — **TLS 1.3 Cipher Suites**: Complete list including all standard TLS 1.3 suites plus CCM variants. - -- 🟢 **OK** — **ECDH Groups**: Includes `X25519MLKEM768` (post-quantum hybrid), `X25519`, and standard NIST curves. Both the new `groups` directive and legacy `ecdh-curves` are set for compatibility. - -- 🟢 **OK** — **STS Policy**: Enabled with configurable duration/preload via environment variables. The phased rollout plan in the comments (1m → 1d → 30d → 180d) is an excellent approach. - -- 🟢 **OK** — **Certificate Expiry Notification**: `certificate-expiry-notification yes;` — good operational practice. - -- 🟢 **OK** — **Trusted CA File**: Explicit CA bundle path set for certificate validation. - -- 💡 **SUGGESTION** — **TLS 1.3 CCM Ciphers**: The `TLS_AES_128_CCM_8_SHA256` and `TLS_AES_128_CCM_SHA256` suites are rarely used by IRC clients and add no real benefit. Consider removing them for a cleaner config, though they cause no harm. - -- 💡 **SUGGESTION** — **`no-client-certificate`**: This is set, which is standard for public IRC servers. If you ever want to support certificate-based authentication (certfp), this would need to be reconsidered at the per-listen level. - ---- - -## C. `set` Block Analysis - -### Network Configuration (lines 735–758) - -- 🟢 **OK** — `network-name`, `default-server`, `services-server`, `stats-server`, `sasl-server` — all correctly templated with `${IRC_SERVICES_SERVER}` and `${IRC_DOMAIN}`. -- 🟢 **OK** — `help-channel "#support"` — reasonable choice. -- 🟢 **OK** — `cloak-keys` — properly sourced from environment variables. -- 🟢 **OK** — `hiddenhost-prefix` — configurable via `${IRC_CLOAK_PREFIX}`. -- 🟢 **OK** — `cloak-method ip` — good choice for privacy. - -### Server Configuration (lines 842–965) - -- 🟢 **OK** — `kline-address` — set to `${IRC_ADMIN_EMAIL}`. -- 🟢 **OK** — `modes-on-connect "+ixw"` — matches upstream example. `+i` (invisible), `+x` (cloaked host), `+w` (wallops). -- 🟢 **OK** — `modes-on-oper "+xws"` — good. Ensures opers get cloaking + wallops + server notices. -- 🟢 **OK** — `modes-on-join "+nt"` — standard default (no external messages + topic lock). -- 🟢 **OK** — `restrict-usermodes "x"` — prevents users from removing cloaking. Excellent security practice. -- 🟢 **OK** — `maxchannelsperuser 10` — matches upstream. -- 🟢 **OK** — `anti-spam-quit-message-time 10s` — matches upstream. -- 🟢 **OK** — `oper-auto-join "#mod-chat"` — appropriate for team use. -- 🟢 **OK** — `hide-ulines` and `show-connect-info` — correct. - -### Missing from upstream example - -- 🟡 **WARNING** — **Missing `set::spamfilter` block**: The upstream example.conf includes: - - ``` - spamfilter { - ban-time 1d; - ban-reason "Spam/Advertising"; - virus-help-channel "#help"; - } - ``` - - The local config has no `set::spamfilter` sub-block. While the included `spamfilter.conf` file provides rules, the global defaults (ban-time, ban-reason, virus-help-channel) are not explicitly set. UnrealIRCd will use built-in defaults, but it's best practice to set these explicitly. - - **Fix:** Add inside a `set { }` block: - - ``` - spamfilter { - ban-time 1d; - ban-reason "Spam/Advertising"; - virus-help-channel "#help"; - } - ``` - -- 🟡 **WARNING** — **Missing `set::connthrottle` block**: The upstream example.conf has a comprehensive `connthrottle` configuration: - - ``` - connthrottle { - except { reputation-score 24; identified yes; } - new-users { local-throttle 20:60; global-throttle 30:60; } - disabled-when { reputation-gathering 1w; start-delay 3m; } - } - ``` - - The local config loads the `connthrottle` module but has no configuration for it. The module will use built-in defaults, which may not be optimal. - - **Fix:** Add a `set { connthrottle { ... } }` block mirroring or customizing the upstream defaults. - -- 🟡 **WARNING** — **Missing `set::oper-only-stats`**: The upstream example restricts stats commands to opers: - - ``` - oper-only-stats "okfGsMRUEelLCXzdD"; - ``` - - The local config doesn't set this, meaning all stats are visible to everyone. While modern UnrealIRCd 6 has better defaults than older versions, consider restricting sensitive stats. - - **Fix:** Add `oper-only-stats "okfGsMRUEelLCXzdD";` inside a `set` block. - -- 💡 **SUGGESTION** — **Missing `set::whois-details` for certfp**: While whois-details are configured for `webirc` and `websocket`, consider adding certfp visibility control. - -- 💡 **SUGGESTION** — **`set::allowed-nickchars`**: The `charsys` module is loaded but no `set::allowed-nickchars` is configured. If you want to allow UTF-8 nicknames (which `utf8functions` module supports), you should explicitly set this. Example: - - ``` - set { allowed-nickchars { latin-utf8; }; } - ``` - ---- - -## D. `allow` Blocks - -**Config lines:** 558–562 - -``` -allow { - mask *@*; - class clients; - maxperip 5; -} -``` - -### Findings - -- 🟢 **OK** — Single allow block, open to all — appropriate for a public IRC server. -- 🟢 **OK** — `maxperip 5` — matches upstream old example.conf (5). The modern example uses 3, which is more restrictive. -- 💡 **SUGGESTION** — Consider lowering `maxperip` to 3 (matching modern upstream defaults) to reduce abuse surface. Users needing more connections can be handled with specific allow blocks. - ---- - -## E. `listen` Blocks - -**Config lines:** 288–456 - -| Port | Options | Purpose | Verdict | -|---|---|---|---| -| Unix socket (`rpc.socket`) | `rpc` | RPC for webpanel | 🟢 OK | -| Unix socket (`services.sock`) | (none) | Atheme services link | 🟢 OK | -| 6697 | `tls` | Standard IRC TLS port | 🟢 OK | -| 6900 | `tls; serversonly` | Server linking (TLS) | 🟢 OK | -| 6901 | `serversonly` | Server linking (plaintext, for Docker) | 🟢 OK (see note) | -| 8600 | `rpc; tls` | RPC API with TLS | 🟢 OK | -| 8000 | `websocket { type text; }` | WebSocket for web clients | 🟢 OK (see note) | - -### Findings - -- 🟢 **OK** — No plaintext client port (6667) is open. Good security practice. -- 🟢 **OK** — Port 8000 WebSocket without TLS is justified because TLS is terminated at the reverse proxy (NPM). -- 🟢 **OK** — Port 8600 RPC has its own TLS configuration with explicit cert/key paths. -- 🟢 **OK** — Port 6901 plaintext for servers is justified for Docker-internal Atheme communication. The `plaintext-policy { server allow; }` is correct for this use case. - -- 💡 **SUGGESTION** — Port 8000 (WebSocket): Consider binding to a specific internal IP rather than `*` if the WebSocket should only be accessible from the reverse proxy, not directly from the internet. - -- 💡 **SUGGESTION** — Port 6901 plaintext server port: Since Atheme connects via Unix socket (`services.sock`), this port may be unnecessary. If it's not used, removing it reduces the attack surface. - ---- - -## F. `link` Blocks - -**Config lines:** 542–550 - -``` -link ${IRC_SERVICES_SERVER} { - incoming { - mask *; - password "${IRC_SERVICES_PASSWORD}"; - } - password "${IRC_SERVICES_PASSWORD}"; - class servers; -} -``` - -### Findings - -- 🟡 **WARNING** — **`incoming { mask * }`**: The mask accepts connections from any IP. While this is somewhat mitigated by the password requirement and Docker network isolation, it would be more secure to restrict the mask to the Docker network range. - - **Fix:** - - ``` - incoming { - mask 172.16.0.0/12; - password "${IRC_SERVICES_PASSWORD}"; - } - ``` - -- 🟡 **WARNING** — **No TLS for services link**: The link block doesn't specify `options { tls; }`. Since Atheme connects via Unix socket (port 6901 or `services.sock`), this is acceptable for the current Docker setup. However, if services ever connect over the network, TLS should be required. - -- 🟢 **OK** — Password is properly templated from environment variable. -- 🟢 **OK** — The services password (`IRC_SERVICES_PASSWORD`) in `.env.example` has a placeholder that forces users to change it. - ---- - -## G. `log` Blocks - -**Config lines:** 293–310, 808–839 - -### Findings - -- 🟢 **OK** — **Memory log**: For RPC (1000 lines, 7 days) — matches upstream best practice. -- 🟢 **OK** — **Text log**: `ircd.log` with 100M maxsize — matches upstream example. -- 🟢 **OK** — **JSON log**: `ircd.json.log` with 250M maxsize — matches upstream example and provides machine-readable audit trail. -- 🟢 **OK** — **Source filters**: Identical exclusions across all log blocks (`!debug`, `!join.*`, `!part.*`, `!kick.*`) — consistent and appropriate. -- 🟢 **OK** — Log path templated via `${IRC_LOG_PATH}`. - ---- - -## H. `except ban` Blocks - -**Config line:** 729–732 - -``` -except ban { - mask *@172.16.0.0/12; - type { blacklist; connect-flood; maxperip; handshake-data-flood; } -} -``` - -### Findings - -- 🟢 **OK** — Covers the entire Docker bridge subnet range (172.16.0.0/12). -- 🟢 **OK** — Exemption types are appropriate: `blacklist` (don't DNSBL internal IPs), `connect-flood` (allow many container connections), `maxperip` (multiple containers share subnet), `handshake-data-flood` (services may send bursts). - -- 💡 **SUGGESTION** — The upstream example also exempts IRCCloud (`*.irccloud.com`). If your network expects IRCCloud users, consider adding a similar exception: - - ``` - except ban { - mask *.irccloud.com; - type { maxperip; connect-flood; } - } - ``` - -- 💡 **SUGGESTION** — Consider adding `type all;` exception for a specific oper IP to ensure opers can always connect even during accidental self-bans. - ---- - -## I. `blacklist` (DNSBL) Blocks - -**Config lines:** 692–723 - -| DNSBL | Reply Codes | Action | Ban Time | -|---|---|---|---| -| `dnsbl.dronebl.org` | 3,5-16 | gline | 24h | -| `rbl.efnetrbl.org` | 1,4,5 | gline | 24h | -| `dnsbl.tornevall.org` | 1-16 | gline | 24h | - -### Findings - -- 🟢 **OK** — DroneBL and EFnetRBL are the two most commonly recommended DNSBLs for IRC. Both match the upstream example exactly. - -- 🟡 **WARNING** — **Tornevall DNSBL** (`dnsbl.tornevall.org`): This DNSBL has had reliability issues historically and some networks have stopped using it. It's not in the upstream UnrealIRCd example. The extremely broad reply code range (1-16 = everything) could lead to false positives. - - **Fix:** Consider removing the `tornevall` blacklist or at minimum narrowing the reply codes to specific abuse categories. If you want a third DNSBL, consider `dnsbl.sectoor.de` or `dnsbl.ahbl.org` instead, though availability varies. - -- 🟢 **OK** — Ban time of 24h is reasonable for all blacklists. -- 🟢 **OK** — Reason messages include lookup URLs for users to check their IP. -- 🟢 **OK** — Action `gline` is appropriate (network-wide ban). - ---- - -## J. `oper` Blocks - -**Config lines:** 761–787 - -### Admin Oper - -``` -oper admin { - class opers; - mask *@*; - password "${IRC_OPER_PASSWORD}"; - operclass netadmin-with-override; - swhois "is the Network Administrator"; - vhost "${IRC_STAFF_VHOST}"; - require-modes ""; -} -``` - -### Findings - -- 🟢 **OK** — Password sourced from `${IRC_OPER_PASSWORD}`. The `.env.example` shows an `$argon2id$...` hash, meaning the password is properly hashed (not plaintext). -- 🟢 **OK** — `operclass netadmin-with-override` — appropriate for the primary admin. -- 🟢 **OK** — `swhois` and `vhost` are set for identification. - -- 🔴 **CRITICAL** — **`mask *@*`**: The admin oper block accepts connections from ANY host. This means anyone who knows (or brute-forces) the oper password can become netadmin from anywhere. Best practice is to restrict this to known IPs or at minimum require TLS certificate fingerprint authentication. - - **Fix (at minimum):** - - ``` - oper admin { - ... - mask *@172.16.0.0/12; /* Docker network only, or specific IPs */ - /* Or better: use certificate fingerprint */ - /* password "$argon2id..."; */ - /* require-modes "z"; */ /* Require TLS connection */ - } - ``` - -- 🟡 **WARNING** — **`require-modes ""`**: This is explicitly set to empty, meaning no user modes are required to OPER up. Consider requiring `require-modes "z"` to force TLS for oper authentication: - - ``` - require-modes "z"; - ``` - -### Bridge Oper - -``` -oper bridge { - class opers; - mask *@*bridge*; - password "${BRIDGE_IRC_OPER_PASSWORD}"; - operclass bridge-oper; -} -``` - -- 🟢 **OK** — Restricted mask (`*@*bridge*`) limits this oper to bridge hostnames. -- 🟢 **OK** — `bridge-oper` operclass has minimal permissions (just channel override + relaymsg). -- 🟢 **OK** — Password sourced from environment variable. - ---- - -## K. `set::anti-flood` / `set::connthrottle` - -### Anti-Flood (lines 896–964) - -- 🟢 **OK** — **Channel anti-flood profiles**: Five profiles (very-strict through very-relaxed) with default "normal" — excellent granularity. -- 🟢 **OK** — **Handshake data flood**: 4k limit with 10m zline — reasonable protection. -- 🟢 **OK** — **Target flood protection**: Comprehensive rate limits for channel/private messages, notices, and tagmsg. -- 🟢 **OK** — **Known vs unknown user differentiation**: Proper two-tier system with stricter limits for unknown users. - -- 🟡 **WARNING** — **`connect-flood 20:10`**: This allows 20 connections per 10 seconds per IP, which is described as "relaxed for testing." For production, this should be tightened significantly. The upstream default is typically 3:60 (3 per 60 seconds). - - **Fix for production:** - - ``` - connect-flood 3:60; - ``` - -### ConnThrottle - -- 🟡 **WARNING** — **Missing `set::connthrottle` configuration**: The `connthrottle` module is loaded but not configured. This module needs explicit configuration to be effective. Without it, the module uses built-in defaults which may not be optimal for this network. - - **Fix:** Add: - - ``` - set { - connthrottle { - except { - reputation-score 24; - identified yes; - } - new-users { - local-throttle 20:60; - global-throttle 30:60; - } - disabled-when { - reputation-gathering 1w; - start-delay 3m; - } - } - } - ``` - ---- - -## L. `drpass` Block - -### Finding - -- 🔴 **CRITICAL** — **Missing `drpass` block**: The local configuration has NO `drpass` block. The `/DIE` and `/RESTART` commands have no password protection. Any IRC operator with sufficient privileges could accidentally or maliciously shut down or restart the server without authentication. - - The upstream example.conf includes: - - ``` - drpass { - restart "restart"; - die "die"; - } - ``` - - **Fix:** Add a `drpass` block with strong, hashed passwords: - - ``` - drpass { - restart "${IRC_DRPASS_RESTART}"; - die "${IRC_DRPASS_DIE}"; - } - ``` - - And add corresponding environment variables to `.env.example` with argon2id-hashed values. - ---- - -## M. `set::plaintext-policy` / `set::outdated-tls-policy` - -**Config lines:** 515–538 - -### Plaintext Policy - -``` -plaintext-policy { - server allow; - user allow; - oper deny; - user-message "..."; - oper-message "..."; -} -``` - -### Findings - -- 🟢 **OK** — `server allow` — correct for Docker-internal Atheme link on plaintext Unix socket. -- 🟢 **OK** — `oper deny` — opers must use TLS. Good security practice. -- 🟢 **OK** — `user allow` with STS redirect — correct phased approach. Users with STS-capable clients get auto-redirected to TLS. -- 🟢 **OK** — Custom user-message and oper-message provide clear guidance. -- 💡 **SUGGESTION** — For production, eventually move `user` to `warn` or `deny` once STS has been in place long enough (Phase 4 in the config comments). - -### Outdated TLS Policy - -``` -outdated-tls-policy { - user warn; - oper deny; - server deny; -} -``` - -- 🟢 **OK** — Users get warned about outdated TLS (not kicked). -- 🟢 **OK** — Opers and servers are denied with outdated TLS. Excellent security practice. -- 🟢 **OK** — Custom messages provide actionable guidance. - ---- - -## N. WebSocket Configuration - -**Config lines:** 443–456 - -``` -listen { - ip *; - port 8000; - options { - websocket { type text; } - } -} -``` - -### Findings - -- 🟢 **OK** — WebSocket listener on port 8000, type `text` — correct for IRC over WebSocket. -- 🟢 **OK** — TLS is correctly disabled here because it's terminated at the reverse proxy (NPM). This is documented in the comments. -- 🟢 **OK** — Both `websocket_common` and `websocket` modules are loaded. -- 🟢 **OK** — The `webserver` module is loaded for HTTP functionality. - -- 💡 **SUGGESTION** — Consider adding `websocket { type text; origin "https://your-domain.com"; }` to restrict WebSocket connections to your web application's origin, preventing unauthorized cross-origin connections. - ---- - -## O. Spamfilter and Badwords - -### Spamfilter (`spamfilter.conf`) - -- 🟡 **WARNING** — **Outdated rules**: The file itself states: "Since 2005 these rules are no longer maintained. The main purpose nowadays is to serve as an example." All the rules target malware/trojans from the early 2000s (sub7, mIRC exploits, fagot worm, etc.). None of these are relevant modern threats. - - **Fix:** Either: - 1. Write new, modern spamfilter rules targeting current IRC spam patterns (crypto scams, phishing links, mass-highlight floods, invite spam), or - 2. Remove/empty the file and rely on dynamic spamfilters via `/SPAMFILTER` command, or - 3. Keep as-is but acknowledge the rules provide no real protection against modern threats. - -### Badwords (`badwords.conf`) - -- 🟡 **WARNING** — **Potentially problematic word list**: The badwords file is the default from UnrealIRCd circa 2000 (by Carsten V. Munk). It contains several slurs including `faggot` and `fag`. For a modern Linux community: - - The word list is very basic (only 20 entries) - - Some entries may create false positives (e.g., "fag" matching in legitimate words, `*fuck*` wildcard matching "Buckfastleigh" etc.) - - The list doesn't cover modern harassment patterns - - Consider whether a word filter is the right approach vs. moderation tooling - - **Fix:** Review and update the badwords list to match your community standards, or disable channel/user mode +G if you prefer moderation-based approaches. - ---- - -## P. Third-Party Modules - -### Installed Modules - -| Module | Config | Status | -|---|---|---| -| `third/showwebirc` | No config needed | 🟢 OK — works out of the box | -| `third/metadata` | `metadata { max-user-metadata 10; max-channel-metadata 10; max-subscriptions 10; }` | 🟢 OK — reasonable limits | -| `third/react` | No config needed | 🟢 OK — works out of the box | -| `third/redact` | No config needed | 🟢 OK — works out of the box | -| `third/relaymsg-atl` | `relaymsg { hostmask "bridge@${IRC_DOMAIN}"; require-separator no; }` | 🟢 OK — custom fork, properly configured | - -### Findings - -- 🟢 **OK** — All third-party modules in `third-party-modules.list` are loaded in the config. -- 🟢 **OK** — `relaymsg` is properly configured with `require-separator no` for clean bridge nicks. -- 🟢 **OK** — `metadata` limits are reasonable (10 per user/channel/subscriptions). - -### Commented-Out Modules in `third-party-modules.list` - -The file lists these as potential additions: - -- `third/commandsno` — SNOMASK-based command logging -- `third/clones` — Clone detection -- `third/repeatprot` — Repeat message protection -- `third/block_masshighlight` — Block mass highlighting in channels - -- 💡 **SUGGESTION** — **`third/block_masshighlight`** is highly recommended for any community IRC server. Mass-highlighting (mentioning many nicks at once) is a common harassment/spam tactic. Consider enabling this module. - -- 💡 **SUGGESTION** — **`third/repeatprot`** would complement the existing anti-flood settings by catching repeated messages that slip through rate limits. - ---- - -## Additional Findings - -### WEBIRC Block (line 370–373) - -``` -webirc { - mask ${ATL_GATEWAY_IP}/32; - password "change_me_webirc_password"; -} -``` - -- 🔴 **CRITICAL** — **Hardcoded placeholder password**: The WEBIRC password is `"change_me_webirc_password"` which is NOT an environment variable. Unlike other passwords in the config (which use `${VAR}` syntax), this one is a literal string. If the config template is processed without changing this, the WEBIRC password will be the placeholder text. - - **Fix:** Change to use an environment variable: - - ``` - password "${ATL_WEBIRC_PASSWORD}"; - ``` - - And add `ATL_WEBIRC_PASSWORD` to `.env.example`. - -### RPC User (line 790–794) - -``` -rpc-user "${WEBPANEL_RPC_USER}" { - match { ip *; } - rpc-class full; - password "${WEBPANEL_RPC_PASSWORD}"; -} -``` - -- 🟡 **WARNING** — **`match { ip *; }`**: The RPC user can connect from any IP. While the RPC port (8600) requires TLS, restricting to known IPs would be more secure: - - ``` - match { ip 172.16.0.0/12; } - ``` - -### Proxy/WEBIRC Block for The Lounge (lines 376–380) - -``` -proxy thelounge { - type webirc; - match { ip 172.16.0.0/12; } - password "${THELOUNGE_WEBIRC_PASSWORD}"; -} -``` - -- 🟢 **OK** — Properly restricted to Docker network range. -- 🟢 **OK** — Password sourced from environment variable. - -### Auto Vhost (lines 797–803) - -``` -vhost { - auto-login yes; - mask { identified yes; } - vhost ${IRC_DOMAIN}; -} -``` - -- 🟢 **OK** — Gives all identified users a clean vhost matching the IRC domain. Good for privacy. - -### Ban Nick Blocks (lines 591–689) - -- 🟢 **OK** — Comprehensive list covering services names, system names, and generic names. -- 🟡 **WARNING** — **Overly broad patterns**: `*IRC*` will block any nick containing "IRC" (e.g., "CircleOfLife", "QuIRCky"). Similarly, `*admin*` blocks "administrator" type nicks but also catches legitimate nicks like "badminton". `*server*` would catch "observer". Consider making these patterns more specific. - - **Fix example:** - - ``` - ban nick { mask "IRC"; reason "Reserved for network"; } - ban nick { mask "IRC-*"; reason "Reserved for network"; } - ``` - -### Missing `aliases` Include - -- 💡 **SUGGESTION** — The upstream example includes `aliases/anope.conf` for service aliases (/NickServ, /ChanServ, etc.). The local config doesn't include any aliases file. Since Atheme is used (which is compatible with Anope aliases), consider adding: - - ``` - include "aliases/anope.conf"; - ``` - - This provides `/NS`, `/CS`, `/OS`, `/MS` shortcut commands. - ---- - -## Summary - -### By Severity - -| Severity | Count | Items | -|---|---|---| -| 🔴 CRITICAL | 3 | Missing `drpass` block; admin oper `mask *@*`; hardcoded WEBIRC password | -| 🟡 WARNING | 8 | Missing connthrottle config; missing spamfilter set block; missing oper-only-stats; relaxed connect-flood; tornevall DNSBL; link mask too open; RPC user match too open; overly broad ban nick patterns | -| 🟢 OK | 35+ | Most configuration is solid and well-documented | -| 💡 SUGGESTION | 12 | Various optional improvements | - -### Priority Fixes - -1. **Add `drpass` block** with hashed passwords for `/DIE` and `/RESTART` -2. **Fix WEBIRC password** to use environment variable instead of hardcoded placeholder -3. **Restrict admin oper mask** from `*@*` to specific IPs or Docker network -4. **Add `set::connthrottle`** configuration block -5. **Tighten `connect-flood`** from `20:10` to production values -6. **Add `set::spamfilter`** defaults block -7. **Restrict link block mask** to Docker network range -8. **Add `set::oper-only-stats`** to hide sensitive stats from regular users - -### Overall Assessment - -The configuration is **well above average** for a Docker-deployed IRC server. The TLS configuration is excellent (including post-quantum cryptography support), the anti-flood settings are comprehensive with proper known/unknown user differentiation, and the module selection is thorough. The use of environment variable templating for sensitive values is good practice. The main areas for improvement are the three critical findings (drpass, oper mask, WEBIRC password) and adding the missing `connthrottle` configuration. diff --git a/docs/bridges/README.md b/docs/bridges/README.md deleted file mode 100644 index e8b94141..00000000 --- a/docs/bridges/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Bridge Infrastructure - -Overview of protocol bridging in atl.chat. - -## Bridge - -The [apps/bridge/](../../apps/bridge/) in-repo package provides Discord↔IRC↔XMPP bridging. It connects to UnrealIRCd, Prosody, and Discord to relay messages across protocols. - -## Compose - -Bridge services are defined in `infra/compose/bridge.yaml`. They use the shared `atl-chat` network to connect to IRC and XMPP. - -## See Also - -- [apps/bridge/README.md](../../apps/bridge/README.md) – Bridge design and setup -- [Networking](../infra/networking.md) – Port allocations diff --git a/docs/deployment.md b/docs/deployment.md deleted file mode 100644 index 37536fab..00000000 --- a/docs/deployment.md +++ /dev/null @@ -1,408 +0,0 @@ -# Production Deployment Guide - -Deploy the atl.chat stack on a server behind an Nginx Proxy Manager (NPM) reverse proxy. - -## Architecture - -``` -Internet Tailscale Mesh - │ ┌──────────────────────┐ - │ DNS: │ │ - │ *.atl.chat → NPM IP │ Server A (NPM) │ Server B (atl.chat) - │ │ 100.64.1.0 │ 100.64.7.0 - ▼ │ │ -┌──────┐ │ Nginx Proxy Manager │ UnrealIRCd (:6697,:8000,:8600) -│Client├────:443/:6697──────►│ :80 → HTTPS redirect │ Atheme (shares irc network) -│ │ │ :443 → reverse proxy │ Prosody (:5222-5281) -└──────┘ │ :6697 → TCP stream │ XMPP Nginx (:5281) [optional] - │ :5222 → TCP stream │ Bridge - │ │ The Lounge (:9000) - └──────────────────────┘ WebPanel (:8080) -``` - -Both servers connected via Tailscale (or WireGuard/VPN). Public DNS points to Server A. -NPM on Server A reverse-proxies to Server B's private Tailscale IP. - ---- - -## Prerequisites - -- **Server A:** Nginx Proxy Manager installed, public IP, Tailscale joined -- **Server B:** Docker + Docker Compose, `just`, `git`, Tailscale joined -- **DNS:** Domain with Cloudflare DNS (for cert-manager DNS-01 challenge) -- **Tailscale:** Both servers on the same tailnet - ---- - -## Step 1: Server B — Clone and Configure - -```bash -git clone https://github.com/allthingslinux/atl.chat.git -cd atl.chat -cp .env.example .env -``` - -Edit `.env` — change these from defaults: - -```bash -# ── Core ── -ATL_ENVIRONMENT=prod -ATL_GATEWAY_IP=100.64.1.0 # Server A's Tailscale IP -ATL_CHAT_IP=100.64.7.0 # Server B's Tailscale IP - -# ── Certs (Cloudflare DNS challenge) ── -CLOUDFLARE_DNS_API_TOKEN= -LETSENCRYPT_EMAIL=admin@allthingslinux.org - -# ── IRC ── -IRC_DOMAIN=irc.atl.chat -IRC_ROOT_DOMAIN=atl.chat -IRC_SSL_CERT_PATH=/home/unrealircd/unrealircd/certs/live/irc.atl.chat/fullchain.pem -IRC_SSL_KEY_PATH=/home/unrealircd/unrealircd/certs/live/irc.atl.chat/privkey.pem - -# CHANGE ALL THESE from defaults: -IRC_OPER_PASSWORD= # Generate: just irc mkpasswd -IRC_DRPASS= -IRC_SERVICES_PASSWORD= -ATL_WEBIRC_PASSWORD= -WEBPANEL_RPC_USER= -WEBPANEL_RPC_PASSWORD= -THELOUNGE_WEBIRC_PASSWORD= - -# Regenerate cloak keys: -# just irc gencloak - -# ── XMPP ── -XMPP_DOMAIN=atl.chat -PROSODY_SSL_KEY=certs/live/atl.chat/privkey.pem -PROSODY_SSL_CERT=certs/live/atl.chat/fullchain.pem -PROSODY_UPLOAD_EXTERNAL_URL=https://xmpp.atl.chat/upload/ -PROSODY_PROXY_ADDRESS=atl.chat -PROSODY_SERVER_NAME=atl.chat -PROSODY_SERVER_WEBSITE=https://atl.chat -PROSODY_SERVER_DESCRIPTION="All Things Linux XMPP" - -# If NPM handles HTTPS for XMPP (recommended): -# PROSODY_HTTPS_VIA_PROXY=true - -# ── TURN ── -TURN_SECRET= -TURN_EXTERNAL_HOST=turn.atl.network - -# ── Bridge ── -BRIDGE_DISCORD_TOKEN= -BRIDGE_DISCORD_CHANNEL_ID= -BRIDGE_XMPP_COMPONENT_JID=bridge.atl.chat -BRIDGE_XMPP_COMPONENT_SECRET= - -# STS (start conservative, increase after testing): -IRC_STS_DURATION=1d -IRC_STS_PRELOAD=no - -# Portal (if using): -# BRIDGE_PORTAL_BASE_URL=https://portal.atl.tools -# BRIDGE_PORTAL_TOKEN= -``` - -**Do NOT create `.env.dev`** — the prod flow should not load dev overrides. - ---- - -## Step 2: Server B — Initialize and Start - -```bash -# Install just if not present -# Documentation: https://github.com/casey/just#installation -# Example for Debian/Ubuntu: -sudo apt install just -# or use the pre-built binaries from their releases page. - -# Initialize (creates data dirs, generates configs, obtains certs) -just prod - -# Check all services are healthy -just status - -# Follow logs for issues -just logs -``` - -The `just prod` command: - -1. Runs `scripts/init.sh` which creates `data/` directories, generates configs from - templates, and generates self-signed certs as fallback -2. Starts `docker compose --env-file .env up -d` - -The cert-manager container will attempt to obtain Let's Encrypt certs via Cloudflare -DNS-01 challenge. Check its logs: - -```bash -docker compose logs cert-manager -``` - ---- - -## Step 3: DNS Records - -Point these at **Server A** (the NPM host's public IP): - -| Record | Type | Value | Purpose | -|--------|------|-------|---------| -| `irc.atl.chat` | A | `` | IRC server | -| `atl.chat` | A | `` | XMPP (VirtualHost domain) | -| `xmpp.atl.chat` | CNAME | `atl.chat` | XMPP HTTP services (BOSH, WebSocket, upload) | -| `webirc.atl.chat` | CNAME | `atl.chat` | The Lounge web IRC client | -| `panel.atl.chat` | CNAME | `atl.chat` | UnrealIRCd WebPanel | - -### XMPP SRV Records - -These tell XMPP clients and servers where to connect. Point at `xmpp.atl.chat` -(which resolves to Server A / NPM): - -| Record | Type | Priority | Weight | Port | Target | -|--------|------|----------|--------|------|--------| -| `_xmpp-client._tcp.atl.chat` | SRV | 0 | 5 | 5222 | `xmpp.atl.chat` | -| `_xmpps-client._tcp.atl.chat` | SRV | 0 | 5 | 5223 | `xmpp.atl.chat` | -| `_xmpp-server._tcp.atl.chat` | SRV | 0 | 5 | 5269 | `xmpp.atl.chat` | -| `_xmpps-server._tcp.atl.chat` | SRV | 0 | 5 | 5270 | `xmpp.atl.chat` | - -### XMPP Component Records (optional, for federation) - -| Record | Type | Value | -|--------|------|-------| -| `muc.atl.chat` | CNAME | `atl.chat` | -| `upload.atl.chat` | CNAME | `atl.chat` | -| `proxy.atl.chat` | CNAME | `atl.chat` | -| `pubsub.atl.chat` | CNAME | `atl.chat` | - ---- - -## Step 4: NPM Configuration — Server A - -### HTTP Proxy Hosts (HTTPS termination at NPM) - -For these services, NPM terminates TLS and proxies to Server B over the Tailscale mesh. - -| Domain | Scheme | Forward Host | Forward Port | Websocket | SSL | -|--------|--------|-------------|--------------|-----------|-----| -| `xmpp.atl.chat` | http | `100.64.7.0` | `5280` | ✅ | Let's Encrypt | -| `webirc.atl.chat` | http | `100.64.7.0` | `9000` | ✅ | Let's Encrypt | -| `panel.atl.chat` | http | `100.64.7.0` | `8080` | ❌ | Let's Encrypt | - -For each: - -1. NPM → Proxy Hosts → Add Proxy Host -2. Domain: `xmpp.atl.chat` -3. Scheme: `http`, Forward Hostname: `100.64.7.0`, Port: `5280` -4. Enable "Websockets Support" (required for BOSH and WebSocket) -5. SSL tab → Request new Let's Encrypt certificate, enable "Force SSL" -6. Custom Nginx Config (Advanced tab) for XMPP: - -```nginx -# Increase timeouts for long-lived BOSH/WebSocket connections -proxy_read_timeout 900s; -proxy_send_timeout 900s; - -# WebSocket upgrade headers (NPM adds these when Websocket is checked, -# but explicit config ensures correct behavior) -proxy_set_header Upgrade $http_upgrade; -proxy_set_header Connection "upgrade"; -``` - -### IRC WebSocket (via HTTP proxy) - -The IRC WebSocket on port 8000 can also go through an HTTP proxy host: - -| Domain | Scheme | Forward Host | Forward Port | Websocket | SSL | -|--------|--------|-------------|--------------|-----------|-----| -| `irc.atl.chat` | http | `100.64.7.0` | `8000` | ✅ | Let's Encrypt | - -This handles `wss://irc.atl.chat/ws` connections from web clients. - -### TCP Stream Proxies (TLS passthrough) - -For non-HTTP protocols, NPM forwards raw TCP. Server B handles TLS. - -In NPM → Streams → Add Stream: - -| Incoming Port | Forward Host | Forward Port | PROXY Protocol | -|---------------|-------------|--------------|----------------| -| `6697` | `100.64.7.0` | `6697` | ❌ | -| `5222` | `100.64.7.0` | `5222` | ❌ | -| `5223` | `100.64.7.0` | `5223` | ❌ | -| `5269` | `100.64.7.0` | `5269` | ❌ | -| `5270` | `100.64.7.0` | `5270` | ❌ | - -**Note on real client IPs:** Two mechanisms handle real-IP forwarding: - -- **WebSocket (port 8000):** NPM sends `X-Forwarded-For` headers. The `proxy npm-x-forwarded` - block in UnrealIRCd trusts these from the gateway IP and Tailscale range. Hosts matching - this are auto-exempted from connect floods and blacklist checks (UnrealIRCd 6.1.8+). -- **WEBIRC (The Lounge, KiwiIRC):** Web gateways send the real client IP via the WEBIRC - protocol. The `proxy npm-webirc` block trusts the gateway for this. -- **Raw TCP streams (port 6697):** UnrealIRCd does not support HAProxy PROXY Protocol. - Direct IRC clients through NPM TCP streams will appear as the proxy IP. This is an - upstream limitation — see the [Proxy block docs](https://www.unrealircd.org/docs/Proxy_block). - -**XMPP ports:** Prosody doesn't support PROXY Protocol on C2S/S2S either. The -`trusted_proxies` config handles X-Forwarded-For for HTTP endpoints, and S2S -authenticates by domain (not IP) via XMPP dialback or certificates. - ---- - -## Step 5: Verify - -### IRC - -```bash -# From your local machine — connect through NPM -openssl s_client -connect irc.atl.chat:6697 - -# Should see the UnrealIRCd TLS certificate and IRC welcome -``` - -### XMPP - -```bash -# Test BOSH endpoint through NPM -curl -sf https://xmpp.atl.chat/http-bind - -# Test with an XMPP client (Conversations, Gajim, etc.) -# Account: user@atl.chat, Server: atl.chat -``` - -### Web Clients - -- The Lounge: `https://webirc.atl.chat` -- Converse.js: `https://xmpp.atl.chat/conversejs` -- WebPanel: `https://panel.atl.chat` - -### Prosody Connectivity Check - -```bash -docker exec atl-xmpp-server prosodyctl check connectivity -docker exec atl-xmpp-server prosodyctl check dns -docker exec atl-xmpp-server prosodyctl check certs -``` - -### IM Observatory - -Test your XMPP server compliance at: - ---- - -## Certificate Renewal - -Certs are obtained by the cert-manager container (Lego + Cloudflare DNS-01). -They are stored in `data/certs/live//`. - -After renewal, services need to reload: - -```bash -# Reload IRC (picks up new certs without restart) -docker exec atl-irc-server /home/unrealircd/unrealircd/bin/unrealircdctl rehash - -# Reload Prosody -docker exec atl-xmpp-server prosodyctl reload - -# The Lounge reads certs on connection — restart to pick up new certs -docker compose restart atl-thelounge -``` - -To automate, add a cron job on Server B: - -```bash -# /etc/cron.weekly/atl-cert-reload -#!/bin/bash -cd /path/to/atl.chat -docker exec atl-irc-server /home/unrealircd/unrealircd/bin/unrealircdctl rehash -docker exec atl-xmpp-server prosodyctl reload -docker compose restart atl-thelounge -``` - ---- - -## Security Checklist - -- [ ] All `change_me_*` passwords replaced with strong random values -- [ ] Cloak keys regenerated (`just irc gencloak`) -- [ ] Oper password is argon2id hash (not plaintext) -- [ ] `CLOUDFLARE_DNS_API_TOKEN` set (scoped to DNS edit only) -- [ ] `.env` file permissions: `chmod 600 .env` -- [ ] `data/` directory permissions: `chmod 700 data/` -- [ ] No `.env.dev` file exists on prod server -- [ ] `ATL_ENVIRONMENT=prod` (not `dev`) -- [ ] IRC STS duration increased after testing (1d → 30d → 180d) -- [ ] Prosody `s2s_secure_auth=true` (default in `.env.example`) -- [ ] NPM SSL certificates obtained and forced -- [ ] Firewall: only 80, 443, 6697, 5222, 5223, 5269, 5270 open to public - ---- - -## Backup Strategy - -```bash -# Backup all persistent data -tar czf atl-chat-backup-$(date +%Y%m%d).tar.gz \ - data/ \ - .env \ - apps/unrealircd/config/unrealircd.conf \ - apps/atheme/config/atheme.conf \ - apps/bridge/config.yaml - -# Critical data: -# data/irc/data/ — UnrealIRCd runtime (tkl.db, reputation.db) -# data/atheme/data/ — Atheme services DB (nickserv, chanserv registrations) -# data/xmpp/data/ — Prosody SQLite (accounts, MAM archives) -# data/certs/ — TLS certificates -# data/thelounge/ — The Lounge user data -``` - -Schedule daily backups to off-site storage. - ---- - -## Troubleshooting - -### Bridge can't connect to IRC - -Check `docker compose logs atl-bridge`. Common causes: - -- G-Line from DNSBL: The `except ban` block covers Docker IPs (`172.16.0.0/12`). - If the bridge connects from Tailscale, add `100.64.0.0/10` to the except block. -- TLS verification: Set `BRIDGE_IRC_TLS_VERIFY=false` if using self-signed certs. - For prod with real certs, leave `true` (default). - -### XMPP federation not working - -```bash -docker exec atl-xmpp-server prosodyctl check dns -docker exec atl-xmpp-server prosodyctl check connectivity -``` - -Common causes: - -- Missing SRV records (see DNS section above) -- Port 5269 not forwarded through NPM streams -- `s2s_secure_auth=true` but cert doesn't cover the domain (check with `prosodyctl check certs`) - -### Certs not renewing - -```bash -docker compose logs cert-manager -``` - -Common causes: - -- `CLOUDFLARE_DNS_API_TOKEN` not set or expired -- Cloudflare API token doesn't have DNS edit permissions for the zone -- Rate limited by Let's Encrypt (check ) - -### WebSocket connections failing - -Ensure NPM proxy hosts have "Websockets Support" enabled. For XMPP BOSH/WebSocket, -the proxy timeout must be long enough (default 60s is too short): - -```nginx -proxy_read_timeout 900s; -``` diff --git a/docs/examples/cloudflare-credentials.ini.example b/docs/examples/cloudflare-credentials.ini.example deleted file mode 100644 index 218bff0d..00000000 --- a/docs/examples/cloudflare-credentials.ini.example +++ /dev/null @@ -1,14 +0,0 @@ -# Cloudflare API credentials for Let's Encrypt DNS-01 challenges -# Copy this file to cloudflare-credentials.ini and fill in your credentials -# Used by cert-manager (Lego) for DNS-01 challenge - -# Option 1: Global API Key (legacy, not recommended) -# dns_cloudflare_email = your-email@example.com -# dns_cloudflare_api_key = your-global-api-key - -# Option 2: API Token (recommended) -# Create a token at https://dash.cloudflare.com/profile/api-tokens -# with Zone:Zone:Read and Zone:DNS:Edit permissions for your domain -dns_cloudflare_api_token = your-api-token-here - -# Security note: Keep this file secure and never commit it to version control diff --git a/docs/examples/nginx-docker.conf b/docs/examples/nginx-docker.conf deleted file mode 100644 index bba1d0f1..00000000 --- a/docs/examples/nginx-docker.conf +++ /dev/null @@ -1,371 +0,0 @@ -# Nginx reverse proxy for Prosody (Docker-aware) -# Proxies WebSocket, BOSH, upload, admin, metrics to the Prosody container -# Uses Let's Encrypt certs mounted at /opt/xmpp.atl.chat/certs - -user nginx; -worker_processes auto; - -events { - worker_connections 1024; - use epoll; -} - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - sendfile on; - tcp_nopush on; - tcp_nodelay on; - keepalive_timeout 75 20; - types_hash_max_size 2048; - ignore_invalid_headers on; - - # Optional rate limiting zones (not enabled by default) - # limit_req_zone $binary_remote_addr zone=websocket:10m rate=10r/m; - # limit_req_zone $binary_remote_addr zone=general:10m rate=100r/m; - - # Redirect HTTP to HTTPS for xmpp.atl.chat - server { - listen 80; - server_name xmpp.atl.chat; - return 301 https://$server_name$request_uri; - } - - # Redirect HTTP to HTTPS for upload.atl.chat - server { - listen 80; - server_name upload.atl.chat; - return 301 https://$server_name$request_uri; - } - - # Minimal vhost for atl.chat to serve only XEP-0156 discovery - server { - listen 80; - server_name atl.chat; - return 301 https://$server_name$request_uri; - } - - # HTTPS server for xmpp.atl.chat (service host) - server { - listen 443 ssl; - http2 on; - server_name xmpp.atl.chat; - - # Use existing atl.chat certificate (covers xmpp.atl.chat with wildcard) - ssl_certificate /opt/xmpp.atl.chat/certs/live/atl.chat/fullchain.pem; - ssl_certificate_key /opt/xmpp.atl.chat/certs/live/atl.chat/privkey.pem; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS; - ssl_prefer_server_ciphers off; - - add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; - add_header X-Content-Type-Options nosniff always; - add_header X-Frame-Options DENY always; - add_header X-XSS-Protection "1; mode=block" always; - - # WebSocket (wss://xmpp.atl.chat/xmpp-websocket) - location /xmpp-websocket { - proxy_pass https://xmpp-prosody:5281/xmpp-websocket; - proxy_http_version 1.1; - proxy_ssl_server_name on; - proxy_ssl_name atl.chat; - proxy_set_header Connection "Upgrade"; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Real-IP $remote_addr; - proxy_read_timeout 900s; - proxy_send_timeout 900s; - proxy_connect_timeout 60s; - proxy_buffering off; - proxy_cache off; - proxy_request_buffering off; - proxy_redirect off; - proxy_set_header X-Forwarded-Host $server_name; - } - - # Friendly WebSocket alias - location /ws { - proxy_pass https://xmpp-prosody:5281/xmpp-websocket; - proxy_http_version 1.1; - proxy_ssl_server_name on; - proxy_ssl_name atl.chat; - proxy_set_header Connection "Upgrade"; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Real-IP $remote_addr; - proxy_read_timeout 900s; - proxy_send_timeout 900s; - proxy_connect_timeout 60s; - proxy_buffering off; - proxy_cache off; - proxy_request_buffering off; - proxy_redirect off; - proxy_set_header X-Forwarded-Host $server_name; - } - - # BOSH (https://xmpp.atl.chat/http-bind) - location /http-bind { - proxy_pass https://xmpp-prosody:5281/http-bind; - proxy_http_version 1.1; - proxy_ssl_server_name on; - proxy_ssl_name atl.chat; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_read_timeout 900s; - proxy_send_timeout 900s; - proxy_connect_timeout 60s; - proxy_buffering off; - proxy_cache off; - } - - # Friendly BOSH alias - location /bosh { - proxy_pass https://xmpp-prosody:5281/http-bind; - proxy_http_version 1.1; - proxy_ssl_server_name on; - proxy_ssl_name atl.chat; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_read_timeout 900s; - proxy_send_timeout 900s; - proxy_connect_timeout 60s; - proxy_buffering off; - proxy_cache off; - } - - # Pastebin (mod_pastebin; MUC long messages → paste URLs) - location /paste { - proxy_pass http://xmpp-prosody:5280/paste; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # Upload (Prosody now serves 'upload' at /upload) - location /upload { - proxy_pass http://xmpp-prosody:5280/upload; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_read_timeout 300s; - proxy_send_timeout 300s; - client_max_body_size 100m; - proxy_request_buffering off; - proxy_buffering off; - } - - # Redirect dashed variant to underscore path - location = /file-share { - return 301 /file_share; - } - - # Redirect legacy file_share endpoints to /upload - location = /file_share { - return 301 /upload/; - } - location /file_share/ { - return 301 /upload/; - } - - # Admin endpoint served by Nginx (no proxy) - location /admin { - default_type text/plain; - add_header X-Content-Type-Options nosniff always; - return 403 "Admin is disabled on the proxy.\n"; - } - - # Prosody OpenMetrics (mod_http_openmetrics) over HTTPS upstream - location = /metrics { - proxy_pass https://xmpp-prosody:5281/metrics; - proxy_http_version 1.1; - proxy_ssl_server_name on; - proxy_ssl_name $host; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # XEP-0156 HTTP Alt-Connect discovery (served over HTTPS by Prosody) - location = /.well-known { return 301 /.well-known/host-meta; } - location /.well-known/ { - proxy_pass https://xmpp-prosody:5281/.well-known/; - proxy_http_version 1.1; - proxy_ssl_server_name on; - proxy_ssl_name $host; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - # CORS for host-meta (XEP-0156 recommends allowing cross-origin reads) - add_header Access-Control-Allow-Origin "*" always; - add_header Access-Control-Allow-Methods "GET, OPTIONS" always; - add_header Access-Control-Allow-Headers "Content-Type" always; - if ($request_method = OPTIONS) { - add_header Access-Control-Max-Age 7200 always; - return 204; - } - } - - # Converse.js app served by Prosody (mod_conversejs) - # https://modules.prosody.im/mod_conversejs.html - location /conversejs/ { - proxy_pass http://xmpp-prosody:5280/conversejs/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - proxy_redirect off; - } - - # Nginx stub status moved to a separate endpoint - location = /nginx-status { - stub_status; - access_log off; - } - - # Prosody HTTP Status API (mod_http_status) - MUST come before location / - # https://modules.prosody.im/mod_http_status.html - location = /status { - proxy_pass http://xmpp-prosody:5280/status; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - proxy_redirect off; - proxy_read_timeout 30s; - proxy_connect_timeout 10s; - } - - # Static files served by Nginx directly from /usr/share/nginx/html - location /files { - alias /usr/share/nginx/html/; - autoindex off; - try_files $uri $uri/ =404; - expires 1h; - add_header Cache-Control "public, immutable"; - } - - # Health endpoint served by Nginx (no proxy) - location = /health { - default_type text/plain; - return 200 "ok\n"; - } - - # Serve static files first, then proxy to Prosody - # This MUST come after all specific location blocks - location / { - root /usr/share/nginx/html; - try_files $uri $uri/ @prosody_fallback; - } - - # Fallback to Prosody for dynamic content - location @prosody_fallback { - proxy_pass http://xmpp-prosody:5280; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - proxy_redirect off; - } - } - - # HTTPS server for upload.atl.chat (clean upload host) - server { - listen 443 ssl; - http2 on; - server_name upload.atl.chat; - - # Use existing atl.chat certificate (covers upload.atl.chat with wildcard) - ssl_certificate /opt/xmpp.atl.chat/certs/live/atl.chat/fullchain.pem; - ssl_certificate_key /opt/xmpp.atl.chat/certs/live/atl.chat/privkey.pem; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS; - ssl_prefer_server_ciphers off; - - add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; - add_header X-Content-Type-Options nosniff always; - add_header X-Frame-Options DENY always; - add_header X-XSS-Protection "1; mode=block" always; - - # Proxy to Prosody HTTPS; keep original URI (includes /upload) - # Prosody serves upload via HTTPS and expects SNI/Host = upload.atl.chat - location / { - proxy_pass https://xmpp-prosody:5281; - proxy_ssl_server_name on; - proxy_ssl_name upload.atl.chat; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_read_timeout 300s; - proxy_send_timeout 300s; - client_max_body_size 100m; - proxy_request_buffering off; - proxy_buffering off; - } - - # Health endpoint - location = /health { - default_type text/plain; - return 200 "ok\n"; - } - } - - # HTTPS server for atl.chat to expose only /.well-known/* (XEP-0156) - server { - listen 443 ssl; - http2 on; - server_name atl.chat; - - ssl_certificate /opt/xmpp.atl.chat/certs/live/atl.chat/fullchain.pem; - ssl_certificate_key /opt/xmpp.atl.chat/certs/live/atl.chat/privkey.pem; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS; - ssl_prefer_server_ciphers off; - - add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; - add_header X-Content-Type-Options nosniff always; - add_header X-Frame-Options DENY always; - add_header X-XSS-Protection "1; mode=block" always; - - # Only proxy /.well-known/* to Prosody (HTTPS upstream; requires SNI) - location = /.well-known { return 301 /.well-known/host-meta; } - location /.well-known/ { - proxy_pass https://xmpp-prosody:5281/.well-known/; - proxy_http_version 1.1; - proxy_ssl_server_name on; - proxy_ssl_name atl.chat; - # Host header must match the XMPP domain (VirtualHost), so altconnect serves atl.chat - proxy_set_header Host atl.chat; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - # CORS for host-meta (XEP-0156 recommends allowing cross-origin reads) - add_header Access-Control-Allow-Origin "*" always; - add_header Access-Control-Allow-Methods "GET, OPTIONS" always; - add_header Access-Control-Allow-Headers "Content-Type" always; - if ($request_method = OPTIONS) { - add_header Access-Control-Max-Age 7200 always; - return 204; - } - } - - # Friendly redirects for clients that tried defaults on atl.chat - location = /ws { return 301 https://xmpp.atl.chat/ws; } - location = /http-bind { return 301 https://xmpp.atl.chat/http-bind; } - location = /bosh { return 301 https://xmpp.atl.chat/bosh; } - - # Everything else = 404 (we don't serve atl.chat content here) - location / { return 404; } - } -} diff --git a/docs/examples/nginx-docker.dev.conf b/docs/examples/nginx-docker.dev.conf deleted file mode 100644 index a68635bc..00000000 --- a/docs/examples/nginx-docker.dev.conf +++ /dev/null @@ -1,126 +0,0 @@ -# Nginx development configuration for localhost -# Simplified configuration focused on local development and testing - -user nginx; -worker_processes auto; - -events { - worker_connections 1024; - use epoll; -} - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - sendfile on; - tcp_nopush on; - tcp_nodelay on; - keepalive_timeout 75 20; - types_hash_max_size 2048; - ignore_invalid_headers on; - - # Development server - handles all localhost requests - server { - listen 80; - server_name localhost 127.0.0.1 _; - - # XEP-0156 HTTP Alt-Connect discovery for localhost - location = /.well-known { return 301 /.well-known/host-meta; } - location /.well-known/ { - proxy_pass http://xmpp-prosody-dev:5280/.well-known/; - proxy_http_version 1.1; - proxy_set_header Host localhost; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - # CORS for host-meta (XEP-0156 recommends allowing cross-origin reads) - add_header Access-Control-Allow-Origin "*" always; - add_header Access-Control-Allow-Methods "GET, OPTIONS" always; - add_header Access-Control-Allow-Headers "Content-Type" always; - if ($request_method = OPTIONS) { - add_header Access-Control-Max-Age 7200 always; - return 204; - } - } - - # BOSH for localhost development - location /http-bind { - proxy_pass http://xmpp-prosody-dev:5280/http-bind; - proxy_http_version 1.1; - proxy_set_header Host localhost; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_read_timeout 900s; - proxy_send_timeout 900s; - proxy_connect_timeout 60s; - proxy_buffering off; - proxy_cache off; - proxy_request_buffering off; - } - - # WebSocket for localhost development - location /xmpp-websocket { - proxy_pass http://xmpp-prosody-dev:5281/xmpp-websocket; - proxy_http_version 1.1; - proxy_set_header Connection "Upgrade"; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Host localhost; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Real-IP $remote_addr; - proxy_read_timeout 900s; - proxy_send_timeout 900s; - proxy_connect_timeout 60s; - proxy_buffering off; - proxy_cache off; - proxy_request_buffering off; - } - - # Pastebin (mod_pastebin) - location /paste { - proxy_pass http://xmpp-prosody-dev:5280/paste; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # Converse.js web client (mod_conversejs, served by Prosody) - location /conversejs/ { - proxy_pass http://xmpp-prosody-dev:5280/conversejs/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - proxy_redirect off; - } - - # Prosody status endpoint for debugging - location = /status { - proxy_pass http://xmpp-prosody-dev:5280/status; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - proxy_redirect off; - proxy_read_timeout 30s; - proxy_connect_timeout 10s; - } - - - - # Development test endpoint - location = /dev-test { - default_type text/plain; - return 200 "Development nginx is working on localhost\nXMPP Server: localhost:5222\nBOSH: http://localhost/http-bind\nWebSocket: ws://localhost/xmpp-websocket\n"; - } - - # Everything else = 404 with helpful message - location / { - default_type text/plain; - return 404 "Development nginx server\n\nAvailable endpoints:\n- /.well-known/host-meta (XEP-0156 discovery)\n- /http-bind (BOSH)\n- /xmpp-websocket (WebSocket)\n- /conversejs/ (Web client)\n- /status (Prosody status)\n- /dev-test (This message)\n\nDirect XMPP connection: localhost:5222\n"; - } - } -} diff --git a/docs/examples/prometheus-scrape-config.yml b/docs/examples/prometheus-scrape-config.yml deleted file mode 100644 index b62f122e..00000000 --- a/docs/examples/prometheus-scrape-config.yml +++ /dev/null @@ -1,149 +0,0 @@ ---- -# ============================================================================ -# PROMETHEUS SCRAPE CONFIGURATION FOR PROSODY XMPP SERVER -# ============================================================================ -# Add this configuration to your main Prometheus prometheus.yml file -# under the scrape_configs section -scrape_configs: - # ============================================================================ - # PROSODY XMPP SERVER METRICS - # ============================================================================ - - job_name: xmpp-prosody - metrics_path: /metrics - scheme: http # Use 'https' if SSL enabled on port 5281 - scrape_interval: 30s - scrape_timeout: 10s - static_configs: - - targets: [xmpp.atl.chat:5280] # Change to your domain/IP - labels: - service: prosody - environment: production # Change as needed - instance_type: xmpp_server - domain: atl.chat # Your XMPP domain -# ============================================================================ -# POSTGRESQL DATABASE METRICS (if postgres_exporter is running) -# ============================================================================ -# Uncomment if you have postgres_exporter running on your system -# - job_name: 'postgresql-prosody' -# static_configs: -# - targets: ['xmpp.atl.chat:9187'] # Default postgres_exporter port -# -# scrape_interval: 30s -# static_configs: -# - targets: ['xmpp.atl.chat:9187'] -# labels: -# service: 'postgresql' -# database: 'prosody' -# environment: 'production' - -# ============================================================================ -# COTURN TURN/STUN SERVER METRICS (if coturn exporter is available) -# ============================================================================ -# Note: Coturn doesn't have built-in Prometheus metrics -# You would need a separate exporter like coturn_exporter -# - job_name: 'coturn-stun' -# static_configs: -# - targets: ['xmpp.atl.chat:9641'] # Example coturn_exporter port -# -# scrape_interval: 30s -# static_configs: -# - targets: ['xmpp.atl.chat:9641'] -# labels: -# service: 'coturn' -# environment: 'production' -# ============================================================================ -# PROSODY METRICS AVAILABLE -# ============================================================================ -# The Prosody metrics endpoint provides the following metric families: -# -# Connection Metrics: -# - prosody_c2s_connections_total # Client connections -# - prosody_s2s_connections_total # Server-to-server connections -# - prosody_c2s_auth_success_total # Successful authentications -# - prosody_c2s_auth_failure_total # Failed authentications -# -# Message Metrics: -# - prosody_messages_sent_total # Messages sent -# - prosody_messages_received_total # Messages received -# - prosody_presence_sent_total # Presence updates sent -# - prosody_presence_received_total # Presence updates received -# -# Storage Metrics: -# - prosody_storage_operations_total # Database operations -# - prosody_storage_errors_total # Storage errors -# -# Module Metrics: -# - prosody_module_* # Various module-specific metrics -# -# HTTP Metrics: -# - prosody_http_requests_total # HTTP requests (BOSH/WebSocket) -# - prosody_http_request_duration_seconds # HTTP request duration -# -# System Metrics: -# - prosody_memory_usage_bytes # Memory usage -# - prosody_cpu_usage_seconds_total # CPU usage -# -# ============================================================================ -# GRAFANA DASHBOARD RECOMMENDATIONS -# ============================================================================ -# For visualizing these metrics, consider creating dashboards with: -# -# 1. Connection Overview: -# - Active C2S connections over time -# - S2S connections by domain -# - Authentication success/failure rates -# -# 2. Message Flow: -# - Messages per second -# - Message types breakdown -# - Peak usage times -# -# 3. Performance: -# - Response times -# - Memory and CPU usage -# - Storage operation latency -# -# 4. Errors and Health: -# - Authentication failures -# - Storage errors -# - Connection drops -# -# 5. Module-Specific: -# - File upload statistics -# - MUC (group chat) activity -# - Push notification delivery -# -# ============================================================================ -# ALERTING RULES EXAMPLES -# ============================================================================ -# Add these to your Prometheus alerting rules: -# -# groups: -# - name: prosody.rules -# rules: -# - alert: ProsodyDown -# expr: up{job="xmpp-prosody"} == 0 -# for: 1m -# labels: -# severity: critical -# annotations: -# summary: "Prosody XMPP server is down" -# description: "Prosody has been down for more than 1 minute" -# -# - alert: ProsodyHighAuthFailures -# expr: rate(prosody_c2s_auth_failure_total[5m]) > 10 -# for: 2m -# labels: -# severity: warning -# annotations: -# summary: "High authentication failure rate" -# description: "Authentication failures: {{ $value }} per second" -# -# - alert: ProsodyHighMemoryUsage -# expr: prosody_memory_usage_bytes > 500000000 # 500MB -# for: 5m -# labels: -# severity: warning -# annotations: -# summary: "Prosody memory usage is high" -# description: "Memory usage: {{ humanize $value }}B" diff --git a/docs/infra/containerization.md b/docs/infra/containerization.md deleted file mode 100644 index 5f59389f..00000000 --- a/docs/infra/containerization.md +++ /dev/null @@ -1,38 +0,0 @@ -# Containerization Standards - -The `atl.chat` monorepo utilizes a standardized Docker-based orchestration model to ensure consistency across Local, Staging, and Production environments. - -## Core Principles - -1. **Standardized Naming**: All services use `compose.yaml` (orchestration) and `Containerfile` (build). -2. **Unified Hub**: The root [compose.yaml](../../compose.yaml) aggregates all service-level configurations using the `include` directive. -3. **Orchestration Tooling**: We use `just` as our primary command runner instead of Makefiles for better cross-platform support and monorepo awareness. - -## Service Structure - -Every application in `apps/` must follow this structure: - -``` -apps// -├── compose.yaml # Service-specific orchestration -├── Containerfile # Build instructions -└── .dockerignore # Build context filtering -``` - -## Environment Management - -- **Local Dev**: Handled via `compose.override.yaml` (automatically loaded). -- **Staging/Prod**: Managed via Docker Compose **profiles**. - -## Common Commands - -| Action | Command | Description | -|--------|---------|-------------| -| Start All | `just up` | Spins up the entire stack | -| Start Specific | `just profile= up` | Spins up a specific profile (e.g., `irc`, `xmpp`) | -| Stop | `just down` | Shuts down and cleans up | -| Config Check | `just compose config` | Validates all included compose files | - -## Networking - -All containers connect to a shared internal network named `atl-network`. Inter-service communication should use Docker service names as hostnames. diff --git a/docs/infra/data-structure.md b/docs/infra/data-structure.md deleted file mode 100644 index 6d30c4f6..00000000 --- a/docs/infra/data-structure.md +++ /dev/null @@ -1,53 +0,0 @@ -# Data Directory Structure - -Canonical layout for `data/` (source of truth: `scripts/init.sh` + `infra/compose/`). - -## Canonical Layout - -``` -data/ -├── irc/ -│ ├── data/ # UnrealIRCd: rpc.socket, services.sock, runtime data -│ ├── logs/ # UnrealIRCd logs -│ └── webpanel-data/ # Webpanel persistent data -├── atheme/ -│ ├── data/ # Atheme SQLite: services.db -│ └── logs/ # Atheme logs (atheme.log) -├── xmpp/ -│ ├── data/ # Prosody: prosody.sqlite -│ └── uploads/ # Prosody file uploads -└── certs/ # Certificates - ├── certificates/ # Lego output (prod: _.domain.crt, _.domain.key) - ├── accounts/ # Lego ACME account data - └── live/ # Service layout: live//fullchain.pem, privkey.pem - ├── irc.localhost/ # Dev - └── irc.atl.chat/ # Prod (or copied from Lego) -``` - -## Obsolete Paths (Do Not Use) - -| Obsolete | Use Instead | -|--------------------|-----------------| -| `data/unrealircd/` | `data/irc/data/` | -| `data/letsencrypt/`| `data/certs/` | -| `data/atheme/atheme.db` | `data/atheme/data/services.db` | -| `logs/atheme/` | `data/atheme/logs/` (log file: `atheme.log`) | -| `logs/atl-irc-server/` | `data/irc/logs/` | -| `data/docs/` | Not used; remove if present | - -**Cleanup:** Remove obsolete dirs manually (e.g. `rm -rf data/unrealircd data/letsencrypt data/docs`) if they exist from older setups. - -## Compose Volume Mapping - -| Host Path | Container Mount | Service | -|--------------------------|------------------------------------------|-----------| -| `data/irc/data` | `/home/unrealircd/unrealircd/data` | UnrealIRCd| -| `data/irc/logs` | `/home/unrealircd/unrealircd/logs` | UnrealIRCd| -| `data/irc/webpanel-data` | `/var/www/html/unrealircd-webpanel/data` | Webpanel | -| `data/atheme/data` | `/usr/local/atheme/data` | Atheme | -| `data/atheme/logs` | `/usr/local/atheme/logs` | Atheme | -| `data/xmpp/data` | `/var/lib/prosody/data` | Prosody | -| `data/xmpp/uploads` | `/var/lib/prosody/uploads` | Prosody | -| `data/certs` | `/home/unrealircd/unrealircd/certs` (ro) | UnrealIRCd| -| `data/certs` | `/etc/prosody/certs` | Prosody | -| `apps/bridge/config.yaml`| `/app/config.yaml` (ro) | Bridge | diff --git a/docs/infra/networking.md b/docs/infra/networking.md deleted file mode 100644 index 3c9f458c..00000000 --- a/docs/infra/networking.md +++ /dev/null @@ -1,49 +0,0 @@ -# Global Infrastructure & Networking - -This document outlines the standard networking topology, port allocations, and SSL/DNS patterns for the `atl.chat` ecosystem within the broader All Things Linux Tailnet. - -## Network Topology - -We operate on a distributed "VPC" using Tailscale's CGNAT subnet: `100.64.0.0/10`. - -- **Internal Gateway**: `atl.network` (Tailnet IP: `100.64.1.0`) -- **Chat Services**: `atl.chat` (Tailnet IP: `100.64.7.0`) - -### Traffic Flow - -1. **HTTP/S Traffic**: - `Internet` -> `Cloudflare` -> `atl.network` (Nginx Proxy Manager) -> `Tailnet` -> `atl.chat` (Containers) - -2. **TCP/UDP (IRC/XMPP) Traffic**: - `Internet` -> `atl.network` (NPM Stream Pass-through) -> `Tailnet` -> `atl.chat` (TCP Ports) - ---- - -## Global Port Registry - -To avoid collisions and simplify proxy configuration, we use standardized port allocations. - -| Service | Port | Protocol | Description | -| :--- | :--- | :--- | :--- | -| **IRC** | 6667 | TCP | Plaintext Client Connection | -| **IRC** | 6697 | TCP | SSL/TLS Client Connection | -| **IRC** | 7000 | TCP | WebIRC / WebSocket | -| **XMPP** | 5222 | TCP | Client-to-Server (C2S) | -| **XMPP** | 5269 | TCP | Server-to-Server (S2S) | -| **XMPP** | 5280 | TCP | HTTP (BOSH/Websockets) | -| **XMPP** | 5281 | TCP | HTTPS (BOSH/Websockets) | -| **Web** | 80/443 | TCP | Standard HTTP/S | - ---- - -## SSL & DNS Strategy - -### Hostnames - -Standardize on Tailnet DNS for internal service discovery (e.g., `irc.atl.chat.tailnet-name.ts.net`). - -### Certificates - -- **Termination**: Handled at `atl.network` (Nginx Proxy Manager). -- **Renewal**: Automatic via Cloudflare DNS-01 challenges. -- **Internal Transport**: Tailscale provides wireguard-level encryption between all nodes in the mesh. diff --git a/docs/infra/ssl.md b/docs/infra/ssl.md deleted file mode 100644 index 3c1e692e..00000000 --- a/docs/infra/ssl.md +++ /dev/null @@ -1,33 +0,0 @@ -# SSL/TLS Termination Strategy - -In the `atl.chat` ecosystem, SSL/TLS termination is decentralized from individual application containers and centralized at the network gateway. - -## The `atl.network` Gateway - -All external traffic passes through the **Nginx Proxy Manager (NPM)** instance at the `atl.network` gateway. - -### Traffic Flow - -```mermaid -graph LR - Client["User Client"] -- "TLS (443/6697)" --> NPM["NPM (atl.network)"] - NPM -- "Plaintext (Internal Network)" --> Service["Service (irc/xmpp)"] -``` - -## Benefits of Centralization - -1. **Simplified Backends**: Services like UnrealIRCd or Prosody don't need to manage Let's Encrypt challenges directly. -2. **Unified Certificate Renewal**: All certificates for `*.atl.chat` are managed in one location. -3. **Internal Security**: Communication between NPM and the chat services happens within the secure Tailscale VPC. - -## Port Mappings - -| External Port | Service | Internal Protocol | -|---------------|---------|-------------------| -| 6697 (SSL) | IRC | 6667 (Plaintext) | -| 5222 (STARTTLS)| XMPP | 5222 (Plaintext) | -| 443 (HTTPS) | Web/API | 3000/8080 (HTTP) | - -## Note on Internal TLS - -While external traffic is halted at NPM, some services may still utilize internal self-signed certificates for specific protocol handshakes (e.g., legacy S2S links) if requested by the protocol standard. diff --git a/docs/onboarding/README.md b/docs/onboarding/README.md deleted file mode 100644 index 93b9f639..00000000 --- a/docs/onboarding/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# Onboarding Guide - -Get your local atl.chat development environment set up. - -## Prerequisites - -- Docker and Docker Compose -- [just](https://github.com/casey/just) command runner -- [pre-commit](https://pre-commit.com/) (optional, for git hooks) - -## Quick Start - -```bash -# Clone and enter the repo -cd atl.chat - -# One-time setup: data dirs, config, dev certs -just init - -# Start the stack -just dev -``` - -## Git Hooks (optional) - -```bash -uv run pre-commit install -uv run pre-commit install --hook-type commit-msg -uv run pre-commit install --hook-type prepare-commit-msg -``` - -## Next Steps - -- [Architecture Overview](../architecture/README.md) -- [IRC Setup](../services/irc/README.md) -- [XMPP Setup](../services/xmpp/README.md) diff --git a/docs/services/MODULES.md b/docs/services/MODULES.md deleted file mode 100644 index be64e17e..00000000 --- a/docs/services/MODULES.md +++ /dev/null @@ -1,105 +0,0 @@ -# Module Management Across ATL Chat Services - -This document explains how modules (plugins, extensions) are handled for UnrealIRCd and Prosody in the atl.chat stack. - -For more IRC-specific details (runtime commands, troubleshooting, etc.), see [IRC MODULES.md](irc/MODULES.md). - ---- - -## Overview - -| Service | Module Type | Config File | When Loaded | Location in Image | -|-----------|--------------------|--------------------------|-------------|-------------------------------------| -| UnrealIRCd| Third-party | `third-party-modules.list` | Build time | Compiled into `/home/unrealircd/unrealircd/` | -| Prosody | Community modules | `modules.list` | Build time | `/usr/local/lib/prosody/prosody-modules-enabled/` | -| Prosody | Custom plugins | N/A | Runtime | `/var/lib/prosody/custom_plugins/` (volume) | - ---- - -## UnrealIRCd Modules - -### Flow - -1. **Config**: `apps/unrealircd/third-party-modules.list` lists modules to install (one per line, `#` for comments). -2. **Build**: During `docker build`, the Containerfile runs `unrealircd module install ` for each module. Custom modules (e.g. `third/relaymsg-atl` atl.chat fork) are built from `contrib/relaymsg/relaymsg-atl.c` into `src/modules/third/` before `Config`. The unique name avoids UnrealIRCd's `make` overwriting with upstream during `unrealircd -m upgrade`. -3. **Runtime**: Modules are compiled C code; they live in the UnrealIRCd install directory. No symlinks or separate dirs. - -### Adding/Removing Modules - -1. Edit `apps/unrealircd/third-party-modules.list`. -2. Rebuild the image: `docker compose build atl-irc-server`. - -### Key Paths - -- **Config**: `apps/unrealircd/third-party-modules.list` -- **Catalog**: -- **List installed**: `docker compose exec atl-irc-server unrealircd module list` - ---- - -## Prosody Modules - -### Two-Layer System - -Prosody uses a **source** directory and an **enabled** directory: - -| Directory | Purpose | -|--------------------------|-------------------------------------------------------------------------| -| `prosody-modules` | Full clone of community modules from | -| `prosody-modules-enabled`| Symlinks to only the modules we want (from `modules.list`) | - -Prosody loads modules from `prosody-modules-enabled` via `plugin_paths` in `prosody.cfg.lua`. - -### Build-Time Flow (Docker) - -1. **Config**: `apps/prosody/modules.list` lists which community modules to enable. -2. **Containerfile** (builder stage): - - Clones full `prosody-modules` repo from Mercurial. - - Deletes modules *not* in `modules.list` (reduces image size). - - Creates `prosody-modules-enabled/` with symlinks: `mod_foo` → `../prosody-modules/mod_foo`. - - Handles community modules with subdirs (e.g. `mod_foo/foo/mod_foo.lua`). - - Creates empty LuaRocks manifest at `prosody-modules-enabled/lib/luarocks/rocks/manifest` to silence modulemanager errors. -3. **Runtime stage**: Copies both dirs into `/usr/local/lib/prosody/` in the image. - -### Runtime Configuration - -- **plugin_paths** in `prosody.cfg.lua`: - - `/usr/local/lib/prosody/prosody-modules-enabled` (community modules from image) - - `/var/lib/prosody/custom_plugins` (custom Lua plugins; persisted via volume) -- **modules_enabled**: List in `prosody.cfg.lua` of which modules to actually load. A module can exist in `prosody-modules-enabled` but only load if listed in `modules_enabled`. - -### Adding/Removing Community Modules - -1. Edit `apps/prosody/modules.list` (add or remove module names). -2. Rebuild: `docker compose build atl-xmpp-server`. - -### Custom Plugins (Runtime) - -- Place Lua files in `/var/lib/prosody/custom_plugins/` (mounted from host if you add a volume). -- Add the module name to `modules_enabled` in `prosody.cfg.lua`. -- No image rebuild needed; restart Prosody to load. - ---- - -## LuaRocks Manifest (Prosody) - -Prosody’s modulemanager looks for a LuaRocks manifest at `prosody-modules-enabled/lib/luarocks/rocks/manifest`. We don’t use LuaRocks; we use symlinks from the Mercurial repo. To avoid "Could not load manifest" errors, the Containerfile creates an empty manifest: - -``` -commands = {} -dependencies = {} -modules = {} -repository = {} -``` - ---- - -## Summary - -| Question | Answer | -|----------------------------------------|------------------------------------------------------------------------| -| Do we need `prosody-modules` in the image? | Yes. It’s the source for community modules. | -| Do we need `prosody-modules-enabled` in the image? | Yes. Prosody loads from here via `plugin_paths`. | -| Do we need them on the host? | No. Modules are baked into the image at build time. | -| How to add a Prosody community module? | Add to `modules.list`, rebuild image. | -| How to add an UnrealIRCd third-party module? | Add to `third-party-modules.list`, rebuild image. | diff --git a/docs/services/irc/API.md b/docs/services/irc/API.md deleted file mode 100644 index 597d4bde..00000000 --- a/docs/services/irc/API.md +++ /dev/null @@ -1,292 +0,0 @@ -# API Reference - -This guide covers the programmatic interfaces available for IRC.atl.chat, focusing on the actual JSON-RPC API provided by UnrealIRCd. - -## Overview - -### Available APIs - -IRC.atl.chat provides: - -1. **JSON-RPC API** - UnrealIRCd's built-in administration API (port 8600) -2. **WebSocket IRC** - Standard IRC protocol over WebSocket (port 8000) - -### API Endpoints - -``` -JSON-RPC API: atl-irc-server:8600 (internal) -WebSocket IRC: atl-irc-server:8000 (external) -``` - -## JSON-RPC API - -### Connection Setup - -The JSON-RPC API is built into UnrealIRCd and provides server administration capabilities. - -#### Basic Connection - -```bash -# Test RPC connectivity -curl -X POST http://localhost:8600/api \ - -H "Content-Type: application/json" \ - -d '{ - "jsonrpc": "2.0", - "method": "server.info", - "params": {}, - "id": 1 - }' -``` - -#### Authentication - -The RPC API uses basic authentication configured in UnrealIRCd: - -```c -// In unrealircd.conf -rpc-user adminpanel { - match { ip 127.*; } - rpc-class full; - password "your_rpc_password"; -} -``` - -### Available Methods - -UnrealIRCd's JSON-RPC API provides these core methods: - -#### Server Information - -- `server.info` - Get server information -- `server.stats` - Get server statistics -- `server.command` - Execute server commands - -#### User Management - -- `user.list` - List online users -- `user.get` - Get user details -- `user.action` - Perform user actions (kill, ban, etc.) - -#### Channel Management - -- `channel.list` - List channels -- `channel.get` - Get channel details -- `channel.action` - Perform channel actions - -#### Ban Management - -- `ban.list` - List active bans -- `ban.add` - Add new ban -- `ban.remove` - Remove ban - -### Example Usage - -#### Get Server Information - -```bash -curl -X POST http://localhost:8600/api \ - -H "Content-Type: application/json" \ - -d '{ - "jsonrpc": "2.0", - "method": "server.info", - "params": {}, - "id": 1 - }' -``` - -#### List Online Users - -```bash -curl -X POST http://localhost:8600/api \ - -H "Content-Type: application/json" \ - -d '{ - "jsonrpc": "2.0", - "method": "user.list", - "params": {"limit": 50}, - "id": 2 - }' -``` - -#### Execute Server Command - -```bash -curl -X POST http://localhost:8600/api \ - -H "Content-Type: application/json" \ - -d '{ - "jsonrpc": "2.0", - "method": "server.command", - "params": { - "command": "REHASH" - }, - "id": 3 - }' -``` - -## WebSocket IRC - -### Connection Setup - -WebSocket IRC provides standard IRC protocol over WebSocket connections. - -#### JavaScript Connection - -```javascript -const ws = new WebSocket('ws://localhost:8000'); - -ws.onopen = function() { - // Send IRC registration - ws.send('NICK mynick\r\n'); - ws.send('USER mynick 0 * :Real Name\r\n'); -}; - -ws.onmessage = function(event) { - console.log('Received:', event.data); - // Handle IRC messages -}; -``` - -#### Python Connection - -```python -import websocket - -def on_message(ws, message): - print(f"Received: {message}") - -def on_open(ws): - ws.send('NICK mynick\r\n') - ws.send('USER mynick 0 * :Real Name\r\n') - -ws = websocket.WebSocketApp('ws://localhost:8000', - on_message=on_message, - on_open=on_open) -ws.run_forever() -``` - -### IRC Protocol - -WebSocket connections use standard IRC protocol: - -#### Registration - -``` -NICK mynick -USER mynick 0 * :Real Name -``` - -#### Channel Operations - -``` -JOIN #channel -PRIVMSG #channel :Hello world! -PART #channel -``` - -#### Ping/Pong - -``` -PING :server -PONG :server -``` - -## Configuration - -### RPC Configuration - -Configure the JSON-RPC API in `unrealircd.conf`: - -```c -// RPC modules, Unix socket, and rpc-class blocks are inlined in unrealircd.conf.template - -// Listen on RPC port (TLS) -listen { - ip *; - port 8600; - options { rpc; } -} - -// RPC user configuration -rpc-user adminpanel { - match { ip 127.*; } - rpc-class full; - password "secure_password"; -} -``` - -### WebSocket Configuration - -WebSocket IRC is enabled by default in UnrealIRCd 6.x: - -```c -// WebSocket listener -listen { - ip *; - port 8000; - options { websocket; } -} -``` - -## Troubleshooting - -### RPC Connection Issues - -**Cannot connect to RPC API:** - -1. **Check if RPC is enabled:** - - ```bash - grep -A5 "listen.*rpc" apps/unrealircd/config/unrealircd.conf - ``` - -2. **Verify RPC user configuration:** - - ```bash - grep -A5 "rpc-user" apps/unrealircd/config/unrealircd.conf - ``` - -3. **Test connectivity:** - - ```bash - curl -v http://localhost:8600/api - ``` - -### WebSocket Issues - -**WebSocket connection fails:** - -1. **Check WebSocket listener:** - - ```bash - grep -A5 "websocket" apps/unrealircd/config/unrealircd.conf - ``` - -2. **Test WebSocket connection:** - - ```bash - curl -i -N -H "Connection: Upgrade" \ - -H "Upgrade: websocket" \ - -H "Sec-WebSocket-Version: 13" \ - -H "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==" \ - http://localhost:8000/ - ``` - -## Security Considerations - -### RPC Security - -- **Authentication**: Always use strong RPC passwords -- **Access Control**: Limit RPC access to trusted IPs -- **HTTPS**: Use HTTPS for RPC connections in production - -### WebSocket Security - -- **TLS**: Use WSS (WebSocket Secure) in production -- **Origin Validation**: Validate WebSocket origins -- **Rate Limiting**: Implement connection rate limiting - -## Related Documentation - -- [UNREALIRCD.md](UNREALIRCD.md) - IRC server configuration -- [WEBPANEL.md](WEBPANEL.md) - Web administration interface -- [CONFIG.md](CONFIG.md) - Configuration management -- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Common issues and solutions diff --git a/docs/services/irc/ATHEME.md b/docs/services/irc/ATHEME.md deleted file mode 100644 index 9b023c6c..00000000 --- a/docs/services/irc/ATHEME.md +++ /dev/null @@ -1,651 +0,0 @@ -# Atheme IRC Services - -This guide covers the configuration and management of Atheme IRC Services, which provide essential IRC functionality like NickServ, ChanServ, OperServ, and other services for IRC.atl.chat. - -## Overview - -### Architecture - -- **Version**: Atheme 7.2.12 (latest stable) -- **Purpose**: IRC services daemon providing user and channel management -- **Integration**: Connects to UnrealIRCd via server protocol -- **Database**: SQLite backend for data persistence -- **Authentication**: PBKDF2 password hashing with SASL support - -### Core Services - -``` -Atheme Services: -├── NickServ - Nickname registration and management -├── ChanServ - Channel registration and access control -├── OperServ - Administrative services -├── MemoServ - Private messaging system -├── HelpServ - User assistance -├── InfoServ - Network information -├── BotServ - Channel bot management -├── GroupServ - Account grouping -├── HostServ - Virtual host management -├── SASLServ - SASL authentication -└── Other services (GameServ, etc.) -``` - -## Installation and Setup - -### Container Configuration - -Atheme runs in a dedicated Docker container: - -```yaml -atheme: - build: - context: ./apps/atheme - dockerfile: Containerfile - container_name: atl-irc-services - depends_on: - atl-irc-server: - condition: service_healthy - volumes: - - ./apps/atheme/config:/usr/local/atheme/etc:ro - - ./data/atheme/data:/usr/local/atheme/data - - ./data/atheme/logs:/usr/local/atheme/logs - network_mode: service:atl-irc-server # Shares network with IRCd -``` - -### Service Dependencies - -- **UnrealIRCd**: Must be running and healthy before Atheme starts -- **Network**: Shares network namespace with IRCd for localhost communication -- **Configuration**: Read-only configuration mount -- **Data**: Persistent SQLite database and logs - -## Configuration - -### Main Configuration File - -The primary configuration is generated from `atheme.conf.template`: - -#### Server Connection (Uplink) - -```c -serverinfo { - name = "${ATHEME_SERVER_NAME}"; // services.atl.chat - desc = "${ATHEME_SERVER_DESC}"; // Service description - uplink = "127.0.0.1"; // Local IRCd connection - recontime = ${ATHEME_RECONTIME}; // Reconnect time (10s) - netname = "${ATHEME_NETNAME}"; // Network name - numeric = "${ATHEME_NUMERIC}"; // Server numeric (00A) -} -``` - -#### Authentication - -```c -uplink { - send_password = "${ATHEME_SEND_PASSWORD}"; - receive_password = "${ATHEME_RECEIVE_PASSWORD}"; - port = ${ATHEME_UPLINK_PORT}; // 6901 -} -``` - -### Module Loading - -#### Core Modules - -```c -loadmodule "protocol/unreal4"; // UnrealIRCd protocol support -loadmodule "backend/opensex"; // SQLite database backend -loadmodule "crypto/pbkdf2v2"; // Password hashing -``` - -#### Service Modules - -```c -// Nickname services -loadmodule "nickserv/main"; -loadmodule "nickserv/identify"; -loadmodule "nickserv/register"; -loadmodule "nickserv/info"; - -// Channel services -loadmodule "chanserv/main"; -loadmodule "chanserv/register"; -loadmodule "chanserv/access"; - -// Administrative services -loadmodule "operserv/main"; -loadmodule "operserv/akill"; -loadmodule "operserv/clones"; - -// Additional services -loadmodule "memoserv/main"; -loadmodule "helpserv/main"; -loadmodule "infoserv/main"; -``` - -### Database Configuration - -```c -database { - type = "opensex"; - name = "/usr/local/atheme/data/services.db"; -} -``` - -### SASL Configuration - -```c -loadmodule "saslserv/main"; -loadmodule "saslserv/scram-sha"; - -saslserv { - hidden = yes; -} -``` - -## Service Management - -### Starting Services - -Atheme services start automatically when the container starts: - -```bash -# Check service status -docker logs atheme - -# Verify services are running -docker exec atheme pgrep -f atheme-services -``` - -### Service Commands - -#### NickServ - Nickname Management - -**Registration:** - -```irc -/msg NickServ REGISTER password email@example.com -/msg NickServ IDENTIFY nickname password -``` - -**Account Management:** - -```irc -/msg NickServ SET PASSWORD newpassword -/msg NickServ SET EMAIL newemail@example.com -/msg NickServ INFO nickname -``` - -**Security Features:** - -```irc -/msg NickServ SET HIDEMAIL ON -/msg NickServ SET PRIVATE ON -/msg NickServ GHOST nickname -``` - -#### ChanServ - Channel Management - -**Channel Registration:** - -```irc -/msg ChanServ REGISTER #channel -/msg ChanServ SET #channel FOUNDER -``` - -**Access Control:** - -```irc -/msg ChanServ ACCESS #channel ADD nickname AOP -/msg ChanServ ACCESS #channel DEL nickname -/msg ChanServ ACCESS #channel LIST -``` - -**Channel Settings:** - -```irc -/msg ChanServ SET #channel GUARD ON -/msg ChanServ SET #channel MLOCK +nt -/msg ChanServ TOPIC #channel New topic -``` - -#### OperServ - Administrative Services - -**Network Management:** - -```irc -/msg OperServ AKILL ADD mask reason -/msg OperServ AKILL DEL mask -/msg OperServ AKILL LIST -``` - -**Service Control:** - -```irc -/msg OperServ RESTART -/msg OperServ SHUTDOWN -/msg OperServ JUPE server reason -``` - -**User Management:** - -```irc -/msg OperServ MODE nickname +o -/msg OperServ KILL nickname reason -``` - -#### MemoServ - Private Messaging - -**Sending Memos:** - -```irc -/msg MemoServ SEND nickname message -/msg MemoServ SEND #channel message -``` - -**Managing Memos:** - -```irc -/msg MemoServ LIST -/msg MemoServ READ number -/msg MemoServ DEL number -``` - -### SASL Authentication - -SASL enables automatic authentication: - -```irc -CAP REQ :sasl -AUTHENTICATE PLAIN - -CAP END -``` - -## Database Management - -### SQLite Database - -Atheme stores all data in a SQLite database: - -```bash -# Database location -ls -la data/atheme/data/services.db - -# Backup database -cp data/atheme/data/services.db backup/ - -# Database size -du -h data/atheme/data/services.db -``` - -### Data Persistence - -```bash -# Persistent volumes -data/atheme/data/ # Database (services.db) -data/atheme/logs/ # Service logs -``` - -### Database Maintenance - -```bash -# Check database integrity -sqlite3 data/atheme/data/services.db "PRAGMA integrity_check;" - -# Optimize database -sqlite3 data/atheme/data/services.db "VACUUM;" - -# View registered nicknames -sqlite3 data/atheme/data/services.db "SELECT * FROM nick_table LIMIT 10;" -``` - -## Security Features - -### Password Hashing - -Atheme uses PBKDF2 v2 for secure password hashing: - -```c -crypto { - pbkdf2v2_digest = "SHA-512"; - pbkdf2v2_rounds = 32768; -} -``` - -### Access Controls - -#### Operator Permissions - -```c -operclass "sra" { - privileges = "admin:*"; -} - -oper "admin" { - operclass = "sra"; -} -``` - -#### Service Restrictions - -```c -nickserv { - register_enabled = yes; - maxnicks = 5; - spam = yes; -} -``` - -### Flood Protection - -```c -floodserv { - enabled = yes; - threshold = 5; - action = "AKILL"; -} -``` - -## Monitoring and Logging - -### Log Configuration - -```c -log { - file = "/usr/local/atheme/logs/atheme.log"; - level = "info"; - source = "*"; -} -``` - -### Log Analysis - -```bash -# View recent logs -tail -f data/atheme/logs/atheme.log - -# Search for specific events -grep "REGISTER" data/atheme/logs/atheme.log - -# Monitor failed authentications -grep "BADPASSWORD" data/atheme/logs/atheme.log -``` - -### Service Health Checks - -```bash -# Container health -docker ps atheme - -# Service process -docker exec atheme ps aux | grep atheme - -# Network connectivity -docker exec atheme nc -z localhost 6901 -``` - -## User Experience - -### Registration Process - -1. **Connect to IRC** - - ```irc - /server irc.atl.chat +6697 - ``` - -2. **Register Nickname** - - ```irc - /msg NickServ REGISTER mypassword user@example.com - ``` - -3. **Verify Email** (if required) - - Check email for verification code - - ```irc - /msg NickServ VERIFY code - ``` - -4. **Identify** - - ```irc - /msg NickServ IDENTIFY mypassword - ``` - -### Channel Management - -1. **Register Channel** - - ```irc - /join #mychannel - /msg ChanServ REGISTER #mychannel - ``` - -2. **Set Channel Modes** - - ```irc - /msg ChanServ SET #mychannel MLOCK +nt - /msg ChanServ SET #mychannel GUARD ON - ``` - -3. **Manage Access** - - ```irc - /msg ChanServ ACCESS #mychannel ADD friend AOP - ``` - -## Troubleshooting - -### Common Issues - -#### Services Not Starting - -```bash -# Check container logs -docker logs atheme - -# Verify IRCd connectivity -docker exec atheme nc -z localhost 6901 - -# Check configuration syntax -docker exec atheme atheme-services -c /usr/local/atheme/etc/atheme.conf -``` - -#### Authentication Problems - -```bash -# Check password hash -grep "BADPASSWORD" data/atheme/logs/atheme.log - -# Verify user registration -sqlite3 data/atheme/data/services.db "SELECT * FROM nick_table WHERE nick='nickname';" -``` - -#### Database Issues - -```bash -# Check database file -ls -la data/atheme/data/services.db - -# Repair database -sqlite3 data/atheme/data/services.db ".recover" > recovery.sql - -# Restore from backup -cp backup/services.db data/atheme/data/services.db -``` - -#### Memory Issues - -```bash -# Monitor memory usage -docker stats atheme - -# Check for memory leaks -docker exec atheme ps aux | grep atheme -``` - -### Debug Mode - -Enable detailed logging: - -```c -log { - file = "/usr/local/atheme/logs/debug.log"; - level = "debug"; - source = "*"; -} -``` - -### Service Recovery - -If services become unresponsive: - -```bash -# Restart container -docker restart atheme - -# Force service restart -docker exec atheme pkill atheme-services -docker exec atheme atheme-services -n -``` - -## Advanced Configuration - -### Custom Services - -Add custom service modules: - -```c -loadmodule "myservice/main"; - -service "myservice" { - nick = "MyServ"; - user = "myserv"; - host = "services.atl.chat"; - real = "My Custom Service"; -} -``` - -### Integration Features - -#### IRCv3 Support - -```c -loadmodule "extensions/ircv3"; - -ircv3 { - enabled = yes; -} -``` - -#### Web Interface - -```c -loadmodule "httpd/main"; - -httpd { - host = "127.0.0.1"; - port = 8081; -} -``` - -### Backup and Recovery - -#### Automated Backups - -```bash -# Database backup script -#!/bin/bash -sqlite3 data/atheme/data/services.db ".backup backup/atheme-$(date +%Y%m%d).db" - -# Configuration backup -cp apps/atheme/config/atheme.conf backup/ -``` - -#### Recovery Process - -```bash -# Stop services -docker stop atheme - -# Restore database -cp backup/atheme-latest.db data/atheme/data/services.db - -# Restore configuration -cp backup/atheme.conf apps/atheme/config/ - -# Restart services -make up -``` - -## Performance Tuning - -### Connection Limits - -```c -serverinfo { - maxclients = 1000; - maxchans = 100; -} -``` - -### Cache Settings - -```c -cache { - expiry = 3600; // 1 hour cache - size = 1048576; // 1MB cache -} -``` - -### Database Optimization - -```c -database { - optimize = yes; - wal_mode = yes; // Write-ahead logging -} -``` - -## Maintenance - -### Regular Tasks - -```bash -# Check service health -make status - -# Monitor logs -make logs-atheme - -# Backup database -./scripts/backup-atheme.sh - -# Update services -make restart -``` - -### Version Upgrades - -```bash -# Backup current setup -./scripts/backup-atheme.sh - -# Update container -make build - -# Test new version -make up - -# Rollback if needed -docker tag atheme:latest atheme:backup -``` - -## Related Documentation - -- [UNREALIRCD.md](UNREALIRCD.md) - IRC server configuration -- [USERMODES.md](USERMODES.md) - IRC user mode reference -- [CONFIG.md](CONFIG.md) - Configuration management -- [Atheme Documentation](https://atheme.dev/) - Official Atheme docs -- [IRC Services](https://wiki.ircnet.net/index.php/IRC_Services) - Services overview diff --git a/docs/services/irc/BACKUP_RECOVERY.md b/docs/services/irc/BACKUP_RECOVERY.md deleted file mode 100644 index f5f2be24..00000000 --- a/docs/services/irc/BACKUP_RECOVERY.md +++ /dev/null @@ -1,135 +0,0 @@ -# Backup & Recovery - -This guide covers essential backup procedures for IRC.atl.chat data and configuration. - -## What to Backup - -### Critical Data - -- **Atheme Database**: User accounts, channel registrations (`data/atheme/data/services.db`) -- **SSL Certificates**: Private keys and certificates (`data/certs/`) -- **Configuration**: Environment variables (`.env`) - -### Optional Data - -- **Logs**: Service logs (`data/irc/logs/`, `data/atheme/logs/`) -- **Channel Data**: UnrealIRCd runtime data (`data/irc/data/`) - -## Backup Procedures - -### Manual Backup - -```bash -# Create backup directory -mkdir -p backup/$(date +%Y%m%d) - -# Backup Atheme database -cp -r data/atheme backup/$(date +%Y%m%d)/ - -# Backup SSL certificates -cp -r data/certs backup/$(date +%Y%m%d)/ - -# Backup configuration -cp .env backup/$(date +%Y%m%d)/ - -# Create archive -tar -czf backup-$(date +%Y%m%d).tar.gz backup/$(date +%Y%m%d)/ -``` - -### Automated Backup Script - -```bash -#!/bin/bash -# Simple backup script - -BACKUP_DIR="backup/$(date +%Y%m%d)" -mkdir -p "$BACKUP_DIR" - -# Stop services -docker compose down - -# Backup data -cp -r data/ "$BACKUP_DIR/" -cp .env "$BACKUP_DIR/" - -# Create archive -tar -czf "irc-backup-$(date +%Y%m%d).tar.gz" "$BACKUP_DIR" - -# Start services -docker compose up -d - -echo "Backup completed: irc-backup-$(date +%Y%m%d).tar.gz" -``` - -## Recovery Procedures - -### Restore from Backup - -```bash -# Stop services -docker compose down - -# Extract backup -tar -xzf irc-backup-YYYYMMDD.tar.gz - -# Restore data -cp -r backup/YYYYMMDD/data/* data/ -cp backup/YYYYMMDD/.env .env - -# Start services -docker compose up -d -``` - -### Verify Recovery - -```bash -# Check services are running -make status - -# Test IRC connection -make test-irc - -# Check SSL certificates -make ssl-status -``` - -## Best Practices - -1. **Regular Backups**: Weekly automated backups -2. **Test Restores**: Monthly restore testing -3. **Offsite Storage**: Store backups in different location -4. **Documentation**: Keep backup procedures documented - -## Troubleshooting - -### Backup Issues - -```bash -# Check disk space -df -h - -# Check backup integrity -tar -tzf backup-file.tar.gz - -# Verify data permissions -ls -la data/ -``` - -### Recovery Issues - -```bash -# Check service logs -make logs - -# Verify configuration -make test-env - -# Check file permissions -ls -la data/ logs/ -``` - -## Related Documentation - -- [CONFIG.md](CONFIG.md) - Configuration management -- [SSL.md](SSL.md) - SSL certificate management -- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Common issues and solutions diff --git a/docs/services/irc/CI_CD.md b/docs/services/irc/CI_CD.md deleted file mode 100644 index 672b2da6..00000000 --- a/docs/services/irc/CI_CD.md +++ /dev/null @@ -1,808 +0,0 @@ -# CI/CD Pipeline - -This guide covers the comprehensive CI/CD pipeline for IRC.atl.chat, including automated testing, building, security scanning, and deployment workflows using GitHub Actions. - -## Overview - -### Pipeline Architecture - -IRC.atl.chat uses GitHub Actions for a complete CI/CD pipeline with the following workflows: - -``` -CI/CD Workflows: -├── ci.yml - Continuous Integration (linting, validation) -├── docker.yml - Docker building and publishing -├── security.yml - Security scanning and vulnerability checks -├── release.yml - Release automation and tagging -├── deploy.yml - Deployment automation -├── maintenance.yml - Automated maintenance tasks -└── cleanup.yml - Cleanup and artifact management -``` - -### Key Features - -- **Multi-stage validation**: Linting, testing, security scanning -- **Automated building**: Docker image creation and publishing -- **Security-first**: Vulnerability scanning and secret detection -- **Release automation**: Semantic versioning and changelog generation -- **Maintenance automation**: Dependency updates and cleanup - -## CI Workflow (ci.yml) - -### Purpose - -Primary CI workflow that runs on every push and pull request to validate code quality, security, and functionality. - -### Triggers - -```yaml -on: - push: - branches: [main] - pull_request: - branches: [main] - workflow_dispatch: -``` - -### Jobs Overview - -#### 1. File Detection (`changes`) - -Detects which types of files have changed to optimize workflow execution: - -```yaml -jobs: - changes: - outputs: - docker: ${{ steps.docker_changes.outputs.any_changed }} - shell: ${{ steps.shell_changes.outputs.any_changed }} - workflows: ${{ steps.workflow_changes.outputs.any_changed }} - yaml: ${{ steps.yaml_changes.outputs.any_changed }} -``` - -**Detection Rules:** - -- **Docker**: Containerfile, Dockerfile, compose.yaml, .dockerignore -- **Shell**: *.sh,*.bash, *.zsh, scripts/ directory -- **Workflows**: .github/workflows/ files -- **YAML**: *.yml,*.yaml, .github/ files - -#### 2. Shell Linting (`shell`) - -Runs when shell scripts change (excluding Renovate bot): - -```yaml -shell: - needs: [changes] - if: needs.changes.outputs.shell == 'true' && github.actor != 'renovate[bot]' -``` - -**Tools:** - -- **shellcheck**: Static analysis for shell scripts -- **shfmt**: Shell script formatting and validation - -#### 3. Workflow Validation (`workflows`) - -Validates GitHub Actions workflow syntax: - -```yaml -workflows: - needs: [changes] - if: needs.changes.outputs.workflows == 'true' && github.actor != 'renovate[bot]' -``` - -**Tools:** - -- **actionlint**: GitHub Actions workflow linter - -#### 4. Docker Linting (`docker`) - -Validates Docker and container configurations: - -```yaml -docker: - needs: [changes] - if: needs.changes.outputs.docker == 'true' && github.actor != 'renovate[bot]' -``` - -**Tools:** - -- **hadolint**: Dockerfile linter with security checks -- **dclint**: Docker Compose linter - -#### 5. YAML Validation (`yaml`) - -Validates YAML syntax and structure: - -```yaml -yaml: - needs: [changes] - if: needs.changes.outputs.yaml == 'true' && github.actor != 'renovate[bot]' -``` - -**Tools:** - -- **yamllint**: YAML syntax and style validation - -#### 6. Security Scanning (`security`) - -Runs security checks on all changes: - -```yaml -security: - needs: [changes] - if: always() && github.actor != 'renovate[bot]' -``` - -**Tools:** - -- **gitleaks**: Secret detection and credential scanning - -## Docker Workflow (docker.yml) - -### Purpose - -Handles Docker image building, validation, publishing, and maintenance for all IRC.atl.chat services. - -### Triggers - -```yaml -on: - push: - tags: [v*] # Release tags - pull_request: - branches: [main] # PR validation - workflow_dispatch: # Manual trigger - schedule: - - cron: 0 2 15 * * # Monthly cleanup -``` - -### Jobs Overview - -#### 1. File Detection (`changes`) - -Detects Docker-related file changes to optimize builds. - -#### 2. Validation (`validate`) - -Validates Docker builds without publishing (runs on PRs): - -```yaml -validate: - strategy: - matrix: - service: [unrealircd, atheme, atl-irc-webpanel] -``` - -**Per-Service Validation:** - -- Build container images -- Cache layers for faster builds -- Security scanning with Trivy -- PR-specific tagging (pr-123-service) - -#### 3. Build & Push (`build`) - -Builds and publishes Docker images (runs on releases): - -```yaml -build: - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') -``` - -**Publishing Strategy:** - -- **Registry**: GitHub Container Registry (ghcr.io) -- **Tagging**: Semantic versioning (v1.0.0, v1.0, latest) -- **Metadata**: OCI image labels and annotations -- **Security**: Final image vulnerability scanning - -#### 4. Cleanup (`cleanup`) - -Monthly maintenance to clean old container images: - -```yaml -cleanup: - if: github.event_name == 'schedule' -``` - -**Cleanup Policy:** - -- Keep last 15 versions -- Remove untagged images -- Automated monthly execution - -### Build Configuration - -#### Multi-Service Matrix - -```yaml -strategy: - matrix: - service: [unrealircd, atheme, atl-irc-webpanel] -``` - -#### Build Arguments - -```yaml -build-args: | - VERSION=${{ steps.release_version.outputs.version }} - GIT_SHA=${{ github.sha }} - BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') -``` - -#### Image Metadata - -```yaml -labels: | - org.opencontainers.image.title=IRC.atl.chat - ${{ matrix.service }} - org.opencontainers.image.description=IRC server infrastructure for All Things Linux Community - org.opencontainers.image.source=https://github.com/allthingslinux/irc.atl.chat - org.opencontainers.image.licenses=GPL-3.0 -``` - -## Security Workflow (security.yml) - -### Purpose - -Comprehensive security scanning and vulnerability assessment. - -### Triggers - -```yaml -on: - push: - branches: [main] - pull_request: - branches: [main] - schedule: - - cron: 0 6 * * 1 # Weekly security scan -``` - -### Security Tools - -#### CodeQL Analysis - -```yaml -- name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: javascript,python,shell - -- name: Autobuild - uses: github/codeql-action/autobuild@v3 - -- name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 -``` - -#### Dependency Scanning - -```yaml -- name: Dependency Review - uses: actions/dependency-review-action@v4 -``` - -#### Container Scanning - -```yaml -- name: Container Scan - uses: anchore/scan-action@v3 - with: - image: ghcr.io/allthingslinux/irc.atl.chat-atl-irc-server:latest -``` - -#### Secret Detection - -```yaml -- name: Secret Scan - uses: trufflesecurity/trufflehog@main - with: - path: ./ - base: main - head: HEAD -``` - -## Release Workflow (release.yml) - -### Purpose - -Automated release creation, versioning, and changelog generation. - -### Triggers - -```yaml -on: - push: - tags: [v*] -``` - -### Release Process - -#### 1. Version Extraction - -```yaml -- name: Get Version - run: | - VERSION=${GITHUB_REF#refs/tags/v} - echo "version=$VERSION" >> $GITHUB_OUTPUT -``` - -#### 2. Changelog Generation - -```yaml -- name: Generate Changelog - uses: tj-actions/git-cliff@v1 - with: - configuration: cliff.toml - args: --latest --strip header - env: - OUTPUT: CHANGELOG.md -``` - -#### 3. Release Creation - -```yaml -- name: Create Release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - body_path: CHANGELOG.md -``` - -#### 4. Artifact Publishing - -```yaml -- name: Upload Release Assets - uses: actions/upload-release-asset@v1 - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./artifacts/release.zip - asset_name: irc-atl-chat-${{ env.VERSION }}.zip -``` - -## Deployment Workflow (deploy.yml) - -### Purpose - -Automated deployment to production environment. - -### Triggers - -```yaml -on: - release: - types: [published] - workflow_dispatch: -``` - -### Deployment Strategy - -#### Environment-Based Deployment - -```yaml -jobs: - deploy: - environment: production - runs-on: ubuntu-latest -``` - -#### Deployment Steps - -```yaml -- name: Deploy to ${{ env.ENVIRONMENT }} - run: | - # Update deployment - kubectl set image deployment/irc-atl-chat \ - irc-atl-chat=ghcr.io/allthingslinux/irc.atl.chat:latest - - # Wait for rollout - kubectl rollout status deployment/irc-atl-chat - - # Health check - curl -f https://irc.atl.chat/health -``` - -## Maintenance Workflow (maintenance.yml) - -### Purpose - -Automated maintenance tasks including dependency updates and health checks. - -### Triggers - -```yaml -on: - schedule: - - cron: 0 2 * * * # Daily at 2 AM - workflow_dispatch: -``` - -### Maintenance Tasks - -#### Dependency Updates - -```yaml -- name: Update Dependencies - uses: renovatebot/github-action@v39 - with: - configurationFile: .github/renovate.json -``` - -#### Health Checks - -```yaml -- name: Health Check - run: | - # Check service status - curl -f https://irc.atl.chat/status - - # Verify SSL certificate - openssl s_client -connect irc.atl.chat:6697 -servername irc.atl.chat < /dev/null - - # Check container health - docker ps --filter "name=irc-atl-chat" -``` - -#### Log Rotation - -```yaml -- name: Rotate Logs - run: | - # Compress old logs - find /var/log/irc -name "*.log" -mtime +7 -exec gzip {} \; - - # Clean old compressed logs - find /var/log/irc -name "*.gz" -mtime +30 -delete -``` - -## Cleanup Workflow (cleanup.yml) - -### Purpose - -Automated cleanup of artifacts, caches, and temporary files. - -### Triggers - -```yaml -on: - schedule: - - cron: 0 3 * * 0 # Weekly on Sunday - workflow_dispatch: -``` - -### Cleanup Tasks - -#### Artifact Cleanup - -```yaml -- name: Clean Artifacts - uses: actions/github-script@v7 - with: - script: | - const artifacts = await github.rest.actions.listArtifactsForRepo({ - owner: context.repo.owner, - repo: context.repo.repo, - }); - - // Delete artifacts older than 30 days - const cutoff = new Date(); - cutoff.setDate(cutoff.getDate() - 30); - - for (const artifact of artifacts.data.artifacts) { - if (new Date(artifact.created_at) < cutoff) { - await github.rest.actions.deleteArtifact({ - owner: context.repo.owner, - repo: context.repo.repo, - artifact_id: artifact.id, - }); - } - } -``` - -#### Cache Cleanup - -```yaml -- name: Clean Cache - run: | - # Clean GitHub Actions cache - gh extension install actions/gh-actions-cache - gh actions-cache delete --all --confirm -``` - -#### Registry Cleanup - -```yaml -- name: Clean Registry - uses: actions/delete-package-versions@v5 - with: - package-name: irc-atl-chat-* - package-type: container - min-versions-to-keep: 10 -``` - -## Workflow Configuration - -### Common Configuration - -#### Environment Variables - -```yaml -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - DOCKER_BUILD_SUMMARY: true - DOCKER_BUILD_CHECKS_ANNOTATIONS: true -``` - -#### Permissions - -```yaml -permissions: - contents: read - packages: write - pull-requests: write - security-events: write -``` - -### Reusable Workflows - -#### Testing Workflow - -```yaml -jobs: - test: - uses: ./.github/workflows/test.yml - secrets: inherit -``` - -#### Security Workflow - -```yaml -jobs: - security: - uses: ./.github/workflows/security.yml - secrets: inherit -``` - -## Monitoring and Alerts - -### Workflow Status Monitoring - -#### Status Badges - -```markdown -![CI](https://github.com/allthingslinux/irc.atl.chat/workflows/CI/badge.svg) -![Docker](https://github.com/allthingslinux/irc.atl.chat/workflows/Docker/badge.svg) -![Security](https://github.com/allthingslinux/irc.atl.chat/workflows/Security/badge.svg) -``` - -#### Alert Configuration - -```yaml -- name: Notify on Failure - if: failure() - uses: 8398a7/action-slack@v3 - with: - status: ${{ job.status }} - webhook_url: ${{ secrets.SLACK_WEBHOOK }} -``` - -### Performance Monitoring - -#### Workflow Metrics - -```yaml -- name: Workflow Telemetry - uses: codacy/git-version@2.7.1 - with: - release-branch: main - dev-branch: develop -``` - -#### Resource Usage - -```yaml -- name: Monitor Resources - run: | - # Check runner disk space - df -h - - # Check runner memory - free -h - - # Log workflow duration - echo "Workflow completed in $SECONDS seconds" -``` - -## Troubleshooting - -### Common CI/CD Issues - -#### Workflow Not Triggering - -```yaml -# Check trigger conditions -on: - push: - branches: [main] # Must push to main branch - pull_request: - branches: [main] # Must target main branch -``` - -#### Permission Errors - -```yaml -# Check permissions -permissions: - contents: read # Required for checkout - packages: write # Required for GHCR publishing - pull-requests: write # Required for reviews -``` - -#### Cache Issues - -```yaml -# Clear cache manually -gh actions-cache delete --all --confirm - -# Check cache usage -gh actions-cache list -``` - -#### Build Failures - -```yaml -# Check build logs -# Look for: -# - Missing dependencies -# - Network timeouts -# - Disk space issues -# - Permission problems -``` - -### Debug Mode - -#### Enable Debug Logging - -```yaml -- name: Enable Debug - run: | - echo "ACTIONS_RUNNER_DEBUG=true" >> $GITHUB_ENV - echo "ACTIONS_STEP_DEBUG=true" >> $GITHUB_ENV -``` - -#### Manual Workflow Dispatch - -```yaml -# Trigger workflow manually -gh workflow run ci.yml --ref main -``` - -## Best Practices - -### Workflow Organization - -#### Naming Conventions - -```yaml -# Consistent naming -name: CI Pipeline # Clear, descriptive names -name: Docker Build # Specific to function -name: Security Scan # Purpose-driven naming -``` - -#### Job Structure - -```yaml -jobs: - lint: # Fast, parallel jobs first - test: # Core validation - build: # Artifact creation - deploy: # Deployment (conditional) -``` - -### Security Best Practices - -#### Secret Management - -```yaml -# Use GitHub secrets -secrets: - DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} - SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} - -# Never hardcode secrets -# Use environment variables for non-sensitive config -``` - -#### Access Control - -```yaml -# Restrict workflow triggers -on: - push: - branches: [main] - pull_request: - branches: [main] - # No manual triggers for sensitive workflows -``` - -### Performance Optimization - -#### Caching Strategy - -```yaml -- name: Cache Dependencies - uses: actions/cache@v4 - with: - path: ~/.npm - key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }} - -- name: Cache Docker Layers - uses: actions/cache@v4 - with: - path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-${{ github.sha }} -``` - -#### Parallel Execution - -```yaml -strategy: - matrix: - service: [unrealircd, atheme, webpanel] - max-parallel: 3 # Control parallelism -``` - -## Integration with External Tools - -### Renovate Integration - -#### Dependency Updates - -```json -{ - "extends": ["config:base"], - "schedule": ["before 4am on Monday"], - "labels": ["dependencies"], - "reviewers": ["team:maintainers"] -} -``` - -### CodeQL Integration - -#### Advanced Configuration - -```yaml -- name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: javascript,python - queries: security-and-quality -``` - -### Slack Integration - -#### Notification Configuration - -```yaml -- name: Notify Slack - uses: 8398a7/action-slack@v3 - with: - status: ${{ job.status }} - fields: repo,message,commit,author,action,eventName,ref,workflow - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} -``` - -## Related Documentation - -- [TESTING.md](TESTING.md) - Test suite documentation -- [DOCKER.md](DOCKER.md) - Container setup details -- [README.md](../README.md) - Quick start guide -- [GitHub Actions Documentation](https://docs.github.com/en/actions) - Official docs diff --git a/docs/services/irc/CONFIG.md b/docs/services/irc/CONFIG.md deleted file mode 100644 index 4e4e3367..00000000 --- a/docs/services/irc/CONFIG.md +++ /dev/null @@ -1,664 +0,0 @@ -# Configuration System - -This guide covers the configuration management system for IRC.atl.chat, including template processing, environment variables, and automated configuration generation. - -## Overview - -### Configuration Architecture - -IRC.atl.chat uses a template-based configuration system: - -``` -Configuration Flow: -├── .env.example - Template with defaults -├── .env - User configuration -├── *.template - Configuration templates -├── envsubst - Variable substitution -└── *.conf - Generated configurations -``` - -### Key Principles - -- **Template-driven**: All configuration from templates -- **Environment-based**: Variables from `.env` file -- **Automated generation**: No manual config editing -- **Version control safe**: Templates committed, configs ignored -- **Validation**: Automated config checking - -## Environment Configuration - -### .env File Structure - -The `.env` file is the central configuration point: - -```bash -# ============================================================================= -# IRC.atl.chat Environment Configuration -# ============================================================================= - -# Core system settings -UNREALIRCD_VERSION=6.2.0.1 -PUID=1000 -PGID=1000 -TZ=UTC - -# Network identity -IRC_DOMAIN=irc.atl.chat -IRC_ROOT_DOMAIN=atl.chat -IRC_NETWORK_NAME=atl.chat - -# Security settings -IRC_OPER_PASSWORD='$argon2id$...' -LETSENCRYPT_EMAIL=admin@atl.chat - -# Service configuration -ATHEME_SEND_PASSWORD=secure_password_here -WEBPANEL_RPC_PASSWORD=admin_password -``` - -### Environment Variable Categories - -#### System Configuration - -```bash -# Container user/group IDs (match host user) -PUID=1000 # Host user ID -PGID=1000 # Host group ID -TZ=UTC # System timezone - -# Software versions -UNREALIRCD_VERSION=6.2.0.1 # IRC server version -``` - -#### Network Configuration - -```bash -# Domain and network identity -IRC_DOMAIN=irc.atl.chat # Server hostname -IRC_ROOT_DOMAIN=atl.chat # Base domain -IRC_NETWORK_NAME=atl.chat # Network name - -# Port configuration -IRC_TLS_PORT=6697 # IRC over TLS -IRC_SERVER_PORT=6900 # Server linking -IRC_RPC_PORT=8600 # JSON-RPC API -IRC_WEBSOCKET_PORT=8000 # WebSocket IRC -``` - -#### Security Configuration - -```bash -# Authentication -IRC_OPER_PASSWORD='$argon2id$...' # IRC operator password -LETSENCRYPT_EMAIL=admin@domain.com # SSL certificate email - -# Service passwords -ATHEME_SEND_PASSWORD=password # Atheme-IRCd communication -ATHEME_RECEIVE_PASSWORD=password # Atheme-IRCd communication -IRC_SERVICES_PASSWORD=password # Services password -``` - -### Environment File Management - -#### Creating .env File - -```bash -# Copy template -cp .env.example .env - -# Edit with your values -vim .env - -# Secure permissions -chmod 600 .env -``` - -#### Environment Validation - -```bash -# Validate .env syntax -bash -n .env - -# Test variable substitution -env | grep IRC_ -``` - -## Template System - -### Template Processing - -#### Template Files - -Configuration templates use environment variable substitution: - -```bash -# Template syntax -${VARIABLE_NAME} -${VARIABLE_NAME:-default_value} - -# Generated output -actual_value -default_value (if VARIABLE_NAME not set) -``` - -#### Processing Pipeline - -```bash -# 1. Read template -cat unrealircd.conf.template - -# 2. Substitute variables -envsubst < unrealircd.conf.template > unrealircd.conf - -# 3. Set permissions -chmod 644 unrealircd.conf -``` - -### Template Locations - -#### UnrealIRCd Templates - -``` -apps/unrealircd/config/ -├── unrealircd.conf.template # Main server config (modules inlined) -├── operclass.default.conf # Operator classes -└── aliases/atheme.conf # Service aliases -``` - -#### Atheme Templates - -``` -apps/atheme/config/ -└── atheme.conf.template # Services configuration -``` - -### Template Examples - -#### UnrealIRCd Main Config - -```c -me { - name "${IRC_DOMAIN}"; - info "${IRC_NETWORK_NAME} IRC Server"; -}; - -admin { - "${IRC_ADMIN_NAME}"; - "admin"; - "${IRC_ADMIN_EMAIL}"; -}; - -listen { - ip *; - port ${IRC_TLS_PORT}; - options { tls; }; -}; -``` - -#### Atheme Services Config - -```c -serverinfo { - name = "${ATHEME_SERVER_NAME}"; - uplink = "127.0.0.1"; - recontime = ${ATHEME_RECONTIME}; -}; - -uplink { - send_password = "${ATHEME_SEND_PASSWORD}"; - receive_password = "${ATHEME_RECEIVE_PASSWORD}"; - port = ${ATHEME_UPLINK_PORT}; -}; -``` - -## Configuration Generation - -### Automated Processing - -#### Build-time Generation - -```bash -# Triggered by make up -# Configuration is processed automatically during container build - -# What happens: -# 1. Templates are processed with envsubst -# 2. Generated .conf files are created -# 3. Proper permissions are set -``` - -#### Manual Generation - -```bash -# Process specific template -export IRC_DOMAIN=irc.example.com -envsubst < template.conf > generated.conf -``` - -### Generated Files - -#### Gitignore Pattern - -Generated configurations are automatically ignored: - -```gitignore -# Generated configurations -apps/unrealircd/config/*.conf -apps/atheme/config/*.conf -!apps/unrealircd/config/*.template -!apps/atheme/config/*.template -!apps/unrealircd/config/modules.*.conf -!apps/atheme/config/modules.*.conf -!apps/unrealircd/config/operclass.*.conf -!apps/atheme/config/operclass.*.conf -``` - -#### File Permissions - -```bash -# Configuration files --rw-r--r-- 644 *.conf - -# Credential files --rw------- 600 .env --rw------- 600 cloudflare-credentials.ini -``` - -## Validation and Testing - -### Configuration Validation - -#### Syntax Checking - -```bash -# UnrealIRCd config validation -unrealircd -c /path/to/unrealircd.conf - -# Atheme config validation -atheme-services -c /path/to/atheme.conf -``` - -#### Environment Validation - -```bash -# Test environment setup -make test-env - -# Check variable formats manually -grep -E "^[A-Z_]+=" .env -``` - -### Testing Configurations - -#### Dry Run Testing - -```bash -# Test UnrealIRCd config -docker run --rm -v $(pwd)/apps/unrealircd/config:/config \ - unrealircd unrealircd -c /config/unrealircd.conf -t - -# Test Atheme config -docker run --rm -v $(pwd)/apps/atheme/config:/config \ - atheme atheme-services -c /config/atheme.conf -n -``` - -#### Runtime Testing - -```bash -# Start with test config -make test-env - -# Validate service startup -make test-irc -``` - -## Security Considerations - -### Secret Management - -#### Password Storage - -```bash -# Hashed passwords (IRC operators) -IRC_OPER_PASSWORD='$argon2id$v=19$m=6144,t=2,p=2$salt$hash' - -# Service passwords (plaintext for communication) -ATHEME_SEND_PASSWORD=secure_random_string -``` - -#### File Permissions - -```bash -# Environment file (owner read/write only) -chmod 600 .env - -# Generated configs (world readable) -chmod 644 *.conf - -# SSL certificates (readable by services) -chmod 644 server.cert.pem -chmod 644 server.key.pem -``` - -### Access Control - -#### Configuration Access - -- `.env` file contains sensitive data -- Templates are safe for version control -- Generated configs are runtime-only - -#### Audit Logging - -```bash -# Log configuration changes -echo "$(date): Config regenerated by $(whoami)" >> config-audit.log - -# Track template modifications -git log --oneline apps/unrealircd/config/*.template apps/atheme/config/*.template -``` - -## Configuration Management - -### Environment Setup - -```bash -# Copy template to create your configuration -cp .env.example .env - -# Edit with your values -vim .env - -# Secure the file -chmod 600 .env -``` - -## Advanced Configuration - -### Dynamic Configuration - -#### Runtime Configuration Changes - -```bash -# Modify .env -vim .env - -# Regenerate configs -make build - -# Restart services -make restart -``` - -#### Configuration Reloading - -Some configuration changes require service restart: - -```bash -# Restart services to apply changes -make restart -``` - -### Template Extensions - -#### Custom Templates - -```bash -# Create custom template -cp unrealircd.conf.template custom.conf.template - -# Add custom variables -echo 'custom_setting = "${CUSTOM_VAR}"' >> custom.conf.template - -# Generate custom config -envsubst < custom.conf.template > custom.conf -``` - -#### Template Includes - -```c -// Include additional config files -include "custom.conf"; -// Modules are inlined in unrealircd.conf.template -``` - -### Configuration Overrides - -#### Environment Overrides - -```bash -# Override at runtime -IRC_DOMAIN=dev.irc.atl.chat make up - -# Temporary overrides -export IRC_TLS_PORT=6698 -make up -``` - -#### Service-Specific Configs - -```yaml -# Docker Compose overrides -environment: - - IRC_DOMAIN=irc.atl.chat - - IRC_TLS_PORT=6698 -``` - -## Troubleshooting - -### Common Configuration Issues - -#### Template Processing Errors - -```bash -# Missing variables -envsubst < template.conf -# Error: bad substitution - -# Check required variables -echo ${REQUIRED_VAR:?"Variable not set"} - -# Validate .env file -bash -n .env -``` - -#### Permission Errors - -```bash -# File permission issues -ls -la .env -# Should be: -rw------- owner - -# Fix permissions -chmod 600 .env - -# Check user alignment -echo "PUID: $(id -u), PGID: $(id -g)" -``` - -#### Configuration Syntax Errors - -```bash -# UnrealIRCd syntax check -unrealircd -c unrealircd.conf 2>&1 | head -20 - -# Atheme syntax check -atheme-services -c atheme.conf -n - -# Common errors: -# - Missing semicolons -# - Unclosed braces -# - Invalid variable names -``` - -#### Variable Substitution Issues - -```bash -# Debug variable values -env | grep IRC_ - -# Test substitution -echo "Domain: ${IRC_DOMAIN}" -echo "Port: ${IRC_TLS_PORT:-6697}" - -# Check for special characters -grep -n '[^a-zA-Z0-9_]' .env -``` - -### Debug Procedures - -#### Enable Debug Logging - -```bash -# Add debug variables -DEBUG=1 -VERBOSE=1 - -# Run with debug output -make up - -# Check logs for config processing -make logs | grep -i config -``` - -#### Configuration Validation - -```bash -# Validate all generated configs -find apps/unrealircd apps/atheme -name "*.conf" -exec echo "Checking {}" \; \ - -exec unrealircd -c {} -t \; 2>/dev/null || echo "Invalid: {}" -``` - -#### Recovery Procedures - -```bash -# Restore from backup -cp backup/.env .env -cp backup/*.template apps/unrealircd/config/; cp backup/*.template apps/atheme/config/ - -# Regenerate configurations -make build - -# Test configuration -make test-env -``` - -## Backup and Recovery - -### Configuration Backups - -#### Automated Backups - -```bash -# Backup .env file -cp .env backup/env-$(date +%Y%m%d).backup - -# Backup templates -tar czf backup/templates-$(date +%Y%m%d).tar.gz \ - apps/unrealircd/config/*.template apps/atheme/config/*.template -``` - -#### Version Control - -```bash -# Templates are version controlled -git add apps/unrealircd/config/*.template apps/atheme/config/*.template -git commit -m "Update configuration templates" - -# .env is ignored (contains secrets) -echo ".env" >> .gitignore -``` - -### Recovery Procedures - -#### Configuration Recovery - -```bash -# Restore .env -cp backup/env-latest.backup .env - -# Restore templates -tar xzf backup/templates-latest.tar.gz - -# Regenerate configs -make build - -# Test recovery -make test-env -``` - -#### Emergency Config Generation - -```bash -# Generate minimal config for testing -export IRC_DOMAIN=localhost -export IRC_TLS_PORT=6697 -export IRC_ADMIN_NAME="Emergency Admin" - -# Create basic .env -cat > .env << EOF -IRC_DOMAIN=localhost -IRC_TLS_PORT=6697 -IRC_ADMIN_NAME=Emergency Admin -IRC_ADMIN_EMAIL=admin@localhost -EOF - -# Regenerate configs -make build -``` - -## Maintenance - -### Regular Tasks - -#### Configuration Audits - -```bash -# Check for outdated templates -find apps/unrealircd apps/atheme -name "*.template" -newer *.conf - -# Validate all configurations -make test-env - -# Review .env for security -grep -E "(PASSWORD|SECRET)" .env -``` - -#### Template Updates - -```bash -# Update templates from upstream -git pull origin main - -# Check for template changes -git diff apps/unrealircd/config/*.template apps/atheme/config/*.template - -# Test new templates -make build -make test -``` - -### Documentation Updates - -#### Configuration Documentation - -```bash -# Update .env.example comments -vim .env.example - -# Document new variables -echo "# NEW_VAR - Description" >> .env.example - -# Update this documentation -vim docs/CONFIG.md -``` - -## Related Documentation - -- [README.md](../README.md) - Quick start and basic configuration -- [UNREALIRCD.md](UNREALIRCD.md) - IRC server configuration details -- [ATHEME.md](ATHEME.md) - IRC services configuration -- [SSL.md](SSL.md) - SSL certificate configuration -- [DOCKER.md](DOCKER.md) - Container configuration -- [MAKE.md](MAKE.md) - Build and automation system -- [SECRET_MANAGEMENT.md](SECRET_MANAGEMENT.md) - Secret handling diff --git a/docs/services/irc/DEVELOPMENT.md b/docs/services/irc/DEVELOPMENT.md deleted file mode 100644 index 0a80af69..00000000 --- a/docs/services/irc/DEVELOPMENT.md +++ /dev/null @@ -1,289 +0,0 @@ -# Development Guide - -This guide covers the development workflow and local setup for IRC.atl.chat. - -## Prerequisites - -- **Docker & Docker Compose**: Container runtime -- **Git**: Version control -- **Modern shell**: Bash/Zsh with standard Unix tools -- **Code editor**: VS Code, Vim, Emacs, etc. - -## Local Setup - -### Clone Repository - -```bash -# Clone with SSH (recommended) -git clone git@github.com:allthingslinux/irc.atl.chat.git -cd irc.atl.chat - -# Or with HTTPS -git clone https://github.com/allthingslinux/irc.atl.chat.git -cd irc.atl.chat -``` - -### Environment Configuration - -```bash -# Copy environment template -cp .env.example .env - -# Edit for local development -vim .env - -# Required for development: -PUID=$(id -u) -PGID=$(id -g) -IRC_DOMAIN=localhost -IRC_ROOT_DOMAIN=localhost -LETSENCRYPT_EMAIL=dev@localhost -``` - -### Development Startup - -```bash -# Start full development environment -make up - -# Or start with debug logging -DEBUG=1 make up - -# Verify services are running -make status -``` - -### Development URLs - -- **IRC Server**: `localhost:6697` (TLS) -- **WebPanel**: `http://localhost:8080` -- **JSON-RPC API**: `localhost:8600` -- **WebSocket**: `localhost:8000` - -## Development Workflow - -### Branching Strategy - -```bash -# Create feature branch -git checkout -b feature/my-feature - -# Create bug fix branch -git checkout -b bugfix/issue-number - -# Create documentation branch -git checkout -b docs/update-guide -``` - -### Making Changes - -```bash -# Start development environment -make up - -# Make your changes -# Edit configuration files, scripts, etc. - -# Test changes -make test - -# Rebuild if needed -make rebuild - -# Check service status -make status -``` - -### Testing - -```bash -# Run all tests -make test - -# Run specific test categories -make test-unit -make test-integration -make test-e2e - -# Quick environment check -make test-quick -``` - -## Configuration Development - -### Template Changes - -```bash -# Edit configuration templates -vim apps/irc/services/unrealircd/config/unrealircd.conf.template -vim apps/irc/services/atheme/config/atheme.conf.template - -# Regenerate configurations -make build - -# Restart services -make restart -``` - -### Environment Variables - -```bash -# Add new variables to .env.example -vim .env.example - -# Update .env -vim .env - -# Test configuration -make test-env -``` - -## Docker Development - -### Container Debugging - -```bash -# Access UnrealIRCd container -docker compose exec atl-irc-server sh - -# Access Atheme container -docker compose exec atl-irc-services sh - -# Check container logs -docker compose logs atl-irc-server -docker compose logs atl-irc-services -``` - -### Container Rebuilding - -```bash -# Rebuild specific service -docker compose build unrealircd - -# Rebuild all services -make rebuild - -# Clean rebuild -docker compose build --no-cache -``` - -## Code Quality - -### Linting - -```bash -# Run linting checks -make lint - -# Check specific files -yamllint compose.yaml -shellcheck scripts/*.sh -``` - -### Testing - -```bash -# Run test suite -make test - -# Run specific tests -uv run pytest tests/unit/ -uv run pytest tests/integration/ -``` - -## Contributing - -### Pull Request Process - -1. **Fork the repository** -2. **Create feature branch**: `git checkout -b feature/my-feature` -3. **Make changes**: Follow coding standards -4. **Test changes**: `make test` -5. **Commit changes**: `git commit -m "Add feature"` -6. **Push branch**: `git push origin feature/my-feature` -7. **Create pull request** - -### Code Standards - -- **Shell scripts**: Use `shellcheck` for validation -- **YAML files**: Use `yamllint` for validation -- **Documentation**: Update relevant docs for changes -- **Testing**: Add tests for new features - -## Debugging - -### Enable Debug Mode - -```bash -# Debug environment setup -DEBUG=1 make up - -# Verbose SSL operations -VERBOSE=1 make ssl-setup - -# Debug configuration processing -DEBUG=1 ./scripts/prepare-config.sh -``` - -### Log Analysis - -```bash -# View all logs -make logs - -# View specific service logs -make logs-ircd -make logs-atheme -make logs-webpanel - -# Follow logs in real-time -docker compose logs -f -``` - -## Common Development Tasks - -### Adding New Modules - -```bash -# Edit module list -vim apps/irc/services/unrealircd/third-party-modules.list - -# Rebuild container -make rebuild - -# Test module installation -make modules-installed -``` - -### SSL Development - -```bash -# Check SSL status -make ssl-status - -# Force SSL renewal -make ssl-renew - -# View SSL logs -make ssl-logs -``` - -### Configuration Changes - -```bash -# Edit templates -vim apps/irc/services/*/config/*.template - -# Regenerate configs -make build - -# Restart services -make restart -``` - -## Related Documentation - -- [CONFIG.md](CONFIG.md) - Configuration management -- [TESTING.md](TESTING.md) - Testing framework -- [MAKE.md](MAKE.md) - Build automation -- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Common issues and solutions diff --git a/docs/services/irc/DOCKER.md b/docs/services/irc/DOCKER.md deleted file mode 100644 index 9342f33c..00000000 --- a/docs/services/irc/DOCKER.md +++ /dev/null @@ -1,257 +0,0 @@ -# Docker Setup - -This guide covers the Docker containerization setup for IRC.atl.chat. - -## Overview - -IRC.atl.chat uses Docker Compose for orchestration with the following services: - -``` -Services: -├── atl-irc-server - Main IRC server -├── atl-irc-services - IRC services (NickServ, etc.) -├── atl-irc-webpanel - Web administration interface -└── atl-cert-manager - SSL certificate automation (Lego) -``` - -## Container Configuration - -### UnrealIRCd Container - -```yaml -atl-irc-server: - build: - context: ./apps/unrealircd - dockerfile: Containerfile - container_name: atl-irc-server - volumes: - - ./apps/unrealircd/config:/home/unrealircd/unrealircd/config - - ./data/irc/logs:/home/unrealircd/unrealircd/logs - - ./data/irc/data:/home/unrealircd/unrealircd/data - ports: - - '6697:6697' # IRC over TLS - - '6900:6900' # Server links - - '6901:6901' # Atheme services - - '8600:8600' # JSON-RPC API - - '8000:8000' # WebSocket IRC - networks: - - atl-chat - restart: unless-stopped -``` - -### Atheme Container - -```yaml -atheme: - build: - context: ./apps/atheme - dockerfile: Containerfile - container_name: atl-irc-services - depends_on: - atl-irc-server: - condition: service_healthy - volumes: - - ./apps/atheme/config:/usr/local/atheme/etc:ro - - ./data/atheme/data:/usr/local/atheme/data - - ./data/atheme/logs:/usr/local/atheme/logs - network_mode: service:atl-irc-server # Shares network with IRCd - restart: unless-stopped -``` - -### WebPanel Container - -```yaml -atl-irc-webpanel: - build: - context: ../../apps/webpanel - dockerfile: services/webpanel/Containerfile - container_name: atl-irc-webpanel - depends_on: - atl-irc-server: - condition: service_healthy - volumes: - - atl-irc-webpanel-data:/var/www/html/atl-irc-webpanel/data - ports: - - '8080:8080' - networks: - - atl-chat - restart: unless-stopped -``` - -## Volume Management - -### Persistent Volumes - -```yaml -volumes: - atl-irc-webpanel-data: - name: atl-irc-webpanel-data - driver: local -``` - -### Bind Mounts - -```yaml -volumes: - - ./apps/unrealircd/config:/home/unrealircd/unrealircd/config - - ./data/irc/logs:/home/unrealircd/unrealircd/logs - - ./data/irc/data:/home/unrealircd/unrealircd/data -``` - -## Networking - -### Network Architecture - -```yaml -networks: - atl-chat: - name: atl-chat - driver: bridge -``` - -### Port Mapping - -- **6697**: IRC over TLS (external) -- **6900**: Server-to-server TLS (external) -- **6901**: Atheme services connection (localhost) -- **8600**: JSON-RPC API (internal) -- **8000**: WebSocket IRC (external) -- **8080**: WebPanel (external) - -## Security - -### Non-Root Containers - -All containers run as non-root users with proper UID/GID mapping: - -```yaml -environment: - - PUID=${PUID:-1000} - - PGID=${PGID:-1000} -``` - -### File Permissions - -```bash -# Set proper permissions -chmod 600 .env cloudflare-credentials.ini -chmod 755 data/ logs/ -``` - -## Health Checks - -### Container Health Monitoring - -```yaml -healthcheck: - test: ['CMD', 'nc', '-z', 'localhost', '6697'] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s -``` - -### Health Check Commands - -```bash -# Check container health -docker ps --filter "health=healthy" - -# Check service status -make status - -# View container logs -docker compose logs atl-irc-server -``` - -## Management Commands - -### Service Management - -```bash -# Start all services -make up - -# Stop all services -make down - -# Restart services -make restart - -# Check service status -make status -``` - -### Container Management - -```bash -# Build containers -make build - -# Rebuild from scratch -make rebuild - -# View logs -make logs -make logs-ircd -make logs-atheme -make logs-webpanel -``` - -### Debugging - -```bash -# Access container shell -docker compose exec atl-irc-server sh -docker compose exec atl-irc-services sh - -# Check container health -docker inspect unrealircd | grep -A 10 "Health" - -# Monitor resource usage -docker stats -``` - -## Troubleshooting - -### Common Issues - -#### Permission Errors - -```bash -# Fix file ownership -sudo chown -R $(id -u):$(id -g) data/ logs/ - -# Check PUID/PGID -echo "PUID: $(id -u), PGID: $(id -g)" -``` - -#### Container Won't Start - -```bash -# Check logs -docker compose logs atl-irc-server - -# Validate configuration -docker compose config - -# Check dependencies -docker compose ps -``` - -#### Network Issues - -```bash -# Check network connectivity -docker network ls -docker network inspect atl-chat - -# Test service communication -docker compose exec atl-irc-server nc -z localhost 6901 -``` - -## Related Documentation - -- [MAKE.md](MAKE.md) - Build automation and management commands -- [CONFIG.md](CONFIG.md) - Configuration management -- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Common issues and solutions diff --git a/docs/services/irc/MAKE.md b/docs/services/irc/MAKE.md deleted file mode 100644 index 8232974b..00000000 --- a/docs/services/irc/MAKE.md +++ /dev/null @@ -1,359 +0,0 @@ -# Makefile Commands - -This guide covers the Makefile system used for building, deploying, and managing IRC.atl.chat. The Makefile provides commands for all aspects of the IRC infrastructure. - -## Overview - -### Command Categories - -#### Quick Start Commands - -```bash -make up # Complete setup and start (recommended) -make down # Stop all services -make restart # Restart services -make status # Check service status -``` - -#### Development Commands - -```bash -make test # Run all tests -make build # Build containers -make logs # View service logs -make lint # Code quality checks -``` - -## Core Commands - -### Service Lifecycle - -#### Complete Setup (`make up`) - -```bash -# What it does: -# 1. Initializes directories and permissions -# 2. Processes configuration templates -# 3. Builds Docker containers -# 4. Starts all services -# 5. Sets up proper file ownership -make up -``` - -#### Service Management - -```bash -# Start services (assumes setup complete) -make start-only - -# Stop all services -make down - -# Stop and remove containers/networks -make stop - -# Restart all services -make restart - -# Force rebuild and restart -make rebuild -``` - -#### Selective Service Control - -```bash -# View specific service logs -make logs-ircd # UnrealIRCd logs -make logs-atheme # Atheme logs -make logs-webpanel # WebPanel logs - -# Quick service access -make webpanel # Open WebPanel info -``` - -### Building and Development - -#### Container Building - -```bash -# Build all containers -make build - -# Rebuild without cache (clean build) -make rebuild - -# Setup only (no start) -make setup -``` - -#### Development Environment - -```bash -# Access development shell -make dev-shell - -# View all logs with timestamps -make dev-logs - -# Run linting checks -make lint -``` - -### Testing Framework - -#### Test Categories - -```bash -# Complete test suite -make test - -# Unit tests (no Docker required) -make test-unit - -# Integration tests (requires Docker) -make test-integration - -# End-to-end tests (full workflow) -make test-e2e - -# Protocol compliance tests -make test-protocol - -# Performance tests -make test-performance - -# Service integration tests -make test-services - -# Docker-specific tests -make test-docker -``` - -#### Specialized Testing - -```bash -# Environment validation -make test-env - -# IRC functionality tests -make test-irc - -# Quick health checks -make test-quick -``` - -### SSL Certificate Management - -#### Certificate Lifecycle - -```bash -# One-command SSL setup -make ssl-setup - -# Check certificate status -make ssl-status - -# Force certificate renewal -make ssl-renew - -# View SSL monitoring logs -make ssl-logs - -# Stop SSL monitoring -make ssl-stop - -# Remove certificates (CAUTION) -make ssl-clean -``` - -### Module Management - -#### Module Operations - -```bash -# List available modules -make modules-list - -# Show installed modules -make modules-installed - -# Generate operator password -make generate-password -``` - -## Advanced Commands - -### System Management - -#### Information and Diagnostics - -```bash -# Comprehensive help -make help - -# System information -make info - -# Quick environment check -make test-quick -``` - -#### Maintenance Operations - -```bash -# Clean up containers and images -make clean - -# Complete system reset (WARNING: destroys data) -make reset -``` - -## Command Reference - -### Service Management - -| Command | Description | Use Case | -|---------|-------------|----------| -| `make up` | Complete setup and start | First-time setup | -| `make down` | Stop all services | Shutdown | -| `make restart` | Restart services | Configuration changes | -| `make status` | Show service status | Monitoring | -| `make logs` | View all logs | Debugging | - -### Building - -| Command | Description | Use Case | -|---------|-------------|----------| -| `make build` | Build containers | Development | -| `make rebuild` | Clean rebuild | Dependency changes | -| `make setup` | Setup only (no start) | Configuration only | - -### Testing - -| Command | Description | Test Type | -|---------|-------------|-----------| -| `make test` | Full test suite | Comprehensive | -| `make test-unit` | Unit tests | Fast, isolated | -| `make test-integration` | Integration tests | Service interaction | -| `make test-e2e` | End-to-end tests | Full workflow | -| `make test-protocol` | IRC protocol tests | Compliance | -| `make test-performance` | Performance tests | Load testing | -| `make test-services` | Service tests | Atheme integration | - -### SSL Management - -| Command | Description | Frequency | -|---------|-------------|-----------| -| `make ssl-setup` | Initial certificate setup | Once | -| `make ssl-status` | Check certificate status | Daily/Weekly | -| `make ssl-renew` | Force renewal | When needed | -| `make ssl-logs` | View SSL logs | Troubleshooting | -| `make ssl-stop` | Stop monitoring | Maintenance | -| `make ssl-clean` | Remove certificates | Emergency | - -## Error Handling - -### Common Issues - -#### Permission Errors - -```bash -# Symptom: "Permission denied" -# Solution: Check PUID/PGID in .env -make up # Automatically fixes permissions - -# Manual fix -export PUID=$(id -u) -export PGID=$(id -g) -``` - -#### Build Failures - -```bash -# Symptom: "Build failed" -# Solution: Clean rebuild -make rebuild - -# Check build logs -docker compose build --progress=plain unrealircd -``` - -#### Service Won't Start - -```bash -# Check service status -make status - -# View specific logs -make logs-ircd - -# Validate configuration -make test-env -``` - -### Debug Mode - -#### Enable Verbose Output - -```bash -# Add debug flags -DEBUG=1 make up - -# Verbose SSL operations -VERBOSE=1 make ssl-setup -``` - -#### Manual Command Execution - -```bash -# Run commands manually -docker compose up -d -docker compose logs -f - -# Check environment -env | grep -E "(PUID|PGID|VERSION)" -``` - -## Best Practices - -### Usage Guidelines - -#### Development Workflow - -```bash -# Daily development cycle -make up # Start environment -# Make changes -make rebuild # Test changes -make test # Validate -make down # Clean shutdown -``` - -#### Production Deployment - -```bash -# Careful production updates -make test # Full validation -make up # Deploy changes -make status # Verify health -``` - -### Security Considerations - -#### Access Control - -```bash -# Secure file permissions -chmod 600 .env cloudflare-credentials.ini - -# Use secure passwords -make generate-password -``` - -## Related Documentation - -- [README.md](../README.md) - Quick start guide -- [DOCKER.md](DOCKER.md) - Container setup details -- [CONFIG.md](CONFIG.md) - Configuration management -- [SSL.md](SSL.md) - SSL certificate management -- [UNREALIRCD.md](UNREALIRCD.md) - IRC server configuration diff --git a/docs/services/irc/MODULES.md b/docs/services/irc/MODULES.md deleted file mode 100644 index 6f5803f1..00000000 --- a/docs/services/irc/MODULES.md +++ /dev/null @@ -1,274 +0,0 @@ -# UnrealIRCd Modules - -This guide covers the module system in UnrealIRCd for IRC.atl.chat, focusing on third-party module management and configuration. - -## Overview - -### Module Architecture - -UnrealIRCd uses a modular architecture where functionality is provided by loadable modules: - -- **Core Modules**: Built-in functionality (user modes, channel modes, etc.) -- **Third-Party Modules**: Community-developed extensions -- **Module Management**: Automated installation and configuration via Docker - -### Module Categories - -``` -Modules/ -├── usermodes/ # User mode implementations -├── chanmodes/ # Channel mode implementations -├── commands/ # IRC command extensions -├── extensions/ # Protocol extensions (IRCv3, etc.) -├── third/ # Third-party modules (unrealircd-contrib + atl.chat custom forks) -├── crypto/ # Cryptographic functions -└── backend/ # Database backends -``` - -## Third-Party Modules Configuration - -### Automatic Installation - -Third-party modules are automatically installed during container build using the `third-party-modules.list` file: - -```bash -# Edit the modules list -vim apps/unrealircd/third-party-modules.list - -# Add modules (one per line) -third/showwebirc -third/geoip - -# Rebuild container to install modules -make rebuild -``` - -### Module List File Format - -```bash -# Comments start with # -# Empty lines are ignored -# One module per line - -# WebIRC/WebSocket information in WHOIS -third/showwebirc - -# Add more modules here as needed: -# third/example-module -``` - -### Installation Process - -1. **Build Time**: Modules listed in `third-party-modules.list` are installed during container build (see Containerfile) -2. **Runtime**: Use `manage-modules.sh install ` for additional modules without rebuilding -3. **Persistence**: Installed modules persist across container restarts (modules dir is in image) - -## Module Management Commands - -### Using Make Commands - -```bash -# List available third-party modules -make modules-list - -# Show installed modules -make modules-installed -``` - -### Using Management Scripts - -```bash -# List available modules -docker compose exec atl-irc-server manage-modules.sh list - -# Show module information -docker compose exec atl-irc-server manage-modules.sh info webpanel - -# Install a module -docker compose exec atl-irc-server manage-modules.sh install webpanel - -# Uninstall a module -docker compose exec atl-irc-server manage-modules.sh uninstall webpanel - -# Show installed modules -docker compose exec atl-irc-server manage-modules.sh installed -``` - -### Configuration Management - -```bash -# Add module to configuration -docker compose exec atl-irc-server module-config.sh add webpanel - -# Remove module from configuration -docker compose exec atl-irc-server module-config.sh remove webpanel - -# List loaded modules in config -docker compose exec atl-irc-server module-config.sh list -``` - -### Runtime Module Management - -```bash -# List loaded modules -MODULE LIST - -# Load module at runtime -MODULE LOAD mymodule - -# Unload module -MODULE UNLOAD mymodule -``` - -## Module Configuration - -### Consolidated Configuration - -All module loading is inlined in `unrealircd.conf.template`. On UnrealIRCd upgrade, diff against the new release's `modules.default.conf` and `modules.optional.conf` to pick up any changes. - -```c -// Core + optional modules are defined at the top of unrealircd.conf.template -// Third-party and custom modules are loaded near the end -loadmodule "cloak_sha256"; -loadmodule "third/showwebirc"; -loadmodule "third/metadata"; -loadmodule "third/relaymsg-atl"; /* atl.chat fork */ -``` - -## Currently Installed Modules - -- **third/showwebirc**: Adds WebIRC and WebSocket information to WHOIS queries -- **third/metadata**: METADATA command for avatars, message coloring, status texts (draft/metadata, draft/metadata-notify-2). Use with PIRC, IRCcloud, or other supporting clients. -- **third/relaymsg-atl**: atl.chat fork for stateless bridging (RELAYMSG); allows clean nicks via `require-separator no` - -## Troubleshooting - -### Common Issues - -#### Module Not Loading - -```bash -# Check module files exist -docker compose exec atl-irc-server ls -la /home/unrealircd/unrealircd/modules/third/ - -# Check logs for errors -make logs-ircd | grep -i module -``` - -#### Configuration Errors - -```bash -# Validate configuration -docker compose exec atl-irc-server unrealircd -c /home/unrealircd/unrealircd/config/unrealircd.conf - -# Check syntax errors -make logs-ircd | grep -i error -``` - -#### Force Reinstallation - -If you need to force reinstallation of modules: - -```bash -# Remove the installation flag -docker compose exec atl-irc-server rm -f /home/unrealircd/.modules_installed - -# Restart the container -docker compose restart atl-irc-server -``` - -### Debug Information - -```bash -# Check module status -docker compose exec atl-irc-server unrealircdctl module list - -# Enable debug logging -# Add to unrealircd.conf: -# set { log-level debug; }; -``` - -## Finding Available Modules - -### Online Repository - -Browse available modules at: - -### Command Line - -List available modules from within a running container: - -```bash -docker compose exec atl-irc-server ./unrealircd module list -``` - -## Adding New Modules - -1. **Edit the configuration file**: - - ```bash - nano apps/unrealircd/third-party-modules.list - ``` - -2. **Add the module name** (one per line): - - ```bash - third/new-module-name - ``` - -3. **Rebuild the container**: - - ```bash - make rebuild - ``` - -## Removing Modules - -1. **Remove from the configuration file**: - - ```bash - nano apps/unrealircd/third-party-modules.list - ``` - -2. **Comment out or delete the line**: - - ```bash - # third/old-module-name - ``` - -3. **Rebuild the container**: - - ```bash - make rebuild - ``` - -## Manual Installation - -If you need to install additional modules after the container is running: - -```bash -# Enter the container -docker compose exec atl-irc-server sh - -# Install a module manually -cd /home/unrealircd/unrealircd -./unrealircd module install third/module-name - -# Rehash to load the module -./bin/unrealircdctl rehash -``` - -## Security Considerations - -- Third-party modules are not officially supported by the UnrealIRCd team -- Review module source code if security is a concern -- Only install modules from trusted sources -- Regularly update modules for security patches - -## Related Documentation - -- [UNREALIRCD.md](UNREALIRCD.md) - Main server configuration -- [CONFIG.md](CONFIG.md) - Configuration management -- [DOCKER.md](DOCKER.md) - Container setup -- [UnrealIRCd Module API](https://www.unrealircd.org/docs/module_api) - Official API docs -- [Third-Party Modules](https://modules.unrealircd.org/) - Module repository diff --git a/docs/services/irc/README.md b/docs/services/irc/README.md deleted file mode 100644 index 0d7fa4a3..00000000 --- a/docs/services/irc/README.md +++ /dev/null @@ -1,89 +0,0 @@ -# Documentation Index - -Welcome to the IRC.atl.chat documentation! This directory contains comprehensive guides for setting up, configuring, and managing your IRC server infrastructure. - -## 🚀 Getting Started - -- **[API.md](./API.md)** - JSON-RPC API reference and WebSocket support for IRC server management -- **[CONFIG.md](./CONFIG.md)** - Configuration system overview, environment variables, and template management -- **[DOCKER.md](./DOCKER.md)** - Docker containerization setup, volumes, networking, and deployment -- **[MAKE.md](./MAKE.md)** - Makefile commands reference for build automation and service management -- **[TROUBLESHOOTING.md](./TROUBLESHOOTING.md)** - Common issues, solutions, and debugging guides - -## 🏗️ Core Components - -- **[UNREALIRCD.md](./UNREALIRCD.md)** - UnrealIRCd server configuration, modules, and management -- **[ATHEME.md](./ATHEME.md)** - Atheme IRC services setup (NickServ, ChanServ, OperServ) -- **[MODULES.md](./MODULES.md)** - UnrealIRCd module system, third-party extensions, and customization -- **[USERMODES.md](./USERMODES.md)** - IRC user mode reference and configuration options -- **[WEBPANEL.md](./WEBPANEL.md)** - Web-based administration interface setup and usage - -## 🔒 Security & Operations - -- **[SSL.md](./SSL.md)** - Let's Encrypt automation, certificate management, and TLS configuration -- **[SECRET_MANAGEMENT.md](./SECRET_MANAGEMENT.md)** - Password management, API tokens, and security best practices -- **[BACKUP_RECOVERY.md](./BACKUP_RECOVERY.md)** - Data protection, backup strategies, and disaster recovery procedures - setup - -## 🛠️ Development & Testing - -- **[DEVELOPMENT.md](./DEVELOPMENT.md)** - Local development setup, contribution guidelines, and workflow -- **[TESTING.md](./TESTING.md)** - Comprehensive test suite framework and testing strategies -- **[CI_CD.md](./CI_CD.md)** - GitHub Actions workflows, automation, and deployment pipelines - -## 🔧 Utilities & Scripts - -- **[SCRIPTS.md](./SCRIPTS.md)** - Management scripts, utilities, and automation tools - -## 📋 Project Management - -- **[TODO.md](./TODO.md)** - Project roadmap, planned features, and development priorities - -## 📁 Examples - -The `examples/` directory contains configuration templates and examples: - -- **[examples/atheme/](./examples/atheme/)** - Atheme services configuration examples - - `atheme.conf.example` - Main Atheme configuration template - - `atheme.motd.example` - Message of the day template -- **[examples/unrealircd/](./examples/unrealircd/)** - UnrealIRCd configuration examples - - `unrealircd.conf` - Main server configuration - - `aliases/` - Service alias configurations - - `examples/` - Multi-language configuration examples - - `help/` - Help system configurations - - `modules.*.conf` - Module configuration files - - `tls/` - TLS/SSL certificate examples - -## Quick Navigation - -### For New Users - -Start with: [CONFIG.md](./CONFIG.md) → [DOCKER.md](./DOCKER.md) → [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) - -### For Administrators - -Focus on: [UNREALIRCD.md](./UNREALIRCD.md) → [ATHEME.md](./ATHEME.md) → [MONITORING.md](./MONITORING.md) - -### For Developers - -Check out: [DEVELOPMENT.md](./DEVELOPMENT.md) → [TESTING.md](./TESTING.md) → [API.md](./API.md) - -### For Security - -Review: [SSL.md](./SSL.md) → [SECRET_MANAGEMENT.md](./SECRET_MANAGEMENT.md) → [BACKUP_RECOVERY.md](./BACKUP_RECOVERY.md) - -## Contributing to Documentation - -When adding or updating documentation: - -1. Follow the existing structure and naming conventions -2. Include clear examples and code snippets -3. Update this README.md when adding new documentation files -4. Test all commands and configurations before documenting them -5. Use descriptive headings and maintain consistent formatting - -## Need Help? - -- Check [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) for common issues -- Review the main project [README.md](../../../README.md) for quick start instructions -- Open an issue on GitHub for specific problems or feature requests diff --git a/docs/services/irc/SCRIPTS.md b/docs/services/irc/SCRIPTS.md deleted file mode 100644 index daa7f75a..00000000 --- a/docs/services/irc/SCRIPTS.md +++ /dev/null @@ -1,188 +0,0 @@ -# Management Scripts - -This guide covers the management and utility scripts for IRC.atl.chat deployment and operations. - -## Overview - -IRC.atl.chat includes several management scripts: - -``` -scripts/ -├── init.sh # System initialization -└── prepare-config.sh # Configuration processing -``` - -SSL: Use cert-manager (Lego) via `just irc ssl-setup`. See SSL.md. - -## Core Scripts - -### Initialization Script (`scripts/init.sh`) - -**Purpose**: Initialize the IRC.atl.chat environment and create required directories. - -**Usage**: - -```bash -# Automatic initialization (called by make up) -./scripts/init.sh - -# Manual initialization with debug output -DEBUG=1 ./scripts/init.sh -``` - -**What it does**: - -1. Creates persistent data directories (`data/`, `logs/`) -2. Sets proper ownership for host user -3. Validates environment variables (PUID/PGID) -4. Checks Docker availability - -### Configuration Preparation (`scripts/prepare-config.sh`) - -**Purpose**: Process template files and generate production configurations. - -**Usage**: - -```bash -# Process all configuration templates -./scripts/prepare-config.sh - -# Process with verbose output -VERBOSE=1 ./scripts/prepare-config.sh -``` - -**What it does**: - -1. Validates `.env` file exists and is readable -2. Processes all `.template` files with `envsubst` -3. Generates `.conf` files from templates -4. Sets proper file permissions - -> **Note**: UnrealIRCd health is checked by Docker via JSON-RPC over the Unix socket (compose healthcheck). No host-side health script is needed. - -## Script Features - -### Error Handling - -All scripts use proper error handling: - -- `set -euo pipefail` for strict error handling -- Proper exit codes -- Clear error messages - -### Logging - -Scripts provide structured logging: - -- Timestamps for all operations -- Clear success/failure messages -- Debug output when `DEBUG=1` is set - -### Configuration - -Scripts are environment-driven: - -- Read from `.env` file -- Use environment variables for configuration -- Support debug and verbose modes - -## Usage Examples - -### Complete Setup - -```bash -# Initialize environment -./scripts/init.sh - -# Prepare configurations -./scripts/prepare-config.sh - -# Setup SSL certificates -./scripts/ssl-manager.sh issue - -# Start services -docker compose up -d - -# Check health (Docker healthcheck handles this) -docker compose ps -``` - -### Maintenance Tasks - -```bash -# Check SSL certificate status -./scripts/ssl-manager.sh check - -# Renew certificates if needed -./scripts/ssl-manager.sh renew - -# Verify service health -docker compose ps -``` - -### Troubleshooting - -```bash -# Debug initialization -DEBUG=1 ./scripts/init.sh - -# Verbose configuration processing -VERBOSE=1 ./scripts/prepare-config.sh - -# Check SSL with debug output -DEBUG=1 ./scripts/ssl-manager.sh check -``` - -## Integration with Makefile - -The scripts are integrated with the Makefile: - -```bash -# These make commands use the scripts internally -make up # Uses init.sh and prepare-config.sh -make ssl-setup # Uses ssl-manager.sh issue -make ssl-status # Uses ssl-manager.sh check -make ssl-renew # Uses ssl-manager.sh renew -``` - -## Troubleshooting - -### Script Failures - -```bash -# Check script permissions -ls -la scripts/ - -# Make scripts executable -chmod +x scripts/*.sh - -# Check environment variables -env | grep -E "(PUID|PGID|IRC_)" -``` - -### Permission Issues - -```bash -# Fix script permissions -chmod +x scripts/*.sh - -# Fix data directory permissions -sudo chown -R $(id -u):$(id -g) data/ logs/ -``` - -### Configuration Issues - -```bash -# Validate .env file -bash -n .env - -# Check required variables -grep -E "(PUID|PGID|IRC_DOMAIN)" .env -``` - -## Related Documentation - -- [MAKE.md](MAKE.md) - Build automation and management commands -- [CONFIG.md](CONFIG.md) - Configuration management -- [SSL.md](SSL.md) - SSL certificate management -- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Common issues and solutions diff --git a/docs/services/irc/SECRET_MANAGEMENT.md b/docs/services/irc/SECRET_MANAGEMENT.md deleted file mode 100644 index d74349ce..00000000 --- a/docs/services/irc/SECRET_MANAGEMENT.md +++ /dev/null @@ -1,133 +0,0 @@ -# Secret Management - -This guide covers the management of passwords and sensitive configuration for IRC.atl.chat. - -## Overview - -All secrets are managed through the `.env` file and external credential files. Never commit secrets to version control. - -## Environment Variables (.env) - -### Critical Secrets - -#### IRC Operator Password - -```bash -# Generate secure password hash -docker compose exec atl-irc-server /home/unrealircd/unrealircd/bin/unrealircd mkpasswd - -# Add to .env -IRC_OPER_PASSWORD='$argon2id$v=19$m=6144,t=2,p=2$...' -``` - -#### Atheme Service Passwords - -```bash -# Generate secure random passwords -openssl rand -base64 32 - -# Configure in .env -ATHEME_SEND_PASSWORD=your_secure_password_here -ATHEME_RECEIVE_PASSWORD=your_secure_password_here -``` - -#### WebPanel RPC Password - -```bash -# Add to .env -WEBPANEL_RPC_PASSWORD=your_secure_password_here -``` - -## External Credentials - -### Cloudflare API Token - -```bash -# Copy template -cp cloudflare-credentials.ini.template cloudflare-credentials.ini - -# Edit with your token -vim cloudflare-credentials.ini -# dns_cloudflare_api_token = your-actual-api-token-here - -# Secure permissions -chmod 600 cloudflare-credentials.ini -``` - -## Security Best Practices - -### File Permissions - -```bash -# Secure .env file -chmod 600 .env - -# Secure credential files -chmod 600 cloudflare-credentials.ini -``` - -### Password Generation - -```bash -# Generate secure passwords -openssl rand -base64 32 - -# Generate password hash for IRC operators -docker compose exec atl-irc-server /home/unrealircd/unrealircd/bin/unrealircd mkpasswd -``` - -### Regular Rotation - -- Rotate passwords every 6-12 months -- Update API tokens when possible -- Monitor for security updates - -## Validation - -### Check Configuration - -```bash -# Validate environment setup -make test-env - -# Check SSL settings -make ssl-status -``` - -### Verify Secrets - -```bash -# Check .env file exists and is secure -ls -la .env - -# Verify credential files -ls -la cloudflare-credentials.ini -``` - -## Troubleshooting - -### Permission Issues - -```bash -# Fix file permissions -chmod 600 .env cloudflare-credentials.ini - -# Check ownership -ls -la .env -``` - -### Missing Secrets - -```bash -# Check required variables -grep -E "(PASSWORD|TOKEN)" .env - -# Verify Cloudflare credentials -cat cloudflare-credentials.ini -``` - -## Related Documentation - -- [CONFIG.md](CONFIG.md) - Configuration management -- [SSL.md](SSL.md) - SSL certificate management -- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Common issues and solutions diff --git a/docs/services/irc/SSL.md b/docs/services/irc/SSL.md deleted file mode 100644 index 0c7f1ffa..00000000 --- a/docs/services/irc/SSL.md +++ /dev/null @@ -1,322 +0,0 @@ -# SSL/TLS Certificate Management - -This guide covers the automated SSL certificate management system for IRC.atl.chat, which uses Let's Encrypt with Cloudflare DNS-01 challenge for secure certificate provisioning and renewal. - -## Overview - -IRC.atl.chat enforces **TLS-only connections** for security. All IRC clients must connect via SSL/TLS on port 6697. Plaintext connections on port 6667 are disabled. - -### Architecture - -- **Certificate Authority**: Let's Encrypt (free, automated certificates) -- **Challenge Method**: DNS-01 via Cloudflare API -- **Automation**: cert-manager (Lego) in `infra/compose/cert-manager.yaml` -- **Storage**: Certificates in `data/certs/certificates/` (Lego layout: `_..crt`, `_..key`) -- **Renewal**: Automatic renewal every 24h by cert-manager container - -## Prerequisites - -### 1. Domain and DNS Setup - -- Domain must be managed by Cloudflare -- DNS records must exist for your domain and `*.yourdomain.com` -- DNS must be propagated (verify with `dig yourdomain.com`) - -### 2. Cloudflare API Token - -1. Log into [Cloudflare Dashboard](https://dash.cloudflare.com/) -2. Go to **My Profile** → **API Tokens** -3. Create a new token with these permissions: - - **Zone:DNS:Edit** permission for your domain -4. Copy the token (keep it secure!) - -### 3. Environment Configuration - -Ensure your `.env` file has the required SSL variables: - -```bash -# Required for cert-manager (Lego) -CLOUDFLARE_DNS_API_TOKEN=your-cloudflare-api-token -LETSENCRYPT_EMAIL=admin@yourdomain.com - -# Optional: domain (default: atl.chat) -IRC_ROOT_DOMAIN=yourdomain.com -``` - -## SSL Setup Process - -### Step 1: Configure Cloudflare Credentials - -Add `CLOUDFLARE_DNS_API_TOKEN` to your `.env` file. See `docs/examples/cloudflare-credentials.ini.example` for format reference. - -### Step 2: Start cert-manager - -```bash -# Start cert-manager (Lego) - issues certs to data/certs/certificates/ -just irc ssl-setup - -# Or directly: -docker compose up -d cert-manager -``` - -**Important**: cert-manager must run before IRC/XMPP services to populate `data/certs/`. For dev, `just init` creates self-signed certs in `data/certs/live//` (Let's Encrypt layout, shared by IRC and XMPP). - -### Step 3: Start Services - -```bash -# Start all services (cert-manager populates data/certs/ for prod) -just dev -# or: docker compose up -d -``` - -## Certificate Management - -### Checking Certificate Status - -```bash -# Quick status check -just irc ssl-status - -# View cert-manager logs -just irc ssl-logs -``` - -### cert-manager Operations - -```bash -# Start cert-manager -just irc ssl-setup - -# Restart to trigger renewal check -just irc ssl-renew - -# Stop cert-manager -just irc ssl-stop -``` - -### Certificate Locations - -**Certificate layout:** - -``` -data/certs/ -├── certificates/ # cert-manager (Lego) output -│ ├── _.atl.chat.crt -│ └── _.atl.chat.key -├── live/ # Let's Encrypt layout (dev certs from init, or certbot/copied prod certs) -│ ├── irc.atl.chat/ -│ │ ├── fullchain.pem -│ │ └── privkey.pem -│ └── xmpp.atl.chat/ -│ ├── fullchain.pem -│ └── privkey.pem -└── accounts/ # ACME account data (cert-manager) - -apps/unrealircd/config/tls/ -└── curl-ca-bundle.crt # CA bundle for TLS peer validation (server certs live in data/certs) -``` - -See [Data Directory Structure](../../infra/data-structure.md) for the full canonical layout. - -## Automation and Monitoring - -### Automatic Renewal - -cert-manager (Lego) runs a renewal loop every 24 hours. Certificates are renewed automatically when nearing expiry. - -### Monitoring Commands - -```bash -# Check SSL status -just irc ssl-status - -# View cert-manager logs -just irc ssl-logs - -# Check overall service health -just status -``` - -## Troubleshooting - -### Common Issues - -#### "CLOUDFLARE_DNS_API_TOKEN not set" - -```bash -# Add to .env file -echo "CLOUDFLARE_DNS_API_TOKEN=your-token" >> .env - -# Restart cert-manager -just irc ssl-stop -just irc ssl-setup -``` - -#### "DNS challenge failed" - -```bash -# Verify DNS records exist -dig TXT _acme-challenge.yourdomain.com -dig TXT _acme-challenge.*.yourdomain.com - -# Check Cloudflare DNS settings -# Ensure records exist and are not proxied (orange cloud off) - -# Wait for DNS propagation (can take 24+ hours) -``` - -#### "Certificate expiry warnings" - -```bash -# Restart cert-manager to trigger renewal check -just irc ssl-renew - -# Check cert validity -openssl x509 -in data/certs/certificates/_.atl.chat.crt -noout -dates -``` - -#### "Services won't start after certificate update" - -```bash -# Check certificate file permissions -ls -la data/certs/live/ - -# Manually restart services -docker restart unrealircd atl-irc-webpanel - -# Check service logs -make logs -``` - -### Debug Mode - -View cert-manager logs for troubleshooting: - -```bash -# Follow cert-manager logs -just irc ssl-logs - -# Or directly -docker compose logs -f cert-manager -``` - -### Certificate Validation - -```bash -# Verify certificate chain (replace irc.atl.chat with your IRC_DOMAIN) -openssl verify -CAfile apps/unrealircd/config/tls/curl-ca-bundle.crt \ - data/certs/live/irc.atl.chat/fullchain.pem - -# Check certificate details -openssl x509 -in data/certs/live/irc.atl.chat/fullchain.pem -text -noout - -# Test SSL connection -openssl s_client -connect yourdomain.com:6697 -servername yourdomain.com -``` - -## Security Considerations - -### Certificate Security - -- **Private keys**: Stored with 644 permissions (readable by UnrealIRCd) -- **File permissions**: Credentials file must be 600 (owner read/write only) -- **API tokens**: Never commit to version control -- **Certificate validation**: Full chain validation with trusted CA bundle - -### Network Security - -- **TLS-only policy**: Plaintext IRC connections disabled -- **Modern TLS**: Configured for security (see UnrealIRCd config) -- **Perfect Forward Secrecy**: Supported cipher suites -- **HSTS**: HTTP Strict Transport Security headers - -### Monitoring and Alerts - -- **Certificate expiry**: Monitored automatically -- **Renewal failures**: Logged with error details -- **Service restarts**: Automatic after certificate updates -- **Health checks**: Certificate validity included in service health - -## Advanced Configuration - -### Custom Certificate Paths - -IRC and XMPP both use `data/certs/live//` (Let's Encrypt layout). Override in `.env`: - -```bash -IRC_SSL_CERT_PATH=/home/unrealircd/unrealircd/certs/live/irc.atl.chat/fullchain.pem -IRC_SSL_KEY_PATH=/home/unrealircd/unrealircd/certs/live/irc.atl.chat/privkey.pem -``` - -### Multiple Domains - -The current setup issues certificates for: - -- `yourdomain.com` -- `*.yourdomain.com` - -For additional domains, set `IRC_ROOT_DOMAIN` in `.env` and restart cert-manager. - -### Rate Limiting - -Let's Encrypt has rate limits: - -- **Certificates per domain**: 5 per week -- **Failed validations**: 5 per hour -- **Duplicate certificates**: 1 per week - -## Maintenance - -### Regular Tasks - -```bash -# Weekly: Check certificate status -just irc ssl-status - -# Monthly: Verify automation works -just irc ssl-renew - -# Quarterly: Review SSL configuration -# Check UnrealIRCd TLS settings -# Verify Cloudflare DNS settings -``` - -### Emergency Procedures - -If certificates expire unexpectedly: - -1. **Immediate action**: Check why renewal failed - - ```bash - just irc ssl-logs - ``` - -2. **Manual renewal**: Force certificate issuance - - ```bash - just irc ssl-setup - ``` - -3. **Service restart**: Ensure services use new certificates - - ```bash - make restart - ``` - -### Backup and Recovery - -Certificates live in `data/certs/`. cert-manager writes to `data/certs/certificates/`; dev certs and Let's Encrypt-style layouts use `data/certs/live/`. To restore: - -```bash -# Restart services -docker compose restart atl-irc-server -``` - -## Related Documentation - -- [README.md](../README.md) - Quick start guide -- [SECRET_MANAGEMENT.md](SECRET_MANAGEMENT.md) - API token and password management -- [USERMODES.md](USERMODES.md) - IRC user mode reference -- [UnrealIRCd Documentation](https://www.unrealircd.org/docs/) - Server configuration -- [Let's Encrypt Documentation](https://letsencrypt.org/docs/) - Certificate authority -- [Cloudflare API Documentation](https://developers.cloudflare.com/api/) - DNS management diff --git a/docs/services/irc/TESTING.md b/docs/services/irc/TESTING.md deleted file mode 100644 index d27aefc9..00000000 --- a/docs/services/irc/TESTING.md +++ /dev/null @@ -1,820 +0,0 @@ -# Testing Strategy & Framework - -This guide covers the comprehensive testing framework for IRC.atl.chat, including unit tests, integration tests, end-to-end tests, and performance testing. - -## Overview - -### Testing Philosophy - -IRC.atl.chat employs a **defense-in-depth** testing strategy with multiple layers of quality assurance: - -- **Unit Tests**: Fast, isolated component testing -- **Integration Tests**: Service interaction validation -- **End-to-End Tests**: Complete workflow verification -- **Performance Tests**: Load and stress testing -- **Protocol Tests**: RFC compliance validation - -### Test Architecture - -``` -tests/ -├── unit/ # Fast, isolated unit tests -├── integration/ # Service interaction tests -├── e2e/ # Complete workflow tests -├── protocol/ # IRC protocol compliance -├── controllers/ # Test infrastructure -├── fixtures/ # Test data and helpers -├── utils/ # Testing utilities -└── legacy/ # Deprecated tests (reference only) -``` - -## Test Categories - -### Unit Tests (`tests/unit/`) - -Fast, isolated tests that don't require external dependencies: - -#### Configuration Testing - -```python -def test_environment_validation(): - """Test environment variable validation logic""" - validator = EnvironmentValidator() - assert validator.validate_domain("irc.example.com") - assert not validator.validate_domain("invalid..domain") -``` - -#### Docker Client Testing - -```python -def test_docker_client_connection(docker_client): - """Test Docker API connectivity""" - assert docker_client.ping() - containers = docker_client.containers.list() - assert isinstance(containers, list) -``` - -#### IRC Server Mock Testing - -```python -def test_irc_server_mock(): - """Test IRC server mock responses""" - mock = IRCdMock() - mock.start() - client = IRCClient() - client.connect("localhost", mock.port) - assert client.receive_welcome() -``` - -### Integration Tests (`tests/integration/`) - -Tests that verify component interactions with controlled environments: - -#### IRC Protocol Compliance - -```python -def test_rfc1459_nick_command(irc_server): - """Test RFC1459 NICK command implementation""" - client = IRCClient() - client.connect(irc_server.host, irc_server.port) - - # Test valid nick change - client.send("NICK testuser") - response = client.receive() - assert ":testuser" in response - - # Test nick collision - client2 = IRCClient() - client2.connect(irc_server.host, irc_server.port) - client2.send("NICK testuser") - response = client2.receive() - assert "433" in response # ERR_NICKNAMEINUSE -``` - -#### Service Integration - -```python -def test_nickserv_registration(irc_services): - """Test NickServ registration workflow""" - client = IRCClient() - client.connect(irc_services.host, irc_services.port) - - # Register nickname - client.send("NICKSERV REGISTER mypass user@example.com") - response = client.receive() - assert "Nickname registered" in response - - # Verify registration - client.send("NICKSERV INFO mynick") - response = client.receive() - assert "user@example.com" in response -``` - -#### Client Library Integration - -```python -def test_python_irc_client(): - """Test python-irc library integration""" - import irc.client - - def on_welcome(connection, event): - connection.quit("Test complete") - - client = irc.client.IRC() - server = client.server() - server.connect("localhost", 6667, "testuser") - server.add_global_handler("welcome", on_welcome) - client.process_forever() -``` - -### End-to-End Tests (`tests/e2e/`) - -Complete workflow validation from user perspective: - -#### Full IRC Session - -```python -def test_complete_irc_workflow(): - """Test complete user journey""" - # Start services - services = DockerServices() - services.start() - - try: - # Connect client - client = IRCClient() - client.connect("localhost", 6697, tls=True) - - # Register with services - client.nickserv_register("testpass", "test@example.com") - client.nickserv_identify("testpass") - - # Join channel - client.join("#testchannel") - - # Send message - client.privmsg("#testchannel", "Hello World!") - - # Verify message received - messages = client.get_messages() - assert "Hello World!" in str(messages) - - finally: - services.stop() -``` - -### Protocol Tests (`tests/protocol/`) - -IRC specification compliance validation: - -#### Message Parsing - -```python -def test_irc_message_parsing(): - """Test IRC message format parsing""" - parser = IRCMessageParser() - - # Test PRIVMSG - msg = parser.parse(":nick!user@host PRIVMSG #channel :Hello World") - assert msg.command == "PRIVMSG" - assert msg.params[0] == "#channel" - assert msg.params[1] == "Hello World" - - # Test JOIN - msg = parser.parse(":nick!user@host JOIN #channel") - assert msg.command == "JOIN" - assert msg.params[0] == "#channel" -``` - -#### RFC Compliance - -```python -def test_rfc2812_channel_modes(): - """Test RFC2812 channel mode specifications""" - # Test mode combinations - modes = ChannelModes() - modes.set("+nt") # Topic protection + no external messages - assert modes.has_mode("n") - assert modes.has_mode("t") - - # Test mode conflicts - modes.set("+ps") # Private + secret (should be valid) - assert modes.has_mode("p") and modes.has_mode("s") -``` - -## Test Infrastructure - -### Controllers - -Test controllers provide controlled test environments: - -#### IRC Server Controller - -```python -class UnrealIRCdController: - def __init__(self, config_path): - self.config_path = config_path - self.container = None - - def start(self): - """Start IRC server with test configuration""" - self.container = docker.containers.run( - "atl-irc-server:test", - volumes={self.config_path: "/config"}, - ports={"6667": None}, # Random port - detach=True - ) - self.host = "localhost" - self.port = self._get_mapped_port(6667) - - def stop(self): - """Stop IRC server""" - if self.container: - self.container.stop() - self.container.remove() -``` - -#### Atheme Services Controller - -```python -class AthemeController: - def __init__(self, irc_host, irc_port): - self.irc_host = irc_host - self.irc_port = irc_port - self.container = None - - def start(self): - """Start Atheme services linked to IRC server""" - network = docker.networks.get("test-network") - self.container = docker.containers.run( - "atheme:test", - network_mode=f"container:{irc_container.id}", - detach=True - ) -``` - -### Fixtures - -Reusable test fixtures provide common test data: - -#### Docker Fixtures - -```python -@pytest.fixture(scope="session") -def docker_client(): - """Docker API client fixture""" - import docker - client = docker.from_env() - assert client.ping() - return client - -@pytest.fixture(scope="session") -def docker_compose_helper(project_root): - """Docker Compose operations helper""" - return DockerComposeHelper(project_root) -``` - -#### IRC Test Fixtures - -```python -@pytest.fixture -def irc_server(): - """Running IRC server fixture""" - controller = UnrealIRCdController("tests/fixtures/unrealircd.conf") - controller.start() - yield controller - controller.stop() - -@pytest.fixture -def irc_client(irc_server): - """Connected IRC client fixture""" - client = IRCClient() - client.connect(irc_server.host, irc_server.port) - yield client - client.disconnect() -``` - -### Test Helpers - -Utility functions for common testing operations: - -#### IRC Client Helper - -```python -class IRCTestClient: - def __init__(self, host, port, tls=False): - self.socket = None - self.host = host - self.port = port - self.tls = tls - - def connect(self, nickname="testuser"): - """Connect to IRC server""" - if self.tls: - context = ssl.create_default_context() - self.socket = context.wrap_socket( - socket.socket(socket.AF_INET, socket.SOCK_STREAM) - ) - else: - self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - - self.socket.connect((self.host, self.port)) - - # Send NICK and USER - self.send(f"NICK {nickname}") - self.send(f"USER {nickname} 0 * :Test User") - - def send(self, message): - """Send IRC message""" - self.socket.send(f"{message}\r\n".encode()) - - def receive(self, timeout=1): - """Receive IRC message""" - self.socket.settimeout(timeout) - try: - data = self.socket.recv(4096).decode() - return data.strip() - except socket.timeout: - return None -``` - -## Running Tests - -### Command Line Execution - -#### Using Make (Recommended) - -```bash -# Complete test suite -make test - -# Specific test categories -make test-unit # Unit tests (~30 seconds) -make test-integration # Integration tests (~2 minutes) -make test-e2e # End-to-end tests (~5 minutes) -make test-protocol # Protocol tests (~1 minute) -make test-performance # Performance tests (~10 minutes) - -# Targeted testing -make test-services # Service integration tests -make test-docker # Docker-related tests -make test-quick # Fast environment check -``` - -#### Using uv Directly - -```bash -# All tests -uv run pytest tests/ - -# With coverage -uv run pytest --cov=src --cov-report=html tests/ - -# Specific markers -uv run pytest -m "docker and integration" - -# Verbose output -uv run pytest -v tests/unit/ -``` - -#### Selective Test Execution - -```bash -# Run specific test file -uv run pytest tests/integration/test_services.py - -# Run specific test function -uv run pytest tests/unit/test_configuration.py::test_domain_validation - -# Run tests matching pattern -uv run pytest -k "nickserv" - -# Run with different log levels -uv run pytest --log-level=DEBUG tests/ -``` - -### Test Configuration - -#### pytest.ini - -```ini -[tool:pytest] -testpaths = tests -python_files = test_*.py -python_classes = Test* -python_functions = test_* -addopts = -v --tb=short --strict-markers -markers = - unit: Unit tests - integration: Integration tests - e2e: End-to-end tests - docker: Tests requiring Docker - irc: Tests requiring IRC server - slow: Slow-running tests - network: Tests requiring network access - performance: Performance tests -``` - -#### pyproject.toml - -```toml -[tool.pytest.ini_options] -minversion = "7.0" -addopts = "-ra -q" -testpaths = ["tests"] -python_files = "test_*.py" -python_classes = "Test*" -python_functions = "test_*" -markers = [ - "unit: Unit tests", - "integration: Integration tests", - "e2e: End-to-end tests", - "docker: Tests requiring Docker", - "irc: Tests requiring IRC server", - "slow: Slow-running tests", -] -``` - -## Test Data & Fixtures - -### Sample Data - -```python -@pytest.fixture -def sample_irc_config(): - """Sample IRC server configuration""" - return { - "server_name": "test.irc.example.com", - "server_description": "Test IRC Server", - "network_name": "TestNet", - "admin_info": { - "name": "Test Admin", - "email": "admin@test.example.com" - }, - "ports": { - "plain": 6667, - "ssl": 6697, - "server": 6900 - } - } - -@pytest.fixture -def sample_user_registration(): - """Sample user registration data""" - return { - "nickname": "testuser", - "username": "test", - "realname": "Test User", - "password": "securepassword123", - "email": "test@example.com" - } -``` - -### Test Scenarios - -#### Channel Operations - -```python -def test_channel_creation_and_join(irc_server, irc_client): - """Test channel creation and joining""" - # Create channel - irc_client.send("JOIN #testchannel") - - # Verify join - response = irc_client.receive() - assert "JOIN #testchannel" in response - - # Check topic - irc_client.send("TOPIC #testchannel") - response = irc_client.receive() - assert "331" in response # RPL_NOTOPIC -``` - -#### User Modes - -```python -def test_user_mode_changes(irc_client): - """Test user mode modifications""" - # Set invisible mode - irc_client.send("MODE testuser +i") - response = irc_client.receive() - assert "MODE testuser +i" in response - - # Verify mode set - irc_client.send("MODE testuser") - response = irc_client.receive() - assert "+i" in response -``` - -## Performance Testing - -### Load Testing - -```python -def test_concurrent_connections(irc_server): - """Test multiple concurrent connections""" - clients = [] - - # Create 100 concurrent connections - for i in range(100): - client = IRCClient() - client.connect(irc_server.host, irc_server.port) - client.nick(f"user{i}") - clients.append(client) - - # Verify all connections active - active_count = sum(1 for c in clients if c.is_connected()) - assert active_count == 100 - - # Clean up - for client in clients: - client.quit() -``` - -### Stress Testing - -```python -def test_message_flood_protection(irc_server, irc_client): - """Test flood protection mechanisms""" - # Join channel - irc_client.join("#floodtest") - - # Send rapid messages - for i in range(50): - irc_client.privmsg("#floodtest", f"Flood message {i}") - - # Verify flood protection activated - response = irc_client.receive() - assert "482" in response # ERR_CHANOPRIVSNEEDED or flood message -``` - -### Benchmarking - -```python -def test_message_throughput(benchmark): - """Benchmark message processing throughput""" - - def send_messages(): - client = IRCClient() - client.connect("localhost", 6697, tls=True) - client.join("#benchmark") - - for i in range(1000): - client.privmsg("#benchmark", f"Message {i}") - - client.quit() - - # Run benchmark - result = benchmark(send_messages) - assert result.stats.mean < 1.0 # Should complete within 1 second -``` - -## CI/CD Integration - -### GitHub Actions Testing - -```yaml -- name: Run Tests - run: | - make test-unit - make test-integration - -- name: Run Performance Tests - run: | - make test-performance - -- name: Upload Coverage - uses: codecov/codecov-action@v3 - with: - file: ./coverage.xml -``` - -### Test Reporting - -```bash -# Generate HTML reports -uv run pytest --html=report.html --self-contained-html - -# Generate coverage reports -uv run pytest --cov=src --cov-report=html --cov-report=xml - -# JUnit XML for CI -uv run pytest --junitxml=test-results.xml -``` - -## Debugging Tests - -### Test Isolation - -```python -# Run single failing test -uv run pytest tests/integration/test_services.py::test_nickserv_registration -v - -# Run with debugging -uv run pytest --pdb tests/unit/test_configuration.py - -# Capture logs -uv run pytest --log-cli-level=DEBUG -s -``` - -### Common Issues - -#### Docker Connection Issues - -```bash -# Verify Docker is running -docker ps - -# Check Docker socket permissions -ls -la /var/run/docker.sock - -# Test Docker connectivity -docker run hello-world -``` - -#### Port Conflicts - -```bash -# Find used ports -netstat -tlnp | grep :6697 - -# Use random ports in tests -@pytest.fixture -def irc_server(): - controller = UnrealIRCdController() - controller.start() # Uses random available port - return controller -``` - -#### Service Dependencies - -```bash -# Ensure services start in order -@pytest.fixture(scope="session", autouse=True) -def start_services(): - services = DockerServices() - services.start() - yield - services.stop() -``` - -## Test Maintenance - -### Adding New Tests - -#### Unit Test Template - -```python -import pytest - -class TestConfigurationValidation: - """Test configuration validation functions""" - - def test_valid_domain_names(self): - """Test domain name validation""" - validator = DomainValidator() - - valid_domains = [ - "irc.example.com", - "chat.example.org", - "irc.subdomain.example.net" - ] - - for domain in valid_domains: - assert validator.is_valid(domain) - - def test_invalid_domain_names(self): - """Test invalid domain rejection""" - validator = DomainValidator() - - invalid_domains = [ - "", - "invalid..domain", - "domain", - "domain.", - ".domain.com" - ] - - for domain in invalid_domains: - assert not validator.is_valid(domain) -``` - -#### Integration Test Template - -```python -import pytest - -class TestIRCConnectivity: - """Test IRC server connectivity""" - - def test_ssl_connection(self, irc_server): - """Test SSL/TLS connection to IRC server""" - client = IRCClient() - client.connect(irc_server.host, irc_server.port, tls=True) - - # Verify secure connection - assert client.is_connected() - assert client.is_encrypted() - - # Test basic IRC commands - client.nick("ssltest") - response = client.receive() - assert "ssltest" in response - - client.quit() - - def test_plaintext_connection_disabled(self, irc_server): - """Test that plaintext connections are disabled""" - client = IRCClient() - - with pytest.raises(ConnectionError): - client.connect(irc_server.host, 6667, tls=False) -``` - -### Test Organization - -#### Test File Structure - -``` -tests/ -├── unit/ -│ ├── test_configuration.py -│ ├── test_validation.py -│ └── test_utils.py -├── integration/ -│ ├── test_irc_server.py -│ ├── test_services.py -│ └── test_clients.py -├── e2e/ -│ ├── test_user_workflow.py -│ └── test_admin_workflow.py -└── fixtures/ - ├── docker_fixtures.py - ├── irc_fixtures.py - └── data_fixtures.py -``` - -#### Test Naming Conventions - -```python -# Unit tests -def test_function_name_condition_expected_result(): - pass - -# Integration tests -def test_component_interaction_scenario(): - pass - -# End-to-end tests -def test_complete_user_journey(): - pass -``` - -## Coverage Goals - -### Target Coverage Metrics - -- **Unit Tests**: >90% coverage -- **Integration Tests**: >80% coverage -- **Critical Paths**: 100% coverage -- **Error Handling**: Complete coverage - -### Coverage Reporting - -```bash -# Generate coverage report -uv run pytest --cov=src --cov-report=html --cov-report=term - -# Check coverage thresholds -uv run pytest --cov=src --cov-fail-under=85 - -# Exclude files from coverage -uv run pytest --cov=src --cov-report=html \ - --cov-report=term-missing \ - --cov-exclude="*/tests/*" \ - --cov-exclude="*/migrations/*" -``` - -## Performance Benchmarks - -### Test Execution Times - -- **Unit Tests**: <30 seconds -- **Integration Tests**: <2 minutes -- **End-to-End Tests**: <5 minutes -- **Performance Tests**: <10 minutes -- **Full Suite**: <20 minutes - -### Resource Usage - -- **Memory**: <512MB per test run -- **Disk**: <1GB for test data -- **Network**: Minimal external dependencies - -## Related Documentation - -- [README.md](../README.md) - Quick start guide -- [DOCKER.md](DOCKER.md) - Container setup for testing -- [CONFIG.md](CONFIG.md) - Configuration testing -- [CI_CD.md](CI_CD.md) - CI/CD testing integration -- [DEVELOPMENT.md](DEVELOPMENT.md) - Development testing workflow diff --git a/docs/services/irc/TODO.md b/docs/services/irc/TODO.md deleted file mode 100644 index 65e9dc20..00000000 --- a/docs/services/irc/TODO.md +++ /dev/null @@ -1,546 +0,0 @@ -# IRC.atl.chat 1.0 Release TODO - -This document outlines the comprehensive checklist for preparing IRC.atl.chat for its 1.0 release. This is a production-ready Docker-based IRC server with UnrealIRCd, Atheme Services, and automated SSL certificate management. - -**Current Version**: 0.1.0 → **Target**: 1.0.0 -**Last Updated**: September 16, 2025 -**Status**: Pre-Release Phase - -## 🚀 Pre-Release Checklist - -### 📋 Core Functionality Verification - -- [x] **IRC Server Core (UnrealIRCd 6.2.0.1)** - - [x] Server starts successfully with generated configuration - - [ ] All IRC protocol compliance tests pass (RFC1459, RFC2812, IRCv3) - - [x] TLS-only connections work on port 6697 (plaintext 6667 disabled) - - [ ] Server-to-server linking functional on port 6900 - - [ ] WebSocket IRC accessible on port 8000 - - [ ] JSON-RPC API responsive on port 8600 - - [ ] IRCv3 capabilities properly advertised and functional - - [ ] User modes and channel modes working correctly - - [ ] SASL authentication integration with services - -- [x] **IRC Services (Atheme 7.2.12)** - - [x] Services connect to UnrealIRCd on port 6901 (localhost) - - [ ] NickServ: registration, authentication, password recovery - - [ ] ChanServ: channel registration, access control, auto-ops - - [ ] OperServ: administrative functions, network management - - [x] Service passwords properly configured and secure - - [ ] Database persistence working correctly - - [ ] Service integration with IRCv3 features - -- [x] **WebPanel Administration** - - [x] WebPanel accessible on port 8080 - - [ ] Authentication system functional (file-based configuration) - - [ ] Real-time server statistics and monitoring - - [ ] User and channel management interface - - [ ] Configuration management through web interface - - [ ] Log viewing and filtering capabilities - - [ ] Network topology visualization - -- [x] **SSL/TLS Certificate Management** - - [x] Let's Encrypt certificate provisioning via Cloudflare DNS-01 - - [x] Automatic certificate renewal system operational - - [x] SSL certificate validation and chain verification - - [ ] TLS configuration hardened (modern cipher suites) - - [x] Certificate monitoring and alerting - - [ ] Graceful certificate rotation without service interruption - -### 🧪 Testing & Quality Assurance - -- [ ] **Comprehensive Test Suite Execution** - - [ ] Unit tests pass (`make test-unit`) - Fast, no Docker required - - [ ] Integration tests pass (`make test-integration`) - Full IRC server testing - - [ ] End-to-end tests pass (`make test-e2e`) - Complete workflow validation - - [ ] Protocol compliance tests pass (`make test-protocol`) - RFC compliance - - [ ] Performance tests pass (`make test-performance`) - Load and stress testing - - [ ] Service integration tests pass (`make test-services`) - Atheme functionality - - [ ] Docker-specific tests pass (`make test-docker`) - Container functionality - - [ ] Environment tests pass (`make test-env`) - Configuration validation - - [ ] IRC functionality tests pass (`make test-irc`) - Core IRC features - - [ ] Quick smoke tests pass (`make test-quick`) - Basic health checks - -- [ ] **Test Coverage & Quality** - - [ ] Test coverage >80% for critical components - - [ ] All test markers properly configured and functional - - [ ] IRCv3 capability tests comprehensive - - [ ] Async test handling working correctly - - [ ] Test fixtures and controllers properly implemented - - [ ] Legacy test cleanup completed (tests/legacy/ removed) - -- [ ] **Manual Testing Scenarios** - - [ ] Fresh installation from scratch (`make up`) - - [ ] SSL certificate setup (`make ssl-setup`) - - [ ] Service health monitoring and recovery - - [ ] Multiple IRC client connections (various clients) - - [ ] WebPanel administrative operations - - [ ] Service commands (NickServ, ChanServ, OperServ) - - [ ] Server restart and graceful shutdown - - [ ] Configuration template processing - - [ ] Log rotation and management - -- [ ] **Load & Performance Validation** - - [ ] 100+ concurrent connections handled gracefully - - [ ] Channel operations under load (joins, parts, messages) - - [ ] Memory usage stable over extended periods - - [ ] CPU usage acceptable under normal and peak load - - [ ] Network bandwidth usage optimized - - [ ] Database performance (Atheme) under load - - [ ] WebSocket connection stability - -### 🔒 Security Hardening - -- [ ] **Authentication & Authorization** - - [ ] All default passwords changed from .env.example - - [ ] IRC operator passwords properly hashed (bcrypt/argon2) - - [ ] Atheme service passwords cryptographically secure (>32 chars) - - [ ] WebPanel authentication configured with strong credentials - - [ ] File permissions properly set (600 for credentials, 644 for configs) - - [ ] No hardcoded secrets in configuration files - - [ ] Environment variable validation for sensitive data - -- [ ] **Network Security** - - [ ] TLS-only connections enforced (plaintext disabled) - - [ ] Modern TLS configuration (TLS 1.2+, strong cipher suites) - - [ ] Perfect Forward Secrecy (PFS) enabled - - [ ] HSTS (HTTP Strict Transport Security) configured - - [ ] Rate limiting configured for connections and commands - - [ ] DDoS protection measures in place - - [ ] Firewall rules documented and validated - -- [x] **Container Security** - - [x] All containers run as non-root users (PUID/PGID mapping) - - [ ] Container images scanned for vulnerabilities - - [ ] Resource limits configured (memory, CPU, file descriptors) - - [ ] Secrets management via Docker secrets or env files - - [ ] Container networking properly isolated - - [ ] Read-only filesystems where possible - - [ ] Security contexts properly configured - -- [ ] **Certificate & Cryptographic Security** - - [ ] SSL certificates valid and properly chained - - [ ] Private keys secured with 600 permissions - - [ ] Certificate transparency monitoring - - [ ] Key rotation procedures documented - - [ ] Weak cipher suites disabled - - [ ] OCSP stapling configured - - [ ] Certificate pinning considerations documented - -- [ ] **Application Security** - - [ ] Input validation on all user inputs - - [ ] SQL injection prevention (parameterized queries) - - [ ] XSS protection in WebPanel - - [ ] CSRF protection implemented - - [ ] Secure session management - - [ ] Audit logging for administrative actions - - [ ] Security headers configured (CSP, X-Frame-Options, etc.) - -### 📚 Documentation & User Experience - -- [x] **Documentation Completeness** - - [x] README.md comprehensive and up-to-date - - [x] Quick Start guide works for new users (tested) - - [x] SSL setup documentation complete (docs/SSL.md) - - [x] Secret management guide (docs/SECRET_MANAGEMENT.md) - - [x] User modes reference (docs/USERMODES.md) - - [x] Test suite documentation (tests/README.md) - - [ ] API documentation for JSON-RPC endpoints - - [ ] WebPanel user guide - - [ ] Troubleshooting guides with common solutions - -- [x] **Configuration Documentation** - - [x] All environment variables documented in .env.example - - [x] Configuration template system explained - - [x] Port mapping and networking requirements - - [x] Docker Compose configuration options - - [x] Makefile commands comprehensive help - - [x] Service dependencies and startup order - - [ ] Performance tuning recommendations - -- [x] **Installation & Setup** - - [x] Prerequisites clearly listed (Docker, Docker Compose) - - [x] Platform-specific instructions (Linux distributions) - - [x] Cloudflare DNS setup guide - - [x] SSL certificate requirements and setup - - [x] Initial configuration walkthrough - - [ ] Common deployment scenarios covered - - [ ] Migration guide from other IRC servers - -- [ ] **User Interface & Experience** - - [ ] WebPanel interface intuitive and responsive - - [ ] Error messages clear and actionable - - [ ] Log output structured and informative - - [x] Status commands provide useful information - - [x] Help text comprehensive in all tools - - [ ] Configuration validation with helpful error messages - -### 🛠️ Code Quality & Maintenance - -- [x] **Code Standards & Linting** - - [x] All code passes Ruff linting (`make lint`) - - [ ] Type hints implemented with basedpyright validation - - [x] Code follows project style guidelines (120 char line length) - - [ ] No deprecated code patterns or legacy implementations - - [ ] Proper error handling and exception management - - [ ] Docstrings for all public functions and classes - - [ ] Pre-commit hooks configured and functional - -- [x] **Dependencies & Security** - - [x] All dependencies up-to-date and secure (uv.lock current) - - [ ] No known vulnerabilities in dependency tree - - [x] Docker base images use latest stable versions - - [x] Python 3.11+ compatibility maintained - - [x] Renovate configuration working for automated updates - - [ ] Security scanning integrated in CI/CD pipeline - - [ ] Dependency licenses compatible with MIT - -- [x] **Configuration Management** - - [x] All configuration templates complete and validated - - [x] Environment variable substitution robust (envsubst) - - [ ] Configuration validation with clear error messages - - [ ] Default values secure and production-ready - - [x] Configuration documentation matches implementation - - [x] Template processing handles edge cases - - [ ] Configuration backup and restore procedures - -- [ ] **Code Organization & Architecture** - - [ ] Clear separation of concerns (backend/frontend) - - [ ] Test organization follows project structure - - [ ] Utility functions properly modularized - - [ ] Controller pattern implemented for test infrastructure - - [ ] Async/await patterns used consistently - - [ ] Error handling centralized and consistent - -### 🏗️ Infrastructure & Deployment - -- [x] **Docker & Containerization** - - [x] All containers build successfully from compose.yaml - - [x] Container health checks working and responsive - - [x] Proper restart policies configured (unless-stopped) - - [ ] Resource limits appropriate for production use - - [x] Volume mounts correctly configured and persistent - - [ ] Container networking isolated and secure - - [ ] Multi-architecture support (amd64, arm64) - -- [x] **Service Orchestration** - - [x] Docker Compose v2 compatibility - - [x] Service dependencies properly defined - - [x] Startup order ensures services availability - - [ ] Graceful shutdown procedures implemented - - [x] Service discovery and internal networking - - [x] Environment variable propagation working - - [x] Init scripts and configuration processing - -- [ ] **Monitoring & Observability** - - [x] Health check endpoints functional for all services - - [ ] Structured logging implemented (JSON format) - - [ ] Log aggregation and rotation configured - - [ ] Performance metrics collection (CPU, memory, network) - - [ ] Error tracking and alerting capabilities - - [x] Service status monitoring automated - - [ ] Basic metrics collection endpoints (if applicable) - -- [ ] **Data Persistence & Backup** - - [x] Data volumes properly configured and persistent - - [ ] Database backup procedures documented and tested - - [ ] Configuration backup automated - - [ ] Log retention policies implemented - - [ ] Recovery procedures tested and documented - - [ ] Disaster recovery plan comprehensive - - [ ] Point-in-time recovery capabilities - -### 🔧 DevOps & Automation - -- [x] **CI/CD Pipeline (GitHub Actions)** - - [x] Main CI workflow functional (.github/workflows/ci.yml) - - [x] Security scanning workflow operational (.github/workflows/security.yml) - - [x] Docker build and push workflow (.github/workflows/docker.yml) - - [x] Release automation configured (.github/workflows/release.yml) - - [x] Deployment workflow ready (.github/workflows/deploy.yml) - - [x] Maintenance automation (.github/workflows/maintenance.yml) - - [x] Cleanup workflows for artifacts (.github/workflows/cleanup.yml) - -- [ ] **Automated Testing & Quality** - - [ ] All test suites run in CI/CD pipeline - - [ ] Code coverage reporting integrated - - [ ] Security vulnerability scanning (Snyk, CodeQL) - - [ ] Container image scanning for vulnerabilities - - [x] Dependency update automation (Renovate) - - [ ] Performance regression testing - - [ ] Integration test environments provisioned - -- [ ] **Release Management** - - [ ] Semantic versioning implemented - - [ ] Automated changelog generation - - [ ] Release notes automation - - [ ] Docker image tagging strategy - - [ ] GitHub releases with artifacts - - [ ] Version bumping automation - - [ ] Release candidate testing procedures - -- [x] **Maintenance & Operations** - - [x] SSL certificate renewal automated - - [ ] Log rotation and cleanup configured - - [ ] Container cleanup and pruning automated - - [ ] Health monitoring and alerting scripts - - [ ] Update procedures documented and tested - - [ ] Rollback procedures defined and tested - - [ ] Maintenance window procedures - -### 🧹 Cleanup & Optimization - -- [ ] **Code Cleanup** - - [ ] Remove legacy test files (tests/legacy/ directory) - - [ ] Clean up temporary files and development artifacts - - [ ] Remove unused dependencies from pyproject.toml - - [ ] Optimize Docker images for size and security - - [ ] Remove development-only configurations and comments - - [ ] Clean up **pycache** and .pytest_cache directories - - [ ] Remove any TODO comments in production code - -- [ ] **Performance Optimization** - - [ ] Optimize container startup time - - [ ] Minimize memory footprint for all services - - [ ] Optimize network latency and throughput - - [ ] Database query optimization (Atheme) - - [ ] Resource usage efficiency improvements - - [ ] Container image layer optimization - - [ ] Configuration processing optimization - -- [ ] **File Organization & Structure** - - [ ] All files in appropriate directories - - [ ] No orphaned or duplicate configuration files - - [ ] .gitignore properly configured and comprehensive - - [ ] .dockerignore optimized for build context - - [ ] Proper file permissions across all components - - [ ] Clean git history with meaningful commit messages - - [ ] Documentation files properly organized - -- [ ] **Security Cleanup** - - [ ] Remove any test credentials or example passwords - - [ ] Ensure no secrets in git history - - [ ] Clean up any debug logging that might expose sensitive data - - [ ] Remove development certificates or keys - - [ ] Validate all file permissions are production-appropriate - - [ ] Remove any development-only network configurations - -### 🏭 Production Readiness - -- [ ] **Scalability & Performance** - - [ ] Load testing with realistic user scenarios completed - - [ ] Resource requirements documented for different scales - - [ ] Horizontal scaling capabilities documented - - [ ] Database performance under load validated - - [ ] Memory leak testing completed - - [ ] Connection pooling optimized - - [ ] Rate limiting properly configured - -- [ ] **Reliability & Availability** - - [ ] Service uptime targets defined and achievable - - [ ] Failover procedures tested and documented - - [ ] Health check endpoints comprehensive - - [ ] Circuit breaker patterns implemented where needed - - [ ] Graceful degradation strategies defined - - [ ] Service restart procedures automated - - [ ] Dependency failure handling robust - -- [ ] **Operational Excellence** - - [ ] Monitoring dashboards created and functional - - [ ] Alerting rules configured for critical metrics - - [ ] Runbook procedures documented for common issues - - [ ] Incident response procedures defined - - [ ] Change management procedures established - - [ ] Capacity planning guidelines documented - - [ ] Performance baseline established - -### 🔍 Compliance & Standards - -- [ ] **IRC Protocol Compliance** - - [ ] RFC 1459 compliance verified - - [ ] RFC 2812 compliance verified - - [ ] IRCv3 specifications implemented correctly - - [ ] CTCP/DCC handling appropriate - - [ ] Character encoding (UTF-8) properly handled - - [ ] Message length limits enforced - - [ ] Protocol error handling robust - -- [ ] **Security Standards** - - [ ] OWASP security guidelines followed - - [ ] TLS configuration meets modern standards - - [ ] Authentication mechanisms secure - - [ ] Input validation comprehensive - - [ ] Output encoding prevents injection attacks - - [ ] Session management secure - - [ ] Audit logging comprehensive - -- [ ] **Accessibility & Usability** - - [ ] WebPanel meets accessibility standards (WCAG 2.1) - - [ ] Documentation accessible to users with disabilities - - [ ] Error messages clear and actionable - - [ ] User interface intuitive for administrators - - [ ] Multi-language support considerations documented - - [ ] Mobile-friendly interfaces where applicable - -### 📦 Release Preparation - -- [ ] **Version Management** - - [ ] Update version from 0.1.0 to 1.0.0 in pyproject.toml - - [ ] Update any hardcoded version references in documentation - - [ ] Create and push version tags in git (v1.0.0) - - [ ] Update changelog with 1.0.0 release notes - - [ ] Verify version consistency across all components - -- [ ] **Release Artifacts** - - [ ] Docker images built and tagged for 1.0.0 - - [ ] Multi-architecture images (amd64, arm64) available - - [ ] Source code archive prepared - - [ ] Documentation package ready - - [ ] Installation scripts validated - - [ ] Release notes comprehensive and accurate - - [ ] Migration guide prepared (if needed from pre-1.0) - -- [ ] **Quality Gates** - - [ ] All critical and high-priority issues resolved - - [ ] Security audit completed and passed - - [ ] Performance benchmarks meet requirements - - [ ] Documentation review completed - - [ ] Legal review completed (licenses, attributions) - - [ ] Final testing in production-like environment - - [ ] Rollback plan prepared and tested - -- [ ] **Distribution & Publishing** - - [ ] GitHub release created with proper assets - - [ ] Docker Hub images published and verified - - [ ] Documentation deployed to GitHub Pages (if applicable) - - [ ] Release announcements prepared - - [ ] Community notifications scheduled - - [ ] Social media announcements ready - - [ ] Package registry submissions (if applicable) - -## 🎯 Post-Release Tasks - -### 📊 Monitoring & Support - -- [ ] **Launch Monitoring** - - [ ] Monitor system performance post-launch - - [ ] Watch for user issues and feedback - - [ ] Monitor SSL certificate renewal - - [ ] Track resource usage patterns - -- [ ] **Community Support** - - [ ] Respond to user issues promptly - - [ ] Update documentation based on feedback - - [ ] Create FAQ for common questions - - [ ] Monitor community channels - -### 🔄 Continuous Improvement - -- [ ] **Feedback Integration** - - [ ] Collect user feedback systematically - - [ ] Prioritize feature requests - - [ ] Plan next version improvements - - [ ] Update roadmap based on usage - -- [ ] **Maintenance Planning** - - [ ] Schedule regular security updates - - [ ] Plan dependency updates - - [ ] Schedule performance reviews - - [ ] Plan feature development cycles - -## 🚨 Critical Success Criteria - -Before releasing 1.0, ensure ALL of the following are met: - -### ✅ Core Functionality - -1. **All tests pass** - Zero failing tests across all test suites -2. **Services operational** - UnrealIRCd, Atheme, and WebPanel fully functional -3. **SSL/TLS working** - Certificates provision, renew, and secure all connections -4. **Configuration system** - Templates process correctly, validation works - -### 🔒 Security Requirements - -1. **Security hardened** - All default passwords changed, TLS enforced, containers secured -2. **Vulnerability-free** - No known security vulnerabilities in dependencies or code -3. **Access controls** - Proper authentication and authorization throughout -4. **Certificate security** - Valid certificates with proper chain and strong ciphers - -### 📚 Documentation & UX - -1. **Documentation complete** - Users can install and operate without confusion -2. **Installation tested** - Fresh installation works on clean systems -3. **Troubleshooting guides** - Common issues documented with solutions -4. **WebPanel functional** - Administrative interface fully operational - -### 🏗️ Infrastructure & Operations - -1. **Performance acceptable** - System handles expected load with reasonable resources -2. **Monitoring operational** - Health checks, logging, and metrics functional -3. **Backup/recovery** - Data protection and recovery procedures tested -4. **CI/CD pipeline** - Automated testing, building, and deployment working - -### 🧹 Code Quality - -1. **Clean codebase** - No legacy code, proper organization, passes all linting -2. **Dependencies current** - All dependencies up-to-date and secure -3. **Test coverage** - Comprehensive test coverage for critical components -4. **Version ready** - Version bumped to 1.0.0, tags created, release notes prepared - -### 🎯 Production Readiness - -1. **Load tested** - Performance validated under realistic conditions -2. **Reliability proven** - Service stability and recovery capabilities demonstrated -3. **Compliance verified** - IRC protocol compliance and security standards met -4. **Operational procedures** - Monitoring, alerting, and incident response ready - -## 📝 Notes & Project Details - -### 🛠️ Technology Stack - -- **IRC Server**: UnrealIRCd 6.2.0.1 (latest stable) -- **Services**: Atheme 7.2.12 (NickServ, ChanServ, OperServ) -- **WebPanel**: UnrealIRCd WebPanel (administrative interface) -- **SSL/TLS**: Let's Encrypt + Cloudflare DNS-01 challenge -- **Container**: Docker + Docker Compose v2 -- **Language**: Python 3.11+ with uv package management -- **Testing**: pytest with comprehensive test suite (2800+ test files) - -### 🔧 Key Features - -- **TLS-only** connections enforced (plaintext disabled for security) -- **Automated SSL** certificate management via Let's Encrypt + Cloudflare -- **Comprehensive test suite** with unit, integration, and e2e tests -- **Docker Compose** for easy deployment and management -- **Renovate** for automated dependency management (not Dependabot) -- **Non-root containers** for enhanced security -- **Configuration templates** with environment variable substitution -- **IRCv3** protocol support with modern capabilities - -### 📊 Current Status - -- **Version**: 0.1.0 → Target: 1.0.0 -- **Test Files**: ~2,883 Python files in comprehensive test suite -- **CI/CD**: 7 GitHub Actions workflows for automation -- **Documentation**: README, SSL guide, secret management, user modes -- **Architecture**: Backend (UnrealIRCd, Atheme) + Frontend (WebPanel) - -### 🎯 Release Timeline - -- **Pre-Release Phase**: Complete all checklist items -- **Release Candidate**: Final testing and validation -- **1.0.0 Release**: Production-ready stable release -- **Post-Release**: Community support and continuous improvement - -### 🔗 Important Links - -- **Repository**: -- **Homepage**: -- **License**: MIT License -- **Maintainer**: All Things Linux () - ---- - -**Last Updated**: September 16, 2025 -**Target Release**: 1.0.0 -**Project**: IRC.atl.chat - Production-ready Docker-based IRC server diff --git a/docs/services/irc/TROUBLESHOOTING.md b/docs/services/irc/TROUBLESHOOTING.md deleted file mode 100644 index 2b2d88c5..00000000 --- a/docs/services/irc/TROUBLESHOOTING.md +++ /dev/null @@ -1,268 +0,0 @@ -# Troubleshooting Guide - -This guide covers common issues and solutions for IRC.atl.chat deployment and operation. - -## Quick Diagnosis - -### System Health Check - -```bash -# Check service status -make status - -# View service logs -make logs - -# Quick environment check -make test-quick -``` - -## Common Issues - -### Docker Not Available - -**Symptoms:** `docker: command not found` - -**Solutions:** - -```bash -# Install Docker (Ubuntu/Debian) -sudo apt update && sudo apt install docker.io docker-compose-plugin - -# Start Docker service -sudo systemctl start docker -sudo systemctl enable docker - -# Add user to docker group -sudo usermod -aG docker $USER -# Logout and login again - -# Verify installation -docker --version -docker compose version -``` - -### Permission Denied - -**Symptoms:** `Permission denied` when accessing files - -**Solutions:** - -```bash -# Fix .env file permissions -chmod 600 .env - -# Fix data directory permissions -sudo chown -R $(id -u):$(id -g) data/ logs/ - -# Check PUID/PGID match host user -echo "Host user: $(id -u):$(id -g)" -echo "PUID: $PUID, PGID: $PGID" -``` - -### Services Won't Start - -**Symptoms:** Containers fail to start or exit immediately - -**Solutions:** - -```bash -# Check service status -make status - -# View specific logs -make logs-ircd -make logs-atheme - -# Check configuration -make test-env - -# Restart services -make restart -``` - -### SSL Certificate Issues - -**Symptoms:** SSL setup fails or certificates expire - -**Solutions:** - -```bash -# Check SSL status -make ssl-status - -# View SSL logs -make ssl-logs - -# Force SSL renewal -make ssl-renew - -# Check Cloudflare credentials -cat cloudflare-credentials.ini -``` - -### Configuration Issues - -**Symptoms:** Services start but don't work properly - -**Solutions:** - -```bash -# Validate configuration -make test-env - -# Check generated configs -ls -la apps/unrealircd/config/*.conf apps/atheme/config/*.conf - -# Regenerate configurations -make build - -# Restart services -make restart -``` - -### Network Issues - -**Symptoms:** Can't connect to IRC server - -**Solutions:** - -```bash -# Check if ports are open -netstat -tlnp | grep -E "(6697|8080|8600)" - -# Test IRC connection -telnet localhost 6697 - -# Check firewall -sudo ufw status - -# Allow IRC ports -sudo ufw allow 6697/tcp -sudo ufw allow 8080/tcp -``` - -## Service-Specific Issues - -### UnrealIRCd Issues - -```bash -# Check UnrealIRCd logs -make logs-ircd - -# Test IRC functionality -make test-irc - -# Check configuration syntax -docker compose exec atl-irc-server unrealircd -c /home/unrealircd/unrealircd/config/unrealircd.conf -``` - -### Atheme Issues - -```bash -# Check Atheme logs -make logs-atheme - -# Test service connection -docker compose exec atl-irc-services pgrep atheme-services - -# Check database -ls -la data/atheme/data/ -``` - -### WebPanel Issues - -```bash -# Check WebPanel logs -make logs-webpanel - -# Test WebPanel access -curl -I http://localhost:8080 - -# Check RPC connection -docker compose exec atl-irc-server nc -z localhost 8600 -``` - -## Debug Mode - -### Enable Verbose Logging - -```bash -# Add debug flags -DEBUG=1 make up - -# Verbose SSL operations -VERBOSE=1 make ssl-setup -``` - -### Manual Service Access - -```bash -# Access UnrealIRCd container -docker compose exec atl-irc-server sh - -# Access Atheme container -docker compose exec atl-irc-services sh - -# Check container health -docker ps --filter "health=unhealthy" -``` - -## Recovery Procedures - -### Complete Reset - -```bash -# WARNING: Destroys all data -make reset - -# Followed by fresh setup -make up -``` - -### Service Recovery - -```bash -# Restart failed services -make restart - -# Rebuild if needed -make rebuild - -# Check logs for errors -make logs -``` - -## Getting Help - -### Log Analysis - -```bash -# All service logs -make logs - -# Specific service logs -make logs-ircd -make logs-atheme -make logs-webpanel - -# Follow logs in real-time -docker compose logs -f -``` - -### System Information - -```bash -# Show system info -make info - -# Check Docker status -docker system df -docker system prune -``` - -## Related Documentation - -- [CONFIG.md](CONFIG.md) - Configuration management -- [SSL.md](SSL.md) - SSL certificate management -- [DOCKER.md](DOCKER.md) - Container setup diff --git a/docs/services/irc/UNREALIRCD.md b/docs/services/irc/UNREALIRCD.md deleted file mode 100644 index 20955d94..00000000 --- a/docs/services/irc/UNREALIRCD.md +++ /dev/null @@ -1,501 +0,0 @@ -# UnrealIRCd Server Configuration - -This guide covers the configuration and management of the UnrealIRCd IRC server, the core component of IRC.atl.chat. UnrealIRCd is a high-performance, feature-rich IRC daemon that powers the IRC network. - -## Overview - -### Architecture - -- **Version**: UnrealIRCd 6.2.0.1 (latest stable) -- **Security**: TLS-only connections enforced -- **Features**: IRCv3 support, cloaking, services integration -- **Performance**: Multi-threaded, event-driven design -- **Configuration**: Template-based with environment variable substitution - -### Core Components - -``` -unrealircd/ -├── config/ # Configuration files -│ ├── unrealircd.conf # Main server configuration -│ ├── modules.*.conf # Module loading -│ ├── operclass.*.conf # Operator permissions -│ └── tls/ # SSL certificates -├── logs/ # Server logs -├── data/ # Persistent data (channel.db, etc.) -└── scripts/ # Management utilities -``` - -## Server Configuration - -### Main Configuration File - -The primary configuration is generated from `unrealircd.conf.template` using environment variables: - -#### Server Identity - -```c -me { - name "${IRC_DOMAIN}"; // Server name (irc.atl.chat) - info "${IRC_NETWORK_NAME} IRC Server"; // Server description - sid "001"; // Server ID (unique per server) -} -``` - -#### Administrator Information - -```c -admin { - "${IRC_ADMIN_NAME}"; // Admin name - "admin"; // Department - "${IRC_ADMIN_EMAIL}"; // Contact email -} -``` - -### Connection Classes - -#### Client Connections - -```c -class clients { - pingfreq 90; // Ping frequency in seconds - maxclients 1000; // Maximum concurrent clients - sendq 200k; // Send queue size - recvq 8000; // Receive queue buffer -} -``` - -#### Operator Connections - -```c -class opers { - pingfreq 90; - maxclients 50; // Fewer operator slots - sendq 1M; // Larger send queue for ops - recvq 8000; -} -``` - -#### Server Links - -```c -class servers { - pingfreq 90; - maxclients 10; // Limited server connections - sendq 5M; // Large send queue for server data - recvq 8000; -} -``` - -### Network Security - -#### TLS Configuration - -```c -listen { - ip *; - port ${IRC_TLS_PORT}; // 6697 (TLS only) - options { - tls; - clientsonly; - }; - tls-options { - certificate "/home/unrealircd/unrealircd/certs/live/irc.atl.chat/fullchain.pem"; - key "/home/unrealircd/unrealircd/certs/live/irc.atl.chat/privkey.pem"; - }; -} -``` - -#### Cloaking (Host Privacy) - -```c -cloak { - enabled yes; - cloak-keys { - "aoAr1HnR6gl3sI7hJHpOeMZ7ciaqek+vZv8EGM+HA"; - "aoAr1HnR6gl3sI7hJHpOeMZ7ciaqek+vZv8EGM+HB"; - "aoAr1HnR6gl3sI7hJHpOeMZ7ciaqek+vZv8EGM+HC"; - }; - cloak-prefix "atl"; // Network-specific prefix -} -``` - -### IRC Operator Configuration - -#### Operator Account Setup - -```c -oper yournick { - password "$argon2id$..."; // Argon2id hashed password - class "netadmin"; // Permission class - modes "+xwgs"; // Default operator modes - vhost "staff.atl.chat"; // Virtual host - mask "127.0.0.1"; // Connection mask -} -``` - -#### Operator Classes - -```c -class netadmin { - maxcon 10; // Connection limit - permissions { - admin; // Administrative permissions - oper; // Operator permissions - stats; // Statistics access - }; -} -``` - -### Services Integration - -#### Atheme Services Link - -```c -listen { - ip 127.0.0.1; // Localhost only - port ${ATHEME_UPLINK_PORT}; // 6901 - options { - serversonly; // Services only - }; -} -``` - -#### Services Authentication - -```c -link services.atl.chat { - incoming { - mask *; - }; - password "${ATHEME_RECEIVE_PASSWORD}"; - class servers; -} -``` - -## Module System - -### Consolidated Configuration - -All modules (core, optional, RPC) are defined in `unrealircd.conf.template`. On upgrade, manually diff against the new UnrealIRCd release's `modules.default.conf` and `modules.optional.conf` to pick up changes. - -### Third-Party Modules - -IRC.atl.chat includes support for third-party modules: - -```bash -# Edit apps/unrealircd/third-party-modules.list, then rebuild the image -# Modules are installed during the Docker build - -# List available modules (container running) -just irc modules-list - -# Install module at runtime (after container is running) -docker compose exec atl-irc-server manage-modules.sh install module-name -``` - -## Security Features - -### TLS-Only Enforcement - -```c -set { - modes-on-connect "+ixw"; // i=invisible, x=cloak, w=wallops - // No plaintext port configured -} -``` - -### Flood Protection - -```c -set { - anti-flood { - unknown-users { - connect-flood 3:60; // 3 connections per minute - nick-flood 4:30; // 4 nick changes per 30 seconds - }; - known-users { - // Less restrictive for registered users - }; - }; -} -``` - -### Spam Filtering - -```c -spamfilter { - // Block common spam patterns - regex "*fuck*"; - action block; - reason "Language filter"; -} -``` - -### Rate Limiting - -```c -set { - max-unknown-connections-per-ip 3; - restrict-usermode "+r"; // Registered users only -} -``` - -## Performance Tuning - -### Connection Limits - -```c -set { - maxclients 1000; // Global connection limit - ping-warning 15; // Seconds before ping warning - ping-deadline 30; // Seconds before disconnect -} -``` - -### Queue Management - -```c -class clients { - sendq 200k; // Send queue per client - recvq 8k; // Receive buffer per client -} -``` - -### DNS Configuration - -```c -set { - dns { - timeout 3; // DNS timeout in seconds - retries 2; // DNS retry attempts - }; -} -``` - -## Monitoring and Logging - -### Log Configuration - -```c -log "ircd.log" { - source { - all; - }; - level info; // Logging level -} -``` - -### JSON-RPC API - -```c -listen { - ip 127.0.0.1; // Local API access - port ${IRC_RPC_PORT}; // 8600 - options { - rpc; // Enable RPC - }; -} -``` - -### Server Statistics - -```c -set { - stats-server "stats.atl.chat"; - hide-stats "opers"; // Hide operator stats -} -``` - -## WebSocket Support - -### WebIRC Configuration - -```c -listen { - ip *; - port ${IRC_WEBSOCKET_PORT}; // 8000 - options { - websocket; // Enable WebSocket - }; - websocket-options { - type text; // IRC message format - }; -} -``` - -## IRCv3 Protocol Support - -### Modern Features - -UnrealIRCd 6 supports IRCv3 capabilities: - -```c -set { - ircv3-capabilities { - echo-message; // Message echo - sasl; // SASL authentication - tls; // TLS capability - userhost-in-names; // Userhost in NAMES - }; -} -``` - -### SASL Integration - -```c -sasl { - target localhost:${ATHEME_UPLINK_PORT}; - password "${ATHEME_SEND_PASSWORD}"; -} -``` - -## Troubleshooting - -### Common Issues - -#### Connection Problems - -```bash -# Check server status -docker logs atl-irc-server - -# Verify TLS certificates -openssl s_client -connect localhost:6697 -servername ${IRC_DOMAIN} - -# Check network connectivity -telnet localhost 6697 -``` - -#### Module Loading Errors - -```bash -# Check module dependencies -ldd /home/unrealircd/unrealircd/modules/*.so - -# Verify configuration syntax -unrealircd -c /path/to/config -``` - -#### Performance Issues - -```bash -# Monitor resource usage -docker stats atl-irc-server - -# Check connection counts -unrealircd stats -``` - -### Debug Mode - -Enable detailed logging for troubleshooting: - -```bash -# In unrealircd.conf -log "debug.log" { - source { - all; - }; - level debug; -} -``` - -## Configuration Management - -### Template Processing - -Configuration is generated from templates: - -```bash -# Manual regeneration -./scripts/prepare-config.sh - -# Check generated config -cat apps/unrealircd/config/unrealircd.conf -``` - -### Backup and Recovery - -```bash -# Backup configuration -cp apps/unrealircd/config/unrealircd.conf backup/ - -# Restore from backup -cp backup/unrealircd.conf apps/unrealircd/config/ -``` - -## Advanced Features - -### Server Linking - -```c -link hub.irc.atl.chat { - outgoing { - hostname "hub.irc.atl.chat"; - port 6900; - options { tls; }; - }; - password "shared-link-password"; - class servers; -} -``` - -### Channel Persistence - -```c -set { - channeldb { - file "channel.db"; // Persistent channel storage - }; -} -``` - -### Custom Commands - -```c -command "CUSTOMCMD" { - class "opers"; // Operator only - require_oper yes; -} -``` - -## Maintenance - -### Regular Tasks - -```bash -# Check server health -make status - -# Monitor logs -make logs-ircd - -# Update certificates -make ssl-status - -# Restart services -make restart -``` - -### Performance Monitoring - -```bash -# Server statistics -unrealircd stats - -# Connection information -unrealircd clients - -# Module status -unrealircd modules -``` - -## Related Documentation - -- [SSL.md](SSL.md) - SSL certificate management -- [ATHEME.md](ATHEME.md) - IRC services configuration -- [MODULES.md](MODULES.md) - Module management -- [DOCKER.md](DOCKER.md) - Container setup -- [CONFIG.md](CONFIG.md) - Configuration system -- [UnrealIRCd Documentation](https://www.unrealircd.org/docs/) - Official docs diff --git a/docs/services/irc/USERMODES.md b/docs/services/irc/USERMODES.md deleted file mode 100644 index a627c0f6..00000000 --- a/docs/services/irc/USERMODES.md +++ /dev/null @@ -1,200 +0,0 @@ -# UnrealIRCd User Modes Reference - -This is the list of all user modes that can be set on a user. You can only set user modes on yourself and not on other users. Use the command: `MODE yournick +modeshere`. - -## Detailed Mode Reference - -| User Mode | Module | Description | Restrictions | Sysop Notes | -|-----------|--------|-------------|--------------|-------------| -| `B` | usermodes/bot | Marks you as being a bot. This will add a line to /WHOIS so people can easily recognize bots. | | **Bot Setup:** Essential for transparency. Users can see bot status in /WHOIS. Consider for all automated services. | -| `d` | *built-in* | Makes it so you can not receive channel PRIVMSG's, except for messages prefixed with a channel-command-prefix character. Could be used by bots to reduce traffic so they only see !somecmd type of things. | | **Bot Optimization:** Reduces message load for command bots. Configure channel-command-prefix in your bot's channels (usually `!` or `.`). | -| `D` | usermodes/privdeaf | Makes it so you can not receive private messages (PM's) from anyone except IRCOps, servers and services. | | **High Security:** Recommended for public-facing staff accounts. Prevents PM spam but allows official communications. | -| `G` | usermodes/censor | Swear filter: filters out all the "bad words" configured in the Badword block | | **Content Filtering:** Useful for family-friendly environments. Configure badword blocks in server config. May impact legitimate technical discussions. | -| `H` | *built-in* | Hide IRCop status. Regular users using /WHOIS or other commands will not see that you are an IRC Operator. | IRCOp-only | **Stealth Moderation:** Allows ops to monitor without revealing status. Useful for undercover moderation and reducing targeted harassment. | -| `I` | *built-in* | Hide idle time in /WHOIS. | see set block for more details: set::hide-idle-time | **Privacy:** Configure `set::hide-idle-time` to control who can see idle times. Useful for staff who need to appear available. | -| `i` | *built-in* | Makes you so called 'invisible'. A confusing term to mean that you're just hidden from /WHO and /NAMES if queried by someone outside the channel. Normally set by default through set::modes-on-connect and often by the users' IRC client as well. | | **Default Privacy:** Should be in `set::modes-on-connect`. Doesn't make you truly invisible - just hides from broad searches. Essential for user privacy. | -| `o` | *built-in* | IRC Operator | Set by server | **Operator Status:** Granted via OPER command with valid credentials. Cannot be set manually. Gives access to operator commands and channels. | -| `p` | usermodes/privacy | Hide channels you are in from /WHOIS, for extra privacy. | | **Enhanced Privacy:** Prevents channel list disclosure. Useful for staff who join monitoring channels or users wanting maximum privacy. | -| `q` | usermodes/nokick | Unkickable (only by U:lines, eg: services) | IRCOp-only (but not all) | **Protection Mode:** Prevents kicks except by services. Use sparingly - can be seen as abuse. Good for critical bots in important channels. | -| `r` | *built-in* | Indicates this is a "registered nick" | Set by services | **Authentication Status:** Shows user is identified with services. Cannot be set manually. Used by other modes like `+R` for filtering. | -| `R` | usermodes/regonlymsg | Only receive private messages from users who are "registered users" (authenticated by Services) | | **Anti-Spam:** Highly effective against PM spam. Requires users to register with services first. Recommended for public channels' regular users. | -| `S` | usermodes/servicebot | User is a services bot (gives some extra protection) | Services-only | **Services Protection:** Automatically set by services software. Provides additional protections against kicks/bans. Cannot be manually set. | -| `s` | *built-in* | Server notices for IRCOps, see Snomasks | IRCOp-only | **Monitoring:** Enables server notices. Use `/MODE yournick +s +snomask` to select specific notices. Essential for network monitoring. | -| `T` | usermodes/noctcp | Prevents you from receiving CTCP's. | | **Anti-Flood:** Blocks CTCP requests (VERSION, TIME, etc.). Recommended for bots and users experiencing CTCP floods. May break some client features. | -| `t` | *built-in* | Indicates you are using a /VHOST | Set by server upon /VHOST, /OPER, /*HOST, .. | **Virtual Host Status:** Shows when custom hostname is active. Set automatically by server. Configure vhost blocks for custom hostnames. | -| `W` | usermodes/showwhois | Lets you see when people do a /WHOIS on you. | IRCOp-only | **Monitoring Tool:** Shows who is checking your information. Useful for detecting surveillance or troubleshooting user issues. | -| `w` | *built-in* | Can listen to wallops messages (/WALLOPS from IRCOps') | | **Network Announcements:** Receives important network-wide messages from operators. Recommended for channel operators and regular helpers. | -| `x` | *built-in* | Gives you a hidden / cloaked hostname. | | **Privacy Essential:** Hides real IP/hostname. Should be in `set::modes-on-connect`. Configure `cloak-keys` properly. Critical for user safety. | -| `Z` | usermodes/secureonlymsg | Allows only users on a secure connection to send you private messages/notices/CTCPs. Conversely, you can't send any such messages to non-secure users either. | | **SSL-Only Communication:** Enforces encrypted communications. Good for security-conscious networks. May limit communication with users on non-SSL ports. | -| `z` | *built-in* | Indicates you are connected via SSL/TLS | Set by server | **Security Indicator:** Shows encrypted connection status. Set automatically when connecting via SSL/TLS ports (typically 6697). Cannot be manually set. | - -## Configuration Tips for System Operators - -### Default User Modes - -Set in your `unrealircd.conf`: - -```c -set { - /* Default user modes set on new users */ - modes-on-connect "+ixw"; - - /* Hide idle time for users with +I */ - hide-idle-time { - everyone; - } -} -``` - -### Security Considerations - -#### Recommended Default Modes - -```c -set { - /* Secure defaults: cloak (+x), invisible (+i), wallops (+w) */ - modes-on-connect "+ixw"; - - /* Additional privacy: hide channels (+p) */ - modes-on-connect "+ixwp"; -} -``` - -#### Cloaking Configuration - -```c -cloak { - /* Enable hostname cloaking */ - enabled yes; - - /* Cloak keys - generate unique keys for your network */ - cloak-keys { - "aoAr1HnR6gl3sI7hJHpOeMZ7ciaqek+vZv8EGM+HA"; - "aoAr1HnR6gl3sI7hJHpOeMZ7ciaqek+vZv8EGM+HB"; - "aoAr1HnR6gl3sI7hJHpOeMZ7ciaqek+vZv8EGM+HC"; - } - - /* Cloak prefix for your network */ - cloak-prefix "atl"; -} -``` - -### IRC Operator Configuration - -#### Operator Block Example - -```c -oper yournick { - /* Password hash from mkpasswd */ - password "$argon2id$v=19$m=6144,t=2,p=2$WXOLpTE+DPDr8q6OBVTx3w$bqXpBsaAK6lkXfR/IPn+TcE0VJEKjUFD7xordE6pFSo"; - - /* Operator class with permissions */ - class "netadmin"; - - /* Default modes for IRCops */ - modes "+xwgs"; - - /* Vhost for operators */ - vhost "staff.atl.chat"; - - /* Mask for /WHOIS (hide real host) */ - mask "127.0.0.1"; -} -``` - -#### Operator Classes - -```c -class netadmin { - /* Maximum connections */ - maxcon 10; - - /* Permissions */ - permissions { - admin; - oper; - stats; - } -} -``` - -### Bot Configuration - -#### Bot Service Modes - -- Bots should use `+B` (marks as bot) -- Consider `+d` (deaf to channel messages) for command bots -- Use `+q` (unkickable) sparingly for critical bots - -#### Example Bot Oper Block - -```c -oper botserv { - password "$argon2id$..."; - class "bots"; - modes "+Bd"; - vhost "services.atl.chat"; -} -``` - -### Monitoring and Management - -#### Snomask Configuration - -```c -/* Enable server notice masks for operators */ -set { - snomasks-on-connect "+s"; -} -``` - -#### Common Snomasks - -- `+s` - Server notices -- `+k` - Kill notices -- `+c` - Connect notices -- `+f` - Flood notices - -### Troubleshooting - -#### Mode Issues - -- **Can't set mode**: Check oper privileges or mode restrictions -- **Modes not working**: Verify module loading in unrealircd.conf -- **Cloaking not working**: Check cloak-keys configuration - -#### Permission Errors - -- **Oper denied**: Verify password hash and oper block -- **Mode restricted**: Check oper class permissions -- **Vhost failed**: Ensure vhost block exists in config - -### Best Practices - -#### Security - -- Use strong passwords with Argon2id hashing -- Limit oper privileges to necessary permissions -- Regularly audit oper accounts -- Use vhosts to hide operator identities - -#### User Experience - -- Set appropriate default modes for privacy -- Configure cloaking to protect user privacy -- Use snomasks for effective monitoring -- Document custom modes for users - -#### Performance - -- Avoid excessive mode changes in channels -- Use appropriate flood controls -- Monitor for mode lock conflicts -- Consider mode persistence needs - -### Related Configuration - -- **Channel Modes**: Configure in channel blocks -- **Flood Controls**: Set in set::anti-flood block -- **Spam Filters**: Configure badword blocks -- **Security**: Review oper class permissions diff --git a/docs/services/irc/WEBPANEL.md b/docs/services/irc/WEBPANEL.md deleted file mode 100644 index 147cd6cf..00000000 --- a/docs/services/irc/WEBPANEL.md +++ /dev/null @@ -1,120 +0,0 @@ -# UnrealIRCd WebPanel - -The UnrealIRCd WebPanel provides a web-based administration interface for managing your IRC network. It connects to UnrealIRCd via JSON-RPC API and offers user management, channel administration, and server monitoring. - -## Quick Start - -1. **Start the WebPanel:** - - ```bash - make up - ``` - -2. **Access the interface:** - - ``` - http://localhost:8080 - ``` - -3. **Default credentials:** - - Username: `admin` - - Password: `admin` (change immediately!) - -## Configuration - -The WebPanel is automatically configured through Docker. Key settings: - -- **Port**: `8080` (configurable via `WEBPANEL_PORT` in `.env`) -- **RPC Connection**: Automatically connects to UnrealIRCd on port `8600` -- **Authentication**: File-based (default) or SQL -- **Database**: SQLite (stored in Docker volume) - -## Features - -### Dashboard - -- Server status and uptime -- User count and channel statistics -- Recent activity logs - -### User Management - -- View online users -- Manage user bans (K-line, Z-line, G-line) -- User search and details - -### Channel Administration - -- Channel list and details -- Channel mode management -- Topic editing - -### Server Configuration - -- Remote configuration editing -- Module management -- Log viewing - -## Troubleshooting - -### Connection Issues - -**WebPanel shows "Connection failed":** - -1. **Check RPC connectivity:** - - ```bash - docker exec atl-irc-webpanel nc -z unrealircd 8600 - ``` - -2. **Check UnrealIRCd logs:** - - ```bash - make logs-ircd | grep -i rpc - ``` - -3. **Verify RPC configuration:** - - ```bash - grep -A5 "listen.*rpc" apps/unrealircd/config/unrealircd.conf - ``` - -### Performance Issues - -**Slow loading:** - -1. **Restart WebPanel:** - - ```bash - docker restart atl-irc-webpanel - ``` - -2. **Clear cache:** - - ```bash - docker exec atl-irc-webpanel rm -rf /tmp/* - ``` - -## Backup - -**Backup WebPanel data:** - -```bash -docker run --rm -v atl-irc-webpanel-data:/data \ - alpine tar czf - -C /data . > webpanel-backup-$(date +%Y%m%d).tar.gz -``` - -**Restore WebPanel data:** - -```bash -docker run --rm -v atl-irc-webpanel-data:/data \ - -v $(pwd):/backup alpine \ - tar xzf /backup/webpanel-backup-latest.tar.gz -C /data -``` - -## Related Documentation - -- [UNREALIRCD.md](UNREALIRCD.md) - IRC server configuration -- [CONFIG.md](CONFIG.md) - Configuration management -- [API.md](API.md) - JSON-RPC API documentation -- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Common issues and solutions diff --git a/docs/services/irc/examples/atheme/atheme.conf.example b/docs/services/irc/examples/atheme/atheme.conf.example deleted file mode 100644 index 24acbf97..00000000 --- a/docs/services/irc/examples/atheme/atheme.conf.example +++ /dev/null @@ -1,3133 +0,0 @@ -/* This is an example configuration for Services. - * - * All statements end in semi-colons (';'). - * Shell style, C style, and C++ style comments may be used. - * - * Items marked with "(*)" are reconfigurable at runtime via REHASH. - * - * Boolean options that default to false can be disabled by removing them or - * commenting them out. Boolean options that default to true can be disabled - * by setting their value to "no". For example: - * - * nickserv { - * spam; # <-- enabled - * spam = no; # <-- disabled - * #spam; # <-- disabled if it defaults to false - * }; - * - * Items marked with "(B)" are boolean options that default to true. - */ - - - -/**************************************************************************** - * MODULES SECTION. * - ****************************************************************************/ - -/* - * These are the modules included with the core distribution of Services. - * - * You MUST load exactly ONE IRCd protocol module, followed by exactly ONE - * database backend module. These MUST be loaded before any pseudoservice - * modules (NickServ, ChanServ, et al). If in doubt, preserve the relative - * ordering of modules in this file and simply comment/uncomment "loadmodule" - * lines as necessary. - * - * You may be interested in the atheme community modules distribution as well, - * which adds additional features that may or may not be compatible with the - * project paradigms intended for maintainance of the core of atheme-services. - * - * Visit the atheme-services website for more information and to download - * them. - * - * Modules marked [experimental] will taint your atheme-services instance. Do - * not file any bug reports with us about using Services with those modules; - * they will be ignored. - * - * - * - * Modules can be loaded in one of two ways. You can either pass the module - * path as a string: - * - * loadmodule "foo/bar"; - * loadmodule "foo/baz"; - * - * ... which is how it used to be done in previous versions of this software, - * and how all of the loadmodule lines are listed in this file (for backward - * compatibility). This is also the only way to load a module by absolute, - * rather than relative, path. - * - * New in v7.3 is that you can use the module path to construct a series of - * configuration blocks: - * - * loadmodule { - * foo { - * bar; - * baz; - * }; - * }; - * - * ... which is equivalent to the individual two lines using strings above. - * You can mix and match both styles in the same configuration file, but not - * in the same directive. For example, this is valid: - * - * loadmodule "foo/bar"; - * loadmodule { - * baz { - * quux; - * }; - * }; - * - * ... while this is not valid: - * - * loadmodule "foo/bar" { - * baz { - * quux; - * }; - * }; - */ - - - -/* Dynamic security modules. - * - * WARNING: If you select one of these modules, the default security policy - * included with Atheme may break. These modules are intended for people who - * know what they are doing and understand the implications of what they do. - * - * Security modules which are likely to break the default policy are prefixed - * with "[!]"; if you are new to Atheme, you should avoid enabling them. - * - * If you find your security policy is broken, you may debug it while allowing - * normal operation of your IRC network by putting Atheme into "permissive - * mode". To do this, enable general::permissive_mode. - * - * [!] Infer "command:" namespace permissions security/cmdperm - */ -#loadmodule "security/cmdperm"; - - - -/* Protocol module. - * - * Please select a protocol module. Different servers use different protocols. - * Below is a listing of IRCds known to work with the various protocol - * modules available. - * - * Asuka 1.2.1 or later protocol/asuka - * Bahamut 2.1.x protocol/bahamut - * Charybdis IRCd protocol/charybdis - * ChatIRCd protocol/chatircd1.1 - * DreamForge 4.6.7 or later protocol/dreamforge - * InspIRCd 3.x and 4.x protocol/inspircd - * ircd-ratbox 2.0 and later protocol/ratbox - * IRCNet ircd (ircd 2.11) protocol/ircnet - * solanum protocol/solanum - * Nefarious IRCu 0.4.0 or later protocol/nefarious - * ngIRCd 19 or later [experimental] protocol/ngircd - * UnrealIRCd 3.2.* protocol/unreal - * UnrealIRCd 4 or later protocol/unreal4 - * - * If your IRCd vendor has supplied a module file, build it and load it here - * instead of one above. - */ -#loadmodule "protocol/charybdis"; - -/* Protocol mixins. - * - * These should be used if you do not have/want certain features on your - * network that your IRCd normally has. If you do not know what this means, - * you do not need any of them. - * - * Disable halfops protocol/mixin_nohalfops - * Disable holdnick (use enforcer clients) protocol/mixin_noholdnick - * Disable "protect" mode on channels protocol/mixin_noprotect - * Disable "owner" mode on channels protocol/mixin_noowner - */ -#loadmodule "protocol/mixin_nohalfops"; -#loadmodule "protocol/mixin_noholdnick"; -#loadmodule "protocol/mixin_noprotect"; -#loadmodule "protocol/mixin_noowner"; - - - -/* Database backend module. - * - * Please select a database backend module. Different backends allow for - * different ways in which the services data can be manipulated. YOU MAY ONLY - * HAVE ONE OF THESE BACKENDS LOADED. - * - * The following backends are available: - * - * Atheme 0.1 flatfile database format backend/flatfile - * Open Services Exchange database format backend/opensex - * - * Most networks will want opensex. - */ -loadmodule "backend/opensex"; - - - -/* Password hashing modules. - * - * If you would like encryption for your services passwords, or to migrate - * from another IRC services package which used encryption for its passwords, - * please select a module here. - * - * The following encryption-capable crypto modules are available: - * - * Argon2 (Password Hashing Competition 2015) crypto/argon2 - * scrypt (Tarsnap Online Backup Service) crypto/scrypt - * PBKDF2 (Including support for SASL SCRAM-SHA) crypto/pbkdf2v2 - * bcrypt (EksBlowfish; from Niels Provos etc.) crypto/bcrypt - * SHA2-512 crypt(3) a la '$6$...' crypto/crypt3-sha2-512 - * SHA2-256 crypt(3) a la '$5$...' crypto/crypt3-sha2-256 - * - * If you do not load an encryption-capable crypto module, some features will - * not work correctly, and errors will be logged on e.g. user registration - * that it was not possible to encrypt their password. Support for running - * without an encryption-capable crypto module will be removed in a later - * version of this software; for now it is just *HIGHLY* discouraged. - * - * Note, that upon starting with an encryption-capable crypto module, YOUR - * UNENCRYPTED PASSWORDS ARE IMMEDIATELY AND *IRREVERSIBLY* CONVERTED. Make - * at least TWO backups of your database before experimenting with this. If - * you have several thousand accounts, this conversion may take a long time. - * - * The following modules can only be used to /verify/ existing encrypted - * passwords, for example when upgrading from an older version of this - * software, or migrating from something else: - * - * PBKDF2 v1 (Atheme <= 7.2 compatibility) crypto/pbkdf2 - * Raw SHA2-512 crypto/rawsha2-512 - * Raw SHA2-256 crypto/rawsha2-256 - * Anope SHA2-256 (Anope 2.0 compatibility) crypto/anope-enc-sha256 - * Raw SHA1 (Anope ~1.8 compatibility) crypto/rawsha1 - * Raw MD5 (Anope ~1.8 compatibility) crypto/rawmd5 - * IRCServices (+ Anope) compatibility crypto/ircservices - * MD5 crypt(3) (Atheme Linux compatibility) crypto/crypt3-md5 - * DES crypt(3) (Atheme OS X compatibility) crypto/crypt3-des - * Base64 (Anope ~1.8 compatibility) crypto/base64 - * - * To transition between crypto schemes, load the preferred scheme first, - * and as users login or set new passwords, they will be migrated to the new - * preferred scheme. Like so: - * - * loadmodule "crypto/argon2"; - * loadmodule "crypto/scrypt"; - * loadmodule "crypto/pbkdf2v2"; - * loadmodule "crypto/pbkdf2"; - * loadmodule "crypto/crypt3-md5"; - * - * The Argon2 module requires the argon2 reference library (./configure - * --with-argon2) and is *NOT* available in Atheme v7.2 or earlier. If you - * wish to use this module while retaining the possibility to downgrade to - * v7.2, please see the crypto {} documentation below. - * - * The Scrypt module requires libsodium (./configure --with-libsodium) and is - * *NOT* available in Atheme v7.2 or earlier. This module may also require a - * 64-bit Operating System to function correctly. - * - * The PBKDF2v2 module has no dependencies and is recommended. If you were - * previously using the PBKDF2 v1 module on v7.2, you must still keep it in - * the configuration here; the PBKDF2 v2 module cannot verify its password - * hashes. However, you should also load PBKDF2 v2 (if you don't decide to use - * anything else), because the PBKDF2 v1 module is now verify-only. - * - * The bcrypt module will truncate passwords greater than 72 characters. It is - * also capable of verifying the older $2a$ digests that contain an integer - * wrap-around bug, as used on e.g. Anope. It is not capable of verifying the - * PHP-bcrypt $2x$ and $2y$ digests; but $2y$ can simply be changed to $2b$. - * All successfully-verified passwords not using $2b$ will be converted to it. - * This is an encryption-capable module, but its use is discouraged unless you - * need to use it for interoperability with some other piece of software. - * - * The crypt3-* modules depend on your platform crypt(3) supporting the - * respective algorithms. This is not guaranteed to be the case. If you used - * "crypto/posix" on Linux, you need "crypto/crypt3-md5". If you used - * "crypto/posix" on OS X, you need "crypto/crypt3-des". These modules issue - * informational messages when loaded to the effect that they might break in - * the future. They also run selftests on load to verify that they will work. - * - * All available modules are listed below, in the preferred load order. The - * modules that are commented out are not available by default (please see the - * v7.3 release notes in NEWS.md) or may require a third-party library to use. - * If you know that you do not need a specific module, it is better to not - * load it, so comment it out. Do not change the order of the modules below - * unless you need to migrate from one to the other (as described above); in - * particular, putting verify-only modules above encryption-capable modules - * would be a waste of CPU time every time password verification for a user - * whose password was not encrypted by them is attempted. - * - * Comments that start with -- describe the ./configure option necessary to - * have this module built. - */ -#loadmodule "crypto/argon2"; /* --with-argon2 */ -#loadmodule "crypto/scrypt"; /* --with-sodium */ -loadmodule "crypto/pbkdf2v2"; -#loadmodule "crypto/bcrypt"; /* See notes above */ -loadmodule "crypto/pbkdf2"; /* Verify-only, see prev. */ -#loadmodule "crypto/crypt3-sha2-512"; /* Needs crypt(3) support */ -#loadmodule "crypto/crypt3-sha2-256"; /* Needs crypt(3) support */ -#loadmodule "crypto/crypt3-md5"; /* --enable-legacy-pwcrypto */ -#loadmodule "crypto/rawsha2-512"; /* --enable-legacy-pwcrypto */ -#loadmodule "crypto/rawsha2-256"; /* --enable-legacy-pwcrypto */ -#loadmodule "crypto/anope-enc-sha256"; /* --enable-legacy-pwcrypto */ -#loadmodule "crypto/rawsha1"; /* --enable-legacy-pwcrypto */ -#loadmodule "crypto/rawmd5"; /* --enable-legacy-pwcrypto */ -#loadmodule "crypto/ircservices"; /* --enable-legacy-pwcrypto */ -#loadmodule "crypto/crypt3-des"; /* --enable-legacy-pwcrypto */ -#loadmodule "crypto/base64"; /* --enable-legacy-pwcrypto */ - - - -/* Authentication module. - * - * These allow using passwords from an external system. The password given - * when registering a new account is also checked against the external - * system. - * - * The following authentication modules are available: - * - * LDAP auth/ldap - * - * The LDAP module requires OpenLDAP client libraries. It uses them in a - * synchronous manner, which means that an unresponsive LDAP server can - * freeze services. - */ -#loadmodule "auth/ldap"; - - - -/* NickServ modules. - * - * Here you can disable or enable certain features of NickServ, by defining - * which modules are loaded. You can even disable NickServ entirely. Please - * note, however, that an authentication service (either NickServ, UserServ, - * or SASLServ) is required for proper functionality. - * - * Core components nickserv/main - * Nickname access lists nickserv/access - * Bad e-mail address blocking nickserv/badmail - * CertFP fingerprint managment nickserv/cert - * DROP command nickserv/drop - * Nickname enforcement nickserv/enforce - * GHOST command nickserv/ghost - * GROUP and UNGROUP commands nickserv/group - * HELP command nickserv/help - * Nickname expiry override (HOLD command) nickserv/hold - * IDENTIFY command nickserv/identify - * INFO command nickserv/info - * Last quit message in INFO nickserv/info_lastquit - * LIST command nickserv/list - * LISTLOGINS command nickserv/listlogins - * LISTMAIL command nickserv/listmail - * LISTOWNMAIL command nickserv/listownmail - * LOGIN command (for no_nick_ownership) nickserv/login - * LOGINNOLIMIT command nickserv/loginnolimit - * LOGOUT command nickserv/logout - * MARK command nickserv/mark - * Password quality validation nickserv/pwquality - * FREEZE command nickserv/freeze - * LISTCHANS command nickserv/listchans - * LISTGROUPS command nickserv/listgroups - * REGISTER command nickserv/register - * Bypass registration limits (REGNOLIMIT) nickserv/regnolimit - * Password reset (RESETPASS command) nickserv/resetpass - * RESTRICT command nickserv/restrict - * Password return (RETURN command) nickserv/return - * Password retrieval (SENDPASS command) nickserv/sendpass - * Password retrieval allowed to normal users nickserv/sendpass_user - * Change primary nickname (SET ACCOUNTNAME) nickserv/set_accountname - * SET BADPASSWDMSG command nickserv/set_badpasswdmsg - * SET EMAIL command nickserv/set_email - * SET EMAILMEMOS command nickserv/set_emailmemos - * SET ENFORCETIME command nickserv/set_enforcetime - * SET HIDEMAIL command nickserv/set_hidemail - * SET LANGUAGE command nickserv/set_language - * SET NEVERGROUP command nickserv/set_nevergroup - * SET NEVEROP command nickserv/set_neverop - * SET NOGREET command nickserv/set_nogreet - * SET NOMEMO command nickserv/set_nomemo - * SET NOOP command nickserv/set_noop - * SET NOPASSWORD command nickserv/set_nopassword - * SET PASSWORD command nickserv/set_password - * PRIVMSG instead of NOTICE (SET PRIVMSG cmd) nickserv/set_privmsg - * Account info hiding (SET PRIVATE command) nickserv/set_private - * SET PROPERTY command nickserv/set_property - * SET PUBKEY command nickserv/set_pubkey - * SET QUIETCHG command nickserv/set_quietchg - * Password retrieval uses code (SETPASS cmd) nickserv/setpass - * STATUS command nickserv/status - * Nickname metadata viewer (TAXONOMY command) nickserv/taxonomy - * VACATION command nickserv/vacation - * VERIFY command nickserv/verify - * VHOST command nickserv/vhost - * Delay services account registrations nickserv/waitreg - */ -loadmodule "nickserv/main"; -#loadmodule "nickserv/access"; -loadmodule "nickserv/badmail"; -#loadmodule "nickserv/cert"; -loadmodule "nickserv/drop"; -#loadmodule "nickserv/enforce"; -loadmodule "nickserv/ghost"; -loadmodule "nickserv/group"; -loadmodule "nickserv/help"; -loadmodule "nickserv/hold"; -loadmodule "nickserv/identify"; -loadmodule "nickserv/info"; -#loadmodule "nickserv/info_lastquit"; -loadmodule "nickserv/list"; -#loadmodule "nickserv/listlogins"; -loadmodule "nickserv/listmail"; -#loadmodule "nickserv/listownmail"; -#loadmodule "nickserv/login"; -#loadmodule "nickserv/loginnolimit"; -loadmodule "nickserv/logout"; -loadmodule "nickserv/mark"; -#loadmodule "nickserv/pwquality"; -loadmodule "nickserv/freeze"; -loadmodule "nickserv/listchans"; -loadmodule "nickserv/listgroups"; -loadmodule "nickserv/register"; -loadmodule "nickserv/regnolimit"; -loadmodule "nickserv/resetpass"; -loadmodule "nickserv/restrict"; -loadmodule "nickserv/return"; -loadmodule "nickserv/setpass"; -#loadmodule "nickserv/sendpass"; -loadmodule "nickserv/sendpass_user"; -loadmodule "nickserv/set_accountname"; -#loadmodule "nickserv/set_badpasswdmsg"; -loadmodule "nickserv/set_email"; -loadmodule "nickserv/set_emailmemos"; -#loadmodule "nickserv/set_enforcetime"; -loadmodule "nickserv/set_hidemail"; -loadmodule "nickserv/set_language"; -loadmodule "nickserv/set_nevergroup"; -loadmodule "nickserv/set_neverop"; -loadmodule "nickserv/set_nogreet"; -loadmodule "nickserv/set_nomemo"; -loadmodule "nickserv/set_noop"; -#loadmodule "nickserv/set_nopassword"; -loadmodule "nickserv/set_password"; -#loadmodule "nickserv/set_privmsg"; -#loadmodule "nickserv/set_private"; -loadmodule "nickserv/set_property"; -loadmodule "nickserv/set_pubkey"; -loadmodule "nickserv/set_quietchg"; -loadmodule "nickserv/status"; -loadmodule "nickserv/taxonomy"; -loadmodule "nickserv/vacation"; -loadmodule "nickserv/verify"; -loadmodule "nickserv/vhost"; -#loadmodule "nickserv/waitreg"; - - - -/* ChanServ modules. - * - * Here you can disable or enable certain features of ChanServ, by defining - * which modules are loaded. You can even disable ChanServ entirely. Please - * note, however, that an authentication service (either NickServ, UserServ, - * or SASLServ) is required for proper functionality of ChanServ. - * - * Core components chanserv/main - * ACCESS command (simplified ACL editing) chanserv/access - * AKICK command chanserv/akick - * BAN/UNBAN commands chanserv/ban - * UNBAN self only (load ban or this not both) chanserv/unban_self - * BANSEARCH command chanserv/bansearch - * CLOSE command chanserv/close - * CLONE command chanserv/clone - * CLEAR command chanserv/clear - * CLEAR AKICKS command chanserv/clear_akicks - * CLEAR BANS command chanserv/clear_bans - * CLEAR FLAGS command chanserv/clear_flags - * CLEAR USERS command chanserv/clear_users - * COUNT command chanserv/count - * DROP command chanserv/drop - * Forced flags changes chanserv/fflags - * FLAGS command chanserv/flags - * Forced foundership transfers chanserv/ftransfer - * GETKEY command chanserv/getkey - * HALFOP/DEHALFOP commands chanserv/halfop - * HELP command chanserv/help - * Channel expiry override (HOLD command) chanserv/hold - * INFO command chanserv/info - * INVITE command chanserv/invite - * KICK/KICKBAN commands chanserv/kick - * LIST command chanserv/list - * MARK command chanserv/mark - * Moderated channel registrations chanserv/moderate - * OP/DEOP commands chanserv/op - * OWNER/DEOWNER commands chanserv/owner - * PROTECT/DEPROTECT commands chanserv/protect - * QUIET command (+q support) chanserv/quiet - * Channel takeover recovery (RECOVER command) chanserv/recover - * REGISTER command chanserv/register - * SET EMAIL command chanserv/set_email - * SET ENTRYMSG command chanserv/set_entrymsg - * SET FANTASY command chanserv/set_fantasy - * SET GAMESERV command chanserv/set_gameserv - * SET GUARD command chanserv/set_guard - * SET KEEPTOPIC command chanserv/set_keeptopic - * SET LIMITFLAGS command chanserv/set_limitflags - * SET MLOCK command chanserv/set_mlock - * SET PREFIX command chanserv/set_prefix - * Channel info hiding (SET PRIVATE command) chanserv/set_private - * SET PROPERTY command chanserv/set_property - * SET PUBACL command chanserv/set_pubacl - * SET RESTRICTED command chanserv/set_restricted - * SET SECURE command chanserv/set_secure - * SET TOPICLOCK command chanserv/set_topiclock - * SET URL command chanserv/set_url - * SET VERBOSE command chanserv/set_verbose - * STATUS command chanserv/status - * SYNC command (and automatic ACL syncing) chanserv/sync - * Named Successor ACL flag chanserv/successor_acl - * Channel metadata viewer (TAXONOMY command) chanserv/taxonomy - * TEMPLATE command chanserv/template - * TOPIC/TOPICAPPEND commands chanserv/topic - * VOICE/DEVOICE commands chanserv/voice - * WHY command chanserv/why - * - * VOP/HOP/AOP/SOP commands chanserv/xop - * - * This module provides emulation of the ircservices XOP scheme ONLY. - * Do not report discrepencies when using native commands to edit channel - * ACLs. This is intentional. - * - * Flood protection chanserv/antiflood - * - * This module should be loaded after at least "chanserv/quiet" if you want - * the autoquiet feature to work. - */ -loadmodule "chanserv/main"; -loadmodule "chanserv/access"; -loadmodule "chanserv/akick"; -loadmodule "chanserv/ban"; -#loadmodule "chanserv/unban_self"; -loadmodule "chanserv/bansearch"; -loadmodule "chanserv/clone"; -loadmodule "chanserv/close"; -loadmodule "chanserv/clear"; -loadmodule "chanserv/clear_akicks"; -loadmodule "chanserv/clear_bans"; -loadmodule "chanserv/clear_flags"; -loadmodule "chanserv/clear_users"; -loadmodule "chanserv/count"; -loadmodule "chanserv/drop"; -#loadmodule "chanserv/fflags"; -loadmodule "chanserv/flags"; -loadmodule "chanserv/ftransfer"; -loadmodule "chanserv/getkey"; -#loadmodule "chanserv/halfop"; -loadmodule "chanserv/help"; -loadmodule "chanserv/hold"; -loadmodule "chanserv/info"; -loadmodule "chanserv/invite"; -loadmodule "chanserv/kick"; -loadmodule "chanserv/list"; -loadmodule "chanserv/mark"; -#loadmodule "chanserv/moderate"; -loadmodule "chanserv/op"; -#loadmodule "chanserv/owner"; -#loadmodule "chanserv/protect"; -#loadmodule "chanserv/quiet"; -loadmodule "chanserv/recover"; -loadmodule "chanserv/register"; -loadmodule "chanserv/set_email"; -loadmodule "chanserv/set_entrymsg"; -loadmodule "chanserv/set_fantasy"; -#loadmodule "chanserv/set_gameserv"; -loadmodule "chanserv/set_guard"; -loadmodule "chanserv/set_keeptopic"; -#loadmodule "chanserv/set_limitflags"; -loadmodule "chanserv/set_mlock"; -loadmodule "chanserv/set_prefix"; -#loadmodule "chanserv/set_private"; -loadmodule "chanserv/set_property"; -#loadmodule "chanserv/set_pubacl"; -loadmodule "chanserv/set_restricted"; -loadmodule "chanserv/set_secure"; -loadmodule "chanserv/set_topiclock"; -loadmodule "chanserv/set_url"; -loadmodule "chanserv/set_verbose"; -loadmodule "chanserv/status"; -loadmodule "chanserv/sync"; -#loadmodule "chanserv/successor_acl"; -loadmodule "chanserv/taxonomy"; -loadmodule "chanserv/template"; -loadmodule "chanserv/topic"; -loadmodule "chanserv/voice"; -loadmodule "chanserv/why"; -#loadmodule "chanserv/xop"; -loadmodule "chanserv/antiflood"; - - - -/* ChanFix module. - * - * Here you can disable or enable certain features of ChanFix, by defining - * which modules are loaded. - * - * Core components chanfix/main - */ -#loadmodule "chanfix/main"; - - - -/* OperServ modules. - * - * Here you can disable or enable certain features of OperServ, by defining - * which modules are loaded. - * - * AKILL system operserv/akill - * CLEARCHAN command operserv/clearchan - * CLONES system operserv/clones - * COMPARE command operserv/compare - * GENHASH command operserv/genhash - * GREPLOG command operserv/greplog - * HELP command operserv/help - * IGNORE system operserv/ignore - * IDENTIFY command operserv/identify - * INFO command operserv/info - * INJECT command operserv/inject - * JOINRATE command & join rate monitoring operserv/joinrate - * JUPE command operserv/jupe - * MODE command operserv/mode - * MODLIST command operserv/modlist - * Module inspect/load/reload/unload commands operserv/modmanager - * NOOP system operserv/noop - * Regex mass akill (RAKILL command) operserv/rakill - * RAW command operserv/raw - * READONLY command operserv/readonly - * REHASH command operserv/rehash - * RESTART command operserv/restart - * Display regex matching (RMATCH command) operserv/rmatch - * Most common realnames (RNC command) operserv/rnc - * RWATCH system operserv/rwatch - * - * Note that ALL of these SET commands only apply until the next rehash: - * - * ALL of the below SET commands (deprecated) operserv/set - * SET AKICKTIME subcommand (temporarily) operserv/set_akicktime - * SET CHANEXPIRE subcommand (temporarily) operserv/set_chanexpire - * SET COMMITINTERVAL subcommand (temporarily) operserv/set_commitinterval - * SET ENFORCEPREFIX subcommand (temporarily) operserv/set_enforceprefix - * SET KLINETIME subcommand (temporarily) operserv/set_klinetime - * SET MAXCHANACS subcommand (temporarily) operserv/set_maxchanacs - * SET MAXCHANS subcommand (temporarily) operserv/set_maxchans - * SET MAXFOUNDERS subcommand (temporarily) operserv/set_maxfounders - * SET MAXLOGINS subcommand (temporarily) operserv/set_maxlogins - * SET MAXNICKS subcommand (temporarily) operserv/set_maxnicks - * SET MAXUSERS subcommand (temporarily) operserv/set_maxusers - * SET MDLIMIT subcommand (temporarily) operserv/set_mdlimit - * SET NICKEXPIRE subcommand (temporarily) operserv/set_nickexpire - * SET RECONTIME subcommand (temporarily) operserv/set_recontime - * SET SPAM subcommand (temporarily) operserv/set_spam - * - * SGLINE system operserv/sgline - * SHUTDOWN command operserv/shutdown - * Non-config oper privileges (SOPER command) operserv/soper - * Oper privilege display (SPECS command) operserv/specs - * SQLINE system operserv/sqline - * UPDATE command operserv/update - * UPTIME command operserv/uptime - */ -loadmodule "operserv/akill"; -#loadmodule "operserv/clearchan"; -#loadmodule "operserv/clones"; -loadmodule "operserv/compare"; -#loadmodule "operserv/genhash"; -#loadmodule "operserv/greplog"; -loadmodule "operserv/help"; -loadmodule "operserv/identify"; -loadmodule "operserv/ignore"; -loadmodule "operserv/info"; -#loadmodule "operserv/joinrate"; -loadmodule "operserv/jupe"; -loadmodule "operserv/mode"; -loadmodule "operserv/modlist"; -loadmodule "operserv/modmanager"; -loadmodule "operserv/noop"; -#loadmodule "operserv/rakill"; -loadmodule "operserv/readonly"; -loadmodule "operserv/rehash"; -loadmodule "operserv/restart"; -loadmodule "operserv/rmatch"; -loadmodule "operserv/rnc"; -loadmodule "operserv/rwatch"; -loadmodule "operserv/set"; -loadmodule "operserv/sgline"; -loadmodule "operserv/shutdown"; -#loadmodule "operserv/soper"; -loadmodule "operserv/specs"; -loadmodule "operserv/sqline"; -loadmodule "operserv/update"; -loadmodule "operserv/uptime"; - - - -/* MemoServ modules. - * - * Here you can disable or enable certain features of MemoServ, by defining - * which modules are loaded. You can even disable MemoServ entirely. - * - * HELP command memoserv/help - * SEND command memoserv/send - * Channel memos (SENDOPS command) memoserv/sendops - * Group memos (SENDGROUP command) memoserv/sendgroup - * LIST command memoserv/list - * READ command memoserv/read - * FORWARD command memoserv/forward - * DELETE command memoserv/delete - * IGNORE command memoserv/ignore - */ -loadmodule "memoserv/help"; -loadmodule "memoserv/send"; -loadmodule "memoserv/sendops"; -loadmodule "memoserv/sendgroup"; -loadmodule "memoserv/list"; -loadmodule "memoserv/read"; -loadmodule "memoserv/forward"; -loadmodule "memoserv/delete"; -loadmodule "memoserv/ignore"; - - - -/* Global module. - * - * Like the other services, the Global noticer is a module. You can disable or - * enable it to your liking below. Please note that the Global noticer is - * dependent on OperServ for full functionality. - */ -loadmodule "global/main"; - - - -/* InfoServ module. - * - * Like the other services, InfoServ is a module. You can disable or enable it - * to your liking below. - */ -loadmodule "infoserv/main"; - - - -/* SASL agent module. - * - * Allows clients to authenticate to services via SASL with an appropriate - * IRCd. If you want this, you need to enable at least one mechanism. - * - * AUTHCOOKIE mechanism (for IRIS) saslserv/authcookie - * ECDH-X25519-CHALLENGE mechanism saslserv/ecdh-x25519-challenge - * ECDSA-NIST256P-CHALLENGE mechanism saslserv/ecdsa-nist256p-challenge - * EXTERNAL mechanism (IRCv3.1+) saslserv/external - * PLAIN mechanism saslserv/plain - * SCRAM-SHA-* mechanisms saslserv/scram - * - * ECDH-X25519-CHALLENGE support requires that Atheme be compiled against a - * cryptographic library that provides X25519 ECDH support (BoringSSL, - * LibreSSL, ARM mbedTLS, Nettle, Sodium). This will be checked while running - * ./configure. - * - * ECDSA-NIST256P-CHALLENGE support requires that Atheme be compiled against - * an OpenSSL with ECDSA support (not RHEL etc. unless you compile your own). - * This will be checked while running ./configure. - * - * You MUST read doc/SASL-SCRAM before loading "saslserv/scram"! - */ -loadmodule "saslserv/authcookie"; -#loadmodule "saslserv/ecdh-x25519-challenge"; -#loadmodule "saslserv/ecdsa-nist256p-challenge"; -#loadmodule "saslserv/external"; -loadmodule "saslserv/plain"; -#loadmodule "saslserv/scram"; /* READ doc/SASL-SCRAM FIRST! */ - - - -/* GameServ modules. - * - * Here you can disable or enable certain features of GameServ, by defining - * which modules are loaded. You can even disable GameServ entirely. - * - * DICE/WOD commands gameserv/dice - * EIGHTBALL command gameserv/eightball - * Game-specific dice calculators gameserv/gamecalc - * HELP commands gameserv/help - * LOTTERY command gameserv/lottery - * NAMEGEN command gameserv/namegen - * RPS command gameserv/rps - */ -#loadmodule "gameserv/dice"; -#loadmodule "gameserv/eightball"; -#loadmodule "gameserv/gamecalc"; -#loadmodule "gameserv/help"; -#loadmodule "gameserv/lottery"; -#loadmodule "gameserv/namegen"; -#loadmodule "gameserv/rps"; - - - -/* RPGServ modules. - * - * Here you can disable or enable certain features of RPGServ, by defining - * which modules are loaded. You can even disable RPGServ entirely. - * - * ENABLE/DISABLE commands rpgserv/enable - * HELP command rpgserv/help - * INFO command rpgserv/info - * LIST command rpgserv/list - * SEARCH command rpgserv/search - * SET commands rpgserv/set - */ -#loadmodule "rpgserv/enable"; -#loadmodule "rpgserv/help"; -#loadmodule "rpgserv/info"; -#loadmodule "rpgserv/list"; -#loadmodule "rpgserv/search"; -#loadmodule "rpgserv/set"; - - - -/* BotServ modules. - * - * Here you can disable or enable certain features of BotServ, by defining - * which modules are loaded. You can even disable BotServ entirely. - * - * Core components botserv/main - * HELP command botserv/help - * INFO command botserv/info - * NPC commands (SAY, ACT) botserv/bottalk - * SET FANTASY command botserv/set_fantasy - * SET NOBOT command botserv/set_nobot - * SET PRIVATE command botserv/set_private - * SET SAYCALLER command botserv/set_saycaller - */ -#loadmodule "botserv/main"; -#loadmodule "botserv/help"; -#loadmodule "botserv/info"; -#loadmodule "botserv/bottalk"; -#loadmodule "botserv/set_fantasy"; -#loadmodule "botserv/set_nobot"; -#loadmodule "botserv/set_private"; -#loadmodule "botserv/set_saycaller"; - - - -/* HostServ modules. - * - * Here you can disable or enable certain features of HostServ, by defining - * which modules are loaded. You can even disable HostServ entirely. - * - * HostServ is a more complex, and optional virtual host management service. - * Users wishing only to set vhosts need not use it (they can use the builtin - * vhost management of NickServ instead). - * - * HELP command hostserv/help - * OFFER system hostserv/offer - * ON and OFF commands hostserv/onoff - * REQUEST system hostserv/request - * VHOST and LISTVHOST commands hostserv/vhost - * VHOSTNICK command hostserv/vhostnick - * GROUP command hostserv/group - * DROP command hostserv/drop - */ -#loadmodule "hostserv/help"; -#loadmodule "hostserv/onoff"; -#loadmodule "hostserv/offer"; -#loadmodule "hostserv/request"; -#loadmodule "hostserv/vhost"; -#loadmodule "hostserv/vhostnick"; -#loadmodule "hostserv/group"; -#loadmodule "hostserv/drop"; - - - -/* HelpServ modules. - * - * HelpServ allows users to request help from network staff in a few different - * ways. - * - * HELPME command helpserv/helpme - * Help Ticket system helpserv/ticket - * Service List helpserv/services - * - * The ticket system works like a bugtracker or helpdesk ticket system, HELPME - * works like a one-time alert. You should probably only load one of the two - * systems. - */ -#loadmodule "helpserv/helpme"; -#loadmodule "helpserv/ticket"; -#loadmodule "helpserv/services"; - - - -/* Channel listing service. - * - * Allows users to list channels with more flexibility than the IRC /LIST - * command. - * - * Core components alis/main - */ -#loadmodule "alis/main"; - - - -/* StatServ module. - * - * StatServ provides basic statistics and split tracking. - * - * CHANNEL command statserv/channel - * NETSPLIT command statserv/netsplit - * SERVER command statserv/server - */ -#loadmodule "statserv/channel"; -#loadmodule "statserv/netsplit"; -#loadmodule "statserv/server"; - - - -/* GroupServ module. - * - * GroupServ allows users to create groups to easily mass-manage channel - * access and more. - * - * Core components groupserv/main - * ACSNOLIMIT command groupserv/acsnolimit - * DROP command groupserv/drop - * FFLAGS command groupserv/fflags - * FLAGS command groupserv/flags - * HELP command groupserv/help - * INFO command groupserv/info - * JOIN command groupserv/join - * LIST command groupserv/list - * LISTCHANS command groupserv/listchans - * REGISTER command groupserv/register - * REGNOLIMIT command groupserv/regnolimit - * INVITE command groupserv/invite - * SET command groupserv/set - * SET CHANNEL command groupserv/set_channel - * SET DESCRIPTION command groupserv/set_description - * SET EMAIL command groupserv/set_email - * SET GROUPNAME command groupserv/set_groupname - * SET JOINFLAGS command groupserv/set_joinflags - * SET OPEN command groupserv/set_open - * SET PUBLIC command groupserv/set_public - * SET URL command groupserv/set_url - */ -loadmodule "groupserv/main"; -loadmodule "groupserv/acsnolimit"; -loadmodule "groupserv/drop"; -loadmodule "groupserv/fflags"; -loadmodule "groupserv/flags"; -loadmodule "groupserv/help"; -loadmodule "groupserv/info"; -loadmodule "groupserv/join"; -loadmodule "groupserv/list"; -loadmodule "groupserv/listchans"; -loadmodule "groupserv/register"; -loadmodule "groupserv/regnolimit"; -#loadmodule "groupserv/invite"; -loadmodule "groupserv/set"; -loadmodule "groupserv/set_channel"; -loadmodule "groupserv/set_description"; -loadmodule "groupserv/set_email"; -loadmodule "groupserv/set_groupname"; -loadmodule "groupserv/set_joinflags"; -loadmodule "groupserv/set_open"; -loadmodule "groupserv/set_public"; -loadmodule "groupserv/set_url"; - - - -/* - * Various modules. - * - * Atheme includes an optional HTTP server that can be used for integration - * with portal software and other useful things. To enable it, load this - * module, and uncomment the httpd { } block towards the bottom of the config. - * - * HTTP Server misc/httpd - * - * If you wish to throttle password-based login attempts (regardless of - * whether the password is correct or not), load this module, and uncomment - * the throttle { } block towards the bottom of the config. - * - * Password-based login throttling misc/login_throttling - */ -#loadmodule "misc/httpd"; -#loadmodule "misc/login_throttling"; - - - -/* XMLRPC server module. - * - * The XML-RPC handler requires "misc/httpd" to be loaded as it merely - * registers a path handler for XML-RPC. The path used for XML-RPC is /xmlrpc. - * - * XMLRPC handler for the httpd transport/xmlrpc - */ -#loadmodule "transport/xmlrpc"; - - - -/* Extended target entity types. - * - * Atheme can set up special target mapping entities which match multiple - * users in channel access entries. These target mapping entity types are - * defined through the 'exttarget' modules listed below. - * - * $oper exttarget match type exttarget/oper - * $registered exttarget match type exttarget/registered - * $channel exttarget match type exttarget/channel - * $chanacs exttarget match type exttarget/chanacs - * $server exttarget match type exttarget/server - */ -#loadmodule "exttarget/oper"; -#loadmodule "exttarget/registered"; -#loadmodule "exttarget/channel"; -#loadmodule "exttarget/chanacs"; -#loadmodule "exttarget/server"; - - - -/* Proxyscan (DNSBL) modules. - * - * Atheme can also check set DNS Blacklists for matches and respond as set. - * Activate modules here and customize further down under Proxyscan section. - */ -#loadmodule "proxyscan/dnsbl"; - - - -/* Other modules. - * - * Put any other modules you want to load on startup here. The path is - * relative to PREFIX or PREFIX/lib/atheme, depending on how Atheme was - * compiled. - */ -#loadmodule "contrib/backtrace"; - - - -/**************************************************************************** - * SERVICES RUNTIME CONFIGURATION SECTION. * - ****************************************************************************/ - -/* - * This block controls the configuration options for crypto modules. - * - * It is recommended to either leave the values at their defaults, or - * experiment with them so that it takes approximately 0.2-0.4 seconds for - * users to identify. Services blocks while the password is being encrypted or - * verified, so don't set these too large, or people can hang services by - * trying many password attempts at once. - * - * A benchmark program for the Argon2, scrypt & PBKDF2 crypto code is - * available to assist with tuning these parameters: - * - * - ./configure --prefix=foo ... - * - make - * - make install - * - ${foo}/bin/atheme-crypto-benchmark -o - * - * If you wish to deploy SASL SCRAM support, please read 'doc/SASL-SCRAM' and - * pass the '-i' flag to the included cryptographic benchmarking utility too. - * - * If you are using the PBKDF2 module, its performance will be significantly - * affected by your choice of cryptographic digest library. This software can - * currently interface with 3 libraries; in decreasing order of performance: - * - * - OpenSSL (libcrypto) - * - GnuPG (libgcrypt) - * - ARM mbedTLS (libmbedcrypto) - * - * If you have one of these libraries available at configure-time, the PBKDF2 - * module will perform significantly better, allowing you to raise its - * iteration count without affecting the computation time. This is indicated - * by the output of the configure script; "Digest Frontend". The benchmark - * program will also inform you what cryptographic digest library it is using, - * if any. - * - * - * - * If you are migrating from "crypto/argon2d" (v7.2) to "crypto/argon2", and - * you wish to use the same parameters as the older module's defaults, to - * retain the ability to downgrade services, configure it like so: - * - * crypto { - * argon2_type = "argon2d"; - * argon2_memcost = 14; - * argon2_timecost = 32; - * argon2_threads = 1; - * argon2_saltlen = 32; - * argon2_hashlen = 64; - * }; - * - * - * - * If you are migrating from "crypto/pbkdf2" (v7.2) to "crypto/pbkdf2v2", and - * you wish to use the same parameters as the older module, configure it like - * so: - * - * crypto { - * pbkdf2v2_digest = "SHA512"; - * pbkdf2v2_rounds = 128000; - * }; - * - * Note that this will still result in passwords being re-encrypted with the - * newer module (as the older module successfully verifies them); another new - * PBKDF2 computation with a new salt will occur, but this is still no worse - * than an invocation of NickServ's "SET PASSWORD" command. You will still - * need to keep the old module in your loadmodule configuration above, as the - * new module cannot verify digests produced by the old one. - * - * If you wish to deploy SASL SCRAM support, please read 'doc/SASL-SCRAM'. - * Its advice regarding parameter choice takes precedence over this! - */ -crypto { - - /* (*) argon2_type - * - * The algorithm type to use for new passwords. - * - * Argon2d is suitable for use on a dedicated machine that has - * limited access. It provides the most resistance to GPU and ASIC - * cracking attacks, but its operation is data-dependent; that is, - * during its operation, keying material derived from the password - * itself is indirectly affecting the execution choices made by the - * algorithm. This creates a side-channel that can leak information - * about the password to other software running on the same physical - * machine. - * - * Argon2i avoids this by being data-independent. The order of memory - * accesses, conditional execution, etc. does not depend on the - * password, or any material derived from the password, so no side- - * channel that can reveal any information about the password is - * created. However, this means that it is easier to bruteforce by a - * password cracker, which does not have to account for execution - * differences in its implementation. This is the most suitable choice - * for running on a virtual machine that is co-located with other, - * untrusted, virtual machines, or on a dedicated machine that runs - * other, untrusted, software, or has untrusted user access. - * - * Argon2id is a blend of both, limiting the exploitability of any - * side-channels while retaining excellent resistance to GPU and ASIC - * cracking. This is suitable for all but the most sensitive of - * deployments. - * - * All algorithm types perform about equally as well as each other; - * changing this will not significantly affect the computation time. - * - * The "argon2id" type requires a more recent libargon2 library. This - * is indicated in your ./configure output ("checking if libargon2 - * algorithm type Argon2id appears to be usable..."). - * - * Valid values are "argon2d", "argon2i", and "argon2id" - * The default is "argon2id"; unless unsupported, then "argon2d" - */ - #argon2_type = "argon2id"; - - /* (*) argon2_memcost - * - * Memory cost (as a power of 2, in KiB) to use for new passwords. - * - * You should set this as high as is reasonable for the machine you - * will be running this software on. If this results in too slow a - * computation time, reset the time cost below to its minimum value. - * If it is still too slow, decrement this value (halving the memory - * usage) until it is fast enough. Alternatively, if it is still too - * fast after setting this to its highest reasonable value, raise the - * time cost below until it is not. A benchmark program is available - * alongside this software to aid in this process. - * - * WARNING: - * Do *NOT* set this to more than 20 (1 GiB RAM) on a 32-bit - * Operating System! - * - * Valid values are 3 (8 KiB RAM) to 30 (1 TiB RAM) (inclusive) - * The default is 16 (64 MiB RAM) - */ - #argon2_memcost = 16; - - /* (*) argon2_timecost - * - * Time cost (iterations over the memory pool). - * - * Valid values are 3 to 1,048,576 (inclusive) - * The default is 3 - */ - #argon2_timecost = 3; - - /* (*) argon2_threads - * - * Number of processor threads to use for new passwords. - * - * If you want to increase the amount of computation effort required, - * while not increasing the real ("wall clock") time required, raise - * this setting to its maximum reasonable value for the machine you - * will be running this software on. - * - * This software is not multi-threaded, so only one password will be - * verified at a time. Therefore, you do NOT need to divide this by - * the expected maximum number of simultaneous logins. - * - * It is pointless to set this higher than the number of hardware - * processing threads you have; increase the time cost above if you - * want to make it arbitrarily slower. Diminishing returns are to be - * expected once you exceed the number of hardware processing /cores/ - * you have; hyperthreading does NOT provide much (if any) of a boost - * for this workload. - * - * Increasing this value will *decrease* the real ("wall clock") time - * required, so you may have to subsequently increase the time cost - * above again to make it "just slow enough" once more. A benchmark - * program is available alongside this software to aid in this - * process. - * - * WARNING: - * The (size of the) memory pool configured above is split between - * the threads, which can result in too small a memory area per- - * thread if many threads are used. If you set this value, it is - * HIGHLY RECOMMENDED that you run the included benchmarking - * program with the same configuration options, to confirm that it - * works! - * - * WARNING: - * This feature is experimental. Some of the code in this software - * is not thread-safe, and although every effort has been made to - * ensure that this feature will not interfere with the operation of - * this software, this cannot be guaranteed. - * - * Valid values are 1 to 255 (inclusive) - * The default is 1 (do not use any computation parallelism) - */ - #argon2_threads = 1; - - /* (*) argon2_saltlen - * - * Salt length (in bytes) to use for new passwords. You should only - * change this if absolutely necessary; for example, to interoperate - * with other software. Its value doesn't significantly affect the - * computation time. - * - * Valid values are 4 to 48 (inclusive) - * The default is 16 - */ - #argon2_saltlen = 16; - - /* (*) argon2_hashlen - * - * Digest length (in bytes) to use for new passwords. You should only - * change this if absolutely necessary; for example, to interoperate - * with other software. Its value doesn't significantly affect the - * computation time. - * - * Valid values are 16 to 128 (inclusive) - * The default is 64 - */ - #argon2_hashlen = 64; - - /* (*) scrypt_memlimit - * - * Memory limit (as a power of 2, in KiB) to use for new passwords. - * - * You should set this as high as is reasonable for the machine you - * will be running this software on. If this results in too slow a - * computation time, reset the opslimit below to its default value. - * If it is still too slow, decrement this value (halving the memory - * usage) until it is fast enough. Alternatively, if it is still too - * fast after setting this to its highest reasonable value, raise the - * opslimit below until it is not. A benchmark program is available - * alongside this software to aid in this process. - * - * WARNING: - * Do *NOT* set this to more than 20 (1 GiB RAM) on a 32-bit - * Operating System! - * - * Valid values are 14 (16 MiB RAM) to 26 (64 GiB RAM) (inclusive) - * The default is 14 (16 MiB RAM) - */ - #scrypt_memlimit = 14; - - /* (*) scrypt_opslimit - * - * Amount of computation to perform for new passwords. - * - * The default value for this option is based on the default value of - * the above option. The recommended value is (memlimit_bytes / 32). - * - * Valid values are 32,768 to 4,294,967,295 (inclusive) - * The default is 524,288 - */ - #scrypt_opslimit = 524288; - - /* (*) pbkdf2v2_digest - * - * Cryptographic digest algorithm to use (in HMAC mode). - * - * Valid values are "SHA1", "SHA2-256", and "SHA2-512". - * Additionally, the following aliases exist, for compatibility: - * - * "SHA-1" -> SHA1 - * "SHA256" -> SHA2-256 - * "SHA512" -> SHA2-512 - * "SHA-256" -> SHA2-256 - * "SHA-512" -> SHA2-512 - * - * Finally, you can prefix any of these values with "SCRAM-" to enable - * the computation and storage of an RFC5802 ServerKey and StoredKey, - * instead of a raw PBKDF2 digest (SaltedPassword). Verification of - * plaintext passwords against these digests can still be performed - * (for e.g. NickServ IDENTIFY or SASL PLAIN), by computing a new - * SCRAM ServerKey from the provided password and comparing it to the - * stored ServerKey, so setting this to a SCRAM mode does NOT prevent - * non-SCRAM logins. For these variants, please read doc/SASL-SCRAM. - * - * The default is "SHA2-512" - */ - #pbkdf2v2_digest = "SHA2-512"; - - /* (*) pbkdf2v2_rounds - * - * This is the PBKDF2 "iteration count". You should raise this as high - * as is reasonable for the machine you will be running services on. - * However, note that if you are going to deploy SASL SCRAM support, - * the *client*, NOT services, performs the PBKDF2 calculation during - * login, so keep in mind that many mobile clients will not perform as - * well as a server, and reduce the iteration count accordingly. Also, - * some clients will refuse to perform a login at all if this is set - * too high. A benchmark program is included alongside this software - * to aid in tuning this parameter. - * - * Valid values are 10,000 to 5,000,000 (inclusive) - * The default is 64,000 - */ - #pbkdf2v2_rounds = 64000; - - /* (*) pbkdf2v2_saltlen - * - * Salt length (in bytes) to use for new passwords. You should only - * change this if absolutely necessary; for example, to interoperate - * with other software. Its value doesn't significantly affect the - * computation time. - * - * Valid values are 8 to 64 (inclusive) - * The default is 32 - */ - #pbkdf2v2_saltlen = 32; - - /* (*) scram_mechanisms - * - * This option only has any effect if you are using the SASL SCRAM - * module in conjunction with the PBKDF2 v2 crypto module (above). - * - * If you wish to restrict the list of SCRAM mechanisms advertised - * to the network, set this option to a comma-separated list of - * mechanisms to advertise. - * - * If this value is unset, all mechanisms are advertised, under the - * assumptions that the database may contain password hashes in many - * forms (changing the value of pbkdf2v2_digest over time) and that - * clients either will retry other mechanisms when one of them fails - * or can be configured to use a specific mechanism based upon your - * network's documentation or reference material. - * - * If you plan to never change the digest, it would be wise to set - * this to only the specific digest you are using (above). - * - * Valid mechanisms are "SCRAM-SHA-1", "SCRAM-SHA-256", and - * "SCRAM-SHA-512", in any order. - */ - #scram_mechanisms = "SCRAM-SHA-1,SCRAM-SHA-256,SCRAM-SHA-512"; - - /* (*) bcrypt_cost - * - * Amount of rounds to perform for new passwords (as a power of 2). - * You should raise this as high as is reasonable. A benchmark - * program is available alongside this software to aid in this - * process. - * - * Valid values are 4 to 31 (inclusive) - * The default is 7 - */ - #bcrypt_cost = 7; - - /* (*) crypt3_sha2_256_rounds - * (*) crypt3_sha2_512_rounds - * - * Use of this option is restricted to certain C libraries! - * At present, only GNU libc6 ("glibc") v2.7+ is known to work. - * - * Valid values are 5,000 to 1,000,000 (inclusive) - * The default is 5,000 - */ - #crypt3_sha2_256_rounds = 5000; - #crypt3_sha2_512_rounds = 5000; -}; - - - -// The serverinfo{} block defines how we appear on the IRC network. -serverinfo { - - /* name - * - * The server name that this program uses on the IRC network. This is - * the name you'll have to use in C:/N:Lines ("connect blocks"). It - * must be unique on the IRC network and contain at least one dot, but - * does not have to be equal to any DNS name. - */ - name = "services.int"; - - /* desc - * - * The ``server comment'' we send to the IRC network. - */ - desc = "Atheme IRC Services"; - - /* numeric - * - * Some protocol drivers (Charybdis, Ratbox2, P10, IRCNet) require a - * server ID, also known as a numeric. Please consult your IRCd - * documentation when providing this value. - */ - numeric = "00A"; - - /* (*) recontime (seconds) - * - * How long before we reconnect to the uplink. - */ - recontime = 10; - - /* (*) netname - * - * The name of your network. - */ - netname = "misconfigured network"; - - /* (*) hidehostsuffix - * - * P10 +x host hiding gives .. - * If using +x on asuka, this must agree with F:HIDDEN_HOST. - */ - hidehostsuffix = "users.misconfigured"; - - /* (*) adminname - * - * The name of the person running this service. - */ - adminname = "misconfigured admin"; - - /* (*) adminemail - * - * The e-mail address of the person running this service. - */ - adminemail = "misconfigured@admin.invalid"; - - /* (*) registeremail - * - * The e-mail address that messages should be originated from. - * - * If this is not set, then "noreply." will be prefixed to the - * adminemail setting above, and that will be used instead. - */ - registeremail = "noreply@admin.invalid"; - - /* (*) hidden - * - * If this is enabled, Atheme will indicate to the uplink IRCd that it - * should not be included in /LINKS output. This only works on the - * following IRCds at present: ratbox, charybdis, solanum. - */ - #hidden; - - /* (*) mta - * - * The full path to your mail transfer agent. This is used for e-mail - * authorization and password retrieval. Comment this out to disable - * sending e-mail. - * - * WARNING: - * Sending e-mail can disclose the IP address of your services box - * unless you take appropriate precautions (not discussed here). - */ - mta = "/usr/sbin/sendmail"; - - /* (*) loglevel - * - * Specify the default categories of logging information to record in - * the master Atheme log file, usually var/atheme.log. - * - * Options include: - * debug, all - Meta-keyword for all possible categories - * trace - Meta-keyword for a little bit of info - * misc - Like trace, but with more miscellaneous info - * notice - Meta-keyword for notice-like information - * ------------------------------------------------------------------- - * error - Critical errors - * info - Miscillaneous log notices - * verbose - A bit more verbose than info, not quite as - * spammy as debug - * commands - All command use - * admin - Administrative command use - * register - Account and channel registrations - * set - Changes of account or channel settings - * request - User requests (currently only vhosts) - * network - Log notices related to network status - * rawdata - Log raw data sent and received by services - * denycmd - Security model denials (commands, - * permissions) - * wallops - - */ - loglevel = { admin; error; info; network; wallops; }; - - /* (*) maxcertfp - * - * What is the maximum number of certificate fingerprints allowed to - * be associated with one account? - * - * Set to 0 for no limit. - */ - maxcertfp = 0; - - /* (*) maxlogins - * - * What is the maximum number of sessions allowed to login to one - * username? This reduces potential abuse. It is only checked on - * login. - */ - maxlogins = 5; - - /* (*) maxusers - * - * What are the maximum usernames that one e-mail address can - * register? Set to 0 to disable this check (it can be slow - * currently). - */ - maxusers = 5; - - /* (*) mdlimit - * - * How many metadata entries can be added to an object? - */ - mdlimit = 30; - - /* (*) emaillimit - * (*) emailtime (seconds) - * - * The maximum number of e-mails allowed to be sent in the specified - * interval. If this is exceeded, wallops will be sent, at most one - * per minute. - */ - emaillimit = 10; - emailtime = 300; - - /* (*) auth - * - * What type of username registration authorization do you want? If - * "email", Atheme will send a confirmation e-mail to the address to - * ensure it's valid. If registration is not completed within one day, - * the username will expire. If "none", no message will be sent and - * the username will be fully registered. - * - * Valid values are: email, none. - */ - auth = none; - - /* casemapping - * - * Specify the case mapping to use. Almost all TSora (and any that - * follow the RFC correctly) IRCds will use RFC1459 case mapping. - * Bahamut, Unreal, and other "DALnet"-style IRCds will use ASCII - * case mapping. - * - * Valid values are: rfc1459, ascii. - */ - casemapping = rfc1459; -}; - - - -/* uplink{} blocks define connections to IRC servers. - * - * Multiple may be defined but only one will be used at a time (IRC being a - * tree-shaped network). Atheme does not currently link over TLS. To link - * Atheme over TLS, please connect Atheme to a local IRCd and then have that - * connect to your network over TLS. - */ -uplink "irc.example.net" { - - // The server name of the IRCd you're linking to goes above. - - /* host - * - * The IP address or hostname to connect to. - */ - host = "127.0.0.1"; - - /* vhost - * - * The source IP address to connect from, used on machines with - * multiple interfaces. - */ - #vhost = "192.0.2.5"; - - /* port - * - * The port to connect to. - */ - port = 6667; - - /* send_password - * - * The password sent for linking. - */ - send_password = "mypassword"; - - /* receive_password - * - * The password received for linking. - */ - receive_password = "theirpassword"; -}; - -// This is an example for using an IPv6 address as an uplink -uplink "irc6.example.net" { - - host = "::1"; - port = 6667; - - /* password - * - * If you want to have same send_password and accept_password, you - * can specify both using 'password' instead of individually. - */ - password = "linkage"; -}; - - - -/* Services configuration. - * - * Each of these blocks can contain the following entries: - * - * nick The nickname you want the service to have. - * user The username you want the service to have. - * host The hostname you want the service to have. - * real The realname (GECOS) you want the service to have. - * - * aliases Command aliases; to map new commands onto existing ones. - * - * access Command restrictions; to require certain privileges for - * certain commands. Restrictions only apply to proper commands, - * not aliases. A non-exhaustive list of permissions is located - * near the bottom of this file, in the operclass section. See - * also the file "doc/PRIVILEGES" in the source distribution. - * You may also add completely custom privileges; they are just - * strings. - * - * For an example of the syntax to use for restricting access to - * commands using this configuration item, see the bottom of - * doc/PRIVILEGES. - * - * Several of them also have options specific to the service. - */ - - - -/* NickServ configuration. - * - * NickServ provides nickname or username registration and authentication - * services. It provides necessary authentication features required for - * Services to operate correctly. You should make sure these settings - * are properly configured for your network. - */ -nickserv { - - nick = "NickServ"; - user = "NickServ"; - host = "services.int"; - real = "Nickname Services"; - - aliases { - "ID" = "IDENTIFY"; - "MYACCESS" = "LISTCHANS"; - }; - - access { - }; - - /* (*) spam - * - * Have NickServ tell people about how great it and ChanServ are. - */ - spam; - - /* no_nick_ownership - * - * Enable this to disable nickname ownership (old userserv{}). This - * changes changes "nickname" to "account" in most messages, disables - * GHOST on users not logged in to the same account, and makes the - * spam directive ineffective. - * - * It is suggested that the nick be set to "UserServ", the - * "nickserv/login" module be loaded instead of "nickserv/identify", - * and the "nickserv/ghost" module not be loaded. - */ - #no_nick_ownership; - - /* (*) maxnicks - * - * If GROUP is loaded, what are the maximum nicknames that one - * account can register? - */ - maxnicks = 5; - - /* (*) expire (days) - * - * How long registrations have to be inactive before being dropped. - * Set this to zero to disable expiry. - */ - expire = 30; - - /* (*) enforce_expire (days) - * - * How long registrations have to be inactive before ignoring - * enforcement settings on their nicks. - */ - #enforce_expire = 14; - - /* (*) enforce_delay (seconds) - * - * The default delay before taking enforcement action on nicks. This - * can be changed on an individual account basis if the - * "nickserv/set_enforcetime" module is loaded. - */ - #enforce_delay = 30; - - /* (*) enforce_prefix - * - * The prefix to use when changing the user's nick on enforcement. - */ - #enforce_prefix = "Guest"; - - /* (*) waitreg_time (seconds) - * - * How long users have to wait between connecting to the network, and - * being able to register a services account. Minimum value 0 - * (disables the enforced delay), default value 0, maximum value 43200 - * (12 hours). Requires the "nickserv/waitreg" module to be loaded to - * do anything. - */ - #waitreg_time = 0; - - /* (*) cracklib_dict - * - * The location and filename prefix of the cracklib dictionaries for - * use with "nickserv/pwquality". This must be provided if you are - * going to be using "nickserv/pwquality" with cracklib support - * enabled. - */ - #cracklib_dict = "/var/cache/cracklib/cracklib_dict"; - - /* (*) passwdqc_* - * - * Please see the passwdqc.conf(5) documentation for an explanation of - * these values. Affects "nickserv/pwquality" if passwdqc support is - * enabled. Default values given below. - */ - #passwdqc_max = 288; /* (8 <= value <= 288) */ - #passwdqc_min_n0 = 20; /* (0 <= value <= passwdqc_max) */ - #passwdqc_min_n1 = 16; /* (0 <= value <= passwdqc_min_n0) */ - #passwdqc_min_n2 = 16; /* (0 <= value <= passwdqc_min_n1) */ - #passwdqc_min_n3 = 12; /* (0 <= value <= passwdqc_min_n2) */ - #passwdqc_min_n4 = 8; /* (0 <= value <= passwdqc_min_n3) */ - #passwdqc_words = 4; /* (2 <= value <= 8) */ - - /* (*) pwquality_warn_only - * - * If this option is set and "nickserv/pwquality" is loaded, nickserv - * will just warn users that their password is insecure, recommend - * they change it and still register the nick. If this option is - * unset, it will refuse to register the nick at all until the user - * chooses a better password. - */ - #pwquality_warn_only; - - /* (*)(B) show_custom_metadata - * - * Setting this option to false will prevent user-set metadata (via - * SET PROPERTY) from showing up in the INFO output. The TAXONOMY - * command will still function as usual, and INFO will point this out - * if users have metadata set. - */ - show_custom_metadata; - - /* (*) emailexempts - * - * A list of e-mail addresses that will be exempt from the check of - * how many accounts one user may have. Any e-mail address in this - * block may register an unlimited number of accounts/usernames. - */ - emailexempts { - }; - - /* (*) shorthelp - * - * A list of commands that are displayed (with their full description) - * in the output of `/msg NickServ HELP'. Commands not in this list - * will be listed, but not with their descriptions. All commands with - * descriptions are still listed in `/msg NickServ HELP COMMANDS' - * regardless of the value set here. - * - * Optional; defaults to "ACCESS CERT DROP GHOST GROUP IDENTIFY INFO - * LISTCHANS LISTGROUPS LISTLOGINS LISTOWNMAIL LOGOUT REGAIN REGISTER - * RELEASE SENDPASS SET UNGROUP". - * - * A command in this list will only be printed if the corresponding - * module is loaded and the user has permission to use it. Set to an - * empty string to disable listing command descriptions in - * `/msg NickServ HELP'. - */ - #shorthelp = ""; - - /* (*)(B) listownmail_canon - * - * Whether to canonicalize emails when doing LISTOWNMAIL. Depending on - * what custom canonicalizers you have loaded, LISTOWNMAIL can expose - * user:email mappings via LISTOWNMAIL when it might not be safe to do - * so (e.g. if you unconditionally strip +subaddress from emails.) - */ - listownmail_canon; - - /* (*) bad_password_message - * - * Whether or not failed password-based login attempts are, by default, - * announced to the target account. Can be changed per-account with - * nickserv/set_badpasswdmsg. It is only advisable to disable if you - * have enabled misc/login_throttling. - */ - bad_password_message; -}; - -/* ChanServ configuration. - * - * ChanServ provides channel registration services, which allows users to own - * channels. It is not required, but is strongly recommended. - */ -chanserv { - - nick = "ChanServ"; - user = "ChanServ"; - host = "services.int"; - real = "Channel Services"; - - aliases { - }; - - access { - }; - - /* reggroup - * - * The group that will receive memos about channel registration - * requests when "chanserv/moderate" is loaded. - */ - #reggroup = "!Services-Team"; - - /* (*) maxchans - * - * What are the maximum channels that one username can register? - */ - maxchans = 5; - - /* fantasy - * - * Do you want to enable fantasy commands? This can use a lot of CPU, - * and will only work if you have general::join_chans enabled as well. - */ - fantasy; - - /* (*) hide_xop - * - * Hide the XOP templates from sight. This is useful if you want to - * use templates and not have the XOP templates displayed. - */ - #hide_xop; - - /* (*) hide_flags_akicks - * - * Hide AKICKs (ACL entries with only the +b flag) from FLAGS output. - * - * AKICK expiries and reasons will not be disclosed to unauthorized - * users in any case. - * - * This option is only meaningful if you are using the PUBACL flag - * to allow channel access lists to be public. - */ - #hide_flags_akicks; - - /* (*) hide_pubacl_akicks - * - * Hide AKICKs (ACL entries with only the +b flag) from the public - * access lists of channels. Users with the +A flag or chan:auspex - * will see these entries listed as normal. - * - * AKICK expiries and reasons will not be disclosed to unauthorized - * users in any case. - * - * This option is only meaningful if you are using the PUBACL flag - * to allow channel access lists to be public. - */ - #hide_pubacl_akicks; - - /* (*) templates - * - * Defines what flags the global templates comprise. - * - * For the special XOP templates: - * These should all be different and not equal to the empty set, - * except that "hop" may be equal to "vop" to disable "hop". Each - * subsequent level should have more flags (except "+VHO"). For - * optimal functioning of "/CS FORCEXOP", "aop" should not have any - * of "+sRf", "hop" should not have any of "+sRfoOr", and "vop" - * should not have any of "+sRfoOrhHt". - * - * If this is not specified, the values of Atheme 0.3 are used, which - * are generally less intuitive than these. - * - * NOTE: - * Changing these leaves the flags of existing channel access - * entries unchanged, thus removing them of the view of "/CS XOP" - * list. Usually, the channel founder can use "/CS FORCEXOP" to - * update the entries to the new levels. - * - * Advice: - * If you want to add a co-founder role, remove the flags permission - * ("+f") from the "sop" role, and define a co-founder role with - * flags permission. - */ - templates { - vop = "+AV"; - hop = "+AHehitrv"; - aop = "+AOehiortv"; - sop = "+AOaefhiorstv"; - - founder = "+AFORaefhioqrstv"; - - /* some examples (which are commented out...) */ - #member = "+Ai"; - #op = "+AOiortv"; - }; - - /* (*) deftemplates - * - * Defines default templates to set on new channels, as a space- - * separated list of name=+flags pairs. - * - * WARNING: - * At this time, no syntax checking is done on this; it is your own - * responsibility to make sure that it is correct. - */ - #deftemplates = "MEMBER=+Ai OP=+AOeiortv"; - - /* (*) changets - * - * Change the channel TS to the channel registration timestamp when - * someone re-creates a registered channel, ensuring that they are de- - * opped and all of their mode changes are undone. Note that this - * involves ChanServ joining. When the channel was not re-created, no - * de-ops will be done (apart from the SECURE option). This also - * solves the "join-mode" problem where someone re-creates a - * registered channel and then sets some modes before they are de- - * opped. - * - * This is currently supported for ratbox, charybdis, bahamut, and - * inspircd 1.1+. For ratbox and charybdis, it only fully works with - * TS6; with TS5, bans and last-moment modes will still apply. - * - * (That can also be used to advantage, when first enabling this.) - */ - #changets; - - /* (*) trigger - * - * This setting allows you to change the trigger prefix for ChanServ's - * in-channel command feature (configurable via chanserv::fantasy). If - * no setting is provided, the default is used, which is "!". - * - * Other settings you could consider trying: ".", "~", "?", "`", "'". - */ - trigger = "!"; - - /* (*) expire (days) - * - * How long registrations have to be inactive before being dropped. - * Set this to zero to disable expiry. - */ - expire = 30; - - /* (*) maxchanacs - * - * The maximum number of entries allowed in a channel's access list - * (both channel ops and akicks), 0 for unlimited. - */ - maxchanacs = 0; - - /* (*) maxfounders - * - * The maximum number of founders ("+F") allowed in a channel. Note - * that all founders have the exact same privileges and the list of - * founders is shown in various places. - */ - maxfounders = 4; - - /* (*) founder_flags - * - * The flags a user will get when they register a new channel. This - * MUST include at least "F" or it will be ignored. If it is not set, - * Atheme will give the user all channel flags. - */ - #founder_flags = "AFORefiorstv"; - - /* (*) default_mlock - * - * The default MLOCK setting to apply on newly registered channels. - * This currently only supports simple modes (without parameters). - * - * Services will automatically lock -l and -k unless the channel - * to be registered already has a limit or key, respectively. - * This behaviour cannot currently be modified. - */ - #default_mlock = "+nt"; - - /* (*) akick_time (minutes) - * - * The default expiration time for AKICKs. Comment this option out or - * set to zero for permanent AKICKs by default (the old behaviour). - */ - #akick_time = 10; - - /* (*) antiflood_enforce_method - * - * The enforcement method to use for flood protection by default. - * This may also be configured with ChanServ's SET ANTIFLOOD command. - * Requires "chanserv/antiflood" to be loaded to do anything. - * - * Available options are: quiet, kickban, and akill. - */ - antiflood_enforce_method = quiet; - - /* (*)(B) show_custom_metadata - * - * Setting this option to false will prevent user-set metadata (via - * SET PROPERTY) from showing up in the INFO output. The TAXONOMY - * command will still function as usual, and INFO will point this out - * if channels have metadata set. - */ - show_custom_metadata; - - /* (*) shorthelp - * - * A list of commands that are displayed (with their full description) - * in the output of `/msg ChanServ HELP'. Commands not in this list - * will be listed, but not with their descriptions. All commands with - * descriptions are still listed in `/msg ChanServ HELP COMMANDS' - * regardless of the value set here. - * - * Optional; defaults to "AKICK BAN CLEAR DEOP DEVOICE DROP FLAGS - * GETKEY INFO INVITE KICK KICKBAN OP QUIET REGISTER SET TOPIC UNBAN - * UNQUIET VOICE WHY". - * - * A command in this list will only be printed if the corresponding - * module is loaded and the user has permission to use it. Set to an - * empty string to disable listing command descriptions in - * `/msg ChanServ HELP'. - */ - #shorthelp = ""; -}; - -/* ChanFix configuration. - * - * ChanFix provides channel recovery services without registration, which - * allows users to maintain control of channels even if ChanServ is not used - * to register them. - */ -chanfix { - - nick = "ChanFix"; - user = "ChanFix"; - host = "services.int"; - real = "Channel Fixing Service"; - - aliases { - }; - - access { - }; - - /* (*) autofix - * - * Automatically fix channels if they become opless and meet fixing - * criteria. - */ - autofix; -}; - -/* Global noticing configuration. - * - * The Global notice module provides the ability to mass-notify a network. - */ -global { - - nick = "Global"; - user = "Global"; - host = "services.int"; - real = "Network Announcements"; - - aliases { - }; - - access { - }; -}; - -/* InfoServ configuration - * - * The InfoServ modules provide the ability to mass-notify a network and send - * news to users when they connect to the network. - */ -infoserv { - - nick = "InfoServ"; - user = "InfoServ"; - host = "services.int"; - real = "Information Service"; - - aliases { - }; - - access { - }; - - /* (*) logoninfo_count - * - * The number of InfoServ messages a user will see upon connect. If - * there are more than this number, the user will be able to see the - * rest of them with the InfoServ LIST command. - * - * Set this to 0 for unlimited. - */ - logoninfo_count = 3; - - /* (*)(B) logoninfo_reverse - * - * Whether to display the InfoServ on-connect messages in reverse - * chronological order, i.e. newest message first, oldest last. - * - * Note that if this option is disabled, messages added after - * the logoninfo_count limit will not be shown at connection time - * as only the oldest N messages will be shown, rather than - * the most recent N messages (as configured by default). - */ - logoninfo_reverse; - - /* (*)(B) logoninfo_show_metadata - * - * By default, the InfoServ on-connect messages and the output - * of the LIST command include information about the author - * of the message as well as the time it was added. - * - * Disabling this option will remove that information for normal users. - * Privileged users will still be able to see the information - * in the output of the LIST (or OLIST) command. - */ - logoninfo_show_metadata; -}; - -/* OperServ configuration. - * - * OperServ provides essential network management tools for IRC operators on - * the IRC network. - */ -operserv { - - nick = "OperServ"; - user = "OperServ"; - host = "services.int"; - real = "Operator Services"; - - aliases { - }; - - access { - }; - - /* (*) modinspect_use_colors - * - * Whether to use colors (green for unloadable modules, yellow for - * reload-only modules, red for permanent modules) when printing - * whether a module is unloadable in the MODINSPECT command - * (provided by the "operserv/modmanager" module). - */ - #modinspect_use_colors; -}; - -/* SaslServ configuration. - * - * SaslServ provides an authentication agent which is compatible with the SASL - * over IRC (SASL/IRC) protocol extension. It does not provide any commands, - * so there is no point in configuring any aliases{} or access{} blocks; they - * will be ignored. - */ -saslserv { - - nick = "SaslServ"; - user = "SaslServ"; - host = "services.int"; - real = "SASL Authentication Agent"; - - /* (*) hide_server_names - * - * Hide server names in the bad_password message. - */ - #hide_server_names; -}; - -/* MemoServ configuration. - * - * MemoServ provides a note-taking service that you can use to send notes to - * offline users (provided they are registered with Services). - */ -memoserv { - - nick = "MemoServ"; - user = "MemoServ"; - host = "services.int"; - real = "Memo Services"; - - aliases { - }; - - access { - }; - - /* (*) maxmemos - * - * What is the maximum amount of memos a user can have in their inbox? - */ - maxmemos = 30; -}; - -/* GameServ configuration. - * - * GameServ provides various in-channel commands for games. - */ -gameserv { - - nick = "GameServ"; - user = "GameServ"; - host = "services.int"; - real = "Game Services"; - - aliases { - }; - - access { - }; -}; - -/* RPGServ configuration. - * - * RPGServ provides a facility for finding roleplaying channels. - */ -rpgserv { - - nick = "RPGServ"; - user = "RPGServ"; - host = "services.int"; - real = "RPG Finding Services"; - - aliases { - }; - - access { - }; -}; - -/* BotServ configuration. - * - * BotServ provides virtual channel bots. - */ -botserv { - - nick = "BotServ"; - user = "BotServ"; - host = "services.int"; - real = "Bot Services"; - - aliases { - }; - - access { - }; - - /* (*) min_users - * - * Minimum number of users a channel must have before a Bot is allowed - * to be assigned to that channel. - */ - min_users = 0; -}; - -/* GroupServ configuration. - * - * GroupServ provides features for managing a collection of channels at once. - */ -groupserv { - - nick = "GroupServ"; - user = "GroupServ"; - host = "services.int"; - real = "Group Management Services"; - - aliases { - }; - - access { - }; - - /* (*) maxgroups - * - * Maximum number of groups one username can be founder of. - */ - maxgroups = 5; - - /* (*) maxgroupacs - * - * Maximum number of access entries you may have in a group. - */ - maxgroupacs = 100; - - /* (*) enable_open_groups - * - * Setting this option will allow any group founder to mark their - * group as "anyone can join". - */ - enable_open_groups; - - /* (*) join_flags - * - * This is the GroupServ flagset that users who JOIN an open group - * will get upon join. Please check the "groupserv/flags" help file - * before changing this option. Valid flagsets (for example) would - * be: "+v" or "+cv". - * - * It is NOT valid to use minus flags (such as "-v") here. - */ - join_flags = "+"; -}; - -/* HostServ configuration. - * - * HostServ provides advanced virtual host management. - */ -hostserv { - - nick = "HostServ"; - user = "HostServ"; - host = "services.int"; - real = "Host Management Services"; - - aliases { - "APPROVE" = "ACTIVATE"; - "DENY" = "REJECT"; - }; - - access { - }; - - /* reggroup - * - * The group that will receive Memos about - * vHost requests. - */ - #reggroup = "!Services-Team"; - - /* (*) no_subsequent_requests - * - * Prevent a user from making another vhost request while they - * already have an outstanding request that has not been accepted - * or rejected. - */ - #no_subsequent_requests; - - /* (*) request_per_nick - * - * Whether the request system should work per nick or per account. - * The recommended setting is to leave this disabled, so that - * vhosts work as consistently as possible. - */ - #request_per_nick; -}; - -/* HelpServ configuration - * - * HelpServ adds a few different ways for users to request help from network - * staff. - */ -helpserv { - - nick = "HelpServ"; - user = "HelpServ"; - host = "services.int"; - real = "Help Services"; - - aliases { - }; - - access { - }; -}; - -/* StatServ configuration - * - * StatServ adds basic stats and split tracking. - */ -statserv { - - nick = "StatServ"; - user = "StatServ"; - host = "services.int"; - real = "Statistics Services"; - - aliases { - }; - - access { - }; -}; - -/* ALIS configuration. - * - * ALIS provides a more flexible way to list channels. - */ -alis { - - nick = "ALIS"; - user = "alis"; - host = "services.int"; - real = "Channel Directory"; - - aliases { - }; - - access { - }; - - /* (*) maxmatches - * - * The default maximum number of channels returned in a query. - * Privilege (chan:auspex) is required to ask for more. - * Minimum 8, default 64, maximum 128. - */ - #maxmatches = 64; -}; - -/* ProxyScan configuration. - * - * Here you can configure the details of your proxyscan (DNS Blacklist) - * scanner service. - */ -proxyscan { - - nick = "Proxyscan"; - user = "dnsbl"; - host = "services.int"; - real = "Proxyscan Service"; - - aliases { - }; - - access { - }; - - // (*) List of DNSBLs - blacklists { - "dnsbl.dronebl.org"; - "rbl.efnetrbl.org"; - "tor.efnet.org"; - }; - - /* (*) dnsbl_action - * - * Available values: - * - * none Do nothing - * notify Notify user that they are listed in a DNSBL - * snoop Report the user to the services log channel - * kline AKILL the user from the network - * (default AKILL is 24 hours) - */ - dnsbl_action = kline; -}; - - - -/* HTTP server configuration. - * - * The HTTP server in Services is used for serving XMLRPC requests. It can - * also serve static documents and statistics pages. - */ -httpd { - - /* host - * - * The host that the HTTP server will listen on. - * Use 0.0.0.0 if you want to listen on all available hosts. - */ - host = "0.0.0.0"; - - /* host (ipv6) - * - * If you want, you can have Atheme listen on an IPv6 host too. - * Use :: if you want to listen on all available IPv6 hosts. - */ - #host = "::"; - - /* www_root - * - * The directory that contains the files that should be served by the - * httpd. - */ - www_root = "/var/www"; - - /* port - * - * The port that the HTTP server will listen on. - */ - port = 8080; -}; - -/* Password-based login attempt throttling configuration. - * - * This module can throttle both login attempts from IP addresses, and login - * attempts from a combination of both IP address and account ID. That is, - * the former throttles any logins from the same address, and the latter - * throttles any logins from the same address to the same account. - * - * This is achieved with a rudimentary token bucket system. You configure a - * "burst" of attempts that can be made immediately, and configure a - * "replenish" rate of how many seconds it takes for a token to replenish. For - * example, with "burst" set to 2 and "replenish" set to 2, you can attempt at - * most twice in 2 seconds, and then once every 2 seconds thereafter. - * - * To disable a specific throttling mechanism (e.g. to only throttle IP - * addresses, and not combinations of IP address and account), set the - * burst for that mechanism to zero. The replenish is ignored in this case. - * - * The check for IP address is performed before the check for combination of - * IP address and account. This is important, because a tighter set of values - * for IP address alone will render looser values for the combination of IP - * address and account pointless; the former will hit first and begin - * throttling. - * - * All of these options are runtime reconfigurable. Changing the value of - * these options and then rehashing services will affect all new login - * attempts, but it will not change the existing values of any buckets. In - * other words, if you previously had replenish set to free up a token after - * 2 minutes, and then you change it to 30 seconds and rehash, users may still - * have to wait up to 2 minutes after being throttled before they can try to - * log in again, and only then will they only have to wait up to 30 seconds. - * - * This module has absolutely no effect on any other form of login attempt - * (for example, certificate fingerprints or public-key challenges). - * - * All values are in seconds. - * The legal range for burst values is 0 through 200 (inclusive). - * The legal range for replenish values is 0.005 through 200 (inclusive). - * The commented-out example values given below are the default values. - */ -throttle { - - #address_burst = 5; - #address_replenish = 1; - - #address_account_burst = 2; - #address_account_replenish = 2; -}; - - - -// LDAP configuration. -ldap { - - /* (*) url - * - * LDAP URL of the server to use. - */ - url = "ldap://127.0.0.1"; - - /* (*) dnformat - * - * Format string to convert an account name to an LDAP DN. - * Must contain exactly one %s which will be replaced by the account - * name. - * - * Services will attempt a simple bind with this DN and the given - * password; if this is successful the password is considered correct. - */ - dnformat = "cn=%s,dc=jillestest,dc=com"; -}; - - - -/**************************************************************************** - * LOGGING SECTION. * - ****************************************************************************/ - -/* - * logfile{} blocks can be used to set up log files other than the master - * log file used by services, which is controlled by serverinfo::loglevel. - * - * Options include: - * debug, all - Meta-keyword for all possible categories - * trace - Meta-keyword for a little bit of info - * misc - Like trace, but with some more miscellaneous info - * notice - Meta-keyword for notice-like information - * --------------------------------------------------------------------------- - * error - Critical errors - * info - Miscillaneous log notices - * verbose - A bit more verbose than info, not quite as - * spammy as debug - * commands - All command use - * admin - Administrative command use - * register - Account and channel registrations - * set - Changes of account or channel settings - * request - User requests (currently only vhosts) - * network - Log notices related to network status - * rawdata - Log raw data sent and received by services - * denycmd - Security model denials (commands, permissions) - * wallops - - */ - -/* - * This block logs all account and channel registrations and drops, and - * account and channel setting changes to var/account.log. - */ -#logfile "var/account.log" { register; set; }; - -/* - * This block logs all command use to var/commands.log. - */ -#logfile "var/commands.log" { commands; }; - -/* - * This block logs all security auditing information. - */ -#logfile "var/audit.log" { denycmd; }; - -/* - * You can log to IRC channels, and even split it by category, too. This entry - * provides roughly the same functionality as the old snoop feature. - */ -#logfile "#services" { admin; denycmd; error; info; register; request; }; - -/* - * This block logs to server notices. - */ -#logfile "!snotices" { denycmd; error; info; request; }; - - - -/**************************************************************************** - * GENERAL PARAMETERS CONFIGURATION SECTION. * - ****************************************************************************/ - -general { - - /* (*) permissive_mode - * - * Whether or not security denials should be soft denials instead of - * hard denials. If security denials are soft denials, then they will - * only be logged to the denial log. - */ - #permissive_mode; - - /* (*) helpchan - * - * Network help channel. Shown to users when they request help for a - * command that doesn't exist. - */ - #helpchan = "#help"; - - /* (*) helpurl - * - * Network webpage for services help. Shown to users when they request - * help for a command that doesn't exist. - */ - #helpurl = "https://www.stack.nl/~jilles/irc/atheme-help/"; - - /* (*) silent - * - * If you want to prevent services from sending WALLOPS/GLOBOPS about - * things uncomment this. Not recommended. - */ - #silent; - - /* (*) verbose_wallops - * - * If you want services to send you more information about events that - * are occuring (in particular AKILLs), uncomment the directive below. - * - * WARNING: - * This may result in large amounts of wallops/globops floods. - */ - #verbose_wallops; - - /* (*) join_chans - * - * Should ChanServ be allowed to join registered channels? - * This option is useful for the fantasy command set. - * - * If enabled, you can tell ChanServ to join via SET GUARD ON. - * - * If you use ircu-like IRCd (asuka), you must leave this enabled, and - * put guard in default cflags. - * - * For ratbox it is recommended to leave it on and put guard in the - * default cflags, in order that ChanServ does not have to join/part - * to do certain things. On the other hand, enabling this increases - * potential for bots fighting with ChanServ. - * - * Regardless of this option, ChanServ will temporarily join channels - * which would otherwise be empty if necessary in order to enforce - * akick/restricted/close, and to change the TS if changets is - * enabled. - */ - join_chans; - - /* (*) leave_chans - * - * Do we leave registered channels after everyone else has left? - * Turning this off serves little purpose, except to mark "official" - * network channels by keeping them open, and to preserve the topic - * and +beI lists. - */ - leave_chans; - - /* secure - * - * Do you want to require the use of /msg @? - * Turning this on helps protect against spoofers, but is disabled as - * most networks do not presently use it. - */ - #secure; - - /* (*) uflags - * - * The default flags to set for users upon registration. - * - * Valid values are: emailmemos, enforce, hidemail, hold, neverop, - * nomemo, noop, private, privmsg, quietchg, none. - */ - uflags = { hidemail; }; - - /* (*) cflags - * - * The default flags to set for channels upon registration. - * - * Valid values are: guard, hold, keeptopic, limitflags, nosync, - * private, pubacl, secure, topiclock, verbose, - * verbose_ops, none. - */ - cflags = { guard; verbose; }; - - /* (*) raw - * - * Do you want to allow SRAs to use the RAW and INJECT commands? These - * commands are for debugging. If you don't know how to use them then - * don't enable them. They are not supported. - */ - #raw; - - /* (*) flood_msgs - * (*) flood_time (seconds) - * - * Do you want services to detect floods? - * - * If services receives `flood_msgs' within `flood_time' the user will - * trigger the flood protection. - * - * Setting flood_msgs to zero disables flood protection. - * - * Note that some messages that need a lot of processing are counted - * as two or even four messages. - */ - flood_msgs = 7; - flood_time = 10; - - /* (*) ratelimit_uses - * (*) ratelimit_period (seconds) - * - * This configures denied command throttling. - * - * After `ratelimit_uses' of a rate-limited command within - * `ratelimit_period', users will not be able to run rate-limited - * commands until the period is up. Comment one or both options to - * disable limiting. - * - * Currently used in the following modules: - * - * chanserv/register - * helpserv/helpme - * helpserv/ticket - * hostserv/request - * nickserv/register - */ - ratelimit_uses = 5; - ratelimit_period = 60; - - /* (*) vhost_change (days) - * - * The minimum interval between vHost changes once a user has used - * HostServ TAKE or REQUEST. - * - * Helps to deter rabid host-swappers and ban evaders. - */ - #vhost_change = 30; - - /* (*) kline_time (days) - * - * The default expiry time for KLINEs. - * Setting this to 0 makes all KLINEs permanent. - */ - kline_time = 7; - - /* (*) kline_with_ident - * - * KLINE user@host instead of *@host. - * Applies to all automatic KLINE's set by services. - */ - #kline_with_ident; - - /* (*) kline_verified_ident - * - * KLINE *@host if the first character of the ident is ~, - * irrespective of the value of kline_with_ident. - */ - #kline_verified_ident; - - /* (*) clone_time (minutes) - * - * This is the default expiry time for CLONE exemptions. - * Setting this to 0 makes all CLONE exemptions permanent. - */ - clone_time = 0; - - /* (*) commit_interval (minutes) - * - * The time between periodic database writes; between 1 and 60 - * (inclusive). Default is 5 minutes. - */ - #commit_interval = 5; - - /* (*) db_save_blocking - * - * Whether to always use a blocking database save (even in the - * periodic db save timer; see commit_interval above). - * - * Useful if you are running services with LeakSanitizer enabled, as - * services does not free its resources before exiting. A regular db - * save forks to the background, writes the database, and exits, which - * will pollute the console with pointless memory leak messages. - * - * Not recommended if you are not running services in the console. - */ - #db_save_blocking; - - /* (*) operstring - * - * The string returned in WHOIS (against services) for IRC operators. - */ - #operstring = "is an IRC Operator"; - - /* (*) servicestring - * - * The string returned in WHOIS (against services) for services. - */ - #servicestring = "is a Network Service"; - - /* (*) default_clone_allowed - * - * The limit after which clones will be KILLed or TKLINEd. - * Used by operserv/clones. - */ - default_clone_allowed = 5; - - /* (*) default_clone_warn - * - * The limit after which clones will be warned that they may not have - * any more concurrent connections. Should be lower than - * default_clone_allowed. Requires "operserv/clones" to be loaded to - * do anything. - */ - default_clone_warn = 4; - - /* (*) clone_identified_increase_limit - * - * If this option is enabled, the clone limit for a IP/host will be - * increased by 1 per clone that's identified to services. This effect - * has an upper limit of double the clone limits above. - */ - clone_identified_increase_limit; - - /* (*) uplink_sendq_limit - * - * The maximum amount of data that may be queued to be sent to the - * uplink, in bytes. This should be enough to contain Atheme's - * response to the netburst, but smaller than the IRCd's sendq limit - * for servers. - */ - uplink_sendq_limit = 1048576; - - /* (*) language - * - * Language to use for channel and oper messages and as default for - * users. - */ - language = "en"; - - /* exempts - * - * This block contains a list of user@host masks. Users matching any - * of these will not be automatically K:lined by services. - */ - exempts { - }; - - /* allow_taint - * - * By enabling this option, Atheme will run in configurations where - * the upstream will not provide support. By enabling this feature, - * you void any perceived rights to support. - */ - #allow_taint; - - /* (*) immune_level - * - * This option allows you to customize the operlevel which gets kick - * immunity privileges. - * - * The following flags are available: - * immune - require whatever IRCd usermode is needed for kick - * immunity (this is the default); - * admin - require admin privileges for kick immunity - * ircop - require any IRCop privileges for kick immunity - * (generally umode +o) - */ - immune_level = immune; - - /* (*) show_entity_id - * - * This makes nick/user & group entity IDs visible to everyone, rather - * than just opers with user:auspex or group:auspex privileges. - */ - show_entity_id; - - /* (*) load_database_mdeps - * - * For module dependencies listed in the services database (if any), - * whether to load those modules on startup (if they are not already - * loaded) or abort startup with a more helpful error message than - * e.g.: - * - * db services.db:123: unknown directive 'BE' - * corestorage: exiting to avoid data loss - * - * Comment this out to abort startup instead of silently loading the - * modules you need to process the database successfully. The abort - * reason will tell you what module the database requires so that you - * can fix your configuration file. - */ - load_database_mdeps; - - /* (*) hide_opers - * - * whether or not to hide oper status in remote whois - */ - #hide_opers; - - /* (*)(B) match_masks_through_vhost - * - * This enables matching of bans as well as channel access entries - * based on the real host/IP behind a vhost. - * - * You should only disable this if your ircd has been configured - * to not match bans through vhosts either; otherwise, services - * will be inconsistent with ircd behaviour. - */ - match_masks_through_vhost; - - /* (*) default_password_length - * - * The password length used for services commands that generate - * passwords on behalf of users; including NickServ's RESETPASS, - * SENDPASS, and RETURN commands. - * - * Default 16, minimum 16, maximum 64. - */ - #default_password_length = 16; -}; - - - -/**************************************************************************** - * OPERATOR AND PRIVILEGES CONFIGURATION SECTION. * - ****************************************************************************/ - -/* Operator configuration - * - * See the doc/PRIVILEGES file in the source distribution for more - * information. - * - * WARNING: - * ALL changes to the below blocks apply immediately upon rehash. You may - * need to send a signal (`pkill -HUP atheme-services') to regain control. - */ - -/* Operclasses specify groups of services operator privileges. - * - * The "user" operclass specifies privileges all users get. This may be empty - * (default) in which case users get no special privileges. - * - * If you use the "security/cmdperm" module, you will need to grant command: - * privileges to every command that you want users to be able to use. - */ -operclass "user" { }; - -/* The "ircop" operclass specifies privileges all IRCops get. This may be - * empty in which case IRCops get no privs. - * - * At least chan:cmodes, chan:joinstaffonly and general:auspex are suggested. - */ -operclass "ircop" { - - privs { - special:ircop; - }; - - privs { - user:auspex; - user:admin; - user:sendpass; - user:vhost; - user:mark; - }; - - privs { - chan:auspex; - chan:admin; - chan:cmodes; - chan:joinstaffonly; - }; - - privs { - general:auspex; - general:helper; - general:viewprivs; - general:flood; - }; - - privs { - operserv:omode; - operserv:akill; - operserv:jupe; - operserv:global; - }; - - privs { - group:auspex; - group:admin; - }; -}; - -operclass "sra" { - - /* You can inherit privileges from a lower operclass. */ - extends "ircop"; - - privs { - user:exceedlimits; - user:hold; - user:regnolimit; - }; - - privs { - general:metadata; - general:admin; - }; - - privs { - #operserv:massakill; - #operserv:akill-anymask; - operserv:noop; - operserv:grant; - }; - - /* (*) needoper - * - * Only grant privileges to IRC users in this oper class if they - * are opered; other use of privilege (channel succession, XMLRPC, - * etc.) is unaffected by this. - * - * This flag is *not* inherited by operclasses that extend this one; - * you will have to set it explicitly for each operclass. - */ - needoper; -}; - - - -/* (*) Operator blocks specify accounts with certain privileges - * Oper classes must be defined before they are used in operator blocks. - */ -operator "jilles" { - /* operclass */ - operclass = "sra"; - - /* (*) password - * - * Normally, the user needs to identify/log in using the account's - * password, and may need to be an IRCop (see operclass::needoper - * above). If you consider this not secure enough, you can - * specify an additional password here, which the user must enter - * using the OperServ IDENTIFY command, before the privileges can - * be used. - * - * The password must be encrypted if a crypto module is in use. - * - * If you are using crypto/crypt3-*, you can probably use - * the "mkpasswd" program included with most Linux distributions. - * Otherwise you can use operserv/genhash to encrypt a - * password for use here. - */ - #password = "$1$3gJMO9by$0G60YE6GqmuHVH3AnFPor1"; -}; - - - -/**************************************************************************** - * INCLUDE CONFIGURATION SECTION. * - ****************************************************************************/ - -/* You may also specify other files for inclusion. - * For example: - * - * include "etc/sras.conf"; - */ diff --git a/docs/services/irc/examples/atheme/atheme.motd.example b/docs/services/irc/examples/atheme/atheme.motd.example deleted file mode 100644 index cf013fc3..00000000 --- a/docs/services/irc/examples/atheme/atheme.motd.example +++ /dev/null @@ -1,11 +0,0 @@ -This is the services daemon for &network&. - -Nickname registrations will expire after &nickexpiry& days. -Channel registrations will expire after &chanexpiry& days. - -Registered users: &myusers& -Registered nicks: &mynicks& -Registered channels: &mychans& - -The services administrator can add additional information -here such as where to get additional help with services. diff --git a/docs/services/irc/examples/unrealircd/aliases/aliases.conf b/docs/services/irc/examples/unrealircd/aliases/aliases.conf deleted file mode 100644 index ea484aa0..00000000 --- a/docs/services/irc/examples/unrealircd/aliases/aliases.conf +++ /dev/null @@ -1,43 +0,0 @@ -/* Standard Aliases */ - -alias identify { - format "^#" { - target chanserv; - type services; - parameters "IDENTIFY %1-"; - } - format "^[^#]" { - target nickserv; - type services; - parameters "IDENTIFY %1-"; - } - type command; -} - -alias services { - format "^#" { - target chanserv; - type services; - parameters "%1-"; - } - format "^[^#]" { - target nickserv; - type services; - parameters "%1-"; - } - type command; -} - -alias register { - format "^#" { - target chanserv; - type services; - parameters "REGISTER %1-"; - } - format "^[^#]" { - target nickserv; - type services; - parameters "REGISTER %1-"; - } - type command; -} diff --git a/docs/services/irc/examples/unrealircd/aliases/anope.conf b/docs/services/irc/examples/unrealircd/aliases/anope.conf deleted file mode 100644 index 9b82e3de..00000000 --- a/docs/services/irc/examples/unrealircd/aliases/anope.conf +++ /dev/null @@ -1,16 +0,0 @@ -/* Anope Aliases */ - -alias nickserv { type services; } -alias ns { target nickserv; type services; } -alias chanserv { type services; } -alias cs { target chanserv; type services; } -alias memoserv { type services; spamfilter yes; } -alias ms { target memoserv; type services; spamfilter yes; } -alias operserv { type services; } -alias os { target operserv; type services; } -alias botserv { type services; } -alias bs { target botserv; type services; } -alias hostserv { type services; } -alias hs { target hostserv; type services; } - -include "aliases/aliases.conf"; diff --git a/docs/services/irc/examples/unrealircd/aliases/atheme.conf b/docs/services/irc/examples/unrealircd/aliases/atheme.conf deleted file mode 100644 index a6549826..00000000 --- a/docs/services/irc/examples/unrealircd/aliases/atheme.conf +++ /dev/null @@ -1,25 +0,0 @@ -/* Atheme Aliases */ - -alias nickserv { type services; } -alias ns { target nickserv; type services; } -alias chanserv { type services; } -alias cs { target chanserv; type services; } -alias memoserv { type services; spamfilter yes; } -alias ms { target memoserv; type services; spamfilter yes; } -alias operserv { type services; } -alias os { target operserv; type services; } -alias helpserv { type services; } -alias botserv { type services; } -alias bs { target botserv; type services; } -alias hostserv { type services; } -alias hs { target hostserv; type services; } -alias saslserv { type services; } -alias sss { target saslserv; type services; } -alias gameserv { type services; } -alias gms { target gameserv; type services; } -alias groupserv { type services; } -alias grs { target groupserv; type services; } -alias alis { type services; } -alias ls { target alis; type services; } - -include "aliases/aliases.conf"; diff --git a/docs/services/irc/examples/unrealircd/aliases/auspice.conf b/docs/services/irc/examples/unrealircd/aliases/auspice.conf deleted file mode 100644 index 5a4b86b0..00000000 --- a/docs/services/irc/examples/unrealircd/aliases/auspice.conf +++ /dev/null @@ -1,33 +0,0 @@ -/* Auspice Aliases */ - -/* Uncomment this, if you have enabled "MassServ, W and X" in auspice */ -# alias massserv { type services; } -# alias ma { target massserv; type services; } -# alias W { type services; } -# alias X { type services; } - -/* Uncomment this, if you have enabled "WebServ" in auspice */ -# alias webserv { type services; } -# alias ws { target webserv; type services; } - -alias agent { type services; } -alias adminserv { type services; } -alias as { target adminserv; type services; } -alias botserv { type services; } -alias bs { target botserv; type services; } -alias chanserv { type services; } -alias cs { target chanserv; type services; } -alias helpserv { type services; } -alias hs { target helpserv; type services; } -alias hostserv { type services; } -alias ho { target hostserv; type services; } -alias memoserv { type services; spamfilter yes; } -alias ms { target memoserv; type services; spamfilter yes; } -alias nickserv { type services; } -alias ns { target nickserv; type services; } -alias operserv { type services; } -alias os { target operserv; type services; } -alias rootserv { type services; } -alias rs { target rootserv; type services; } - -include "aliases/aliases.conf"; diff --git a/docs/services/irc/examples/unrealircd/aliases/cygnus.conf b/docs/services/irc/examples/unrealircd/aliases/cygnus.conf deleted file mode 100644 index 96b90ecc..00000000 --- a/docs/services/irc/examples/unrealircd/aliases/cygnus.conf +++ /dev/null @@ -1,12 +0,0 @@ -/* Cygnus Aliases */ - -alias nickserv { type services; } -alias ns { target nickserv; type services; } -alias chanserv { type services; } -alias cs { target chanserv; type services; } -alias memoserv { type services; spamfilter yes; } -alias ms { target memoserv; type services; spamfilter yes; } -alias rootserv { type services; } -alias rs { target rootserv; type services; } - -include "aliases/aliases.conf"; diff --git a/docs/services/irc/examples/unrealircd/aliases/epona.conf b/docs/services/irc/examples/unrealircd/aliases/epona.conf deleted file mode 100644 index 73f868fc..00000000 --- a/docs/services/irc/examples/unrealircd/aliases/epona.conf +++ /dev/null @@ -1,16 +0,0 @@ -/* Epona Aliases */ - -alias nickserv { type services; } -alias ns { target nickserv; type services; } -alias chanserv { type services; } -alias cs { target chanserv; type services; } -alias memoserv { type services; spamfilter yes; } -alias ms { target memoserv; type services; spamfilter yes; } -alias operserv { type services; } -alias os { target operserv; type services; } -alias helpserv { type services; } -alias hs { target helpserv; type services; } -alias botserv { type services; } -alias bs { target botserv; type services; } - -include "aliases/aliases.conf"; diff --git a/docs/services/irc/examples/unrealircd/aliases/generic.conf b/docs/services/irc/examples/unrealircd/aliases/generic.conf deleted file mode 100644 index 24a83a20..00000000 --- a/docs/services/irc/examples/unrealircd/aliases/generic.conf +++ /dev/null @@ -1,14 +0,0 @@ -/* Generic Aliases */ - -alias nickserv { type services; } -alias ns { target nickserv; type services; } -alias chanserv { type services; } -alias cs { target chanserv; type services; } -alias memoserv { type services; spamfilter yes; } -alias ms { target memoserv; type services; spamfilter yes; } -alias operserv { type services; } -alias os { target operserv; type services; } -alias helpserv { type services; } -alias hs { target helpserv; type services; } - -include "aliases/aliases.conf"; diff --git a/docs/services/irc/examples/unrealircd/aliases/genericstats.conf b/docs/services/irc/examples/unrealircd/aliases/genericstats.conf deleted file mode 100644 index 01aa3f57..00000000 --- a/docs/services/irc/examples/unrealircd/aliases/genericstats.conf +++ /dev/null @@ -1,4 +0,0 @@ -/* Generic StatServ Aliases */ - -alias statserv { type stats; } -alias ss { target statserv; type stats; } diff --git a/docs/services/irc/examples/unrealircd/aliases/ircservices.conf b/docs/services/irc/examples/unrealircd/aliases/ircservices.conf deleted file mode 100644 index 2101fbb4..00000000 --- a/docs/services/irc/examples/unrealircd/aliases/ircservices.conf +++ /dev/null @@ -1,17 +0,0 @@ -/* IRCServices Aliases */ - -alias nickserv { type services; } -alias ns { target nickserv; type services; } -alias chanserv { type services; } -alias cs { target chanserv; type services; } -alias memoserv { type services; spamfilter yes; } -alias ms { target memoserv; type services; spamfilter yes; } -alias operserv { type services; } -alias os { target operserv; type services; } -alias helpserv { type services; } -alias hs { target helpserv; type services; } -alias irciihelp { type services; } -alias statserv { type services; } -alias ss { target statserv; type services; } - -include "aliases/aliases.conf"; diff --git a/docs/services/irc/examples/unrealircd/aliases/operstats.conf b/docs/services/irc/examples/unrealircd/aliases/operstats.conf deleted file mode 100644 index 61acce43..00000000 --- a/docs/services/irc/examples/unrealircd/aliases/operstats.conf +++ /dev/null @@ -1,6 +0,0 @@ -/* OperStats Aliases */ - -alias operserv { type stats; } -alias os { target operserv; type stats; } -alias statserv { type stats; } -alias ss { target statserv; type stats; } diff --git a/docs/services/irc/examples/unrealircd/badwords.conf b/docs/services/irc/examples/unrealircd/badwords.conf deleted file mode 100644 index 3b52ea7d..00000000 --- a/docs/services/irc/examples/unrealircd/badwords.conf +++ /dev/null @@ -1,50 +0,0 @@ -/* - Unreal Internet Relay Chat Daemon - Copyright (C) Carsten V. Munk 2000 - - NOTE: Those words are not meant to insult you (the user) - but is meant to be a list of words so that the +G channel/user mode - will work properly. You can easily modify this file at your will. - - - - - - - This is some filling space, scroll down to see the words - - - - - - - - - - - - - - - - -*/ -badword all { word "pussy"; } -badword all { word "fuck"; } -badword all { word "whore"; } -badword all { word "slut"; } -badword all { word "shit"; } -badword all { word "asshole"; } -badword all { word "bitch"; } -badword all { word "cunt"; } -badword all { word "vagina"; } -badword all { word "penis"; } -badword all { word "jackass"; } -badword all { word "*fucker*"; } -badword all { word "faggot"; } -badword all { word "fag"; } -badword all { word "horny"; } -badword all { word "dickhead"; } -badword all { word "sonuvabitch"; } -badword all { word "*fuck*"; } -badword all { word "tits"; } diff --git a/docs/services/irc/examples/unrealircd/dccallow.conf b/docs/services/irc/examples/unrealircd/dccallow.conf deleted file mode 100644 index 6f655e3e..00000000 --- a/docs/services/irc/examples/unrealircd/dccallow.conf +++ /dev/null @@ -1,42 +0,0 @@ -/* Example of a possible semi-secure /DCCALLOW configuration written by Syzop. - * $Id$ - * - * Actually nothing is *100% secure*... there could still be - * bugs in the software itself (think: a winamp bug that can - * be exploited via an mp3, or: a wmplayer bug that can be - * exploited via a specially crafted .wmv, etc..). - * If you are really that paranoid you could just remove - * all 'allow dcc'-blocks and prompt the user for EVERY file ;). - * - * Still, I think this file is a good tradeoff between userfriendlyness - * and security. Note that when you try to only DENY specific - * file type (exe, com, etc) you are *guaranteed* to miss ones - * (like: did you know .r17 gets treated as a rar archive? - * and that an exe can be disguished as .cmd which is executable - * on nt/w2k/xp?) - */ - -/* first.. deny everything, then allow known-good stuff... */ -deny dcc { filename "*"; reason "Possible executable content"; soft yes; } -/* common image formats */ -allow dcc { filename "*.jpg"; soft yes; } -allow dcc { filename "*.jpeg"; soft yes; } -allow dcc { filename "*.gif"; soft yes; } -allow dcc { filename "*.png"; soft yes; } -allow dcc { filename "*.bmp"; soft yes; } -/* audio / video (but not scripted/playlists!) */ -allow dcc { filename "*.mp1"; soft yes; } -allow dcc { filename "*.mp2"; soft yes; } -allow dcc { filename "*.mp3"; soft yes; } -allow dcc { filename "*.mpg"; soft yes; } -allow dcc { filename "*.mpeg"; soft yes; } -allow dcc { filename "*.m1v"; soft yes; } -allow dcc { filename "*.m2v"; soft yes; } -allow dcc { filename "*.vob"; soft yes; } -allow dcc { filename "*.wav"; soft yes; } -/* text / misc */ -allow dcc { filename "*.txt"; soft yes; } -allow dcc { filename "*.log"; soft yes; } -allow dcc { filename "*.pdf"; soft yes; } -allow dcc { filename "*.c"; soft yes; } -allow dcc { filename "*.cpp"; soft yes; } diff --git a/docs/services/irc/examples/unrealircd/examples/example.conf b/docs/services/irc/examples/unrealircd/examples/example.conf deleted file mode 100644 index ac0023e3..00000000 --- a/docs/services/irc/examples/unrealircd/examples/example.conf +++ /dev/null @@ -1,680 +0,0 @@ -/* Configuration file for UnrealIRCd 6 - * - * Simply copy this file to your conf/ directory and call it 'unrealircd.conf' - * - * If you are in a hurry then you can CTRL+F for: CHANGE THIS - * The items that must be changed are indicated with those two words. - * However, we actually recommend going through the file line by line - * and edit it where needed, so you can see all the basic items and - * what they are set to. - * - * BEFORE YOU PROCEED: - * Important: all lines, except { and } end with an ; - * This is very important, if you miss a ; somewhere then the - * configuration file parser will complain and the file will not - * be processed correctly! - * If this is your first experience with an UnrealIRCd configuration - * file then we really recommend you to read a little about the syntax, - * this only takes a few minutes and will help you a lot: - * https://www.unrealircd.org/docs/Configuration#Configuration_file_syntax - * - * UnrealIRCd 6 documentation (very extensive!): - * https://www.unrealircd.org/docs/UnrealIRCd_6_documentation - * - * Frequently Asked Questions: - * https://www.unrealircd.org/docs/FAQ - */ - -/* This is a comment, all text here is ignored (comment type #1) */ -// This is also a comment, this line is ignored (comment type #2) -# This is also a comment, again this line is ignored (comment type #3) - -/* UnrealIRCd makes heavy use of modules. Modules allow you to completely - * customize the featureset you wish to enable in UnrealIRCd. - * See: https://www.unrealircd.org/docs/Modules - * - * By using the include below we instruct the IRCd to read the file - * 'modules.default.conf' which will load more than 150 modules - * shipped with UnrealIRCd. In other words: this will simply load - * all the available features in UnrealIRCd. - * If you are setting up UnrealIRCd for the first time we suggest you - * use this. Then, when everything is up and running you can come - * back later to customize the list (if you wish). - */ -include "modules.default.conf"; - -/* Now let's include some other files as well: - * - help/help.conf for our on-IRC /HELPOP system - * - badwords.conf for channel and user mode +G - * - spamfilter.conf as an example for spamfilter usage - * (commented out) - * - operclass.default.conf contains some good operclasses which - * you can use in your oper blocks. - */ -include "help/help.conf"; -include "badwords.conf"; -//include "spamfilter.conf"; -include "operclass.default.conf"; -include "snomasks.default.conf"; - -/* Load the default cloaking module (2021 onwards): */ -loadmodule "cloak_sha256"; -/* Or load the old module from UnrealIRCd 3.2/4/5 instead: */ -//loadmodule "cloak_md5"; - -// CHANGE THIS (the 'name' and the 'info'): -/* This is the me { } block which basically says who we are. - * It defines our server name, some information line and an unique "sid". - * The server id (sid) must start with a digit followed by two digits or - * letters. The sid must be unique for your IRC network (each server should - * have it's own sid). It is common to use 001 for the first server. - */ -me { - name "irc.example.org"; - info "ExampleNET Server"; - sid "001"; -} - -// CHANGE THIS: -/* The admin { } block defines what users will see if they type /ADMIN. - * It normally contains information on how to contact the administrator. - */ -admin { - "Bob Smith"; - "bob"; - "email@example.org"; -} - -/* Clients and servers are put in class { } blocks, we define them here. - * Class blocks consist of the following items: - * - pingfreq: how often to ping a user / server (in seconds) - * - connfreq: how often we try to connect to this server (in seconds) - * - sendq: the maximum queue size for a connection - * - recvq: maximum receive queue from a connection (flood control) - */ - -/* Client class with good defaults */ -class clients -{ - pingfreq 90; - maxclients 1000; - sendq 200k; - recvq 8000; -} - -/* Special class for IRCOps with higher limits */ -class opers -{ - pingfreq 90; - maxclients 50; - sendq 1M; - recvq 8000; -} - -/* Server class with good defaults */ -class servers -{ - pingfreq 60; - connfreq 15; /* try to connect every 15 seconds */ - maxclients 10; /* max servers */ - sendq 20M; -} - -/* Allow blocks define which clients may connect to this server. - * This allows you to add a server password or restrict the server to - * specific IPs only. You also configure the maximum connections - * allowed per IP here. - * See also: https://www.unrealircd.org/docs/Allow_block - */ - -/* Allow everyone in, but only 3 connections per IP */ -allow { - mask *; - class clients; - maxperip 3; -} - -/* Example of a special allow block on a specific IP: - * Requires users on that IP to connect with a password. If the password - * is correct then it permits 20 connections on that IP. - */ -// allow { -// mask 192.0.2.1; -// class clients; -// password "somesecretpasswd"; -// maxperip 20; -// } - -/* Oper blocks define your IRC Operators. - * IRC Operators are people who have "extra rights" compared to others, - * for example they may /KILL other people, initiate server linking, - * /JOIN channels even though they are banned, etc. - * - * For more information about becoming an IRCOp and how to do admin - * tasks, see: https://www.unrealircd.org/docs/IRCOp_guide - * - * For details regarding the oper { } block itself, see - * https://www.unrealircd.org/docs/Oper_block - */ - -/* Here is an example oper block for 'bobsmith' - * YOU MUST CHANGE THIS!! (the oper name and the password) - */ -oper bobsmith { - class opers; - mask *@*; - - /* Technically you can put oper passwords in plaintext in the conf but - * this is HIGHLY DISCOURAGED. Instead you should generate a password hash: - * On *NIX, run: ./unrealircd mkpasswd - * On Windows, run: "C:\Program Files\UnrealIRCd 6\bin\unrealircdctl" mkpasswd - * .. and then paste the result below: - */ - password "$argon2id..etc.."; - /* See https://www.unrealircd.org/docs/Authentication_types for - * more information, including even better authentication types - * such as 'certfp', and how to generate hashes on Windows. - */ - - /* Oper permissions are defined in an 'operclass' block. - * See https://www.unrealircd.org/docs/Operclass_block - * UnrealIRCd ships with a number of default blocks, see - * the article for a full list. We choose 'netadmin' here. - */ - operclass netadmin; - swhois "is a Network Administrator"; - vhost netadmin.example.org; -} - -/* Listen blocks define the ports where the server should listen on. - * In other words: the ports that clients and servers may use to - * connect to this server. - * - * Syntax: - * listen { - * ip ; - * port ; - * options { - * ; - * } - * } - */ - -/* Standard IRC port 6667 */ -listen { - ip *; - port 6667; -} - -/* Standard IRC SSL/TLS port 6697 */ -listen { - ip *; - port 6697; - options { tls; } -} - -/* Special SSL/TLS servers-only port for linking */ -listen { - ip *; - port 6900; - options { tls; serversonly; } -} - -/* NOTE: If you are on an IRCd shell with multiple IP's and you use - * the above listen { } blocks then you will likely get an - * 'Address already in use' error and the ircd won't start. - * This means you MUST bind to a specific IP instead of '*' like: - * listen { ip 1.2.3.4; port 6667; } - * Of course, replace the IP with the IP that was assigned to you. - */ - -/* - * Link blocks allow you to link multiple servers together to form a network. - * See https://www.unrealircd.org/docs/Tutorial:_Linking_servers - */ -//link hub.example.org -//{ -// incoming { -// mask *@something; -// } -// -// outgoing { -// bind-ip *; /* or explicitly an IP */ -// hostname hub.example.org; -// port 6900; -// options { tls; } -// } -// -// /* We use the SPKI fingerprint of the other server for authentication. -// * Open a shell on the OTHER SERVER and run the command to get the fingerprint: -// * On *NIX, run: ./unrealircd spkifp -// * On Windows, run: "C:\Program Files\UnrealIRCd 6\bin\unrealircdctl" spkifp -// */ -// password "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUV=" { spkifp; } -// -// class servers; -//} - -/* The link block for services is usually much simpler. - * For more information about what Services are, - * see https://www.unrealircd.org/docs/Services - */ -//link services.example.org -//{ -// incoming { -// mask 127.0.0.1; -// } -// -// password "changemeplease"; -// -// class servers; -//} - -/* U-lines give other servers (even) more power/commands. - * If you use services you MUST add them here. You must add the - * services server name in ulines { } in the config file on - * every UnrealIRCd server on your network. - * IMPORTANT: Never put the name of an UnrealIRCd server here, - * it's only for Services! - */ -//ulines { -// services.example.org; -//} - -/* Here you can add a password for the IRCOp-only /DIE and /RESTART commands. - * This is mainly meant to provide a little protection against accidental - * restarts and server kills. - */ -drpass { - restart "restart"; - die "die"; -} - -/* The log block defines what should be logged and to what file. - * See also https://www.unrealircd.org/docs/Log_block - */ - -/* This is a good default, it logs everything except - * debug stuff and join/part/kick. - */ -log { - source { - all; - !debug; - !join.LOCAL_CLIENT_JOIN; - !join.REMOTE_CLIENT_JOIN; - !part.LOCAL_CLIENT_PART; - !part.REMOTE_CLIENT_PART; - !kick.LOCAL_CLIENT_KICK; - !kick.REMOTE_CLIENT_KICK; - } - destination { - file "ircd.log" { maxsize 100M; } - } -} - -/* In addition to regular logging, also add a JSON log file. - * This includes lots of information about every event so is great - * for auditing purposes and is machine readable. It is, however - * less readable for humans. - */ -log { - source { - all; - !debug; - !join.LOCAL_CLIENT_JOIN; - !join.REMOTE_CLIENT_JOIN; - !part.LOCAL_CLIENT_PART; - !part.REMOTE_CLIENT_PART; - !kick.LOCAL_CLIENT_KICK; - !kick.REMOTE_CLIENT_KICK; - } - destination { - file "ircd.json.log" { maxsize 250M; type json; } - } -} - -/* With "aliases" you can create an alias like /SOMETHING to send a message to - * some user or bot. They are usually used for services. - * - * We have a number of pre-set alias files, check out the alias/ directory. - * As an example, here we include all aliases used for anope services. - */ -include "aliases/anope.conf"; - -/* Ban nick names so they cannot be used by regular users */ -// ban nick { -// mask "*C*h*a*n*S*e*r*v*"; -// reason "Reserved for Services"; -// } - -/* Ban ip. - * Note that you normally use /KLINE, /GLINE and /ZLINE for this. - */ -// ban ip { -// mask 195.86.232.81; -// reason "Hate you"; -// } - -/* Ban server - if we see this server linked to someone then we delink */ -// ban server { -// mask eris.berkeley.edu; -// reason "Get out of here."; -// } - -/* Ban user - just as an example, you normally use /KLINE or /GLINE for this */ -// ban user { -// mask *tirc@*.saturn.bbn.com; -// reason "Idiot"; -// } - -/* Ban realname allows you to ban clients based on their 'real name' - * or 'gecos' field. - */ -// ban realname { -// mask "Swat Team"; -// reason "mIRKFORCE"; -// } - -// ban realname { -// mask "sub7server"; -// reason "sub7"; -// } - -/* Ban and TKL exceptions. Allows you to exempt users / machines from - * KLINE, GLINE, etc. - * If you are an IRCOp with a static IP (and no untrusted persons on that IP) - * then we suggest you add yourself here. That way you can always get in - * even if you accidentally place a *LINE ban on yourself. - */ - -/* except ban with type 'all' protects you from GLINE, GZLINE, QLINE, SHUN */ -// except ban { -// mask *@192.0.2.1; -// type all; -// } - -/* This allows IRCCloud connections in without maxperip restrictions - * and also exempt them from connect-flood throttling. - */ -except ban { - mask *.irccloud.com; - type { maxperip; connect-flood; } -} - -/* With deny dcc blocks you can ban filenames for DCC */ -// deny dcc { -// filename "*sub7*"; -// reason "Possible Sub7 Virus"; -// } - -/* deny channel allows you to ban a channel (mask) entirely */ -// deny channel { -// channel "*warez*"; -// reason "Warez is illegal"; -// class "clients"; -// } - -/* VHosts (Virtual Hosts) allow users to acquire a different host. - * See https://www.unrealircd.org/docs/Vhost_block - */ - -/* Example vhost which you can use. On IRC type: /VHOST test test - * NOTE: only people with an 'unrealircd.com' host may use it so - * be sure to change the vhost::mask before you test. - */ -// vhost { -// vhost i.hate.microsefrs.com; -// mask *@unrealircd.com; -// login "test"; -// password "test"; -// } - -/* Blacklist blocks will query an external DNS Blacklist service - * whenever a user connects, to see if the IP address is known - * to cause drone attacks, is a known hacked machine, etc. - * Documentation: https://www.unrealircd.org/docs/Blacklist_block - * Or just have a look at the blocks below. - */ - -/* DroneBL, probably the most popular blacklist used by IRC Servers. - * See https://dronebl.org/ for their documentation and the - * meaning of the reply types. At time of writing we use types: - * 3: IRC Drone, 5: Bottler, 6: Unknown spambot or drone, - * 7: DDoS Drone, 8: SOCKS Proxy, 9: HTTP Proxy, 10: ProxyChain, - * 11: Web Page Proxy, 12: Open DNS Resolver, 13: Brute force attackers, - * 14: Open Wingate Proxy, 15: Compromised router / gateway, - * 16: Autorooting worms. - */ -blacklist dronebl { - dns { - name dnsbl.dronebl.org; - type record; - reply { 3; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; } - } - action gline; - ban-time 24h; - reason "Proxy/Drone detected. Check https://dronebl.org/lookup?ip=$ip for details."; -} - -/* EFnetRBL, see https://rbl.efnetrbl.org/ for documentation - * and the meaning of the reply types. - * At time of writing: 1 is open proxy, 4 is TOR, 5 is drones/flooding. - * - * NOTE: If you want to permit TOR proxies on your server, then - * you need to remove the '4;' below in the reply section. - */ -blacklist efnetrbl { - dns { - name rbl.efnetrbl.org; - type record; - reply { 1; 4; 5; } - } - action gline; - ban-time 24h; - reason "Proxy/Drone/TOR detected. Check https://rbl.efnetrbl.org/?i=$ip for details."; -} - -/* You can include other configuration files */ -/* include "klines.conf"; */ - -/* Network configuration */ -set { - // CHANGE THIS, ALL 4 ITEMS: - network-name "ExampleNET"; - default-server "irc.example.org"; - services-server "services.example.org"; - stats-server "stats.example.org"; - - /* Normal defaults */ - help-channel "#Help"; - cloak-prefix "Clk"; - prefix-quit "Quit"; - - /* Cloak keys should be the same at all servers on the network. - * They are used for generating masked hosts and should be kept secret. - * YOU MUST CHANGE THIS! - * The keys should be 3 random strings of 80 characters each (or more). - * and must consist of lowcase (a-z), upcase (A-Z) and digits (0-9). - * On *NIX, you can run './unrealircd gencloak' in your shell to let - * UnrealIRCd generate 3 random strings for you. - * On Windows, you can run "C:\Program Files\UnrealIRCd 6\bin\unrealircdctl" gencloak - */ - cloak-keys { - "Oozahho1raezoh0iMee4ohvegaifahv5xaepeitaich9tahdiquaid0geecipahdauVaij3zieph4ahi"; - "and another one"; - "and another one"; - } -} - -/* Server specific configuration */ -set { - // FINALLY, YOU MUST CHANGE THIS NEXT ITEM: - kline-address 'set.this.to.email.address'; /* e-mail or URL shown when a user is banned */ - - modes-on-connect "+ixw"; /* when users connect, they will get these user modes */ - modes-on-oper "+xws"; /* when someone becomes IRCOp they'll get these modes */ - modes-on-join "+nt"; /* default channel modes when a new channel is created */ - oper-auto-join "#opers"; /* IRCOps are auto-joined to this channel */ - options { - hide-ulines; /* hide U-lines in /MAP and /LINKS */ - show-connect-info; /* show "looking up your hostname" messages on connect */ - } - - maxchannelsperuser 10; /* maximum number of channels a user may /JOIN */ - - /* The minimum time a user must be connected before being allowed to - * use a QUIT message. This will hopefully help stop spam. - */ - anti-spam-quit-message-time 10s; - - /* Or simply set a static quit, meaning any /QUIT reason is ignored */ - /* static-quit "Client quit"; */ - - /* static-part does the same for /PART */ - /* static-part yes; */ - - /* Flood protection: - * There are lots of settings for this and most have good defaults. - * See https://www.unrealircd.org/docs/Set_block#set::anti-flood - */ - anti-flood { - } - - /* Settings for spam filter */ - spamfilter { - ban-time 1d; /* default duration of a *LINE ban set by spamfilter */ - ban-reason "Spam/Advertising"; /* default reason */ - virus-help-channel "#help"; /* channel to use for 'viruschan' action */ - /* except "#help"; channel to exempt from Spamfilter */ - } - - /* Restrict certain commands. - * See https://www.unrealircd.org/docs/Set_block#set::restrict-commands - */ - restrict-commands { - list { - except { - connect-time 60; /* after 60 seconds you can use LIST */ - identified yes; /* or immediately, if you are identified to services */ - reputation-score 24; /* or if you have a reputation score of 24 or more */ - } - } - invite { - except { - connect-time 120; - identified yes; - reputation-score 24; - } - } - /* In addition to the ability to restrict any command, - * such as shown above. There are also 4 special types - * that you can restrict. These are "private-message", - * "private-notice", "channel-message" and "channel-notice". - * They are commented out (disabled) in this example: - */ - //private-message { - // except { connect-time 10; } - //} - //private-notice { - // except { connect-time 10; } - //} - } -} - -/* - * The following will configure connection throttling of "unknown users". - * - * When UnrealIRCd detects a high number of users connecting from IP addresses - * that have not been seen before, then connections from new IP's are rejected - * above the set rate. For example at 10:60 only 10 users per minute can connect - * that have not been seen before. Known IP addresses can always get in, - * regardless of the set rate. Same for users who login using SASL. - * - * See also https://www.unrealircd.org/docs/Connthrottle for details. - * Or just keep reading the default configuration settings below: - */ - -set { - connthrottle { - /* First we configure which users are exempt from the - * restrictions. These users are always allowed in! - * By default these are users on IP addresses that have - * a score of 24 or higher. A score of 24 means that the - * IP was connected to this network for at least 2 hours - * in the past month (or minimum 1 hour if registered). - * We also allow users who are identified to services via - * SASL to bypass the restrictions. - */ - except { - reputation-score 24; - identified yes; - /* for more options, see - * https://www.unrealircd.org/docs/Mask_item - */ - } - - /* New users are all users that do not belong in the - * known-users group. They are considered "new" and in - * case of a high number of such new users connecting - * they are subject to connection rate limiting. - * By default the rate is 20 new local users per minute - * and 30 new global users per minute. - */ - new-users { - local-throttle 20:60; - global-throttle 30:60; - } - - /* This configures when this module will NOT be active. - * The default settings will disable the module when: - * - The reputation module has been running for less than - * a week. If running less than 1 week then there is - * insufficient data to consider who is a "known user". - * - The server has just been booted up (first 3 minutes). - */ - disabled-when { - reputation-gathering 1w; - start-delay 3m; - } - } -} - -/* CHANNEL HISTORY: - * UnrealIRCd has channel mode +H which can be used by users to read back - * channel history, such as from before they joined. For general information - * on this feature, see https://www.unrealircd.org/docs/Channel_history - * - * The history limits can be configured via set::history. The defaults are - * probably already good for you, but if you are on a low-memory system - * or have thousands of channels then you may want to double check. See - * https://www.unrealircd.org/docs/Set_block#set::history for the options. - * - * In addition to that, you can have "persistent channel history", which - * means channel history is stored encrypted on disk so it is preserved - * between IRC server restarts, see - * https://www.unrealircd.org/docs/Set_block#Persistent_channel_history - * The persistent history feature is NOT enabled by default because you - * need to configure a secret { } block for it. The following is a simple - * example with passwords stored directly in the configuration file. - * To get better security, read https://www.unrealircd.org/docs/Secret_block - * on alternative ways so you don't store passwords directly in the config. - */ -//secret historydb { password "somepassword"; } -//set { history { channel { persist yes; db-secret "historydb"; } } } - -/* Finally, you may wish to have a MOTD (Message of the Day), this can be - * done by creating an 'ircd.motd' text file in your conf/ directory. - * This file will be shown to your users on connect. - * For more information see https://www.unrealircd.org/docs/MOTD_and_Rules - */ - -/* - * Problems or need more help? - * 1) https://www.unrealircd.org/docs/ - * 2) https://www.unrealircd.org/docs/FAQ <- answers 80% of your questions! - * 3) If you are still having problems then you can get support: - * - Forums: https://forums.unrealircd.org/ - * - IRC: irc.unrealircd.org (SSL on port 6697) / #unreal-support - * Note that we require you to read the documentation and FAQ first! - */ diff --git a/docs/services/irc/examples/unrealircd/examples/example.es.conf b/docs/services/irc/examples/unrealircd/examples/example.es.conf deleted file mode 100644 index 5ae81c1f..00000000 --- a/docs/services/irc/examples/unrealircd/examples/example.es.conf +++ /dev/null @@ -1,654 +0,0 @@ -/* Archivo de configuración para UnrealIRCd 6 - * - * Simplemente copie este archivo a su directorio conf/, llámelo - * 'unrealircd.conf' y revíselo línea por línea (¡edítelo!) - * - * Importante: Todas las líneas, excepto { y } terminan con ; - * Esto es muy importante, si pierde un ; en algún lugar entonces el - * el analizador del archivo de configuración se quejará y el archivo no - * ser procesado correctamente! - * Si esta es tu primera experiencia con una configuración de UnrealIRCd - * entonces te recomendamos que leas un poco sobre la sintaxis, - * esto solo toma unos minutos y te ayudará mucho: - * https://www.unrealircd.org/docs/Configuration#Configuration_file_syntax - * - * Documentación de UnrealIRCd 6 (¡muy extensa!): - * https://www.unrealircd.org/docs/UnrealIRCd_6_documentation - * - * Preguntas frecuentes: - * https://www.unrealircd.org/docs/FAQ - * - */ - -/* Esto es un comentario, todo el texto aquí es ignorado (tipo de comentario #1) */ -// Esto también es un comentario, esta línea se ignora (tipo de comentario #2) -# Esto también es un comentario, nuevamente esta línea se ignora (tipo de comentario #3) - -/* UnrealIRCd hace un uso intensivo de los módulos. Los módulos le permiten completamente - * personaliza el conjunto de características que deseas habilitar en UnrealIRCd. - * Ver: https://www.unrealircd.org/docs/Modules - * - * Al usar el incluir a continuación, le indicamos al IRCd que lea el archivo - * 'modules.default.conf' que cargará más de 150 módulos - * enviado con UnrealIRCd. En otras palabras: esto simplemente cargará - * todas las funciones disponibles en UnrealIRCd. - * Si está configurando UnrealIRCd por primera vez, le sugerimos - * utilizar esta. Luego, cuando todo esté funcionando, puedes venir - * volver más tarde para personalizar la lista (si lo desea). - */ -include "modules.default.conf"; - -/* Ahora incluyamos algunos otros archivos también: - * - help/help.conf para nuestro sistema on-IRC /HELPOP - * - badwords.conf para canal y modo de usuario +G - * - spamfilter.conf como ejemplo para el uso de spamfilter - * (Comentado) - * - operclass.default.conf contiene algunas buenas operclasses que - * puedes usar en tus bloques operativos. - */ -include "help/help.conf"; -include "badwords.conf"; -//include "spamfilter.conf"; -include "operclass.default.conf"; -include "snomasks.default.conf"; - -/* Cargar el módulo de encubrimiento predeterminado (2021 en adelante): */ -loadmodule "cloak_sha256"; -/* O cargue el módulo antiguo de UnrealIRCd 3.2/4/5 en su lugar: */ -//loadmodule "cloak_md5"; - -/* Este es el bloque yo { } que básicamente dice quiénes somos. - * Define el nombre de nuestro servidor, alguna línea de información y un "sid" único. - * La identificación del servidor (sid) debe comenzar con un dígito seguido de dos dígitos o - * cartas. El sid debe ser único para su red IRC (cada servidor debe - * tener su propio sid). - */ - me { - name "irc.example.org"; - info "ExampleNET Server"; - sid "001"; -} - -/* El bloque admin { } define lo que verán los usuarios si escriben /ADMIN. - * Normalmente contiene información sobre cómo contactar con el administrador. - */ -admin { - "Bob Smith"; - "bob"; - "email@example.org"; -} - -/* Los clientes y servidores se colocan en bloques de clase { }, los definimos aquí. - * Los bloques de clase constan de los siguientes elementos: - * - pingfreq: con qué frecuencia hacer ping a un usuario/servidor (en segundos) - * - connfreq: con qué frecuencia intentamos conectarnos a este servidor (en segundos) - * - sendq: el tamaño máximo de cola para una conexión - * - recvq: máxima cola de recepción de una conexión (control de inundación) - */ - -/* Clase de cliente con buenos valores predeterminados */ -class clients -{ - pingfreq 90; - maxclients 1000; - sendq 200k; - recvq 8000; -} - -/* Clase especial para IRCOps con límites más altos */ -class opers -{ - pingfreq 90; - maxclients 50; - sendq 1M; - recvq 8000; -} - -/* Clase de servidor con buenos valores predeterminados */ -class servers -{ - pingfreq 60; - connfreq 15; /* intenta conectarte cada 15 segundos */ - maxclients 10; /* maximo de servidores */ - sendq 20M; -} - -/* Permitir que los bloques definan qué clientes pueden conectarse a este servidor. - * Esto le permite agregar una contraseña de servidor o restringir el servidor a - * IP específicas solamente. También configuras las conexiones máximas - * permitido por IP aquí. - * Ver también: https://www.unrealircd.org/docs/Allow_block - */ - - /* Permitir el ingreso de todos, pero solo 3 conexiones por IP */ -allow { - mask *; - class clients; - maxperip 3; -} - -/* Los bloques Oper definen sus operadores IRC. - * Los operadores de IRC son personas que tienen "derechos adicionales" en comparación con otros, - * por ejemplo, pueden /MATAR a otras personas, iniciar la vinculación del servidor, - * /ÚNETE a los canales aunque estén prohibidos, etc. - * - * Para obtener más información sobre cómo convertirse en un IRCOp y cómo ser administrador - * tareas, ver: https://www.unrealircd.org/docs/IRCOp_guide - * - * Para obtener detalles sobre el propio bloque oper { }, consulte - * https://www.unrealircd.org/docs/Oper_block - */ - -/* Aquí hay un bloque de operación de ejemplo para 'bobsmith'. - * ¡DEBES cambiar esto! - */ -oper bobsmith { - class opers; - mask *@*; - - /* Technically you can put oper passwords in plaintext in the conf but - * this is HIGHLY DISCOURAGED. Instead you should generate a password hash: - * On *NIX, run: ./unrealircd mkpasswd - * On Windows, run: "C:\Program Files\UnrealIRCd 6\bin\unrealircdctl" mkpasswd - * .. and then paste the result below: - */ - password "$argon2id..etc.."; - /* See https://www.unrealircd.org/docs/Authentication_types for - * more information, including even better authentication types - * such as 'certfp', and how to generate hashes on Windows. - */ - - /* Los permisos de operación se definen en un bloque 'operclass'. - * Ver https://www.unrealircd.org/docs/Operclass_block - * UnrealIRCd se envía con una serie de bloques predeterminados, consulte - * el artículo para una lista completa. Elegimos 'netadmin' aquí. - */ - operclass netadmin; - swhois "is a Network Administrator"; - vhost netadmin.example.org; -} - -/* Los bloques de escucha definen los puertos donde el servidor debe escuchar. - * En otras palabras: los puertos que los clientes y servidores pueden usar para - * conectarse a este servidor. - * - * Sintaxis: - * listen { - * ip ; - * port ; - * options { - * ; - * } - * } - */ - -/* Puerto IRC estándar 6667 */ -listen { - ip *; - port 6667; -} - -/* Puerto IRC estándar 6697 */ -listen { - ip *; - port 6697; - options { tls; } -} - -/* Puerto especial solo para servidores SSL/TLS para vincular */ -listen { - ip *; - port 6900; - options { tls; serversonly; } -} - -/* NOTA: Si está en una shell IRCd con múltiples IP y usa - * los bloques de escucha anteriores { } entonces probablemente obtendrás un - * Error 'Dirección ya en uso' y el ircd no se iniciará. - * Esto significa que DEBE vincularse a una IP específica en lugar de '*' como: - * escucha { ip 1.2.3.4; puerto 6667; } - * Por supuesto, reemplaza la IP con la IP que te fue asignada. - */ - -/* - * Los bloques de enlace le permiten vincular varios servidores para formar una red. - * Ver https://www.unrealircd.org/docs/Tutorial:_Linking_servers - */ -link hub.ejemplo.org -{ - incoming { - mask *@algo; - } - - outgoing { - bind-ip *; /* o explícitamente una IP */ - hostname hub.ejemplo.org; - port 6900; - options { tls; } - } - - /* Usamos la huella digital SPKI del otro servidor para la autenticación. - * Ejecute './unrealircd spkifp' en el otro lado del linkeo para obtenerlo. - * ( Windows: "C:\Program Files\UnrealIRCd 6\bin\unrealircdctl" spkifp ) - */ - password "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUV=" { spkifp; } - - class servers; -} - -/* El bloque de enlace para servicios suele ser mucho más simple. - * Para más información sobre qué son los Servicios, - * ver https://www.unrealircd.org/docs/Services - */ -link services.ejemplo.org -{ - incoming { - mask 127.0.0.1; - } - - password "cambiameporfavor"; - - class servers; -} - -/* Las líneas U dan a otros servidores (incluso) más poder/comandos. - * Si usas servicios debes agregarlos aquí. - * ¡NUNCA pongas el nombre de un servidor UnrealIRCd aquí! - */ -ulines { - services.ejemnplo.org; -} - -/* Aquí puede agregar una contraseña para los comandos /DIE y /RESTART exclusivos de IRCOp. - * Esto está destinado principalmente a proporcionar un poco de protección contra accidentes - * reinicios y muertes del servidor. - */ -drpass { - restart "restart"; - die "die"; -} - -/* El bloque de registro define qué debe registrarse y en qué archivo. - * Ver también https://www.unrealircd.org/docs/Log_block - */ - -/* Este es un buen valor predeterminado, registra todo excepto - * cosas de depuración y unión/parte/kick. - */ -log { - source { - all; - !debug; - !join.LOCAL_CLIENT_JOIN; - !join.REMOTE_CLIENT_JOIN; - !part.LOCAL_CLIENT_PART; - !part.REMOTE_CLIENT_PART; - !kick.LOCAL_CLIENT_KICK; - !kick.REMOTE_CLIENT_KICK; - } - destination { - file "ircd.log" { maxsize 100M; } - } -} - -/* In addition to regular logging, also add a JSON log file. - * This includes lots of information about every event so is great - * for auditing purposes and is machine readable. It is, however - * less readable for humans. - */ -log { - source { - all; - !debug; - !join.LOCAL_CLIENT_JOIN; - !join.REMOTE_CLIENT_JOIN; - !part.LOCAL_CLIENT_PART; - !part.REMOTE_CLIENT_PART; - !kick.LOCAL_CLIENT_KICK; - !kick.REMOTE_CLIENT_KICK; - } - destination { - file "ircd.json.log" { maxsize 250M; type json; } - } -} - -/* Con "aliases" puedes crear un alias como /ALGO para enviar un mensaje a - * algún usuario o bot. Suelen utilizarse para servicios. - * - * Tenemos una cantidad de archivos de alias preestablecidos, consulte el directorio alias/. - * A modo de ejemplo, aquí incluimos todos los alias utilizados para los servicios de anope. - */ -include "aliases/anope.conf"; - -/* Prohibir los apodos para que no puedan ser utilizados por usuarios regulares */ -ban nick { - mask "*C*h*a*n*S*e*r*v*"; - reason "Reservado para Servicios"; -} - -/* Baneo por ip. - * Tenga en cuenta que normalmente usa /KLINE, /GLINE y /ZLINE para esto. - */ -ban ip { - mask 195.86.232.81; - reason "Te odio"; -} - -/* Baneo del servidor: si vemos que este servidor está vinculado a alguien, lo desvinculamos */ -ban server { - mask eris.berkeley.edu; - reason "Sal de aquí."; -} - -/* Baneo de usuario: solo como ejemplo, normalmente usa /KLINE o /GLINE para esto */ -ban user { - mask *tirc@*.saturn.bbn.com; - reason "Idiot"; -} - -/* Baneo del nombre real le permite prohibir clientes en función de su 'nombre real' - * o campo 'gecos'. - */ -ban realname { - mask "Equipo Swat"; - reason "mIRKFORCE"; -} - -ban realname { - mask "sub7server"; - reason "sub7"; -} - -/* Excepciones de baneo y TKL. Le permite eximir a los usuarios/máquinas de - * KLINE, GLINE, etc. - * Si es un IRCOp con una IP estática (y no hay personas que no sean de confianza en esa IP) - * entonces te sugerimos que te añadas aquí. Así siempre puedes entrar - * incluso si accidentalmente colocas una prohibición de *LINE en ti mismo. - */ - -/* excepto el baneo con el tipo 'todos' (all) te proteja de GLINE, GZLINE, QLINE, SHUN */ -except ban { - mask *@192.0.2.1; - type all; -} - -/* This allows IRCCloud connections in without maxperip restrictions - * and also exempt them from connect-flood throttling. - */ -except ban { - mask *.irccloud.com; - type { maxperip; connect-flood; } -} - -/* Con los bloques de denegación de DCC puede prohibir los nombres de archivo para DCC */ -deny dcc { - filename "*sub7*"; - reason "Posible Sub7 Virus"; -} - -/* denegar canal le permite prohibir un canal (máscara) por completo */ -deny channel { - channel "*warez*"; - reason "Warez es ilegal"; - class "clients"; -} - -/* Los VHosts (hosts virtuales) permiten a los usuarios adquirir un host diferente. - * Ver https://www.unrealircd.org/docs/Vhost_block - */ - -/* Ejemplo de vhost que puede usar. En el tipo de IRC: prueba de prueba /VHOST - * NOTA: solo las personas con un host 'unrealircd.com' pueden usarlo para - * asegúrese de cambiar el vhost::mask antes de probar. - */ -vhost { - vhost odio.microsefrs.com; - mask *@unrealircd.com; - login "test"; - password "test"; -} - -/* Los bloques de la lista negra consultarán un servicio de lista negra de DNS externo - * cada vez que un usuario se conecta, para ver si se conoce la dirección IP - * para causar ataques de drones, es una máquina pirateada conocida, etc. - * Documentación: https://www.unrealircd.org/docs/Blacklist_block - * O simplemente eche un vistazo a los bloques a continuación. - */ - -/* DroneBL, probablemente la lista negra más popular utilizada por los servidores IRC. - * Ver https://dronebl.org/ para su documentación y el - * significado de los tipos de respuesta. Al momento de escribir usamos tipos: - * 3: IRC Drone, 5: Embotellador, 6: Spambot o drone desconocido, - * 7: Drone DDoS, 8: Proxy SOCKS, 9: Proxy HTTP, 10: ProxyChain, - * 11: Proxy de página web, 12: Open DNS Resolver, 13: Atacantes de fuerza bruta, - * 14: Proxy Wingate abierto, 15: Enrutador / puerta de enlace comprometidos, - * 16: Gusanos autoenraizadores. - */ -blacklist dronebl { - dns { - name dnsbl.dronebl.org; - type record; - reply { 3; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; } - } - action gline; - ban-time 24h; - reason "Proxy/Drone Detectado. Chequea https://dronebl.org/lookup?ip=$ip para más detalles."; -} - -/* EFnetRBL, consulte https://rbl.efnetrbl.org/ para obtener la documentación - * y el significado de los tipos de respuesta. - * Al momento de escribir: 1 es proxy abierto, 4 es TOR, 5 es drones/inundaciones. - * - * NOTA: Si desea permitir proxies TOR en su servidor, entonces - * necesita eliminar el '4;' abajo en la sección de respuesta. - */ -blacklist efnetrbl { - dns { - name rbl.efnetrbl.org; - type record; - reply { 1; 4; 5; } - } - action gline; - ban-time 24h; - reason "Proxy/Drone/TOR detected. Check https://rbl.efnetrbl.org/?i=$ip for details."; -} - -/* Puede incluir otros archivos de configuración */ -/* include "klines.conf"; */ - -/* Configuración de la red */ -set { - network-name "EjemploNET"; - default-server "irc.ejemplo.org"; - services-server "services.ejemplo.org"; - stats-server "stats.ejemplo.org"; - help-channel "#Ayuda"; - cloak-prefix "Clk"; - prefix-quit "Quit"; - - /* Las claves de ocultación deben ser las mismas en todos los servidores de la red. - * Se utilizan para generar hosts enmascarados y deben mantenerse en secreto. - * Las claves deben ser 3 cadenas aleatorias de 80 caracteres cada una (o más). - * y debe constar de minúsculas (a-z), mayúsculas (A-Z) y dígitos (0-9). - * SUGERENCIA: En *NIX, puede ejecutar './unrealircd gencloak' en su shell para dejar - * UnrealIRCd genera 3 cadenas aleatorias para ti. - * On Windows, you can run "C:\Program Files\UnrealIRCd 6\bin\unrealircdctl" gencloak - */ - cloak-keys { - "Oozahho1raezoh0iMee4ohvegaifahv5xaepeitaich9tahdiquaid0geecipahdauVaij3zieph4ahi"; - "y otra llave"; - "y otra llave"; - } -} - -/* Configuración específica del servidor */ -set { - kline-address 'agrega.una.dirección.de.correo-electrónico'; /* correo electrónico o URL que se muestra cuando un usuario está baneado */ - modes-on-connect "+ixw"; /* cuando los usuarios se conecten, obtendrán estos modos de usuario */ - modes-on-oper "+xws"; /* cuando alguien se convierte en IRCOp obtendrá estos modos */ - modes-on-join "+nt"; /* modos de canal predeterminados cuando se crea un nuevo canal */ - oper-auto-join "#opers"; /* Los IRCOps se unen automáticamente a este canal */ - options { - hide-ulines; /* ocultar líneas U en /MAP y /LINKS */ - show-connect-info; /* mostrar mensajes de "buscando su nombre de host" al conectarse */ - } - - maxchannelsperuser 10; /* número máximo de canales que un usuario puede /JOIN */ - - /* El tiempo mínimo que un usuario debe estar conectado antes de que se le permita - * use un mensaje SALIR. Con suerte, esto ayudará a detener el spam. - */ - anti-spam-quit-message-time 10s; - - /* O simplemente establezca una salida estática, lo que significa que se ignora cualquier razón /QUIT */ - /* static-quit "Salida del cliente"; */ - - /* static-part hace lo mismo para /PART */ - /* static-part yes; */ - - /* Protección contra inundaciones: - * Hay muchas configuraciones para esto y la mayoría tiene buenos valores predeterminados. - * Ver https://www.unrealircd.org/docs/Set_block#set::anti-flood - */ - anti-flood { - } - - /* Configuración del filtro de spam */ - spamfilter { - ban-time 1d; /* duración predeterminada de una prohibición de *LINE establecida por spamfilter */ - ban-reason "Spam/Publicidad"; /* razón por defecto */ - virus-help-channel "#Ayuda"; /* canal a usar para la acción 'viruschan' */ - /* except "#Ayuda"; canal para eximir de Spamfilter */ - } - - /* Restringir ciertos comandos. - * Ver https://www.unrealircd.org/docs/Set_block#set::restrict-commands - */ - restrict-commands { - list { - except { - connect-time 60; - identified yes; - reputation-score 24; - } - } - invite { - except { - connect-time 120; - identified yes; - reputation-score 24; - } - } - /* Además de la capacidad de restringir cualquier comando, - * como se muestra arriba. También hay 4 tipos especiales. - * que puedes restringir. Estos son "mensajes privados", - * "aviso privado", "mensaje de canal" y "aviso de canal". - * Están comentados (deshabilitados) en este ejemplo: - */ - //private-message { - // except { - // connect-time 10; - // } - //} - //private-notice { - // except { - // connect-time 10; - // } - //} - } -} - -/* - * Lo siguiente configurará la limitación de conexión de "usuarios desconocidos". - * - * Cuando UnrealIRCd detecta una gran cantidad de usuarios que se conectan desde direcciones IP - * que no se han visto antes, entonces se rechazan las conexiones de nuevas IP - * por encima de la tarifa establecida. Por ejemplo a las 10:60 solo se pueden conectar 10 usuarios por minuto - * que no se han visto antes. Las direcciones IP conocidas siempre pueden entrar, - * independientemente de la tarifa establecida. Lo mismo para los usuarios que inician sesión con SASL. - * - * Ver también https://www.unrealircd.org/docs/Connthrottle para más detalles. - * O simplemente siga leyendo los ajustes de configuración predeterminados a continuación: - */ -set { - connthrottle { - /* Primero debemos configurar lo que llamamos "usuarios conocidos". - * De forma predeterminada, estos son usuarios en direcciones IP que tienen - * una puntuación de 24 o superior. Una puntuación de 24 significa que el - * La IP estuvo conectada a esta red durante al menos 2 horas - * en el último mes (o mínimo 1 hora si está registrado). - * La opción sasl-bypass es otra configuración. Significa - * que los usuarios que se autentican en los servicios a través de SASL - * también se consideran usuarios conocidos. - * Usuarios en el grupo de "usuarios conocidos" (ya sea por reputación - * o por SASL) siempre están permitidas en este módulo. - */ - except { - reputation-score 24; - identified yes; - } - - /* Los nuevos usuarios son todos los usuarios que no pertenecen al - * grupo de usuarios conocidos. Se consideran "nuevos" y en - * caso de un alto número de tales nuevos usuarios que se conectan - * están sujetos a limitación de velocidad de conexión. - * Por defecto la tarifa es de 20 nuevos usuarios locales por minuto - * y 30 nuevos usuarios globales por minuto. - */ - new-users { - local-throttle 20:60; - global-throttle 30:60; - } - - /* Esto configura cuando este módulo NO estará activo. - * La configuración predeterminada deshabilitará el módulo cuando: - * - El módulo de reputación se ha estado ejecutando durante menos de - * una semana. Si se ejecuta menos de 1 semana, entonces hay - * datos insuficientes para considerar quién es un "usuario conocido". - * - El servidor acaba de iniciarse (primeros 3 minutos). - */ - disabled-when { - reputation-gathering 1w; - start-delay 3m; - } - } -} - -/* HISTORIAL DE UN CANAL: - * UnrealIRCd tiene el modo de canal +H que los usuarios pueden usar para volver a leer - * los mensajes del canal, antes de que se unieran. Para información general - * en esta función, lee https://www.unrealircd.org/docs/Channel_history - * - * El historial del canal puede ser configurado vía set::history. Los valores predeterminados - * son probablemente buenos para ti, pero si está en un sistema con poca memoria - * o tiene miles de canales, entonces es posible que debas volver a verificar. Lee - * https://www.unrealircd.org/docs/Set_block#set::history para las opciones. - * - * Además de eso, puedes tener "persistent channel history", cual - * significa que el historial del canal se almacena encriptado en el disco - * para que se conserve entre reinicios del servidor IRC, lee - * https://www.unrealircd.org/docs/Set_block#Persistent_channel_history - * La función de historial persistente NO está habilitada de manera predeterminada - * porque usted necesita configurar un bloque de secreto { } para ello. Un sencillo - * ejemplo con contraseñas almacenadas directamente en el archivo de configuración. - * Para obtener una mejor seguridad, lee https://www.unrealircd.org/docs/Secret_block - * las diferentes alternativas para que no almacenes contraseñas directamente en la configuración. - */ -//secret historydb { password "somepassword"; } -//set { history { channel { persist yes; db-secret "historydb"; } } } - -/* Finalmente, es posible que desee tener un MOTD (Mensaje del día), esto puede ser - * se hace creando un archivo de texto 'ircd.motd' en su directorio conf/. - * Este archivo se mostrará a tus usuarios al conectarse. - * Para obtener más información, consulte https://www.unrealircd.org/docs/MOTD_and_Rules - */ - -/* - * Problemas o necesita más ayuda? - * 1) https://www.unrealircd.org/docs/ - * 2) https://www.unrealircd.org/docs/Main_Page/es <- ¡responde el 80% de tus preguntas! - * 3) Si aún tiene problemas, puede obtener soporte: - * - Foros: https://forums.unrealircd.org/ - * - IRC: irc.unrealircd.org (SSL en el puerto 6697) / #unreal-support - * ¡Tenga en cuenta que primero le pedimos que lea la documentación y las preguntas frecuentes! - */ diff --git a/docs/services/irc/examples/unrealircd/examples/example.fr.conf b/docs/services/irc/examples/unrealircd/examples/example.fr.conf deleted file mode 100644 index 2381cba8..00000000 --- a/docs/services/irc/examples/unrealircd/examples/example.fr.conf +++ /dev/null @@ -1,661 +0,0 @@ -/* Fichier de configuration pour UnrealIRCd 6 - * - * Copiez ce fichier dans le répertoire conf/, renommez le - * 'unrealircd.conf' et parcourez-le ligne par ligne (modifiez le !) - * - * Important : Toutes les lignes, sauf celles ne comportant qu'un { - * ouvrant, doivent finir par un ; y compris };. C'est très important, - * car si vous oubliez un ; quelque part, alors le parser du fichier de - * configuration se plaindra et votre fichier ne sera pas lu correctement ! - * S'il s'agit de votre première expérience avec le fichier de configuration - * d'UnrealIRCd, nous vous recommandons de vous documenter un peu à propos - * de la syntaxe. Ça ne vous prendra que quelques minutes et vous aidera - * beaucoup : - * https://www.unrealircd.org/docs/Configuration#Configuration_file_syntax - * - * Documentation pour UnrealIRCd 6 (très complète !) : - * https://www.unrealircd.org/docs/UnrealIRCd_6_documentation/fr - * - * Foire Aux Questions : - * https://www.unrealircd.org/docs/FAQ - * - */ - -/* Ceci est un commentaire, ici, tout le texte est ignoré (type #1) */ -// Ceci est aussi un commentaire, cette ligne est ignorée (type #2) -# Ceci est aussi un commentaire, cette ligne est ignorée (type #3) - -/* UnrealIRCd utilise beaucoup les modules. Ceux-ci vous permettent - * de personnaliser complètement les fonctionnalités que vous voulez - * activer sur UnrealIRCd. - * Voir : https://www.unrealircd.org/docs/Modules - * - * En utilisant la ligne include ci-dessous, nous indiquons à l'IRCd de - * lire le fichier 'modules.default.conf' ce qui activera plus de 150 - * modules fournis avec UnrealIRCd. En d'autres termes, ceci activera - * toutes les fonctionnalités disponibles d'UnrealIRCd. - * Si vous configurez UnrealIRCd pour la première fois, nous vous - * conseillons d'utiliser cette ligne. Après, lorsque tout fonctionnera - * vous pourrez revenir personnaliser la liste (si vous le souhaitez). - */ -include "modules.default.conf"; - -/* Incluons aussi d'autres fichiers : - * - help/help.conf pour le système d'aide sur IRC via /HELPOP - * - badwords.conf pour le mode utilisateur et de salon +G - * - spamfilter.conf comme exemple d'utilisation de spamfilter - * - operclass.default.conf qui contient les classes d'opérateurs - * par défaut à utiliser dans vos blocs oper. - */ -include "help/help.conf"; -include "badwords.conf"; -//include "spamfilter.conf"; -include "operclass.default.conf"; -include "snomasks.default.conf"; - -/* Load the default cloaking module (2021 onwards): */ -loadmodule "cloak_sha256"; -/* Or load the old module from UnrealIRCd 3.2/4/5 instead: */ -//loadmodule "cloak_md5"; - -/* Le bloc me { } indique qui est le serveur. - * Il définit le nom du serveur, une ligne d'informations et un identifiant - * "sid" unique. L'id du serveur (sid) doit commencer par un chiffre suivit - * de deux chiffres ou lettres. Le sid doit être unique sur votre réseau IRC - * (chaque serveur doit avoir un sid différent). - */ -me { - name "irc.example.org"; - info "Serveur ExampleNET"; - sid "001"; -} - -/* Le bloc admin { } définit ce que les utilisateurs verront en faisant - * /ADMIN. C'est généralement des infos de contact de l'administrateur. - */ -admin { - "Bob Smith"; - "bob"; - "adresse.email@example.org"; -} - -/* Les clients et serveurs sont placés dans des classes, que nous - * définissons dans ces blocs class { }. - * Les blocs de classe comportent les éléments suivants : - * - pingfreq: à quelle fréquence envoyer un ping à l'utilisateur ou au - * serveur (en secondes) - * - connfreq: à quelle fréquence on essaye de se connecter à ce serveur - * (en secondes) - * - sendq: la taille maximale de la queue d'émission pour une connexion - * - recvq: la taille maximale de la queue de réception pour une connexion - * (contrôle du flood) - */ - -/* Classe pour des clients */ -class clients -{ - pingfreq 90; - maxclients 1000; - sendq 200k; - recvq 8000; -} - -/* Classe spéciale pour des IRCOps avec des limites plus hautes */ -class opers -{ - pingfreq 90; - maxclients 50; - sendq 1M; - recvq 8000; -} - -/* Classe pour des serveurs */ -class servers -{ - pingfreq 60; - connfreq 15; /* essayer de se connecter toutes les 15 sec */ - maxclients 10; /* nombre max de serveurs */ - sendq 5M; -} - -/* Les blocs allow définissent quels clients peuvent se connecter au - * serveur. Ils vous permettent d'ajouter un mot de passe ou de restreindre - * le serveur à certaines IP seulement. C'est aussi là que vous configurez - * le nombre maximum de connexions par IP. - * Voir : https://www.unrealircd.org/docs/Allow_block - */ - -/* Accepter tout le monde, mais seulement 5 connexions par IP */ -allow { - mask *; - class clients; - maxperip 5; -} - -/* Exemple de bloc allow spécial pour une IP donnée : - * Les utilisateurs sur cette IP doivent se connecter avec un mot de passe. - * S'il est correct, alors autoriser 20 connexions sur cette IP. - */ -allow { - mask 192.0.2.1; - class clients; - password "unmotdepassesecret"; - maxperip 20; -} - -/* Les blocs oper définissent vos Opérateurs IRC. - * Les Opérateurs IRC sont des utilisateurs avec des "droits en plus" - * par rapport aux autres, par exemple, ils peuvent /KILL (déconnecter) - * d'autres utilisateurs, faire se connecter des serveurs entre eux, - * /JOIN des salons même s'ils sont bannis, etc ... - * Voir aussi : https://www.unrealircd.org/docs/Oper_block - */ - -/* Voici un exemple de bloc oper pour 'bobsmith'. - * Vous DEVEZ le modifier !! - */ -oper bobsmith { - class opers; - mask *@*; - - /* Technically you can put oper passwords in plaintext in the conf but - * this is HIGHLY DISCOURAGED. Instead you should generate a password hash: - * On *NIX, run: ./unrealircd mkpasswd - * On Windows, run: "C:\Program Files\UnrealIRCd 6\bin\unrealircdctl" mkpasswd - * .. and then paste the result below: - */ - password "$argon2id..etc.."; - /* See https://www.unrealircd.org/docs/Authentication_types for - * more information, including even better authentication types - * such as 'certfp', and how to generate hashes on Windows. - */ - - /* Les permissions Oper sont définies dans un bloc 'operclass'. - * Voir https://www.unrealircd.org/docs/Operclass_block - * UnrealIRCd est fourni avec des classes par défaut, voir la doc - * pour une liste complète. Nous avons choisi 'netadmin' ici. - */ - operclass netadmin; - swhois "est un Administrateur du Réseau"; - vhost netadmin.example.org; -} - -/* Les blocs listen définissent les ports sur lesquels le serveur écoute. - * C'est-à-dire les ports que les clients et les serveurs utilisent pour - * se connecter à ce serveur. - * - * Syntaxe : - * listen { - * ip ; - * port ; - * options { - * ; - * } - * } - */ - -/* Port standard pour IRC 6667 */ -listen { - ip *; - port 6667; -} - -/* Port standard pour IRC sur SSL/TLS 6697 */ -listen { - ip *; - port 6697; - options { tls; } -} - -/* Port SSL/TLS spécial pour la connexion entre serveurs */ -listen { - ip *; - port 6900; - options { tls; serversonly; } -} - -/* NOTE : Si vous utilisez un serveur IRC avec plusieurs IP et que vous - * utilisez les blocs listen ci-dessus, vous aurez peut-être une - * erreur 'Address already in use' et l'IRCd ne démarrera pas. - * Celle-ci indique que vous devez préciser une IP spécifique - * au lieu de '*'. Exemple : - * listen 1.2.3.4:6667; - * Bien sûr, remplacez 1.2.3.4 par l'IP qui vous est assignée. - */ - -/* - * Les blocs link vous permettent de connecter plusieurs serveurs ensemble - * pour former un réseau IRC. - * Voir https://www.unrealircd.org/docs/Tutorial:_Linking_servers - */ -link hub.example.org -{ - incoming { - mask *@something; - } - - outgoing { - bind-ip *; /* ou une IP précise */ - hostname hub.example.org; - port 6900; - options { tls; } - } - - password "00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF"; /* Empreinte SSL de l'autre serveur */ - - class servers; -} - -/* Les U-lines donnent encore plus de pouvoir à certains serveurs. - * Si vous utilisez des Services, vous devez les indiquer ici. - * NE JAMAIS indiquer le nom d'un serveur UnrealIRCd normal ici !!! - * (Si vous ne savez pas ce que sont les Services, voir : - * https://www.unrealircd.org/docs/Services ) - */ -ulines { - services.example.org; -} - -/* Ici vous pouvez indiquer un mot de passe pour les commandes /DIE et - * /RESTART, qui sont restreintes aux IRCops. - * Il s'agit surtout d'une petite protection contre les redémarrages et - * les coupures de serveur accidentels. - */ -drpass { - restart "restart"; - die "die"; -} - -/* Le bloc log indique ce qui doit être journalisé et dans quel fichier. - * Voir aussi https://www.unrealircd.org/docs/Log_block - */ - -/* Ceci est une bonne valeur par défaut, elle journalise presque tout */ -log { - source { - all; - !debug; - !join.LOCAL_CLIENT_JOIN; - !join.REMOTE_CLIENT_JOIN; - !part.LOCAL_CLIENT_PART; - !part.REMOTE_CLIENT_PART; - !kick.LOCAL_CLIENT_KICK; - !kick.REMOTE_CLIENT_KICK; - } - destination { - file "ircd.log" { maxsize 100M; } - } -} - -/* In addition to regular logging, also add a JSON log file. - * This includes lots of information about every event so is great - * for auditing purposes and is machine readable. It is, however - * less readable for humans. - */ -log { - source { - all; - !debug; - !join.LOCAL_CLIENT_JOIN; - !join.REMOTE_CLIENT_JOIN; - !part.LOCAL_CLIENT_PART; - !part.REMOTE_CLIENT_PART; - !kick.LOCAL_CLIENT_KICK; - !kick.REMOTE_CLIENT_KICK; - } - destination { - file "ircd.json.log" { maxsize 250M; type json; } - } -} - -/* Avec des "alias", vous pouvez créer un alias comme /UNTRUC pour envoyer - * un message à un utilisateur ou à un bot. Ils sont souvent utilisés pour - * les services. - * - * Nous fournissons un certain nombre d'alias par défaut, voir les fichiers - * du répertoire aliases/. - * Pour exemple, ici nous ajoutons les alias pour les Services Anope. - */ -include "aliases/anope.conf"; - -/* Bannir des nicks pour qu'ils ne soient pas utilisables par des - * utilisateurs normaux - */ -ban nick { - mask "*C*h*a*n*S*e*r*v*"; - reason "Réservé aux Services"; -} - -/* Bannir une IP. - * NB : vous pouvez aussi utiliser /KLINE, /GLINE et /ZLINE pour ça. - */ -ban ip { - mask 195.86.232.81; - reason "Je vous hais !"; -} - -/* Bannir un serveur - si ce serveur est connecté au réseau, nous nous - * déconnecterons - */ -ban server { - mask eris.berkeley.edu; - reason "Va-t-en d'ici."; -} - -/* Bannir un utilisateur - juste pour l'exemple, on utilise normalement - * /KLINE or /GLINE pour ça - */ -ban user { - mask *tirc@*.saturn.bbn.com; - reason "Idiot"; -} - -/* Bannir un realname (ou 'gecos') */ -ban realname { - mask "Swat Team"; - reason "mIRKFORCE"; -} - -ban realname { - mask "sub7server"; - reason "sub7"; -} - -/* Exceptions de ban et TKL. Vous permet d'exempter des utilisateurs des - * KLINE, GLINE, etc ... - * Si vous êtes un IRCOp avec une IP statique (et qu'il n'y a que des - * personnes de confiance sur cette IP), alors vous pouvez vous ajouter ici. - * Ainsi, vous pourrez toujours vous connecter même si vous vous bannissez - * accidentellement. - */ - -/* except ban avec le type 'all' vous protège des GLINE, GZLINE, QLINE, SHUN */ -except ban { - mask *@192.0.2.1; - type all; -} - -/* This allows IRCCloud connections in without maxperip restrictions - * and also exempt them from connect-flood throttling. - */ -except ban { - mask *.irccloud.com; - type { maxperip; connect-flood; } -} - -/* Avec un bloc deny dcc vous pouvez interdire des noms de fichiers dans - * les échanges DCC - */ -deny dcc { - filename "*sub7*"; - reason "Possible virus Sub7"; -} - -/* deny channel vous permet d'interdire des masques de noms de salons */ -deny channel { - channel "*warez*"; - reason "Le warez est illegal"; - class "clients"; -} - -/* Les VHosts (Virtual Hosts - Hôtes Virtuels) permettent aux utilisateurs - * d'avoir un nom d'hôte différent. - * Voir https://www.unrealircd.org/docs/Vhost_block - */ - -/* Vhost d'exemple. Sur IRC, entrez /VHOST test test - * NOTE : seuls les utilisateurs avec un nom d'hôte 'unrealircd.com' - * peuvent l'utiliser, donc modifiez vhost::mask avant de tester. - */ -vhost { - vhost i.hate.microsefrs.com; - mask *@unrealircd.com; - login "test"; - password "test"; -} - -/* Les blocs de liste noire interrogeront un service de liste noire DNS externe - * chaque fois qu'un utilisateur se connecte, pour voir si l'adresse IP est connue - * pour provoquer des attaques de drones, est une machine piratée connue, etc.. - * Documentation: https://www.unrealircd.org/docs/Blacklist_block - * Ou regardez simplement les blocs ci-dessous. - */ - -/* DroneBL, probablement la liste noire la plus populaire utilisée par les serveurs IRC. - * Voir https://dronebl.org/ pour leur documentation et les - * significations des types de réponse. AAu moment de la rédaction, nous utilisons des types: - * 3: IRC Drone, 5: Bottler, 6: Unknown spambot or drone, - * 7: DDoS Drone, 8: SOCKS Proxy, 9: HTTP Proxy, 10: ProxyChain, - * 11: Web Page Proxy, 12: Open DNS Resolver, 13: Brute force attackers, - * 14: Open Wingate Proxy, 15: Compromised router / gateway, - * 16: Autorooting worms. - */ -blacklist dronebl { - dns { - name dnsbl.dronebl.org; - type record; - reply { 3; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; } - } - action gline; - ban-time 24h; - reason "Proxy/Drone detected. Check https://dronebl.org/lookup?ip=$ip for details."; -} - -/* EFnetRBL, voir https://rbl.efnetrbl.org/ pour la documentation - * et la signification des types de réponse. - * Au moment de la rédaction: 1 is open proxy, 4 is TOR, 5 is drones/flooding. - * - * REMARQUE: Si vous souhaitez autoriser les proxys TOR sur votre serveur, alors - * vous devez supprimer le '4;' ci-dessous dans la section de réponse. - */ -blacklist efnetrbl { - dns { - name rbl.efnetrbl.org; - type record; - reply { 1; 4; 5; } - } - action gline; - ban-time 24h; - reason "Proxy/Drone/TOR detected. Check https://rbl.efnetrbl.org/?i=$ip for details."; -} - -/* Vous pouvez inclure d'autres fichiers de configuration */ -/* include "klines.conf"; */ - -/* Configuration du réseau */ -set { - network-name "ExampleNET"; - default-server "irc.example.org"; - services-server "services.example.org"; - stats-server "stats.example.org"; - help-channel "#Help"; - cloak-prefix "Clk"; - prefix-quit "Quit"; - - /* Les clés de cloaking doivent être identiques sur tous les serveurs - * d'un réseau. Elles sont utilisées pour générer les noms d'hôtes - * masqués et doivent être gardées secrètes. Les clés doivent être - * 3 chaînes de 80 caractères aléatoires et ne comporter que des - * minuscules (a-z), des majuscules (A-Z) et des chiffres (0-9). - * (voir l'exemple) - * NB : sur *NIX, vous pouvez exécuter './unrealircd gencloak' sur votre - * serveur pour que Unrealircd génère 3 clés aléatoires pour vous. - * On Windows, use "C:\Program Files\UnrealIRCd 6\bin\unrealircdctl" gencloak - */ - cloak-keys { - "Oozahho1raezoh0iMee4ohvegaifahv5xaepeitaich9tahdiquaid0geecipahdauVaij3zieph4ahi"; - "et une autre"; - "et une troisième"; - } -} - -/* Configuration spécifique au serveur */ - -set { - kline-address 'indiquez.une.adresse.email'; /* e-mail ou URL indiquée lorsqu'un utilisateur est banni */ - modes-on-connect "+ixw"; /* modes utilisateur ajoutés lorsqu'un utilisateur se connecte */ - modes-on-oper "+xws"; /* modes utilisateur ajoutés lorsqu'un utilisateur devient IRCOp */ - oper-auto-join "#opers"; /* salon que les IRCOps joignent automatiquement */ - options { - hide-ulines; /* cacher les U-lines de /MAP et /LINKS */ - show-connect-info; /* afficher les messages "looking up your hostname" à la connexion */ - } - - maxchannelsperuser 10; /* nombre max de salons par utilisateur */ - - /* Temps minimum qu'un utilisateur doit rester connecter avant de pouvoir - * utiliser un message de QUIT. Le but est pour réduire le spam. - */ - anti-spam-quit-message-time 10s; - - /* Ou indiquez un message de QUIT constant, ce qui fait que les raisons - * de /QUIT sont ignorées. - */ - /* static-quit "Le client a quitté"; */ - - /* static-part fait la même chose pour /PART */ - /* static-part yes; */ - - /* Protections anti-flood. - * Voir: https://www.unrealircd.org/docs/Set_block#set::anti-flood - */ - anti-flood { - } - - /* Paramètres de Spamfilter */ - spamfilter { - ban-time 1d; /* durée par défaut des bans *LINE ajoutés par spamfilter */ - ban-reason "Spam/Publicité"; /* raison par defaut */ - virus-help-channel "#help"; /* salon par défaut pour l'action 'viruschan' */ - /* except "#help"; salon à exempter de Spamfilter */ - } - - /* Restreindre certaines commandes. - * Voir https://www.unrealircd.org/docs/Set_block#set::restrict-commands - */ - restrict-commands { - list { - except { - connect-time 60; /* après 60 secondes, vous pouvez utiliser LIST */ - identified yes; /* ou immédiatement, si vous êtes identifié aux services */ - reputation-score 24; /* ou si vous avez un score de réputation de 24 ou plus */ - } - } - invite { - except { - connect-time 120; - identified yes; - reputation-score 24; - } - } - /* En plus de la possibilité de restreindre toute commande, - * tel qu'illustré ci-dessus. Il existe également 4 types spéciaux - * que vous pouvez restreindre. Ceux-ci sont "private-message", - * "private-notice", "channel-message" and "channel-notice". - * Ils sont commentés (désactivés) dans cet exemple : - */ - //private-message { - // except { connect-time 10; } - //} - //private-notice { - // except { connect-time 10; } - //} - } - -} - -/* - * Ce qui suit configurera la limitation de connexion de "unknown users". - * - * Quand UnrealIRCd détecte un nombre élevé d'utilisateurs se connectant à partir d'adresses IP - * qui n'ont pas été vus auparavant, les connexions des nouvelles IP sont rejetées - * au-dessus du taux fixé. Par exemple à 10:60 seuls 10 utilisateurs par minute peuvent se connecter - * qui n'ont pas été vus auparavant. Les adresses IP connues peuvent toujours entrer, - * quel que soit le tarif fixé. Idem pour les utilisateurs qui se connectent avec SASL. - * - * Voir également https://www.unrealircd.org/docs/Connthrottle pour les détails. - * Ou continuez simplement à lire les paramètres de configuration par défaut ci-dessous: - */ - -set { - connthrottle { - /* Nous configurons d'abord quels utilisateurs sont exemptés de la - * restrictions. Ces utilisateurs sont toujours autorisés! - * Par défaut, ce sont des utilisateurs sur des adresses IP qui ont - * un score de 24 ou plus. Un score de 24 signifie que l'IP - * était connecté à ce réseauk pendant au moins 2 heures - * au cours du mois passé (ou minimum 1h si inscrit). - * Nous permettons également aux utilisateurs qui sont identifiés aux services via - * SASL contourner les restrictions. - */ - except { - reputation-score 24; - identified yes; - /* pour plus d'options, voir restrictions - * https://www.unrealircd.org/docs/Mask_item - */ - } - - /* Les nouveaux utilisateurs sont tous les utilisateurs qui n'appartiennent pas au - * groupe d'utilisateurs connus. Ils sont considérés comme "nouveaux" et dans - * le cas d'un nombre élevé de ces nouveaux utilisateurs se connectant - * ils sont soumis à une limitation du débit de connexion. - * Par défaut, le taux est de 20 nouveaux utilisateurs locaux par minute - * et 30 nouveaux utilisateurs global par minute. - */ - new-users { - local-throttle 20:60; - global-throttle 30:60; - } - - /* Ceci configure quand ce module ne sera PAS actif. - * Les paramètres par défaut désactiveront le module lors que: - * - Le module de réputation fonctionne depuis moins d'une - * semaine. Si vous courez moins d'une semaine, il y a - * données insuffisantes pour déterminer qui est un "utilisateur connu". - * - Le serveur vient d'être démarré (3 premières minutes). - */ - disabled-when { - reputation-gathering 1w; - start-delay 3m; - } - } -} - -/* HISTORIQUE DES CANAUX: - * UnrealIRCd a le mode canal +H qui peut être utilisé par les utilisateurs pour relire - * historique de la chaîne, comme avant leur adhésion. Pour des informations générales - * sur cette fonctionnalité, voir https://www.unrealircd.org/docs/Channel_history - * - * Les limites de l'historique peuvent être configurées via set::history. - * Les valeurs par défaut sont probablement déjà bien pour toi, mais si vous êtes sur un - * système à faible mémoire ou ayant des milliers de canaux, vous voudrez peut-être re vérifier. - * Voir https://www.unrealircd.org/docs/Set_block#set::history pour les options. - * - * En plus de cela, vous pouvez avoir "persistent channel history", qui - * signifie que l'historique des chaînes est stocké crypté sur le disque - * afin qu'il soit préservé entre les redémarrages du serveur IRC, voir - * https://www.unrealircd.org/docs/Set_block#Persistent_channel_history - * La fonction d'historique persistant n'est PAS activée par défaut car vous - * devez de configurer un bloque de secret { }. Ce qui suit est un simple - * exemple avec des mots de passe stockés directement dans le fichier de configuration. - * Pour une meilleure sécurité, voir https://www.unrealircd.org/docs/Secret_block - * sur des moyens alternatifs pour ne pas stocker les mots de passe directement dans la configuration. - */ -//secret historydb { password "somepassword"; } -//set { history { channel { persist yes; db-secret "historydb"; } } } - -/* Enfin, vous souhaiterez peut-être avoir un MOTD (Le message du jour), cela peut être - * fait en créant un archive de text 'ircd.motd' dans votre répertoire conf/. - * Ce fichier sera montré à vos utilisateurs lors de la connexion. - * Pour plus d'informations, voir https://www.unrealircd.org/docs/MOTD_and_Rules - */ - -/* - * Un problème ou besoin d'aide supplémentaire ? - * 1) https://www.unrealircd.org/docs/ - * 2) https://www.unrealircd.org/docs/FAQ <- répond à 80% des questions ! - * 3) Si vous avez toujours des problèmes, vous pouvez aller sur - * irc.unrealircd.org #unreal-support, - * mais nous exigeons que vous lisiez LA DOCUMENTATION et la FAQ d'abord ! - */ diff --git a/docs/services/irc/examples/unrealircd/examples/example.pt.conf b/docs/services/irc/examples/unrealircd/examples/example.pt.conf deleted file mode 100644 index cb0eecee..00000000 --- a/docs/services/irc/examples/unrealircd/examples/example.pt.conf +++ /dev/null @@ -1,669 +0,0 @@ -/* Arquivo de configuração para o UnrealIRCd 6 - * OBSERVAÇÃO: Este arquivo utiliza a tradução Português do Brasil (pt-br). - * - * Apenas copie este arquivo para seu diretório conf/ e renomeie-o para - * 'unrealircd.conf' e siga este arquivo de configuração linha a linha (e altere-o!) - * - * Importante: Todas as linhas, exceto { e } terminam com ; - * Isto é muito importante, visto que se você esquecer um ; em algum lugar, - * a checagem do arquivo de configuração irá criticar e o arquivo não será processado! - * Se esta é sua primeira experiência com o arquivo de configuração do UnrealIRCd - * então nós recomendamos fortemente que você se dedique um pouco para ler sobre a sintaxe, - * isto levará apenas alguns minutos e o ajudará consideravelmente: - * https://www.unrealircd.org/docs/Configuration#Configuration_file_syntax - * - * Documentação completa do UnrealIRCd 6 (bem extensa!): - * https://www.unrealircd.org/docs/UnrealIRCd_6_documentation - * - * Questões Frequentes: - * https://www.unrealircd.org/docs/FAQ - * - */ - -/* Este é um comentário, todo o texto aqui será ignorado (comentário de tipo #1) */ -// Este também é um comentário, e esta linha será ignorada (comentário de tipo #2) -# Este também é um comentário, e novamente esta linha será ignorada (comentário de tipo #3) - -/* O UnrealIRCd faz um intenso uso dos Módulos, que permitem que você personalize completamente - * o conjunto de recursos que você deseja habilitar no UnrealIRCd. - * Veja: https://www.unrealircd.org/docs/Modules - * - * Utilizando o include abaixo, nós instruímos o IRCd a ler o arquivo - * 'modules.default.conf' que carregará mais de 150 módulos - * que vem com o UnrealIRCd. Em outras palavras: Isso simplesmente irá carregar - * todos os recursos disponíveis no UnrealIRCd. - * Se você está configurando o UnrealIRCd pela primeira vez, nós sugerimos que você - * o use. Então, quando tudo estiver configurado e rodando, você poderá retornar - * e personalizar a lista (se você desejar). - */ -include "modules.default.conf"; - -/* Agora vamos incluir alguns outros arquivos de configuração também: - * - help/help.conf para nosso sistema de ajuda /HELPOP - * - badwords.conf para os modos de usuário e canal +G - * - spamfilter.conf como um exemplo para filtragem de spam - * (comentado) - * - operclass.default.conf contém algumas boas classes de operadores que - * você pode usar em seus blocos de operadores. - */ -include "help/help.conf"; -include "badwords.conf"; -//include "spamfilter.conf"; -include "operclass.default.conf"; -include "snomasks.default.conf"; - -/* Carrega por padrão o módulo de cloaking em SHA256 (implementado em 2021): */ -loadmodule "cloak_sha256"; -/* Ou carrega o antigo módulo de clocking em MD5 que veio do UnrealIRCd 3.2/4/5: */ -//loadmodule "cloak_md5"; - -/* Este é o bloco me { } que basicamente diz quem somos. - * Ele define o nome do nosso servidor, algumas linhas informativas e um "sid" único. - * O id do servidor (sid) precisa iniciar com um dígito numérico seguido por dois dígitos numéricos - * ou alfanuméricos de A à Z. O sid precisa ser único para a sua rede de IRC (cada servidor - * deve ter seu próprio sid). - */ -me { - name "irc.exemplo.org"; - info "Servidor ExemploNET"; - sid "001"; -} - -/* O bloco admin { } define quem os usuário verão quando eles digitarem /ADMIN. - * Normalmente contém infomações de como eles podem contatar o administrador. - */ -admin { - "Bob Smith"; - "bob"; - "email@exemplo.org"; -} - -/* Clientes e servidores são colocados no bloco class { }, e os definimos aqui. - * Blocos Class consistem nos seguintes itens: - * - pingfreq: com que frequência será efetuado ping em um usuário / servidor (em segundos) - * - connfreq: quantas vezes tentamos nos conectar a este servidor (em segundos) - * - sendq: o tamanho máximo da fila para uma conexão - * - recvq: o recebimento máximo da fila para uma conexão (controle de flood) - */ - -/* Classe Client padrão, com valores de limites aceitáveis */ -class clients -{ - pingfreq 90; - maxclients 1000; - sendq 200k; - recvq 8000; -} - -/* Uma classe Especial para IRCOps com valores de limites mais altos */ -class opers -{ - pingfreq 90; - maxclients 50; - sendq 1M; - recvq 8000; -} - -/* Classe Server padrão, com valores de limites aceitáveis */ -class servers -{ - pingfreq 60; - connfreq 15; /* tenta se conectar a cada 15 segundos */ - maxclients 10; /* máximo de servidores */ - sendq 20M; -} - -/* Blocos allow definem quais classe clients podem se conectar a este servidor. - * Isto permite que você adicione uma senha ao servidor ou restrinja o acesso ao servidor - * apenas por IPs específicos. Você também pode configurar o máximo de conexões - * permitidas por IP. - * Veja também: https://www.unrealircd.org/docs/Allow_block - */ - -/* Permite todos entrarem, mas apenas 3 conexões simultâneas por IP */ -allow { - mask *; - class clients; - maxperip 3; -} - -/* Exemplo de um bloco especial allow em um IP específico: - * Requer que usuários neste IP conectem por uma senha. Se a senha - * estiver correta, então permite 20 conexões simultâneas deste IP. - */ -allow { - mask 192.0.2.1; - class clients; - password "alguma_senha_secreta"; - maxperip 20; -} - -/* Blocos oper definem os Operadores de IRC. - * Operadores de IRC são pessoas com "privilégios extras" comparado a outros, - * eles podem por exemplo dar /KILL (derrubar) outras pessoas, iniciar uma conexão com server, - * dar /JOIN (entrar) em canais ainda que eles estejam banidos, etc. - * - * Para mais informações sobre como se tornar um IRCOp e como executar - * tarefas administrativas, veja: https://www.unrealircd.org/docs/IRCOp_guide - * - * Para obter mais detalhes sobre o bloco oper { } , veja - * https://www.unrealircd.org/docs/Oper_block - */ - -/* Aqui está um exemplo de um bloco oper para o 'bobsmith' - * Você DEVE alterar isto!! - */ -oper bobsmith { - class opers; - mask *@*; - - /* Tecnicamente você pode deixar as senhas de oper em texto puro no arquivo de configuração, mas - * isto é ALTAMENTE DESENCORAJADO. No lugar disso, você deve gerar uma senha hasheada: - * No *NIX, execute: ./unrealircd mkpasswd - * No Windows, execute: "C:\Program Files\UnrealIRCd 6\bin\unrealircdctl" mkpasswd - * ... e então cole a senha no campo abaixo: - */ - password "$argon2id..etc.."; - /* Veja https://www.unrealircd.org/docs/Authentication_types para - * mais informações, incluindo formas melhores de autenticação - * como por exemplo o 'certfp', e como gerar hashes no Windows. - */ - - /* Permissões de oper são definidos no bloco 'operclass'. - * Veja https://www.unrealircd.org/docs/Operclass_block - * O UnrealIRCd vem com um número padrão de blocos, leia - * o artigo acima para ver a lista completa. Nós escolhemos o 'netadmin' aqui. - */ - operclass netadmin; - swhois "é o Administrador da Rede"; - vhost netadmin.exemplo.org; -} - -/* Blocos listen definem as portas onde o servidor irá escutar. - * Em outras palavras: as portas que os clientes e servidores podem usar - * para se conectar a este servidor. - * - * Sintaxe: - * listen { - * ip ; - * port ; - * options { - * ; - * } - * } - */ - -/* Porta padrão 6667 do IRC */ -listen { - ip *; - port 6667; -} - -/* Porta padrão 6697 do IRC sob tunel SSL/TLS */ -listen { - ip *; - port 6697; - options { tls; } -} - -/* Porta especial padrão para uso de servidores sob tunel SSL/TLS para vincular a outros servidores */ -listen { - ip *; - port 6900; - options { tls; serversonly; } -} - -/* OBSERVAÇÃO: Se você está em uma shell IRCd com múltiplos IPs e você usa - * os blocos listen { } acima, então você provavelmente receberá o erro - * 'Address already in use' e o IRCd não iniciará. - * Isto significa que você DEVE colocar em escuta um IP específico no lugar do '*', como por exemplo: - * listen { ip 1.2.3.4; port 6667; } - * Claro, substituindo o IP pelo IP que foi fornecido a você. - */ - -/* - * Blocos link permitem que você vincule múltiplos servidores uns aos outros para formar uma rede. - * Veja https://www.unrealircd.org/docs/Tutorial:_Linking_servers - */ -link hub.exemplo.org -{ - incoming { - mask *@alguma_coisa; - } - - outgoing { - bind-ip *; /* ou especificar um IP */ - hostname hub.exemplo.org; - port 6900; - options { tls; } - } - - /* Nós usamos a impressão digital SPKI do outro servidor para autenticação. - * Abra uma shell no OUTRO SERVIDOR e execute o comando abaixo para obter a impressão digital: - * No *NIX, execute: ./unrealircd spkifp - * No Windows, execute: "C:\Program Files\UnrealIRCd 6\bin\unrealircdctl" spkifp - */ - password "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUV=" { spkifp; } - - class servers; -} - -/* O Bloco link para o services é muito mais simples. - * Para mais informações sobre o que são o Services, - * leia https://www.unrealircd.org/docs/Services - */ -link services.exemplo.org -{ - incoming { - mask 127.0.0.1; - } - - password "me_altere"; - - class servers; -} - -/* U-lines dão a outros servidores (ainda) mais poderes/comandos. - * Se você usa o services, você DEVE adicioná-los aqui. Você deve adicionar o - * nome do servidor do services no bloco ulines { } no arquivo de configuração - * em todo servidor UnrealIRCd da sua rede. - * IMPORTANTE: Jamais insira o nome do servidor do UnrealIRCd aqui, - * é apenas para o Services! - */ -ulines { - services.exemplo.org; -} - -/* Aqui você pode adicionar uma senha (apenas para IRCOps) para os comandos /DIE e /RESTART. - * Isto para que se tenha uma pouco mais de proteção contra reinicio acidental - * do servidor e ele seja derrubado inadvertidante. - */ -drpass { - restart "reiniciar"; - die "matar"; -} - -/* O bloco log define o que deve ser registrado (logado) e em qual arquivo. - * Leia também https://www.unrealircd.org/docs/Log_block - */ - -/* Este é um bom padrão, ele registra tudo, exceto - * coisas de depuração e comandos join/part/kick. - */ -log { - source { - all; - !debug; - !join.LOCAL_CLIENT_JOIN; - !join.REMOTE_CLIENT_JOIN; - !part.LOCAL_CLIENT_PART; - !part.REMOTE_CLIENT_PART; - !kick.LOCAL_CLIENT_KICK; - !kick.REMOTE_CLIENT_KICK; - } - destination { - file "ircd.log" { maxsize 100M; } - } -} - -/* In addition to regular logging, also add a JSON log file. - * This includes lots of information about every event so is great - * for auditing purposes and is machine readable. It is, however - * less readable for humans. - */ -log { - source { - all; - !debug; - !join.LOCAL_CLIENT_JOIN; - !join.REMOTE_CLIENT_JOIN; - !part.LOCAL_CLIENT_PART; - !part.REMOTE_CLIENT_PART; - !kick.LOCAL_CLIENT_KICK; - !kick.REMOTE_CLIENT_KICK; - } - destination { - file "ircd.json.log" { maxsize 250M; type json; } - } -} - -/* Com o "aliases" você pode criar um atalho como /ALGUMACOISA para enviar uma mensagem para - * algum usuário ou bot. Eles são usados normalmente por services. - * - * Nós temos um arquivo com um número pré-definido de atalhos, confira o diretório alias/ . - * Como exemplo, aqui nós incluímos todos os atalhos utilizados pelo services anope. - */ -include "aliases/anope.conf"; - -/* Bane nicks para que eles não sejam utilizados por usuários comuns */ -ban nick { - mask "*C*h*a*n*S*e*r*v*"; - reason "Reservado para Services"; -} - -/* Bane um IP - * Observe que você normalmente usa /KLINE, /GLINE e /ZLINE para isto. - */ -ban ip { - mask 195.86.232.81; - reason "Te odeio"; -} - -/* Bane um servidor - se observarmos este servidor vinculado a alguém, então o expulsaremos */ -ban server { - mask pedro.usp.br; - reason "Caia fora daqui."; -} - -/* Bane um usuário - mas você normalmente usa /KLINE ou /GLINE para isso */ -ban user { - mask *usuariotroll@*.saturn.bbn.com; - reason "Idiota"; -} - -/* Este tipo de banimento permite que clientes sejam banidos com base no seu nome real (realname) - * ou campo 'gecos'. - */ -ban realname { - mask "Time Swat"; - reason "FORCAOSTENSIVA"; -} - -ban realname { - mask "sub7server"; - reason "sub7"; -} - -/* Exceções de banimento e TKL. Permite que você crie exceções a usuários/IPs a um - * KLINE, GLINE, etc. - * Se você é um IRCOp com IP estático (e não há ninguém não confiável utilizando este IP), - * então nós sugerimos que você seja adicionado aqui. Desta forma, você sempre poderá entrar - * mesmo se acidentalmente você colocar um *LINE em si mesmo. - */ - -/* Exceções de banimento de tipo 'all' protegem você de GLINE, GZLINE, QLINE, SHUN */ -except ban { - mask *@192.0.2.1; - type all; -} - -/* Isto permite que clientes do IRCCloud não tenham restrição de IP por conexão - * e também cria exceção a eles de flood por conexão. - */ -except ban { - mask *.irccloud.com; - type { maxperip; connect-flood; } -} - -/* deny dcc permite você possa banir nomes de arquivos transferidos por DCC */ -deny dcc { - filename "*sub7*"; - reason "Possível Virus Sub7"; -} - -/* deny channel permite a você banir um canal (por máscara) */ -deny channel { - channel "*warez*"; - reason "Warez é ilegal"; - class "clients"; -} - -/* VHosts (Hosts Virtuais) permite aos usuários adquirirem um vhost diferente. - * Veja https://www.unrealircd.org/docs/Vhost_block - */ - -/* Exemplo de vhost que você pode usar. No IRC digite: /VHOST teste teste - * OBSERVAÇÃO: apenas pessoas com o host 'unrealircd.com' podem usá-lo, então - * tenha certeza de modificar o vhost::mask antes de seu teste. - */ -vhost { - vhost eu.odeio.microsefrs.com; - mask *@unrealircd.com; - login "teste"; - password "teste"; -} - -/* Blocos blacklist irão consultar um serviço externo de blacklist - * sempre que um usuário se conectar, para saber se seu endereço de IP é conhecido - * por ataques de drone, como uma máquina hackeada, etc. - * Documentação: https://www.unrealircd.org/docs/Blacklist_block - * Ou apenas dê uma olhada nos blocos abaixo. - */ - -/* DroneBL é provavelmente o serviço de blacklist mais popular usada por servidores de IRC. - * Veja https://dronebl.org/ para ler a documentação e - * o significado dos tipos de resposta. No momento em que este arquivo foi escrito, nós usamos os tipos: - * 3: IRC Drone, 5: Flooder, 6: Drone ou bot de spam desconhecido, - * 7: Drone de DDoS, 8: Proxy SOCKS, 9: Proxy HTTP, 10: ProxyChain, - * 11: Proxy de página web, 12: Resolvedor de DNS aberto, 13: Atacantes de força bruta, - * 14: Proxy Wingate público, 15: Roteador/Gateway comprometido, - * 16: Virus que tentam conseguir root. - */ -blacklist dronebl { - dns { - name dnsbl.dronebl.org; - type record; - reply { 3; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; } - } - action gline; - ban-time 24h; - reason "Proxy/Drone detectado. Confira https://dronebl.org/lookup?ip=$ip para detalhes."; -} - -/* EFnetRBL, veja https://rbl.efnetrbl.org/ para ler a documentação - * e o significado dos tipos de resposta. - * No momento em que este arquivo foi escrito: 1 é proxy público, 4 é TOR, 5 é drones/flooders. - * - * OBSERVAÇÃO: Se você deseja permitir proxies TOR no seu servidor, então - * você precisa remover o '4;' abaixo da seção reply. - */ -blacklist efnetrbl { - dns { - name rbl.efnetrbl.org; - type record; - reply { 1; 4; 5; } - } - action gline; - ban-time 24h; - reason "Proxy/Drone/TOR detectado. Confira https://rbl.efnetrbl.org/?i=$ip para detalhes."; -} - -/* Você pode incluir outros arquivos de configuração */ -/* include "klines.conf"; */ - -/* Configuração da Rede */ -set { - network-name "ExemploNET"; - default-server "irc.exemplo.org"; - services-server "services.exemplo.org"; - stats-server "stats.exemplo.org"; - help-channel "#ajuda"; - cloak-prefix "Clk"; - prefix-quit "Saindo"; - - /* Chaves cloak devem ser a mesma em todos os servidores da rede. - * Eles são usados para geração de hosts mascarados e devem ser mantidos em segredo. - * As chaves devem ser 3 strings (ou mais) de 80 caracteres randômicos - * e devem se constituir de letras minúsculas (a-z), maiúsculas (A-Z) e números (0-9). - * No *NIX, você pode executar './unrealircd gencloak' na sua shell para que o - * UnrealIRCd gere 3 strings randômicas para você. - * No Windows, você pode executar "C:\Program Files\UnrealIRCd 6\bin\unrealircdctl" gencloak - */ - cloak-keys { - "Oozahho1raezoh0iMee4ohvegaifahv5xaepeitaich9tahdiquaid0geecipahdauVaij3zieph4ahi"; - "adicione a outra"; - "adicione a outra"; - } -} - -/* Configurações específicas do servidor */ - -set { - kline-address 'definir.o.endereco.de.email'; /* e-mail ou URL exibido quando um usuário é banido */ - modes-on-connect "+ixw"; /* quando os usuários conectam, esses modos de usuário é atribuído a eles */ - modes-on-oper "+xws"; /* quado alguém se torna IRCOp, esses modos de usuário é atribuído a ele */ - modes-on-join "+nt"; /* modos de canal padrão, quando um novo canal é criado */ - oper-auto-join "#opers"; /* IRCOps entram automaticamente neste canal */ - options { - hide-ulines; /* esconde U-lines do /MAP e /LINKS */ - show-connect-info; /* exibe a mensagem "looking up your hostname" ao se conectar */ - } - - maxchannelsperuser 10; /* número máximo de canais que um usuário pode entrar com /JOIN */ - - /* O tempo mínimo que um usuário precisa estar conectado antes de ser permitido - * utilizar a mensagem de QUIT. Isto irá ajudar no combate a SPAM. - */ - anti-spam-quit-message-time 10s; - - /* Ou simplesmente defina um quit estático, de forma que qualquer motivo de /QUIT seja ignorado */ - /* static-quit "Cliente saindo"; */ - - /* static-part faz com que o mesmo acima aconteça ao comando /PART */ - /* static-part yes; */ - - /* Proteção contra Flood: - * Há uma série de configurações para isso, e a maioria delas tem bons padrões. - * Veja https://www.unrealircd.org/docs/Set_block#set::anti-flood - */ - anti-flood { - } - - /* Configurações para filtragem de SPAM */ - spamfilter { - ban-time 1d; /* duração padrão de um ban *LINE definido pelo filtro de spam */ - ban-reason "Spam/Propaganda"; /* motivo padrão */ - virus-help-channel "#help"; /* canal utilizado para usar em uma ação de 'virus de canal' */ - /* except "#help"; exceção de canal ao filtro de spam */ - } - - /* Restringindo certos comandos - * Veja https://www.unrealircd.org/docs/Set_block#set::restrict-commands - */ - restrict-commands { - list { - except { - connect-time 60; /* após 60 segundos, o usuário pode usar o comando /LIST */ - identified yes; /* ou imediatamente, se estiver identificado ao services */ - reputation-score 24; /* ou se tiver um score de reputação maior ou igual a 24 */ - } - } - invite { - except { - connect-time 120; - identified yes; - reputation-score 24; - } - } - /* Somando a possibilidade de restringir qualquer comando, - * também existem 4 tipos especiais - * que você pode restringir. Eles são "private-message", - * "private-notice", "channel-message" e "channel-notice". - * Eles estão todos comentados neste exemplo: - */ - //private-message { - // except { connect-time 10; } - //} - //private-notice { - // except { connect-time 10; } - //} - } -} - -/* - * A seguir será configurado o limite de conexão para "unknown users". - * - * Quando o UnrealIRCd detecta um número elevado de usuários conectando de um endereço IP - * que nunca viu antes, então conexões do novo IP são rejeitadas quando estiverem - * acima da taxa abaixo especificada. Por exemplo, na taxa 10:60 apenas 10 usuários por minuto podem conectar - * por este IP que nunca foi visto antes. Endereços de IP conhecidos sempre podem entrar, - * independente da taxa definida. O mesmo para usuários que se conectam via SASL. - * - * Leia também https://www.unrealircd.org/docs/Connthrottle para detalhes. - * Ou apenas continue lendo a configuração abaixo: - */ - -set { - connthrottle { - /* Primeiro nós configuramos quais usuários serão excluídos - * das restrições. Estes usuários sempre conseguirão acessar. - * Por padrão, entra como exceção usuários identificados com o services - * com um score igual ou maior a 24. Um score 24 significa que - * este IP foi conectado a esta rede por pelo menos 2 horas em algum momento - * no mês passado (ou no mínimo por 1 hora se registrado). - * Nós também permitimos usuários que se identificaram através do services via - * SASL para passar por cima destas restrições. - */ - except { - reputation-score 24; - identified yes; - /* Para mais informações, leia - * https://www.unrealircd.org/docs/Mask_item - */ - } - - /* Novos usuários são todos os usuários que não pertencem - * ao grupo known-users. Eles são considerados "novos" e no - * caso de um número elevado de novos usuários se conectando, - * eles serão sujeiros ao limite de taxa de conexão. - * Por padrão a taxa é 20 novos usuários locais por minuto - * e 30 novos usuários globais por minuto. - */ - new-users { - local-throttle 20:60; - global-throttle 30:60; - } - - /* Esta seção configura quando este módulo não será ativado. - * As configurações padrão desabilitarão o módulo quando: - * - O módulo reputation esteja em execução a menos de - * uma semana. Se estiver rodando há menos de 1 semana, então ainda temos - * dados insuficientes para considerar quem é um "known user" (usuário conhecido). - * - O servidor acabou de ser inicializado (primeiros 3 minutos). - */ - disabled-when { - reputation-gathering 1w; - start-delay 3m; - } - } -} - -/* HISTÓRICO DE CANAL: - * UnrealIRCd possui modo de canal +H que pode ser usado pelos usuários para recuperar - * o histórico do canal antes deles terem entrado. Para informações gerais - * sobre esta funcionalidade, leia https://www.unrealircd.org/docs/Channel_history - * - * Os limites do histórico podem ser configurados pelo set::history. Os padrões são - * provavelmente bons para você, mas se você estiver em um sistema com pouca memória - * ou tem centenas de canais, então você pode querer verificar esses padrões novamente. Leia - * https://www.unrealircd.org/docs/Set_block#set::history - * para ver as opções disponíveis. - * - * Além disso, você pode definir um "histórico de canal persistente", o que - * significa que o histórico do canal é armazenado de modo criptografado no disco e é preservado - * entre os reinícios do servidor. Leia - * https://www.unrealircd.org/docs/Set_block#Persistent_channel_history - * A funcionalidade de histórico persistente NÃO é habilitado por padrão porque você - * precisa configurar o Bloco secret { } para ele antes. A seguir um exemplo simples - * de senhas armazenadas diretamente no arquivo de configuração: - * Para obter uma melhor segurança, leia https://www.unrealircd.org/docs/Secret_block - * como formas alternativas para não armazenar senhas diretamente no arquivo de configuração. - */ -//secret historydb { password "algumasenha"; } -//set { history { channel { persist yes; db-secret "historydb"; } } } - -/* Finalmente, você pode querer ter um MOTD (Mensagem do Dia), isto pode ser - * feito criando um arquivo de texto 'ircd.motd' no seu diretório conf/ . - * O texto dentro deste arquivo será exibido aos usuários ao se conectarem. - * Para mais informações, veja https://www.unrealircd.org/docs/MOTD_and_Rules - */ - -/* - * Problemas ou precisa de ajuda? - * 1) https://www.unrealircd.org/docs/ - * 2) https://www.unrealircd.org/docs/FAQ <- responde a 80% das suas perguntas! - * 3) Se ainda assim você está enfrentando problemas, você pode obter ajuda em: - * - Fóruns: https://forums.unrealircd.org/ - * - IRC: irc.unrealircd.org (SSL na porta 6697) / #unreal-support - * Observe que pedimos que você leia a documentação e as perguntas frequentes (FAQ) primeiro! - */ diff --git a/docs/services/irc/examples/unrealircd/examples/example.tr.conf b/docs/services/irc/examples/unrealircd/examples/example.tr.conf deleted file mode 100644 index 1b264980..00000000 --- a/docs/services/irc/examples/unrealircd/examples/example.tr.conf +++ /dev/null @@ -1,687 +0,0 @@ -/* UnrealIRCd 6 için yapılandırma dosyası - * - * Türkçe Çeviri: Diablo - (Serkan Sepetçi) - * İletişim: irc.turkirc.net:6667 - diablo@unrealircd.org - * - * Bu dosyayı conf/ dizininize kopyalayın ve 'unrealircd.conf' olarak adlandırın. - * - * Aceleniz varsa CTRL+F tuşlarına basıp şunu yapabilirsiniz: BUNU DEĞİŞTİRİN - * Değiştirilmesi gereken maddeler bu iki kelimeyle belirtilir. - * Ancak aslında dosyayı satır satır incelemenizi ve gereken yerde - * düzenlemenizi öneririz. Böylece tüm temel öğeleri ve bunların nasıl - * ayarlandığını görebilirsiniz. - * - * DEVAM ETMEDEN ÖNCE: - * Önemli: { ve } dışındaki tüm satırlar ; ile biter - * Bu çok önemlidir. Eğer bir şeyi yanlış yaparsanız, - * yapılandırma dosyası ayrıştırıcısı hata verecek ve - * dosya doğru şekilde çalışmayacaktır! - * - * Eğer bu UnrealIRCd yapılandırma dosyasıyla ilk deneyiminizse - * sözdizimi hakkında biraz okumanızı öneririz. - * bu yalnızca birkaç dakika sürer ve size çok yardımcı olacaktır: - * https://www.unrealircd.org/docs/Configuration#Configuration_file_syntax - * - * UnrealIRCd 6 belgeleri (çok kapsamlı!): - * https://www.unrealircd.org/docs/UnrealIRCd_6_documentation - * - * Sıkça Sorulan Sorular: - * https://www.unrealircd.org/docs/FAQ - * - */ - -/* Bu bir açıklamadır, burada tüm metin göz ardı edilir (açıklama tipi #1) */ -// Bu da bir açıklamadır, bu satır göz ardı edilir (açıklama tipi #2) -# Bu da bir açıklamadır, bu satır yine göz ardı edilir (açıklama tipi #3) - -/* UnrealIRCd yoğun modul kullanımını kolaylaştırır. UnrealIRCd'de - * etkinleştirmek istediğiniz özellikleri tamamen moduller ile aktif edebilirsiniz. - * Görmek için; https://www.unrealircd.org/docs/Modules - * - * Biz 'modules.default.conf' dosyasını okumak için IRCd talimatı altında kullanarak - * UnrealIRCd ile birlikte gelen 150'den fazla modülleri yükleyecektir. - * Başka bir deyişle: Bu sadece UnrealIRCd'de mevcut tüm özelliklerini yükleyecektir. - * İlk kez UnrealIRCd kuruyorsanız size bunu kullanmanızı öneririz. - * UnrealIRCd'yi ilk kez kuruyorsanız bunu kullanmanızı öneririz. - * Daha sonra her şey hazır olduğunda ve çalışıyorsa (eğer isterseniz) - * listeyi özelleştirmek için geri dönebilirsiniz. - */ -include "modules.default.conf"; - -/* Şimdi de diğer bazı dosyaları dahil edelim: - * - help/help.conf /HELPOP sistemi - * - badwords.conf kanal ve kullanıcı modu için +G - * - spamfilter.conf için örnek olarak spamfilter kullanımı - * (yorumlandı) - * - operclass.default.conf oper bloklarında kullanabileceğiniz - * oper sınıflarını görüntüler. - */ -include "help/help.conf"; -include "badwords.conf"; -//include "spamfilter.conf"; -include "operclass.default.conf"; -include "snomasks.default.conf"; - -/* Varsayılan gizleme modülünü yükleyin (2021'den itibaren): */ -loadmodule "cloak_sha256"; -/* Veya eski modülü UnrealIRCd 3.2/4/5'ten yükleyin: */ -//loadmodule "cloak_md5"; - -// BUNU DEĞİŞTİRİN ('ad' ve 'bilgi'): -/* me { } bloğu genelde kim olduğumuzu belirtir. - * Sunucumuz için isim, birkaç satır bazı bilgileri belirler "sid". - * Sunucu kimliği (sid) iki basamağı veya harf tarafından izlenen bir rakam ile - * başlamalıdır. Sid IRC ağı için benzersiz olmalıdır (her sunucu için - * kendi sid olmalıdır). İlk sunucu için 001 kullanılması uygundur. - */ -me { - name "irc.example.org"; - info "ExampleNET Server"; - sid "001"; -} - -// BUNU DEĞİŞTİRİN: -/* admin { } bloğu /ADMIN sorgusunda kullanıcılara görüntülenecek metni belirler. - * Normalde yöneticiye ulaşma konusunda bilgi içerir. - */ -admin { - "Bob Smith"; - "bob"; - "email@example.org"; -} - -/* Kullanıcılar ve sunucular için class { } bloğu belirtilir. - * Class blokları aşağıdaki işlemlerden oluşur: - * - pingfreq: kullanıcı/sunucu için ping'ler arası zaman belirtir (saniyede) - * - connfreq: sunucuya bağlanmaya çalıştığınızda tekrar için zaman belirtir (saniyede) - * - sendq: bir bağlantı için maksimum veri boyutu - * - recvq: bir bağlantı için maksimum alınan veri boyutu (flood kontrol) - */ - -/* Kullanıcılar için varsayılan class ayarları */ -class clients -{ - pingfreq 90; - maxclients 1000; - sendq 200k; - recvq 8000; -} - -/* IRCOp'lar için varsaylan yüksek limitli özel class ayarları */ -class opers -{ - pingfreq 90; - maxclients 50; - sendq 1M; - recvq 8000; -} - -/* Sunucular için varsayılan class ayarları */ -class servers -{ - pingfreq 60; - connfreq 15; /* Her 15 saniyede bir bağlanmayı dener */ - maxclients 10; /* maksimum kullanıcı */ - sendq 20M; -} - -/* Allow blockları sunucunuza kimlerin bağlanabileceğini belirtir. - * Bir sunucu şifresi eklenebilir veya belirlitilen bir IP adresi için - * giriş izini verilebilir. Ayrıca IP başına ne kadar bağlantıya izin - * verileceğini belirtir. - * Görmeniz için: https://www.unrealircd.org/docs/Allow_block - */ - -/* IP başına sadece 3 bağlantı izini verir */ -allow { - mask *; - class clients; - maxperip 3; -} - -/* Örnek olarak özel bir IP bloğu izini: - * Bu IP bir şifre ile bağlantı yapması olduğunu gerektirir. - * Şifre doğru ise o zaman bu IP 20 bağlantıya izin verecektir. - */ -// allow { -// mask 192.0.2.1; -// class clients; -// password "somesecretpasswd"; -// maxperip 20; -// } - -/* Oper bloğu, IRC Operatorleri tanımlar. - * IRC Operatörler, diğer kullanıcılara göre "ekstra haklara" sahip kullanıcılardır. - * örneğin diğer kullanıcılara /KILL uygulayabilmesi, sunucu birleştirmesinin başlatılması, - * /JOIN yaptığı odalardan banlansa bile tekrar giriş yapabilmesi, vs. - * - * IRCOp olmak ve nasıl Admin olunacağı hakkında daha fazla bilgi için - * https://www.unrealircd.org/docs/IRCOp_guide - * - * Oper {} bloğunun kendisi ile ilgili ayrıntıları görmeniz için - * https://www.unrealircd.org/docs/Oper_block - */ - -/* İşte 'bobsmith' için örnek bir oper bloğu - * BUNU DEĞİŞTİRMELİSİN!! (oper adı ve şifre) - */ -oper bobsmith { - class opers; - mask *@*; - - /* Teknik olarak oper şifrelerini conf'a düz metin olarak koyabilirsiniz, ancak bu - * KESİNLİKLE ÖNERİLEN bir durum değildir. Bunun yerine bir şifre karması oluşturmalısınız: - * *NIX'te şunu çalıştırın: ./unrealircd mkpasswd - * Windows'ta şunu çalıştırın: "C:\Program Files\UnrealIRCd 6\bin\unrealircdctl" mkpasswd - * .. ve ardından sonucu aşağıya yapıştırın: - */ - - password "$argon2id..etc.."; - - /* 'Certfp' gibi daha iyi kimlik doğrulama türleri ve Windows'ta - * karmaların nasıl oluşturulacağı da dahil olmak üzere daha fazla bilgi için - * https://www.unrealircd.org/docs/Authentication_types adresine bakın. - */ - - /* Oper izinleri bir "operclass 'bloğunda tanımlanır. - * Görmeniz için: https://www.unrealircd.org/docs/Operclass_block - * UnrealIRCd varsayılan bloklar makalesi için, - * tam listesine bakınız. Buradan 'netadmin' seçiyoruz. - */ - - operclass netadmin; - swhois "is a Network Administrator"; - vhost netadmin.example.org; -} - -/* Listen blokları sunucu portu için gereken bağlantı noktalarını tanımlar. - * Diğer bir deyişle: Bu portlar kullanıcılar ve serverlar için - * sunucuya bağlantı kurmasını sağlar. - * - * Kullanımı: - * listen { - * ip ; - * port ; - * options { - * ; - * } - * } - */ - -/* Standard IRC port 6667 */ -listen { - ip *; - port 6667; -} - -/* Standard IRC SSL/TLS port 6697 */ -listen { - ip *; - port 6697; - options { tls; } -} - -/* Özel SSL/TLS sadece sunucuları bağlamak için port */ -listen { - ip *; - port 6900; - options { tls; serversonly; } -} - -/* DiKKAT: Eğer bir çok IP barındıran bir IRCd Shell kullanıyorsanız - * logunuzda olası 'Address already in use' hatasını alacaksınız - * ve ircd başlamayacaktır. - * Bunun anlamı '*' yerine belirli bir IP yazmanız GEREKİR anlamına gelir: - * listen 1.2.3.4:6667; - * Açıkçası, IP yi önceden koyduğunuz IP ile değiştirin. - */ - -/* - * Link blockları bir ağ oluşturmak için birden fazla sunucu bağlamaya izin verir. - * Görmek için: https://www.unrealircd.org/docs/Tutorial:_Linking_servers - */ -link hub.example.org -{ - incoming { - mask *@something; - } - - outgoing { - bind-ip *; /* veya açıkça bir IP */ - hostname hub.example.org; - port 6900; - options { tls; } - } - - /* Kimlik doğrulaması için diğer sunucunun SPKI parmak izini kullanıyoruz. - * Kullanmamız için diğer tarafda './unrealircd spkifp' uygulayıp çalıştırıyoruz. - * *NIX'te şunu çalıştırın: ./unrealircd spkifp - * Windows'ta şunu çalıştırın: "C:\Program Files\UnrealIRCd 6\bin\unrealircdctl" spkifp - */ - password "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUV=" { spkifp; } - - class servers; -} - -/* Servis'ler için bağlantı bloğu genellikle çok daha basittir. - * Servis'lerin ne olduğu hakkında daha fazla bilgi için, - * https://www.unrealircd.org/docs/Services - */ -link services.example.org -{ - incoming { - mask 127.0.0.1; - } - - password "changemeplease"; - - class servers; -} - -/* U-lines satırları sunuculara daha güç/komut kazandırır. - * Eğer hizmetlerini kullanmak istiyorsanız onları buraya eklemeniz gerekir. - * ÖNEMLİ: ASLA buraya (normal) UnrealIRCd sunucunun adını yazmayınız!!! - */ -ulines { - services.example.org; -} - -/* Bu blok /DIE ve /RESTART için şifre tanımlamanızı sağlar. Sadece IRCOp'lar içindir. - * Bu genelde kazara sunucuyu yeniden başlatma ve kapanmasına karşı biraz - * koruma sağlamak içindir. - */ -drpass { - restart "restart"; - die "die"; -} - -/* Bu log bloğu hangi dosyaya ve nelerin olması gerektiğini tanımlar. - * Görmeniz için: https://www.unrealircd.org/docs/Log_block - */ - -/* Bu iyi bir varsayılandır, hata ayıklama ve - * join/part/kick dışındaki her şeyi günlüğe kaydeder. - */ -log { - source { - all; - !debug; - !join.LOCAL_CLIENT_JOIN; - !join.REMOTE_CLIENT_JOIN; - !part.LOCAL_CLIENT_PART; - !part.REMOTE_CLIENT_PART; - !kick.LOCAL_CLIENT_KICK; - !kick.REMOTE_CLIENT_KICK; - } - destination { - file "ircd.log" { maxsize 100M; } - } -} - -/* Düzenli günlük kaydına ek olarak bir JSON günlük dosyası ekleyin. - * Bu her olayla ilgili birçok bilgi içerir dolayısıyla denetim amaçları - * için mükemmeldir ve makine tarafından okunabilir. Ancak insanlar için daha az okunabilir. - */ -log { - source { - all; - !debug; - !join.LOCAL_CLIENT_JOIN; - !join.REMOTE_CLIENT_JOIN; - !part.LOCAL_CLIENT_PART; - !part.REMOTE_CLIENT_PART; - !kick.LOCAL_CLIENT_KICK; - !kick.REMOTE_CLIENT_KICK; - } - destination { - file "ircd.json.log" { maxsize 250M; type json; } - } -} - -/* Bazı kullanıcılara veya botlara bir mesaj göndermek için "aliases" - * takma ad oluşturmanızı sağlar. Genellikle servisler için kullanılır. - * - * Biz önceden ayarlanmış bir takma adı dosyaları dizini oluşturduk, alias/ dizini kontrol ediniz. - * Örnek olarak, burada anope servisler ve kullanılan tüm diğer servisler adları bulunmaktadır. - */ -include "aliases/anope.conf"; - -/* Ban nick bloğu bir nickin sunucuda kullanımını yasaklamanıza olanak sağlar */ -// ban nick { -// mask "*C*h*a*n*S*e*r*v*"; -// reason "Servisler için ayrılmış"; -// } - -/* Ban ip. - * Normalde bunun için /KLINE, /GLINE ve /ZLINE kullanıldığını unutmayınız. - */ -// ban ip { -// mask 195.86.232.81; -// reason "Senden nefret ediyorum"; -// } - -/* Ban server - bir sunucunun bağlanmasını devredışı kılar */ -// ban server { -// mask eris.berkeley.edu; -// reason "Defol buradan."; -// } - -/* Ban user - normalde /KLINE veya /GLINE kullanıldığını unutmayınız */ -// ban user { -// mask *tirc@*.saturn.bbn.com; -// reason "Aptal"; -// } - -/* Ban realname bloğu bir kullanıcıyı, GECOS kısmı esas alınarak - * banlamanıza olanak sağlar. - */ -// ban realname { -// mask "Swat Team"; -// reason "mIRKFORCE"; -// } - -// ban realname { -// mask "sub7server"; -// reason "sub7"; -// } - -/* Ban ve TKL istisnaları. Kullanıcıları / makineleri gözetmeksizin - * KLINE, GLINE, gibi banlardan muaf tutmanıza olanak sağlar. - * Eğer statik IP (ve bu IP üzerinde güvenilmeyen kişiler) ile bir IRCOp - * iseniz o zaman kendinizi burada eklemenizi öneririz. Yanlışlıkla kendinize - * bir *LINE ban koyarsanız bile yinede muaf tutulacaksınız. - */ - -/* except ban bloğu, sizi 'tüm' GLINE, GZLINE, QLINE, SHUN gibi banlardan koruyacaktır */ -// except ban { -// mask *@192.0.2.1; -// type all; -// } - -/* This allows IRCCloud connections in without maxperip restrictions - * and also exempt them from connect-flood throttling. - */ -except ban { - mask *.irccloud.com; - type { maxperip; connect-flood; } -} - -/* Deny dcc bloğu, sunucu üzerinden DCC yoluyla dosya gönderilmesine izin vermeyecektir */ -// deny dcc { -// filename "*sub7*"; -// reason "Possible Sub7 Virus"; -// } - -/* Deny channel bloğu, kullanıcıların belirtilen kanallara girmesini engeller */ -// deny channel { -// channel "*warez*"; -// reason "Warez is illegal"; -// class "clients"; -// } - -/* VHosts (Virtual Hosts) bloğu, kullanıcının yeni bir host alabilmesine olanak sağlar. - * Görmeniz için; https://www.unrealircd.org/docs/Vhost_block - */ - -/* Kullanabileceğiniz örnek vhost. IRC tipi: /VHOST test test - * DiKKAT: Güvenlik açısından aşağıdaki vhost::mask yönergesinde - * maske 'unrealircd.com' olarak belirlenmiştir. - */ -// vhost { -// vhost i.hate.microsefrs.com; -// mask *@unrealircd.com; -// login "test"; -// password "test"; -// } - -/* Blacklist blokları, bir kullanıcı bağlandığında IP adresinin drone saldırılarına - * neden olduğunu, bilinen bir saldırıya uğramış bir makine olup olmadığını görmek - * için harici bir DNS Kara Liste hizmetinden sorgulayacaktır. - * Belgeleme: https://www.unrealircd.org/docs/Blacklist_block - * veya aşağıdaki bloklar satırına bakınız. - */ - -/* DroneBL, muhtemelen IRC Sunucuları tarafından kullanılan en popüler kara liste. - * Belgeler ve cevap (reply) tiplerin anlamlarını görmek için https://dronebl.org/ - * adresine bakınız. Bu zamanda aşağıdaki cevap (reply) tiplerini kullanıyoruz: - * 3: IRC Drone, 5: Bottler, 6: Unknown spambot or drone, - * 7: DDoS Drone, 8: SOCKS Proxy, 9: HTTP Proxy, 10: ProxyChain, - * 11: Web Page Proxy, 12: Open DNS Resolver, 13: Brute force attackers, - * 14: Open Wingate Proxy, 15: Compromised router / gateway, - * 16: Autorooting worms. - */ -blacklist dronebl { - dns { - name dnsbl.dronebl.org; - type record; - reply { 3; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; } - } - action gline; - ban-time 24h; - reason "Proxy/Drone belirlendi. Ayrıntılar için https://dronebl.org/lookup?ip=$ip adresine bakınız."; -} - -/* EFnetRBL, belgeler ve cevap (reply) tiplerini görmek için https://rbl.efnetrbl.org/ - * adresine bakınız. - * Yazma sırasında: 1 is open proxy, 4 is TOR, 5 is drones/flooding. - * - * NOT: Sunucunuzda TOR proxy'lerine izin vermek istiyorsanız, - * cevap (reply) tiplerinden '4;' öğesini kaldırmanız gerekiyor. - */ -blacklist efnetrbl { - dns { - name rbl.efnetrbl.org; - type record; - reply { 1; 4; 5; } - } - action gline; - ban-time 24h; - reason "Proxy/Drone/TOR tespit edildi. Ayrıntılar için https://rbl.efnetrbl.org/?i=$ip adresine bakınız."; -} - -/* Diğer yapılandırma dosyalarını dahil edebilirsiniz */ -/* include "klines.conf"; */ - -/* Ağ yapılandırması */ -set { - // BU 4 ÖĞENİN TÜMÜNÜ DEĞİŞTİRİN: - network-name "ExampleNET"; - default-server "irc.example.org"; - services-server "services.example.org"; - stats-server "stats.example.org"; - /* Normal varsayılanlar */ - help-channel "#Help"; - cloak-prefix "Clk"; - prefix-quit "Quit"; - - /* Gizleme anahtarları ağ üzerindeki bütün sunucularda aynı olmalı. - * Bu anahtarlar maskeli hostlar yaratmak ve bunları saklamak için kullanılır. - * Anahtarlar 80 karakterlik 3 rastgele diziden oluşmalı ve küçük harf (a-z), - * büyük harf (A-Z) ve rakamlardan (0-9) meydana gelmelidirler.. [ilk örneğe bakınız]. - * IPUCU: './unrealircd gencloak' Unrealircd sizin için rastgele 3 adet dizin oluşturur. - * Bunu NIX üzerinde çalıştırabilirsiniz. - * On Windows, use "C:\Program Files\UnrealIRCd 6\bin\unrealircdctl" gencloak - */ - cloak-keys { - "Oozahho1raezoh0iMee4ohvegaifahv5xaepeitaich9tahdiquaid0geecipahdauVaij3zieph4ahi"; - "ve diğeri"; - "ve diğeri"; - } -} - -/* Sunucunun kendine özgü yapılandırması */ - -set { - // SON OLARAK, BU SONRAKİ ÖĞEYİ DEĞİŞTİRMENİZ GEREKİR: - kline-address 'set.this.to.email.address'; /* bir kullanıcı banlandığında e-mail yada URL satırı gösterir */ - modes-on-connect "+ixw"; /* kullanıcılar bağlandığında, bu modları alacaktır */ - modes-on-oper "+xws"; /* Birisi IRC Operatör olduğunda bu modları alacaktır */ - modes-on-join "+nt"; /* yeni bir kanal oluşturulduğunda varsayılan kanal modlarını alacaktır */ - oper-auto-join "#opers"; /* IRCoplar bu kanala otomatik olarak giriş yapacaktır */ - options { - hide-ulines; /* U-lines satırları /MAP ve /LINKS komutunda gözükmez */ - show-connect-info; /* sunucuya bağlanırken "looking up your hostname" mesajı görüntülenecektir */ - } - - maxchannelsperuser 10; /* bir kullanıcının maksimum girebileceği kanal sayısı */ - - /* QUIT mesajının görüntülenebilmesi için, bir kullanıcının sunucuya bağlı kalması - * gereken süre. Bu durum umarım spamları durdurmak için yardımcı olacaktır. - */ - anti-spam-quit-message-time 10s; - - /* Kullanıcı sunucudan ayrılırken çıkış sebebini sabitler. /QUIT sebeb gözardı edilecektir. */ - /* static-quit "Client quit"; */ - - /* static-part /PART komutu ile aynı işi görür */ - /* static-part yes; */ - - /* Flood Koruması: - * Bunun için birçok ayarları vardır ve çoğu iyi varsayılanlara sahiptir. - * Görmeniz için: https://www.unrealircd.org/docs/Set_block#set::anti-flood - */ - anti-flood { - } - - /* Spam filter Ayarları */ - spamfilter { - ban-time 1d; /* varsayılan spamfilter tarafından ban süresini belirtir */ - ban-reason "Spam/Advertising"; /* varsayılan sebep */ - virus-help-channel "#help"; /* 'viruschan' eylemi için kullanılacak kanal */ - /* except "#help"; Spamfilter'den muaf tutulacak kanal */ - } - - /* Belirli komutları kısıtlayın. - * See https://www.unrealircd.org/docs/Set_block#set::restrict-commands - */ - restrict-commands { - list { - except { - connect-time 60; - identified yes; - reputation-score 24; - } - } - invite { - except { - connect-time 120; - identified yes; - reputation-score 24; - } - } - /* Yukarıda gösterildiği gibi herhangi bir komutu kısıtlama - * yeteneğine sahiptir. Ayrıca kısıtlayabileceğiniz 4 - * özel tip vardır. Bunlar "private-message", - * "private-notice", "channel-message" ve "channel-notice". - * Bu örnekte olduğu gibi (kapalı) yorumlanmıştır: - */ - //private-message { - // except { connect-time 10; } - //} - //private-notice { - // except { connect-time 10; } - //} - } -} - -/* - * Aşağıdaki ayar "bilinmeyen kullanıcılar" için bağlantı kısıtlamasını yapılandıracaktır. - * - * UnrealIRCd, IP adreslerinden bağlanan çok sayıda kullanıcı tespit ettiğinde - * daha önce görülmemişse, yeni IP'lerden gelen bağlantılar ayarlanan - * hızın üzerinde reddedilir. örneğin 10:60'ta dakikada sadece 10 kullanıcı daha önce - * görülmemiş şekilde bağlanabilir. Bilinen IP adresleri, ayarlanan orandan bağımsız olarak - * her zaman girebilir. SASL kullanarak giriş yapan kullanıcılar için de aynıdır. - * - * Ayrıntılar için https://www.unrealircd.org/docs/Connthrottle adresine bakınız. - * Veya aşağıdaki varsayılan yapılandırma ayarlarını okumaya devam edin: - */ - -set { - connthrottle { - /* ilk önce "bilinen kullanıcılar" dediğimiz şeyi yapılandırmalıyız. - * Varsayılan olarak bunlar, IP adresleri üzerinde 24 veya - * daha yüksek puana sahip kullanıcılardır. 24 puan IP'nin geçen ay - * en az 2 saat (veya kayıtlıysa en az 1 saat) boyunca bu sunucuya - * bağlı olduğu anlamına gelir. - * Sasl-bypass seçeneği başka bir ayardır. Bu ayar - * SASL aracılığıyla hizmetlere kimlik doğrulaması yapan - * kullanıcıların da bilinen kullanıcılar olarak kabul edildiği anlamına gelir. - * "known-users" grubundaki kullanıcılar (herhangi bir reputation - * veya SASL) modülleri tarafından her zaman izin verilir. - */ - except { - reputation-score 24; - identified yes; - /* daha fazla seçenek için bkz. - * https://www.unrealircd.org/docs/Mask_item - */ - } - - /* Yeni kullanıcılar, aşağıdakilere ait olmayan tüm - * bilinen-kullanıcılar grubundandır. Bunlar "yeni" ve - * bu tür yeni kullanıcıların çok sayıda bağlanması durumunda - * bağlantı hızı sınırlamasına tabidirler. - * Varsayılan bu oran dakikada 20 yeni yerel kullanıcı - * ve dakikada 30 yeni global kullanıcı olarak belirlenmiştir. - */ - new-users { - local-throttle 20:60; - global-throttle 30:60; - } - - /* Bu modülün ne zaman aktif OLMAYACAĞINI yapılandırır. - * Varsayılan ayarlar, şu durumlarda modülü devre dışı bırakacaktır: - * - Reputation modülü 1 haftadan kısa bir süredir çalışıyor ise. - * 1 haftadan az çalışıyorsa, kimin "bilinen kullanıcı" - * olduğunu düşünmek için yeterli veri yoktur. - * - Sunucu yeni açıldı (ilk 3 dakika). - */ - disabled-when { - reputation-gathering 1w; - start-delay 3m; - } - } -} - -/* KANAL GEÇMİŞİ: - * UnrealIRCd, kullanıcılar tarafından bir kanala katılmadan önce - * kanal geçmişini okumak için, kullanılabilen +H kanal moduna sahip olmalıdır. - * Bu özellik hakkında bilgi için bkz. https://www.unrealircd.org/docs/Channel_history - * - * Geçmiş limitleri set::history aracılığıyla yapılandırılabilir. Varsayılan ayarlar - * muhtemelen sizin için zaten iyidir, ancak düşük bellekli bir sistemdeyseniz veya - * binlerce kanalınız varsa, iki kez kontrol etmek isteyebilirsiniz. Seçenekler için - * https://www.unrealircd.org/docs/Set_block#set::history adresine bakın. - * - * Buna ek olarak "kalıcı kanal geçmişine" sahip olabilirsiniz. Bu kanal - * geçmişinin diskte şifreli olarak depolandığı ve böylece IRC sunucusu yeniden - * başlatılması halinde korunması anlamına gelir. - * bkz. https://www.unrealircd.org/docs/Set_block#Persistent_channel_history - * Kalıcı geçmiş özelliği varsayılan olarak ETKİNLEŞTİRİLMEMİŞTİR bunun için - * bir secret { } bloğu yapılandırmanız gerekir. Aşağıdaki yapılandırma dosyasında - * saklanan parolalar doğrudan bunlara basit bir örnektir. - * Daha iyi güvenlik elde etmek için https://www.unrealircd.org/docs/Secret_block - * adresini okuyun böylece şifreleri doğrudan yapılandırmada saklamazsınız. - */ -//secret historydb { password "somepassword"; } -//set { history { channel { persist yes; db-secret "historydb"; } } } - -/* Son olarak, bir MOTD'ye (Günün Mesajı) sahip olmak isteyebilirsiniz, bunu - * conf/ dizininizde bir 'ircd.motd' metin dosyası oluşturarak yapabilirsiniz. - * Bu dosya bağlantıda kullanıcılarınıza gösterilecektir. - * Daha fazla bilgi için bkz. https://www.unrealircd.org/docs/MOTD_and_Rules - */ - - - - -/* - * Sorun mu yaşıyorsunuz veya daha fazla yardıma mı ihtiyacınız var? - * 1) https://www.unrealircd.org/docs/ - * 2) https://www.unrealircd.org/docs/FAQ <- sorularınızın %80 ini kapsamakta! - * 3) Eğer probleminiz hala devam ediyorsa: - * - Forums: https://forums.unrealircd.org/ - * - IRC: irc.unrealircd.org (SSL on port 6697) / #unreal-support - * İlk önce Dökümantasyon ve FAQ kısmını okumanızı gerektirdiğini unutmayın! - */ diff --git a/docs/services/irc/examples/unrealircd/help/help.conf b/docs/services/irc/examples/unrealircd/help/help.conf deleted file mode 100644 index d598b4a8..00000000 --- a/docs/services/irc/examples/unrealircd/help/help.conf +++ /dev/null @@ -1,1572 +0,0 @@ -/* UnrealIRCd 6 Help Configuration - * Based on the original help text written by hAtbLaDe - * Revised by CC (07/2002) and many others - */ - -help { - " Server Commands Help."; - " Specify your Question after the /HELPOP command."; - " You will find all of the server commands and options"; - " available for use."; - " If you need extra assistance please visit the server's"; - " help channel or ask an available IRCop."; - " -"; - " /HELPOP USERCMDS - To get the list of User Commands"; - " /HELPOP OPERCMDS - To get the list of Oper Commands"; - " /HELPOP SVSCMDS - Commands sent via U-Lined Server (Services)"; - " /HELPOP UMODES - To get the list of User Modes"; - " /HELPOP SNOMASKS - To get a list of Snomasks"; - " /HELPOP CHMODES - To get the list of Channel Modes"; - " -"; - " ==-------------------------oOo--------------------------=="; -} - -/* note: indexes were generated by cat somecmds|sort|column -c 70 - * along with tab->space conversion (tabwidth 8). - * Perhaps we should automate this step :). -- Syzop - */ - -help Usercmds { - " Currently the following User commands are available."; - " Use /HELPOP to get more information about"; - " a specific command."; - " -"; - " ==-------------------------oOo-------------------------=="; - " ADMIN LICENSE PART USERHOST"; - " AWAY LINKS PING USERIP"; - " CREDITS LIST PONG VERSION"; - " CYCLE LUSERS PRIVMSG VHOST"; - " DALINFO MAP QUIT WATCH"; - " DCCALLOW MODE RULES WHO"; - " INVITE MODULE SETNAME WHOIS"; - " ISON MOTD SILENCE WHOWAS"; - " JOIN NAMES STATS"; - " KICK NICK TIME"; - " KNOCK NOTICE TOPIC"; - " ==-------------------------oOo-------------------------=="; -} - -help Opercmds { - " This section gives the IRC Operator only commands."; - " Use /HELPOP to get more information about"; - " a specific command."; - " -"; - " See also https://www.unrealircd.org/docs/IRCOp_guide"; - " -"; - " ==-------------------------oOo-------------------------=="; - " DNS SETIDENT"; - " ADDMOTD ELINE OPER SHUN"; - " ADDOMOTD GLINE OPERMOTD SPAMFILTER"; - " GLOBOPS REHASH SQUIT"; - " CHGHOST GZLINE RESTART TEMPSHUN"; - " CHGIDENT KILL TLINE"; - " CHGNAME KLINE SAJOIN TRACE"; - " CLOSE LAG SAMODE TSCTL"; - " CONNECT LOCOPS SAPART UNDCCDENY"; - " DCCDENY MKPASSWD SDESC WALLOPS"; - " DIE MODULE SETHOST ZLINE"; - " ==-------------------------oOo-------------------------=="; -} - -help Svscmds { - " This section gives the commands that can be"; - " sent via a U-Lined Server such as Services."; - " The command is typically sent as:"; - " /MSG OPERSERV RAW :services "; - " Use /HELPOP to get more information about"; - " a specific command."; - " -"; - " ==-------------------------oOo-------------------------=="; - " SQLINE SVSKILL SVSNLINE SVSSILENCE"; - " SVS2MODE SVSLUSERS SVSNOLAG SVSSNO"; - " SVS2SNO SVSMODE SVSNOOP SVSWATCH"; - " SVSFLINE SVSMOTD SVSO SWHOIS"; - " SVSJOIN SVSNICK SVSPART UNSQLINE"; - " ==-------------------------oOo-------------------------=="; -} - -help Umodes { - " Here is a list of all the usermodes which are available for use."; - " -"; - " ==---------------------------oOo---------------------------=="; - " o = IRC Operator"; - " -"; - " d = Only receive channel PRIVMSGs starting with a bot command character (Deaf)"; - " i = Invisible (Not shown in /WHO searches)"; - " p = Hide all channels in /whois and /who"; - " q = Only U-Lines can kick you (Services Admins/Net Admins only)"; - " r = Identifies the nick as being Registered (settable by services only)"; - " s = Can listen to Server notices (see /HELPOP SNOMASKS)"; - " t = Says that you are using a /VHOST"; - " w = Can listen to Wallop messages"; - " x = Gives the user Hidden Hostname (security)"; - " z = Marks the client as being on a Secure Connection (SSL/TLS)"; - " B = Marks you as being a Bot"; - " D = Only receive PRIVMSGs from IRCOps, servers and services (privdeaf)"; - " G = Filters out all Bad words in your messages with "; - " H = Hide IRCop status in /WHO and /WHOIS. (IRC Operators only)"; - " I = Hide a user's idle time (in /whois output). Limited to IRCops, by default."; - " R = Allows you to only receive PRIVMSGs/NOTICEs from registered (+r) users"; - " S = For Services only. (Protects them)"; - " T = Prevents you from receiving CTCPs"; - " W = Lets you see when people do a /WHOIS on you (IRC Operators only)"; - " Z = Only receive/send PRIVMSGs from/to users using a Secure Connection (SSL/TLS)"; - " ==---------------------------oOo---------------------------=="; -} - -help Snomasks { - " Snomask stands for 'Service NOtice MASK', it controls which"; - " server notices you will receive."; - " Usage: /MODE nick +s "; - " Ex: /MODE blah +s +cC-j"; - " The parameter specifies which snomasks you want (or don't want)."; - " You can also remove all snomasks by simply doing /MODE nick -s."; - "-"; - " Below is a list of possible snomasks:"; - " ==-------------------------oOo-----------------------=="; - "b = Server bans (KLINE, GLINE, SHUN, etc)"; - "B = Messages from the DNS Blacklist module"; - "c = Local client connects"; - "C = Remote client connects (on other servers, except services/u-lines)"; - "d = Rejected DCC's due to Deny dcc blocks"; - "D = Debugging / junk (NOT recommended, possibly harmless things, noisy!)"; - "f = flood notices (recommended)"; - "j = joins, parts and kicks"; - "k = kill notices (/KILL usage)"; - "n = Local nick changes"; - "N = Remote nick change notices"; - "q = Deny nick rejection notices (QLINE)"; - "s = Server notices: all other notices that do not fit in the other snomasks"; - " (includes very important messages, so highly recommended)"; - "S = Spamfilter hits"; - "o = IRCOp overriding in channels (OperOverride)"; - "O = IRCOp changing user properties (/CHGNAME, /CHGIDENT, /CHGHOST, ..)"; - " or forcing a user to do things (/SAJOIN, /SAPART)"; - "R = JSON-RPC usage"; - "v = VHOST usage"; - " ==-------------------------oOo------------------------=="; -} - -help Chmodes { - " This section lists all of the possible channel modes that may be used with /MODE"; - " -"; - " ==------------------------------oOo----------------------------=="; - " v = Gives Voice to the user (May talk if chan is +m)"; - " h = Gives HalfOp status to the user (Limited op access)"; - " o = Gives Operator status to the user"; - " a = Gives Channel Admin to the user"; - " q = Gives Owner status to the user"; - " -"; - " b = Bans the nick!ident@host from the channel [h]"; - " (For more info on extended bantypes, see /HELPOP EXTBANS)"; - " c = Block messages containing mIRC color codes [o]"; - " C = No CTCPs allowed in the channel [h]"; - " d = Delayed users remaining after unsetting D [server]"; - " D = Delay showing joins until someone actually speaks [o]"; - " e = Overrides a ban for matching users [h]"; - " F = Apply flood protection profile (see /HELPOP CHMODEF) [o]"; - " f = Advanced flood protection (see /HELPOP CHMODEF) [o]"; - " G = Filters out all Bad words in messages with [o]"; - " H = Record channel history with specified maximums [o]"; - " i = A user must be invited to join the channel [h]"; - " I = Overrides +i for matching users [h]"; - " k = Users must specify to join [h]"; - " K = /KNOCK is not allowed [h]"; - " L = Channel link (if unable to join, user will be forwarded to ) [o]"; - " l = Channel may hold at most of users [h]"; - " m = Moderated channel (only +vhoaq users may speak) [h]"; - " M = Must be using a registered nick (+r), or have voice access to talk [h]"; - " N = No Nickname changes are permitted in the channel [h]"; - " n = Users outside the channel can not send PRIVMSGs to the channel [h]"; - " O = IRC Operator only channel (settable by IRCops)"; - " p = Private channel [o]"; - " P = Permanent channel (the channel is not destroyed when empty) (settable by IRCops)"; - " Q = No kicks allowed [o]"; - " R = Only registered (+r) users may join the channel [h]"; - " r = The channel is registered (settable by services only)"; - " s = Secret channel [o]"; - " S = Strips mIRC color codes [o]"; - " T = No NOTICEs allowed in the channel [o]"; - " t = Only +hoaq may change the topic [h]"; - " V = /INVITE is not allowed [o]"; - " z = Only Clients on a Secure Connection (SSL/TLS) can join [o]"; - " Z = All users on the channel are on a Secure connection (SSL/TLS) [server]"; - " (This mode is set/unset by the server. Only if the channel is also +z)"; - " -"; - " [h] requires at least halfop, [o] requires at least chanop"; - " [server] (un)settable only by the server"; - " ==------------------------------oOo----------------------------=="; -} - -help ExtBans { - " These bans let you ban based on things other than the traditional nick!user@host"; - " mask. For example MODE #chan +e ~account:SomeAccount can be used to add a ban"; - " exception for someone who is identified to services with the account SomeAccount."; - " Extended bans start with a tilde, followed by a name or letter."; - " UnrealIRCd 6 uses 'named' extended bans by default: +e ~account:SomeAccount"; - " while previous versions use 'letter' extended bans: +e ~a:SomeAccount"; - " See also https://www.unrealircd.org/docs/Extended_Bans"; - " -"; - " ==[ Group 1: time limit ]=="; - " The following ban type can be used in front of any (ext)ban:"; - " =Letter-------Name---------------------------Explanation------------------------=="; - " | | Timed bans are automatically unset by the server after "; - " ~t | ~time | the specified number of minutes. For example: "; - " | | +b ~time:3:*!*@hostname "; - " ==------------------------------------------------------------------------------=="; - " -"; - " ==[ Group 2: actions ]=="; - " These bantypes specify which actions are affected by a ban:"; - " =Letter-------Name---------------------------Explanation------------------------=="; - " | | People matching these bans can join but are unable to "; - " ~q | ~quiet | speak, unless they have +v or higher. "; - " | | Example: +bb ~quiet:*!*@blah.blah.com ~quiet:nick*!*@* "; - "-----------------------------------------------------------------------------------"; - " | | People matching these bans cannot change nicks, unless "; - " ~n | ~nickchange | they have +v or higher. "; - " | | Example: +bb ~nickchange:*!*@*.uk ~nickchange:nick*!*@* "; - "-----------------------------------------------------------------------------------"; - " | | Users matching this may not join the channel. However, "; - " ~j | ~join | if they are already in the channel then they may still "; - " | | speak, change nicks, etc. "; - "-----------------------------------------------------------------------------------"; - " | | Bypass message restrictions. This extended ban is only "; - " | | available as a ban exception (+e) and not as a ban (+b)."; - " | | The syntax is: +e ~msgbypass:type:mask. Valid types are:"; - " | | 'external' (bypass +n), 'censor' (bypass +G), "; - " | | 'moderated' (bypass +m/+M), 'color' (bypass +S/+c), and "; - " ~m | ~msgbypass | 'notice' (bypass +T). Some examples: "; - " | | Allow an IP to bypass +m and +n: "; - " | | +e ~msgbypass:moderated:*!*@192.168.1.1 "; - " | | +e ~msgbypass:external:*!*@192.168.1.1 "; - " | | Allow the account 'ColorBot' bypass color restrictions: "; - " | | +e ~msgbypass:color:~account:ColorBot "; - "-----------------------------------------------------------------------------------"; - " | | If a user matches the ban or other limits (eg +l/+k/etc)"; - " ~f | ~forward | then they will be forwarded to the specified channel. "; - " | | Example: +b ~forward:#badisp:*!*@*.isp.xx "; - "-----------------------------------------------------------------------------------"; - " | | Bypass mode +f/+F flood protection. This extended ban is"; - " | | only available as +e and not as +b. "; - " | | Syntax: +e ~flood:types:mask. "; - " | | Valid flood types are: c, j, k, m, n, t, r, and "; - " ~F | ~flood | * for all. "; - " | | For the meaning of the letters, see /HELPOP CHMODEF "; - " | | Example: +e ~flood:*:*!*@192.168.* "; - " | | +e ~flood:m:*!*@192.168.* "; - " ==------------------------------------------------------------------------------=="; - " -"; - " ==[ Group 3: selectors ]=="; - " These bantypes introduce new criteria which can be used:"; - " =Letter-------Name---------------------------Explanation------------------------=="; - " | | If a user is logged in to services with this account "; - " | | name, then this ban will match. "; - " ~a | ~account | There are also two special bans: ~account:* matches all "; - " | | authenticated users and ~account:0 matches all "; - " | | unauthenticated users. "; - " | | Example: +e ~account:Name +I ~account:Name "; - "-----------------------------------------------------------------------------------"; - " | | The GEOIP also module tries to map a users IP address to"; - " | | an ASN (Autonamous System Number), like 16276 (OVH SAS) "; - " ~A | ~asn | and 36925 (ASMedi). You can ban (+b) or exempt (+e) and "; - " | | invite (+I) based on this number. "; - " | | Example: +b ~asn:36925 +e ~asn:16276 "; - "-----------------------------------------------------------------------------------"; - " | | If the user is in this channel then they are unable to "; - " | | join. A prefix can also be specified (+/%/@/&/~) which "; - " ~c | ~channel | means that it will only match if the user has that "; - " | | rights or higher on the specified channel. "; - " | | Example: +b ~channel:#lamers +e ~channel:@#trusted "; - "-----------------------------------------------------------------------------------"; - " | | The GEOIP module tries to map IP addresses of users to "; - " | | a country code, like NL and US. You can ban or exempt a "; - " ~C | ~country | user based on the two letter country code this way. "; - " | | Example: +b ~country:NL +e ~country:NL "; - "-----------------------------------------------------------------------------------"; - " | | If the user is an IRCOp and is logged in with an oper "; - " | | block with a matching oper::operclass name then this "; - " ~O | ~operclass | will match. This way you can create channels which only "; - " | | specific type(s) of opers may join. Set +i and use +I. "; - " | | Example: +iI ~operclass:*admin* "; - "-----------------------------------------------------------------------------------"; - " | | If the realname of a user matches this then they are "; - " | | unable to join. "; - " ~r | ~realname | Example: +b ~realname:*Stupid_bot_script* "; - " | | NOTE: an underscore ('_') matches both a space (' ') and"; - " | | an underscore ('_'), so this ban would "; - " | | match 'Stupid bot script v1.4'. "; - "-----------------------------------------------------------------------------------"; - " | | If the security group of a user matches this then they "; - " ~G | ~security- | are unable to join. "; - " | group | Example: +b ~security-group:unknown-users "; - "-----------------------------------------------------------------------------------"; - " | | When a user is using SSL/TLS with a client certificate "; - " | | then you can match the certificate fingerprint (the one "; - " ~S | ~certfp | you see in /WHOIS). Good for ban and invite exceptions. "; - " | | Example: +iI ~certfp:00112233445566778899aabbccddeeff.. "; - " ==------------------------------------------------------------------------------=="; - " -"; - " ==[ Group 4: special ]=="; - " These bantypes are special and don't fit anywhere else:"; - " =Letter-------Name---------------------------Explanation------------------------=="; - "-----------------------------------------------------------------------------------"; - " | | Inherit channel bans from another channel:"; - " | | If in #test you +b ~inherit:#main then if the user is "; - " | | banned in #main they cannot JOIN #test either. "; - " ~i | ~inherit | Note that: 1) Bans are only checked on-join, so not on- "; - " | | message or on nick-change. 2) If the other channel also "; - " | | has ~inherit bans then they are ignored. 3) You can only"; - " | | add a limited number of ~inherit bans (by default: 1). "; - "-----------------------------------------------------------------------------------"; - " | | Channel-specific text filtering. Supports two actions: "; - " ~T | ~text | 'censor' and 'block'. Two examples: "; - " | | +b ~text:censor:*badword* and +b ~text:block:*something*"; - "-----------------------------------------------------------------------------------"; - " | | Hide part/quit messages on matching users. "; - " ~p | ~partmsg | Example: +b ~partmsg:*!*@*.isp.com "; - " ==------------------------------------------------------------------------------=="; - " -"; - " ==[ Stacking ]=="; - "* You may stack extended bans from the 2nd group with the 3rd group."; - " For example +b ~quiet:~channel:#lamers would quiet all users who are also in #lamers."; - "* Bans from the 3rd group may also be used for invite exceptions (+I),"; - " such as +I ~channel:@#trusted and +I ~account:accountname. The same is also"; - " true for the ~inherit extban, if used in +e/+I it inherits exceptions/invex."; - "* You may put a time limit (group 1) in front of any extended ban,"; - " and even chain group 1 + group 2 + group 3:"; - " +b ~time:60:~join:~country:BD"; -} - -help ExtServerBans { - "This allows you to match on things other than user/host/ip. This can be useful"; - "for banning (GLINE, KLINE) and for exempting users (ELINE)."; - " "; - "===[ ~account: ]==="; - "This matches when the user is logged into services with SASL using the specified"; - "account name. There are also two special cases: ~account:* matches any logged in users"; - "and ~account:0 which matches all unauthenticated users."; - " "; - "===[ ~asn: ]==="; - "Ban or exempt an AS Number. As an IRCOp you can see the AS Number in WHOIS and also"; - "when users connect in the connect notice like [asn: XXX]. For more information see"; - "https://www.unrealircd.org/docs/ASN"; - " "; - "===[ ~country: ]==="; - "Matches a country, as determined by the GeoIP module. This uses the two letter country"; - "code like NL or US."; - " "; - "===[ ~realname: ]==="; - "This ban will match if the realname (gecos) of a user matches the specified string."; - "Since real names may contain spaces you can use an underscore to match a space or an"; - "underscore. Eg: ~realname:*Stupid_bot_script* matches 'Stupid bot script'."; - " "; - "===[ ~security-group: ]==="; - "Ban users matching the specified security group. Note that this can ban large amounts"; - "of users! See also https://www.unrealircd.org/docs/Security-group_block"; - " "; - "===[ ~certfp: ]==="; - "Match a user based on their certificate fingerprint (when using SSL/TLS)."; - "This can be very useful in ELINE to give trusted users certain exemptions."; - "See https://www.unrealircd.org/docs/Certificate_fingerprint"; - " "; - "===[ More information ]==="; - "See https://www.unrealircd.org/docs/Extended_server_bans"; -} - -help Chmodef { - " Both channel mode +F and +f offer advanced anti-flood protection for channels,"; - " they (can) protect against join floods, message floods, nick floods and more."; - " -"; - " The +F channel mode (uppercase F) allows you to pick a \"flood profile\""; - " For example \"MODE #channel +F normal\" would apply the \"normal\" profile,"; - " while \"MODE #channel +F strict\" would apply a more strict flood profile."; - " For a list of all profiles that are available and what their settings are,"; - " such as the thresholds for flood protection to kick in and where exactly"; - " they protect against, see:"; - " https://www.unrealircd.org/docs/Channel_anti-flood_settings#flood-profiles"; - " -"; - " There also exists an +f channel mode (lowercase f). This allows fine-tuning"; - " of flood settings, especially the 't' and 'r' types that +F does not handle."; - " The syntax for this mode's parameter is as follows:"; - " +f [{#}{,...}]:"; - " The amount specifies the number of times the specified flood must occur"; - " before action is taken. Below are the available types:"; - " -"; - " ==-----Type-----Name--------Default Action---Other Actions-----=="; - " c CTCP +C m, M"; - " j Join +i R"; - " k Knock +K"; - " m Messages +m M"; - " n Nickchange +N"; - " t Text kick b, d"; - " r Repeat kick d, b"; - " -"; - " The difference between type m and t is that m is tallied for the entire"; - " channel whereas t is tallied per user."; - " If you choose to specify an action for a mode, you may also specify a"; - " time (in minutes) after which the specific action will be reversed."; - " See also https://www.unrealircd.org/docs/Channel_anti-flood_settings#Channel_mode_f"; -} - -help Nick { - " Changes your \"Online Identity\" on a server."; - " All those in the channel you are in will be"; - " alerted of your nickname change."; - " -"; - " Syntax: NICK "; - " Example: NICK hAtbLaDe"; -} - -help Whois { - " Shows information about the user in question,"; - " such as their \"Name\", channels they are"; - " currently in, their hostmask, etc."; - " -"; - " Syntax: WHOIS "; - " Example: WHOIS hAtbLaDe"; - " -"; - " Status flags:"; - " The list of channels shown in the WHOIS reply can include one or more"; - " status flags to indicate information about the channel. These flags are"; - " described below:"; - " ~ - User is a Channel Owner (+q)"; - " & - User is a Channel Admin (+a)"; - " @ - User is a Channel Operator (+o)"; - " % - User is a Halfop (+h)"; - " + - User is Voiced (+v)"; - " ! - User has channels hidden in whois (+p) and you are an IRC Operator"; - " ? - The channel is secret (+s) and you are an IRC Operator"; -} - -help Who { - " Retrieves information about users"; - " In its most simple form the syntax is 'WHO #channel' or 'WHO nickname'"; - " However we also support the extended who syntax (WHOX):"; - " -"; - " Syntax:"; - " /WHO [options]"; - " /WHO [options [mask2]]"; - " -"; - " The mask can contain wildcards such as * and ?"; - " -"; - " The options consist of [][%[[,]]]"; - " Where:"; - " The define where to SEARCH on, and may contain one or more letters:"; - " n: nick name"; - " u: user name (ident)"; - " h: host name [*]"; - " i: IP address [*]"; - " s: server name [*]"; - " r: real name (gecos)"; - " t: connect time (mask is >seconds or decide which fields appear in the WHO output"; - " (note that any fields are always outputed in this order:)"; - " t: querytype (the parameter that was provided in )"; - " c: first channel name the user is on"; - " u: user name (ident)"; - " i: IP Address [*]"; - " h: hostname [*]"; - " H: real hostname [*]"; - " s: server name"; - " n: nick name"; - " f: status flags (explained later)"; - " m: user modes [*]"; - " d: number of server hops (eg: 0 means user is on same server)"; - " l: seconds idle (only for users on the same server as you, otherwise 0)"; - " a: account name (services account)"; - " o: operclass name [*]"; - " R: reputation score [*]"; - " r: real name (gecos)"; - " Items marked with [*] mean that IRCOp privileges are (possibly) required."; - " And finally, the is a word that clients can use to tag"; - " WHO requests so they can easily see which WHO response belongs to"; - " what WHO request."; - " -"; - " Examples of simple WHO requests:"; - " WHO #channel - To list all users in a channel"; - " WHO 1.2.3.4 - To show all users with this IP address (IRCOp only)"; - " Examples of WHOX requests:"; - " WHO Servic* n - Show all users which name starts with Servic"; - " (Only IRCOps are likely to see the full list)"; - " WHO #chan cI - Show all users in channel #chan with their"; - " IP address instead of hostname (IRCOp only)"; - " WHO z m - Show all users with user mode z set, that is:"; - " all users on SSL/TLS. (IRCOp only command)"; - " WHO -z m - Show all insecure users, without umode z."; - " (IRCOp only command)"; - " WHO <300 t - Show all users that are connected for"; - " less than 300 seconds (IRCOp only command)"; - " Examples of WHOX requests using output modifiers:"; - " WHO #test %acfhnru - Show all users in the channel #test and show"; - " various fields, among which 'a' (services"; - " account) is not displayed by normal WHO."; - " -"; - " Status flags:"; - " The WHO command shows several flags in the returned result to indicate"; - " different information about the user. These flags are explained below:"; - " G - User is /away (gone)"; - " H - User is not /away (here)"; - " r - User is using a registered nickname"; - " B - User is a bot (+B)"; - " s - User is securely connected (SSL/TLS)"; - " * - User is an IRC Operator"; - " ~ - User is a Channel Owner (+q)"; - " & - User is a Channel Admin (+a)"; - " @ - User is a Channel Operator (+o)"; - " % - User is a Halfop (+h)"; - " + - User is Voiced (+v)"; - " ! - User is +H and you are an IRC Operator"; - "-"; -} - -help Whowas { - " Retrieves previous WHOIS information for users"; - " no longer connected to the server."; - " -"; - " Syntax: WHOWAS "; - " WHOWAS "; - " Example: WHOWAS hAtbLaDe"; -} - -help Cycle { - " Cycles the given channel(s). This command is equivilent"; - " to sending a PART then a JOIN command."; - " -"; - " Syntax: CYCLE ,,"; - " Example: CYCLE #help"; - " Example: CYCLE #main,#chat"; -} - -help Dns { - " Returns information about the IRC server's DNS cache."; - " Note, since most clients have a builtin DNS command,"; - " you will most likely need to use /raw DNS to use this."; - " There are also 2 other variants:"; - " 'DNS l' will show you the DNS cache entries"; - " 'DNS i' will give you details about the nameserver config"; - " -"; - "Syntax: DNS [option]"; -} - -help Names { - " Provides a list of users on the specified channel."; - " -"; - "Syntax: NAMES "; - "Example: NAMES #Support"; -} - -help Ison { - " Used to determine if certain user(s) are"; - " currently online based upon their nickname."; - " -"; - " Syntax: ISON "; - " Example: ISON hAtbLaDe Stskeeps OperServ AOLBot"; -} - -help Join { - " Used to enter one or more channels on an IRC server."; - " All occupants of the channel will be notified of your arrival."; - " JOIN with 0 as a parameter makes you Part all channels."; - " If you specify one or more keys, they will be used to join a +k channel"; - " -"; - " Syntax: JOIN ,, ,,"; - " JOIN 0 (Parts all channels)"; - " Example: JOIN #Support"; - " JOIN #Lobby,#IRCd"; - " JOIN #IRCd,#Support,#main letmein,somepass,anotherpass"; -} - -help Part { - " Used to part (or leave) a channel you currently occupy."; - " All those in the channel will be notified of your departure."; - " If you specify a reason it will be displayed to the users on the channel"; - " -"; - " Syntax: PART ,,, "; - " Example: PART #Support"; - " PART #Lobby,#IRCd See ya later!"; -} - -help Motd { - " Displays the Message Of The Day of the IRC Server you are logged onto."; - " -"; - " Syntax: MOTD"; - " MOTD "; -} - -help Rules { - " Shows you the Rules of the Network."; - " -"; - " Syntax: RULES"; - " RULES "; -} - -help Lusers { - " Provides Local and Global user information"; - " (Such as Current and Maximum user count)."; - " -"; - " Syntax: LUSERS [server]"; -} - -help Map { - " Provides a graphical \"Network Map\" of the IRC network."; - " Mainly used for routing purposes."; - " -"; - " Syntax: MAP"; -} - -help Quit { - " Disconnects you from the IRC server. Those in the"; - " channels you occupy will be notified of your departure."; - " If you do not specify a reason, your nickname becomes the reason."; - " -"; - " Syntax: QUIT "; - " Example: QUIT Leaving!"; -} - -help Ping { - " The PING command is used to test the presence of an active client or"; - " server at the other end of the connection. Servers send a PING"; - " message at regular intervals if no other activity detected coming"; - " from a connection. If a connection fails to respond to a PING"; - " message within a set amount of time, that connection is closed. A"; - " PING message MAY be sent even if the connection is active."; - " Note that this is different from a CTCP PING command.."; - " -"; - " Syntax: PING "; - " Example: PING irc.example.org"; - " PING hAtbLaDe"; - " PING hAtbLaDe irc2.dynam.ac"; -} - -help Pong { - " PONG message is a reply to PING message. If parameter is"; - " given, this message will be forwarded to given target. The "; - " parameter is the name of the entity who has responded to PING message"; - " and generated this message."; - " -"; - " Syntax: PONG "; - " Example: PONG irc.example.org irc2.dynam.ac"; - " (PONG message from irc.example.org to irc2.dynam.ac)"; -} - -help Version { - " Provides Version information of the IRCd software in usage."; - " -"; - " Syntax: VERSION"; - " VERSION "; -} - -help Stats { - " Provides certain Statistical information about the server"; - " -"; - " Syntax: STATS "; - " Example: STATS u"; - " -"; - " Type /stats without parameters to get a list of available flags."; -} - -help Links { - " Lists all of the servers currently linked to the network."; - " Only IRCops can see linked U-Lined servers."; - " -"; - " Syntax: LINKS"; -} - -help Admin { - " Provides Administrative information regarding the server."; - " -"; - " Syntax: ADMIN"; - " ADMIN "; -} - -help Userhost { - " Returns the userhost of the user in question."; - " Usually used by scripts or bots."; - " -"; - " Syntax: USERHOST "; - " Example: USERHOST hAtbLaDe"; -} - -help Userip { - " Returns the userip of the user in question."; - " Usually used by scripts or bots."; - " -"; - " Syntax: USERIP "; - " Example: USERIP codemastr"; -} - -help Topic { - " Sets/Changes the topic of the channel in question,"; - " or just display the current Topic."; - " -"; - " Syntax: TOPIC (Displays the current topic)"; - " TOPIC (Changes topic)"; - " Example: TOPIC #Operhelp"; - " TOPIC #Lobby Welcome to #Lobby!!"; -} - -help Invite { - " Sends a user an Invitation to join a particular channel."; - " If the channel is +i, you must be an Operator to use this"; - " command, otherwise any user may use the command."; - " Invite without parameters lists the channels you have been"; - " invited to."; - " -"; - " Syntax: INVITE [ ]"; - " Example: INVITE hAtbLaDe #Support"; - " Example: INVITE"; -} - -help Kick { - " Removes a user from a channel. Can only be used by Operators"; - " or Half-Ops. If no reason is specified, your nickname becomes the reason."; - " -"; - " Syntax: KICK [reason]"; - " Example: KICK #Lobby foobar Lamer.."; -} - -help Away { - " Sets your online status to \"Away\"."; - " -"; - " Syntax: AWAY (Sets you Away with the reason given)"; - " AWAY (Un-Sets you as Away)"; - " Example: AWAY Lunch time!"; -} - -help Watch { - " Watch is a notify-type system on the server which is both faster"; - " and uses less network resources than any old-style notify"; - " system. The server will send you a message when any nickname"; - " in your watch list logs on or off."; - " The watch list DOES NOT REMAIN BETWEEN SESSIONS - You (or your"; - " script or client) must add the nicknames to your watch list every"; - " time you connect to an IRC server."; - " -"; - " Syntax: WATCH +nick1 +nick2 +nick3 (Add nicknames)"; - " WATCH -nick (Delete nicknames)"; - " WATCH (View which users are online)"; -} - -help List { - " Provides a complete listing of all channels on the network."; - " If a search string is specified, it will only show those"; - " matching the search string."; - " -"; - " Syntax: LIST "; - " Example: LIST"; - " LIST *ircd*"; - " -"; - " Some additional flags are also supported."; - " >number List channels with more than people"; - " people"; - " !*mask* List channels that do not match *mask*"; - " -"; - " Any of those may be used instead of a standard mask."; -} - -help Privmsg { - " Send a message to a user, channel or server."; - " /PRIVMSG "; - " Send a private message."; - " Ex: /PRIVMSG Blah hi, how are you?"; - " /PRIVMSG <#channel> "; - " Send a message to a channel."; - " Ex: /PRIVMSG #room Hi all"; - " /PRIVMSG <#channel> "; - " Send a message to users with and higher in <#channel> only"; - " Ex: /PRIVMSG @#room This goes to +oaq"; - " /PRIVMSG +#room This goes to +vhoaq"; - " NOTE: You need at least voice in order to send to +#chan/%#chan/@#chan"; - " and at least ops to send to &#chan/~#chan."; - " /PRIVMSG $ "; - " Send a message to all users on servers matching [Oper only]"; - " This is shown in the status window by most clients."; - " Ex: /PRIVMSG $*.example.org We will be upgrading our net in the next hour"; - " Note that in most cases services (/OS GLOBAL) is a better alternative."; - " -"; - " Multiple targets are also supported, like /PRIVMSG ,,."; - " -"; - " NOTE: In case of some old clients (eg: ircII) you cannot use /msg"; - " or /privmsg to use any of the 'advanced features', you'll then have to use:"; - " '/QUOTE PRIVMSG @#channel blah' or something similar."; -} - -help Notice { - " Send a notice to a user, channel or server."; - " /NOTICE "; - " Send a notice to a user."; - " Ex: /NOTICE Blah hi, how are you?"; - " /NOTICE <#channel> "; - " Send a notice to a channel."; - " Ex: /NOTICE #room Hi all, this is annoying"; - " /NOTICE <#channel> "; - " Send a notice to users with and higher in <#channel> only"; - " Ex: /NOTICE @#room This goes to +oaq"; - " /NOTICE +#room This goes to +vhoaq"; - " NOTE: You need at least voice in order to send to +#chan/%#chan/@#chan"; - " and at least ops to send to &#chan/~#chan."; - " /NOTICE $ "; - " Send a notice to all users on servers matching [Oper only]"; - " This is shown in the status window by most clients."; - " Ex: /NOTICE $*.example.org We will be upgrading our net in the next hour"; - " Note that in most cases services (/OS GLOBAL) is a better alternative."; - " -"; - " Multiple targets are also supported, like /NOTICE ,,."; - " -"; - " NOTE: In case of some old clients (eg: ircII) you cannot use /notice"; - " to use any of the 'advanced features', you'll then have to use:"; - " '/QUOTE NOTICE @#channel blah' or something similar."; -} - -help Knock { - " For channels which are invite only, you can \"Knock\" on the"; - " channel to request an invite."; - " -"; - " Syntax: KNOCK "; - " Example: KNOCK #secret_chan I'm an op, let me in!"; -} - -help Setname { - " Allows users to change their \"Real name\" (GECOS)"; - " directly online at IRC without reconnecting"; - " -"; - " Syntax: SETNAME "; -} - -help Vhost { - " Hides your real hostname with a virtual hostname"; - " provided by the IRC server , using SETHOST."; - " -"; - " Syntax: VHOST "; - " Example: VHOST openbsd ilovecypto"; -} - -help Mode { - " Sets a mode on a Channel or User."; - " Use /HELPOP CHMODES or /HELPOP UMODES to see a list of Modes"; - " -"; - " Syntax: MODE "; - " Example: MODE #Support +tn"; - " MODE #Support +ootn hAtbLaDe XYZ"; -} - -help Credits { - " This command will list the Credits to all the people who"; - " helped create UnrealIRCd."; - " -"; - " Syntax: CREDITS"; - " CREDITS "; -} - -help Dalinfo { - " This command will show historical credits (from ircu, etc..)"; - " -"; - " Syntax: DALINFO"; - " Syntax: DALINFO "; -} - -help License { - " This command displays information about the license UnrealIRCd is released under."; - " Syntax: LICENSE"; - " LICENSE "; -} - -help Time { - " Displays the current Server Date and Time."; - " -"; - " Syntax: TIME"; - " TIME "; -} - -help Silence { - " Ignores messages from a user or list of users at the Server itself."; - " -"; - " Syntax: SILENCE +nickname (Adds a nickname to SILENCE list)"; - " SILENCE -nickname (Removes a nickname from the SILENCE list)"; - " SILENCE (Lists the current SILENCE list)"; -} - -help Oper { - " Attempts to give a user IRC Operator status."; - " (Lets the IRCop oper up)"; - " -"; - " Syntax: OPER "; - " Note: both uid and password are case sensitive"; - " Example: OPER hAtbLaDe foobar234"; -} - -help Wallops { - " Sends a \"Message\" to all those with the umode +w."; - " Only IRCops can send Wallops, while anyone with the mode +w"; - " can view them."; - " -"; - " Syntax: WALLOPS "; -} - -help Locops { - " Sends a message to all IRCops at this server (local)."; - " -"; - " Syntax: LOCOPS "; - " Example: LOCOPS Gonna K-Line that user ..."; -} - -help Globops { - " Sends a message to all ircops (global)."; - " -"; - " Syntax: GLOBOPS "; - " Example: GLOBOPS Gonna K-Line that user ..."; -} - -help Kill { - " Forcefully Disconnects users from an IRC Server."; - " IRC Operator only command."; - " -"; - " Syntax: KILL ,,,... "; - " Example: KILL Jack16 Cloning is not allowed"; -} - -help Kline { - " This command provides timed K-Lines. If you match a K-Line you cannot"; - " connect to the server"; - " A time of 0 in the KLINE makes it permanent (Never Expires)."; - " You may also specify the time in the format 1d10h15m30s."; - " IRC Operator only command."; - " -"; - " Syntax: KLINE [time] (adds a K-Line)"; - " KLINE - (removes a K-Line)"; - " Example: KLINE *@*.aol.com Abuse (Adds a permanent K-Line)"; - " KLINE *@*.someisp.com 2d Abuse (Adds a K-Line for 2 days)"; - " KLINE Idiot 1d Please go away"; - " KLINE -*@*.aol.com"; - " -"; - " Soft actions (more info at https://www.unrealircd.org/docs/Actions)"; - " Syntax: KLINE % [time] (adds a soft K-Line)"; - " KLINE -% (removes a soft K-Line)"; - " Example: KLINE %*@*.aol.com Abuse (Adds a permanent soft K-Line)"; - " KLINE %*@*.someisp.com 2d Abuse (Adds a soft K-Line for 2 days)"; - " KLINE %Idiot 1d Please go away"; - " KLINE -%*@*.aol.com"; - " -"; - " Extended server bans:"; - " These allow you to match on criteria other than user/host/ip."; - " Syntax: KLINE ~: [time] "; - " Example: KLINE ~realname:*Stupid_bot_script*"; - " See /HELPOP EXTSERVERBANS for more ban criteria or the docs online at"; - " https://www.unrealircd.org/docs/Extended_server_bans"; -} - -help Zline { - " This command provides timed Z-Lines. If you match a Z-Line you cannot"; - " connect to the server"; - " A time of 0 in the ZLINE makes it permanent (Never Expires)."; - " You may also specify the time in the format 1d10h15m30s."; - " IRC Operator only command."; - " -"; - " Syntax: ZLINE <*@ipmask> [time] (adds a Z-Line)"; - " ZLINE -<*@ipmask> (removes a Z-Line)"; - " Example: ZLINE *@127.0.0.1 Abuse (Adds a permanent Z-Line)"; - " ZLINE *@127.0.0.1 2d Abuse (Adds a Z-Line for 2 days)"; - " ZLINE -*@127.0.0.1"; -} - -help Gline { - " This command provides timed G-Lines. If you match a G-Line you cannot"; - " connect to ANY server on the IRC network"; - " A time of 0 in the GLINE makes it permanent (Never Expires)."; - " You may also specify the time in the format 1d10h15m30s."; - " IRC Operator only command."; - " -"; - " Syntax: GLINE [time] (Adds a G-Line)"; - " GLINE - (Removes a G-Line)"; - " Example: GLINE *@*.idiot.net 900 Spammers (Adds a 15 min G-Line)"; - " GLINE *@*.idiot.net 1d5h Spammers (Adds a 29 hour G-Line)"; - " GLINE Idiot 1d Abuse"; - " GLINE -*@*.idiot.net"; - " -"; - " Soft Actions (More info at https://www.unrealircd.org/docs/Actions)"; - " -"; - " Syntax: GLINE % [time] "; - " (Adds a G-Line for user@host, but still allows the connection"; - " if the user has a registered account and identifies using SASL)"; - " GLINE -% (Removes a soft G-Line for user@host)"; - " Example: GLINE %*@*.idiot.net 900 Spammers (Adds a 15 min soft G-Line)"; - " GLINE %*@*.idiot.net 1d5h Spammers (Adds a 29 hour soft G-Line)"; - " GLINE %Idiot 1d Abuse (Adds a 1 hour soft G-Line)"; - " GLINE -%*@*.idiot.net (Remove an existing G-Line)"; - " -"; - " Extended server bans:"; - " These allow you to match on criteria other than user/host/ip."; - " Syntax: GLINE ~: [time] "; - " Example: GLINE ~realname:*Stupid_bot_script*"; - " See /HELPOP EXTSERVERBANS for more ban criteria or the docs online at"; - " https://www.unrealircd.org/docs/Extended_server_bans"; -} - -help Shun { - " Prevents a user from executing ANY command except ADMIN"; - " and respond to Server Pings. Shuns are global (like glines)."; - " A time of 0 in the SHUN makes it permanent (Never Expires)."; - " You may also specify the time in the format 1d10h15m30s."; - " IRC Operator only command."; - " -"; - " Syntax: SHUN