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 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/.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 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 6d3869e4..c9fbef6b 100644 --- a/.env.example +++ b/.env.example @@ -1,106 +1,99 @@ # 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 +# SECURITY WARNING: SENSITIVE CREDENTIALS +# ============================================================================= +# All 'change_me_' placeholders MUST be replaced for production. +# Recommended: openssl rand -base64 32 (passwords), openssl rand -hex 32 (keys). # ============================================================================= -ATL_PROJECT_NAME=atl-chat -ATL_BASE_DOMAIN=atl.chat -ATL_ENVIRONMENT=dev # dev, staging, prod + +# Sections: Core → IRC → XMPP → Bridge → Web → Docs → 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, 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 +# Docker logging (json-file driver, applied to all services) +LOG_MAX_SIZE=50m # Rotate container log at this size +LOG_MAX_FILES=5 # Keep this many rotated log files per container -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 +# Generate with: docker run --rm ghcr.io/allthingslinux/unrealircd ./unrealircd mkpasswd argon2 +IRC_OPER_PASSWORD=change_me_irc_oper_password +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' +# --- 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 ATHEME_REGISTER_EMAIL=noreply@allthingslinux.org +ATHEME_SRA_BOOTSTRAP_ACCOUNT=admin 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 @@ -121,13 +114,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 @@ -143,7 +134,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 @@ -159,12 +149,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 @@ -175,13 +159,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 @@ -192,25 +174,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 @@ -218,35 +201,55 @@ 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_OAUTH2_REGISTRATION_KEY=change_me_secure_oauth2_key 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_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=true -# 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 -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/... +# 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 & 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 @@ -254,7 +257,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 @@ -272,7 +275,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 @@ -282,7 +285,9 @@ 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 @@ -290,139 +295,68 @@ 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 -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 +# IRC +BRIDGE_IRC_NICK=bridge 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) +# DOCS (apps/docs — Cloudflare Workers via Alchemy) # ============================================================================= -# Atheme JSON-RPC URL (portal IRC provisioning) -IRC_ATHEME_JSONRPC_URL=http://atl-irc-server:8081/jsonrpc +# Alchemy uses this password to encrypt Secrets stored in deployment state. +# Required for: pnpm run deploy / pnpm run destroy from apps/docs +ALCHEMY_PASSWORD=change-me -# UnrealIRCd JSON-RPC URL (portal admin features) +# ============================================================================= +# PORTAL INTEGRATION (consumed by external portal service, not this monorepo) +# ============================================================================= +# 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 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/.github/renovate.json5 b/.github/renovate.json5 new file mode 100644 index 00000000..22a60709 --- /dev/null +++ b/.github/renovate.json5 @@ -0,0 +1,129 @@ +{ + $schema: 'https://docs.renovatebot.com/renovate-schema.json', + extends: [ + 'config:best-practices', // includes helpers:pinGitHubActionDigests + ':semanticCommits', + ':separateMultipleMajorReleases', + ':prNotPending', + ':dependencyDashboard', + ':updateNotScheduled', + 'schedule:earlyMondays', + 'customManagers:githubActionsVersions', + 'security:openssf-scorecard', + ], + timezone: 'America/New_York', + enabledManagers: ['npm', 'pep621', 'github-actions', 'docker-compose', 'dockerfile'], + dependencyDashboard: true, + dependencyDashboardOSVVulnerabilitySummary: 'unresolved', + osvVulnerabilityAlerts: true, + platformAutomerge: true, + rebaseWhen: 'conflicted', + fetchChangeLogs: 'pr', + commitBodyTable: true, + semanticCommitScope: 'deps', + minimumReleaseAge: '7 days', + internalChecksFilter: 'strict', + pinDigests: true, + prNotPendingHours: 24, + suppressNotifications: ['prIgnoreNotification'], + vulnerabilityAlerts: { + enabled: true, + labels: ['deps: security'], + automerge: false, + }, + lockFileMaintenance: { + enabled: true, + automerge: true, + schedule: ['before 4am on Monday'], + }, + ignorePaths: [ + '**/node_modules/**', + '**/.venv/**', + '**/dist/**', + '**/build/**', + ], + prBodyColumns: ['Package', 'Update', 'Type', 'Change', 'Age', 'Confidence', 'References'], + packageRules: [ + // Label major bumps as breaking + { + matchUpdateTypes: ['major'], + addLabels: ['breaking'], + }, + // Patch updates — group and automerge silently + { + matchUpdateTypes: ['patch'], + groupName: 'patch', + automerge: true, + automergeType: 'branch', + minimumReleaseAge: '7 days', + addLabels: ['deps: patch'], + }, + // Minor updates — group and automerge silently (skip 0.x — may have breaking changes) + { + matchUpdateTypes: ['minor'], + matchCurrentVersion: '!/^0/', + groupName: 'minor', + automerge: true, + automergeType: 'branch', + minimumReleaseAge: '7 days', + addLabels: ['deps: minor'], + }, + // Major updates — no automerge, needs review + { + matchUpdateTypes: ['major'], + groupName: 'major', + automerge: false, + addLabels: ['deps: major', 'deps: needs-review'], + }, + // GitHub Actions — group and automerge + { + matchManagers: ['github-actions'], + groupName: 'github-actions', + automerge: true, + automergeType: 'branch', + addLabels: ['deps: github-actions'], + commitMessageTopic: '{{depName}}', + }, + // Docker — group and automerge + { + matchManagers: ['docker-compose', 'dockerfile'], + groupName: 'docker', + automerge: true, + automergeType: 'branch', + addLabels: ['deps: docker'], + }, + // Python deps — update lockfile in place rather than bumping ranges + { + matchManagers: ['pep621'], + rangeStrategy: 'update-lockfile', + }, + // Dev/lint tools — low priority, automerge + { + matchPackageNames: ['ruff', 'basedpyright', 'hypothesis'], + groupName: 'dev', + automerge: true, + automergeType: 'branch', + addLabels: ['deps: dev'], + prPriority: -1, + }, + // Test deps — low priority, automerge + { + matchDepTypes: ['test'], + automerge: true, + automergeType: 'branch', + groupName: 'test', + addLabels: ['deps: test'], + prPriority: -1, + }, + // Type stubs — low priority, monthly, automerge + { + matchPackageNames: ['/^types?-/', '/^typing-/', '/^@types\\//', '/-types$/', '/-stubs?$/'], + groupName: 'types', + automerge: true, + automergeType: 'branch', + addLabels: ['deps: types'], + schedule: ['before 4am on the first day of the month'], + prPriority: -2, + }, + ], +} diff --git a/.github/workflows/bridge-docker.yml b/.github/workflows/bridge-docker.yml index 7026e359..8584fb54 100644 --- a/.github/workflows/bridge-docker.yml +++ b/.github/workflows/bridge-docker.yml @@ -30,13 +30,14 @@ jobs: changes: name: File Detection runs-on: ubuntu-latest + timeout-minutes: 5 outputs: docker: ${{ steps.docker_changes.outputs.any_changed }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: fetch-depth: 0 - - uses: tj-actions/changed-files@v47 + - uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47 id: docker_changes with: files: | @@ -48,6 +49,7 @@ jobs: validate: name: Validate needs: [changes] + timeout-minutes: 20 if: github.event_name == 'pull_request' && needs.changes.outputs.docker == 'true' runs-on: ubuntu-latest permissions: @@ -55,16 +57,16 @@ jobs: pull-requests: write packages: write steps: - - uses: actions/checkout@v5 - - uses: docker/setup-buildx-action@v3 - - uses: docker/login-action@v3 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | @@ -86,7 +88,7 @@ jobs: id: build_date run: echo "date=$(./.github/scripts/docker.sh generate-build-date)" >> "$GITHUB_OUTPUT" - name: Build - uses: docker/build-push-action@v6 + uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 timeout-minutes: 15 with: context: . @@ -107,6 +109,7 @@ jobs: build: name: Build & Push needs: [changes] + timeout-minutes: 20 if: | github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/main' && @@ -117,7 +120,7 @@ jobs: packages: write id-token: write steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 - name: Extract version from pyproject.toml id: version run: | @@ -129,15 +132,15 @@ jobs: echo "major_minor=$MAJOR_MINOR" echo "short_sha=$SHORT_SHA" } >> "$GITHUB_OUTPUT" - - uses: docker/setup-buildx-action@v3 - - uses: docker/login-action@v3 + - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | @@ -166,7 +169,7 @@ jobs: id: build_date run: echo "date=$(./.github/scripts/docker.sh generate-build-date)" >> "$GITHUB_OUTPUT" - name: Build and Push - uses: docker/build-push-action@v6 + uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 timeout-minutes: 15 env: SOURCE_DATE_EPOCH: ${{ steps.source_date.outputs.epoch }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7f4163fe..5fc0e367 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,13 +1,17 @@ --- -name: Global CI +name: CI on: push: branches: [main, develop] pull_request: branches: [main, develop] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: changes: runs-on: ubuntu-latest + timeout-minutes: 5 outputs: irc: ${{ steps.filter.outputs.irc }} xmpp: ${{ steps.filter.outputs.xmpp }} @@ -15,8 +19,8 @@ jobs: bridge: ${{ steps.filter.outputs.bridge }} infra: ${{ steps.filter.outputs.infra }} steps: - - uses: actions/checkout@v5 - - uses: dorny/paths-filter@v3 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 id: filter with: filters: | @@ -55,6 +59,7 @@ jobs: needs: changes if: needs.changes.outputs.irc == 'true' || needs.changes.outputs.xmpp == 'true' uses: ./.github/workflows/security.yml + secrets: inherit with: working-directory: . scan-image: false @@ -76,43 +81,52 @@ jobs: needs: changes if: needs.changes.outputs.bridge == 'true' runs-on: ubuntu-latest + timeout-minutes: 10 defaults: run: working-directory: apps/bridge steps: - - uses: actions/checkout@v5 - - uses: astral-sh/setup-uv@v6 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6 with: enable-cache: true - run: uv python install 3.12 - run: uv sync --frozen --all-extras - run: uv run ruff check src tests - run: uv run ruff format --check src tests + - name: Prune uv cache + if: always() + run: uv cache prune --ci test-bridge: needs: changes if: needs.changes.outputs.bridge == 'true' runs-on: ubuntu-latest + timeout-minutes: 15 defaults: run: working-directory: apps/bridge steps: - - uses: actions/checkout@v5 - - uses: astral-sh/setup-uv@v6 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6 with: enable-cache: true - run: uv python install 3.12 - run: uv sync --frozen --all-extras - run: uv run pytest tests -v --tb=short + - name: Prune uv cache + if: always() + run: uv cache prune --ci lint-web: needs: changes if: needs.changes.outputs.web == 'true' runs-on: ubuntu-latest + timeout-minutes: 10 steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v3 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3 with: version: 9 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: 20 cache: pnpm @@ -122,12 +136,13 @@ jobs: needs: changes if: needs.changes.outputs.web == 'true' runs-on: ubuntu-latest + timeout-minutes: 15 steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v3 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3 with: version: 9 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: 20 cache: pnpm @@ -136,26 +151,26 @@ jobs: release: name: Release runs-on: ubuntu-latest + timeout-minutes: 10 if: github.event_name == 'push' && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, 'skip ci') - timeout-minutes: 10 permissions: contents: write issues: write pull-requests: write id-token: write steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: fetch-depth: 0 - - uses: pnpm/action-setup@v3 + - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3 with: version: 9 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: 20 cache: pnpm - - uses: astral-sh/setup-uv@v6 + - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6 with: enable-cache: true - run: pnpm install --frozen-lockfile diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 0f70f1cb..53714525 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -12,14 +12,15 @@ on: jobs: build: runs-on: ubuntu-latest + timeout-minutes: 20 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - name: Build Image run: | cd ${{ inputs.working-directory }} - docker build -t ${{ inputs.image-name }}:test . + docker build -f Containerfile -t ${{ inputs.image-name }}:test . - name: Smoke Test run: |- - docker run --rm ${{ inputs.image-name }}:test echo "Smoke test passed" + docker run --rm --entrypoint echo ${{ inputs.image-name }}:test "Smoke test passed" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7cf6f4e2..d6099eda 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,22 +12,30 @@ on: jobs: lint: runs-on: ubuntu-latest + timeout-minutes: 15 steps: - - uses: actions/checkout@v5 - - name: Shell Lint + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 if: inputs.language == 'shell' - uses: luizm/action-sh-checker@master with: - sh_checker_comment: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + python-version: '3.11' + - name: shellcheck + if: inputs.language == 'shell' + uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 + with: + extra_args: shellcheck --all-files + - name: shfmt + if: inputs.language == 'shell' + uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 + with: + extra_args: shfmt --all-files - name: Lua Lint if: inputs.language == 'lua' - uses: lunarmodules/luacheck@v1 + uses: lunarmodules/luacheck@cc089e3f65acdd1ef8716cc73a3eca24a6b845e4 # v1 with: args: ${{ inputs.working-directory }} - name: YAML/JSON Lint if: inputs.language == 'yaml' || inputs.language == 'json' - uses: ibiqlik/action-yamllint@v3 + uses: ibiqlik/action-yamllint@2576378a8e339169678f9939646ee3ee325e845c # v3 with: file_or_dir: ${{ inputs.working-directory }} diff --git a/.github/workflows/maintenance.yml b/.github/workflows/maintenance.yml index ce7dd861..2d4b27b3 100644 --- a/.github/workflows/maintenance.yml +++ b/.github/workflows/maintenance.yml @@ -24,10 +24,10 @@ jobs: contents: read issues: write steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: fetch-depth: 0 - - uses: alstr/todo-to-issue-action@v5.1.14 + - uses: alstr/todo-to-issue-action@64aca8fda7023259aada83ba44ad988c4c443657 # v5.1.14 with: CLOSE_ISSUES: true INSERT_ISSUE_URLS: true diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml index 5ebe6e6e..f6ddc34c 100644 --- a/.github/workflows/pr-title.yml +++ b/.github/workflows/pr-title.yml @@ -18,7 +18,7 @@ jobs: pull-requests: read statuses: write steps: - - uses: amannn/action-semantic-pull-request@v6.1.1 + - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index fe8fc899..e715e38d 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -13,19 +13,21 @@ on: jobs: security: runs-on: ubuntu-latest + timeout-minutes: 15 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: fetch-depth: 0 - name: Run GitLeaks - uses: gitleaks/gitleaks-action@v2 + uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7 # v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} - name: Build and Scan Image if: inputs.scan-image run: |- cd ${{ inputs.working-directory }} - docker build -t scan-target . + docker build -f Containerfile -t scan-target . docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ -v "$PWD:/tmp/work" \ ghcr.io/aquasecurity/trivy:latest image \ diff --git a/.gitignore b/.gitignore index f7519f43..560fe708 100644 --- a/.gitignore +++ b/.gitignore @@ -136,6 +136,12 @@ atheme_logs/ webpanel_data/ webpanel_config/ .turbo +wrangler.jsonc apps/atheme/config/atheme.conf apps/unrealircd/config/unrealircd.conf .hypothesis +references +apps/docs/.open-next +apps/docs/.alchemy +apps/docs/.source/ +docs-old 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/.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 8e665714..fa46a86b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,7 +6,7 @@ Unified chat infrastructure for All Things Linux: IRC, XMPP, web, and protocol b ## Quick Facts -- **Layout:** Monorepo with `apps/*` (UnrealIRCd, Atheme, Prosody, WebPanel, Web, Bridge, The Lounge, Gamja) +- **Layout:** Monorepo with `apps/*` (UnrealIRCd, Atheme, Prosody, WebPanel, Web, Docs, Bridge, The Lounge, Gamja) - **Orchestration:** Docker Compose (root `compose.yaml` includes `infra/compose/*.yaml`) - **Task Runner:** just (root + per-app via `mod`) - **Key Commands:** `just init`, `just dev`, `just prod`, `just test`, `just test-all` @@ -22,15 +22,17 @@ apps/ ├── web/ # Next.js web application ├── bridge/ # Discord↔IRC↔XMPP bridge (Python, in-repo) ├── thelounge/ # Web IRC client (private mode, WebIRC, janitor/giphy plugins) -└── gamja/ # IRC web client (planned) +├── gamja/ # IRC web client (planned) +└── docs/ # Fumadocs documentation site (Next.js) 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 tests/ # Root pytest suite (IRC, integration, e2e, protocol) -docs/ # Architecture, services, onboarding, bridges +docs/ # Static assets (curl-ca-bundle.crt) ``` ## Key Commands (Root) @@ -39,10 +41,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 | @@ -78,8 +78,56 @@ docs/ # Architecture, services, onboarding, bridges - [apps/unrealircd/AGENTS.md](apps/unrealircd/AGENTS.md) - [apps/web/AGENTS.md](apps/web/AGENTS.md) - [apps/webpanel/AGENTS.md](apps/webpanel/AGENTS.md) -- [docs/AGENTS.md](docs/AGENTS.md) +- [apps/docs/AGENTS.md](apps/docs/AGENTS.md) - [infra/AGENTS.md](infra/AGENTS.md) - [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 &`. For non-root Docker access add your user to the `docker` group (`sudo usermod -aG docker $USER` then re-login), or prefix Docker commands with `sudo`. Avoid `chmod 666 /var/run/docker.sock` — it grants root-equivalent access to all local users. + +### 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: 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. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..6208a563 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,14 @@ +# Changelog + +This project uses [semantic-release](https://semantic-release.gitbook.io/) to automate versioning and changelog generation. Every merge to `main` triggers a release pipeline that: + +1. Analyzes commits using [Conventional Commits](https://www.conventionalcommits.org/) format +2. Determines the next version (major, minor, or patch) +3. Generates release notes from commit messages +4. Updates this file and creates a [GitHub release](https://github.com/allthingslinux/atl.chat/releases) + +The release configuration lives in [`.releaserc.json`](.releaserc.json). + +## Release history + +See [GitHub Releases](https://github.com/allthingslinux/atl.chat/releases) for the full release history and changelogs. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..ede0275d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,3 @@ +Refer to all AGENTS.md + +Refer to all ~/.claude/skills diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..2d2af4bc --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,64 @@ +# Contributing to atl.chat + +Thanks for your interest in contributing to atl.chat! This document gets you started quickly — the full guide lives in the [docs site](https://docs.atl.chat/docs/development/contributing). + +## Quick start + +1. **Fork and clone** the repository. +2. **Create a branch** from `main`: + + ```bash + git checkout -b feat/my-feature + ``` + +3. **Set up the dev environment**: + + ```bash + cp .env.example .env && cp .env.dev.example .env.dev + just dev + ``` + +4. **Make your changes.** Run tests and linting before committing: + + ```bash + just test + just lint + ``` + +5. **Commit** using [Conventional Commits](https://www.conventionalcommits.org/) format: + + ```bash + feat(bridge): add XMPP MUC room join/part relay + ``` + +6. **Push and open a pull request** against `main`. + +## Commit conventions + +All commits must follow [Conventional Commits](https://www.conventionalcommits.org/). A commitlint hook enforces this automatically. Common types: `feat`, `fix`, `docs`, `chore`, `refactor`, `test`. Use the app name as scope (e.g., `irc`, `bridge`, `web`, `docs`). + +Run `pnpm exec cz` for an interactive commit prompt. + +## Pre-commit hooks + +The repo uses [pre-commit](https://pre-commit.com/) for automated linting and formatting. Set up hooks after cloning: + +```bash +uv sync +git config --unset-all core.hooksPath # if your environment sets this +uv run pre-commit install +uv run pre-commit install --hook-type commit-msg +``` + +## Code style + +| Language | Tool | +|---|---| +| Python | [ruff](https://docs.astral.sh/ruff/) | +| TypeScript/JSX | [Biome](https://biomejs.dev/) via ultracite | +| Lua | [luacheck](https://luacheck.readthedocs.io/) | +| Shell | [shellcheck](https://www.shellcheck.net/) + shfmt | + +## Full guide + +For the complete contributing guide — PR workflow, branch naming, code review expectations, and detailed code style references — see the [Contributing](https://docs.atl.chat/docs/development/contributing) page in the documentation site. 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 diff --git a/apps/atheme/AGENTS.md b/apps/atheme/AGENTS.md index b2be7117..17e747bf 100644 --- a/apps/atheme/AGENTS.md +++ b/apps/atheme/AGENTS.md @@ -26,4 +26,4 @@ Config is generated by `scripts/prepare-config.sh` (run via `just init`). Uses ` ## Related - [Monorepo AGENTS.md](../../AGENTS.md) -- [docs/services/irc/ATHEME.md](../../docs/services/irc/ATHEME.md) +- [docs-old/services/irc/ATHEME.md](../../docs-old/services/irc/ATHEME.md) diff --git a/apps/atheme/Containerfile b/apps/atheme/Containerfile index f1b15eec..ed4ddfa4 100644 --- a/apps/atheme/Containerfile +++ b/apps/atheme/Containerfile @@ -47,6 +47,12 @@ FROM alpine:3.23@sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314 ARG UID=1000 ARG GID=1000 +LABEL org.opencontainers.image.source="https://github.com/allthingslinux/atl.chat" \ + org.opencontainers.image.description="Atheme IRC services for atl.chat" \ + org.opencontainers.image.licenses="MIT" \ + org.opencontainers.image.vendor="All Things Linux" \ + org.opencontainers.image.title="atl-atheme" + # Install runtime dependencies # hadolint ignore=DL3018 RUN apk add --no-cache \ @@ -59,16 +65,16 @@ RUN apk add --no-cache \ RUN addgroup -g ${GID} -S atheme && \ adduser -u ${UID} -D -S -G atheme atheme -# Copy built application from builder stage -COPY --from=builder /usr/local/atheme /usr/local/atheme +# Copy built application from builder stage; set ownership in one step (no extra chown layer) +COPY --from=builder --chown=atheme:atheme /usr/local/atheme /usr/local/atheme -# Create directory structure with proper permissions -# etc=config (mounted), data=DB (mounted), logs=log files (mounted), var=PID file +# Create runtime directories (not present in builder); chown only these new dirs +# etc=config (mounted), data=DB (mounted), var=PID file +# Logging goes to stdout (/dev/stdout) — no logs directory needed RUN mkdir -p /usr/local/atheme/etc \ /usr/local/atheme/data \ - /usr/local/atheme/logs \ /usr/local/atheme/var && \ - chown -R atheme:atheme /usr/local/atheme + chown atheme:atheme /usr/local/atheme/etc /usr/local/atheme/data /usr/local/atheme/var # Copy entrypoint script COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh diff --git a/apps/atheme/config/atheme.conf.template b/apps/atheme/config/atheme.conf.template index 493e52a7..963412bf 100644 --- a/apps/atheme/config/atheme.conf.template +++ b/apps/atheme/config/atheme.conf.template @@ -10,15 +10,11 @@ loadmodule "protocol/unreal4"; loadmodule "backend/opensex"; /* ---------- Crypto: pbkdf2v2 (SCRAM-SHA) ---------- */ -/* Password Hashing - HIGHLY RECOMMENDED to use pbkdf2v2 */ -/* SCRAM-SHA Setup Requirements: */ -/* 1. Build Atheme with: ./configure --with-libidn */ -/* 2. Load pbkdf2v2 BEFORE any other crypto module (done below) */ -/* 3. Configure pbkdf2v2_digest = "SCRAM-SHA-256" (recommended) */ -/* 4. Ensure pbkdf2v2_rounds is 10000-65536 for Cyrus SASL compatibility */ -/* 5. Load modules/saslserv/scram-sha module (done below) */ -loadmodule "crypto/pbkdf2v2"; /* PBKDF2 v2 - Needed for SASL SCRAM-SHA support, HIGHLY RECOMMENDED */ -#loadmodule "crypto/pbkdf2"; /* PBKDF2 v1 - Verify-only for Atheme <= 7.2 - DEPRECATED */ +/* PBKDF2 v2 — required for SASL SCRAM-SHA support. Must be loaded BEFORE any other + * crypto module. Configure in the crypto{} block below. + * Docs: https://atheme.dev/docs/config/crypto */ +loadmodule "crypto/pbkdf2v2"; +/* loadmodule "crypto/pbkdf2"; -- PBKDF2 v1 - verify-only for Atheme <= 7.2 - DEPRECATED */ /* ---------- NickServ ---------- */ loadmodule "nickserv/main"; /* Core components */ @@ -143,6 +139,7 @@ loadmodule "operserv/set_maxlogins"; /* SET MAXLOGINS command */ loadmodule "operserv/set_maxnicks"; /* SET MAXNICKS command */ loadmodule "operserv/set_maxusers"; /* SET MAXUSERS command */ loadmodule "operserv/set_mdlimit"; /* SET MDLIMIT command */ +loadmodule "operserv/set_enforceprefix"; /* SET ENFORCEPREFIX command */ loadmodule "operserv/set_nickexpire"; /* SET NICKEXPIRE command */ loadmodule "operserv/set_recontime"; /* SET RECONTIME command */ loadmodule "operserv/set_spam"; /* SET SPAM command */ @@ -255,7 +252,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 */ #loadmodule "gameserv/dice"; /* DICE/WOD commands */ #loadmodule "gameserv/eightball"; /* EIGHTBALL command */ #loadmodule "gameserv/gamecalc"; /* Game-specific dice calculators */ @@ -299,9 +296,9 @@ loadmodule "groupserv/invite"; /* INVITE command */ loadmodule "misc/httpd"; /* HTTP Server */ loadmodule "misc/login_throttling"; /* Password-based login throttling */ loadmodule "transport/jsonrpc"; /* JSON-RPC handler for the httpd */ -#loadmodule "exttarget/oper"; /* $oper exttarget match type */ -#loadmodule "exttarget/registered"; /* $registered exttarget match type */ -#loadmodule "exttarget/channel"; /* $channel exttarget match type */ +loadmodule "exttarget/oper"; /* $oper exttarget match type */ +loadmodule "exttarget/registered"; /* $registered exttarget match type */ +loadmodule "exttarget/channel"; /* $channel exttarget match type */ #loadmodule "exttarget/chanacs"; /* $chanacs exttarget match type */ #loadmodule "exttarget/server"; /* $server exttarget match type */ loadmodule "proxyscan/main"; /* Core components */ @@ -309,35 +306,35 @@ loadmodule "proxyscan/dnsbl"; /* Proxyscan (DNSBL) modules */ #loadmodule "contrib/backtrace"; /* Backtrace module */ crypto { - /* Argon2 Configuration - Alternative to pbkdf2v2 */ - #argon2_type = "argon2id"; /* Type: argon2d (GPU-resistant), argon2i (side-channel resistant), argon2id (balanced) */ - #argon2_memcost = 16; /* Memory cost as power of 2 in KiB (3-30, default 16 = 64 MiB RAM) */ - #argon2_timecost = 3; /* Time cost iterations (3-1048576, default 3) */ - #argon2_threads = 1; /* CPU threads to use (1-255, default 1, experimental) */ - #argon2_saltlen = 16; /* Salt length in bytes (4-48, default 16) */ - #argon2_hashlen = 64; /* Digest length in bytes (16-128, default 64) */ - - /* Scrypt Configuration - Alternative to pbkdf2v2 */ - #scrypt_memlimit = 14; /* Memory cost as power of 2 in KiB (14-26, default 14 = 16 MiB RAM) */ - #scrypt_opslimit = 524288; /* Computation amount (32768-4294967295, default 524288) */ - - /* PBKDF2v2 Configuration - HIGHLY RECOMMENDED */ - /* Configure pbkdf2v2 for SCRAM-SHA-256 support */ - /* SCRAM-SHA Requirements: */ - /* 1. Must be loaded BEFORE any other crypto module */ - /* 2. Use SCRAM-SHA-256 (recommended), SCRAM-SHA-512, or SCRAM-SHA-1 */ - /* 3. Rounds must be 10000-65536 for Cyrus SASL compatibility */ - /* 4. Load modules/saslserv/scram-sha module for SASL support */ - pbkdf2v2_digest = "SCRAM-SHA-256"; /* Digest: SHA1, SHA2-256, SHA2-512, or SCRAM-* variants */ - pbkdf2v2_rounds = 64000; /* Iteration count (10000-65536 for SCRAM-SHA, default 64000) */ - pbkdf2v2_saltlen = 32; /* Salt length in bytes (default 32 for SCRAM-SHA-256) */ - - /* bcrypt Configuration - Alternative to pbkdf2v2 */ - #bcrypt_cost = 7; /* Rounds as power of 2 (4-31, default 7) */ - - /* Crypt3 SHA2 Configuration - Requires GNU glibc v2.7+ */ - #crypt3_sha2_256_rounds = 5000; /* SHA2-256 rounds (5000-1000000, default 5000) */ - #crypt3_sha2_512_rounds = 5000; /* SHA2-512 rounds (5000-1000000, default 5000) */ + /* Argon2 — alternative to pbkdf2v2 (requires --with-argon2 at build time) + * argon2id is recommended: balances side-channel and GPU resistance. */ + #argon2_type = "argon2id"; /* argon2d (GPU-resistant), argon2i (side-channel resistant), argon2id (balanced) */ + #argon2_memcost = 16; /* memory cost as power of 2 in KiB (3–30, default 16 = 64 MiB) */ + #argon2_timecost = 3; /* time cost iterations (3–1048576, default 3) */ + #argon2_threads = 1; /* CPU threads to use (1–255, default 1, experimental) */ + #argon2_saltlen = 16; /* salt length in bytes (4–48, default 16) */ + #argon2_hashlen = 64; /* digest length in bytes (16–128, default 64) */ + + /* Scrypt — alternative to pbkdf2v2 (requires --with-sodium at build time) */ + #scrypt_memlimit = 14; /* memory cost as power of 2 in KiB (14–26, default 14 = 16 MiB) */ + #scrypt_opslimit = 524288; /* computation amount (32768–4294967295, default 524288) */ + + /* PBKDF2v2 — active configuration (loaded above). + * Using "SCRAM-SHA-256" digest stores both a PBKDF2 key and a SCRAM ServerKey/StoredKey, + * enabling SASL SCRAM-SHA-256 without preventing SASL PLAIN logins. + * Valid rounds range: 10000–5000000 (default 64000). Keep ≤65536 for Cyrus SASL client compatibility. + * Docs: https://atheme.dev/docs/config/crypto#pbkdf2v2 */ + pbkdf2v2_digest = "SCRAM-SHA-256"; + pbkdf2v2_rounds = 64000; + pbkdf2v2_saltlen = 32; + scram_mechanisms = "SCRAM-SHA-256"; /* advertise only mechanisms with stored digests */ + + /* bcrypt — alternative to pbkdf2v2 */ + #bcrypt_cost = 7; /* rounds as power of 2 (4–31, default 7) */ + + /* Crypt3 SHA2 — requires GNU glibc v2.7+ */ + #crypt3_sha2_256_rounds = 5000; /* rounds (5000–1000000, default 5000) */ + #crypt3_sha2_512_rounds = 5000; /* rounds (5000–1000000, default 5000) */ }; /* ---------- Server info and uplink ---------- */ @@ -353,7 +350,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 +445,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 +458,7 @@ chanfix { }; autofix; }; +*/ global { nick = "${ATHEME_GLOBAL_NICK}"; @@ -679,34 +679,31 @@ ldap { }; /* ---------- Logging ---------- */ -/* Single comprehensive log file - captures everything */ -logfile "logs/atheme.log" { debug; }; /* ALL LOGGING - Everything goes here */ - -/* Optional: Log to IRC channels (uncomment if needed) */ -#logfile "#services" { debug; }; -#logfile "!snotices" { debug; }; +/* All categories to stdout — captured by Docker json-file driver with rotation. + * Use `docker compose logs atl-irc-services` to read. */ +logfile "/dev/stdout" { error; info; admin; network; commands; register; set; denycmd; request; }; /* ---------- General settings ---------- */ general { - #permissive_mode; /* Unknown function - not documented */ - helpchan = "${ATHEME_HELP_CHANNEL}"; /* Network help channel shown for unknown commands */ - helpurl = "${ATHEME_HELP_URL}"; /* Webpage for services help */ - #silent; /* Prevents WALLOPS/GLOBOPS - not recommended to uncomment */ - #verbose_wallops; /* Additional event information - may cause large amounts of messages */ - join_chans; /* Allow ChanServ to join registered channels (recommended) */ - leave_chans; /* ChanServ leaves channels after everyone else (recommended) */ - #secure; /* Requires /msg @ instead of /msg */ - uflags = { hidemail; }; /* Default user flags: hides email address */ - cflags = { guard; verbose; }; /* Default channel flags: ChanServ inhabits channel, verbose notifications */ - #raw; /* Allows RAW/INJECT commands - TAINTS the instance, use for debugging only */ - flood_msgs = 7; /* Number of messages to trigger flood protection */ + #permissive_mode; /* Purpose undocumented; not recommended to enable */ + helpchan = "${ATHEME_HELP_CHANNEL}"; /* Channel shown when user requests help for an unknown command */ + helpurl = "${ATHEME_HELP_URL}"; /* URL shown when user requests help for an unknown command */ + #silent; /* Suppresses WALLOPS/GLOBOPS — not recommended */ + #verbose_wallops; /* Enables additional event WALLOPS (e.g. AKILLs) — can be noisy */ + join_chans; /* Allow ChanServ to join registered channels (required for fantasy commands) */ + leave_chans; /* ChanServ leaves channels after everyone else leaves */ + #secure; /* Require /msg @ instead of /msg */ + uflags = { hidemail; }; /* Default flags for new user accounts */ + cflags = { guard; verbose; }; /* Default flags for newly registered channels */ + #raw; /* Allows RAW/INJECT commands — TAINTS the instance; debugging only */ + flood_msgs = 7; /* Number of messages within flood_time that triggers flood protection */ flood_time = 10; /* Time window in seconds for flood detection */ - ratelimit_uses = 5; /* Number of uses before ratelimit kicks in */ - ratelimit_period = 60; /* Time period in seconds for ratelimit */ - #vhost_change = 30; /* Days before vHost can be changed again */ - kline_time = 7; /* Default expiration time for KLINEs in days (0 = permanent) */ + ratelimit_uses = 5; /* Uses of ratelimited commands before cooldown applies */ + ratelimit_period = 60; /* Cooldown period in seconds for ratelimited commands */ + #vhost_change = 30; /* Days a user must wait before changing vHost again (HostServ) */ + kline_time = 7; /* Default KLINE expiry in days (0 = permanent) */ #kline_with_ident; /* KLINE user@host instead of *@host */ - #kline_verified_ident; /* KLINE *@host for unverified, user@host for verified idents */ + #kline_verified_ident; /* KLINE *@host for unverified idents, user@host for verified */ clone_time = 0; commit_interval = 5; /* Save database every 5 minutes */ #db_save_blocking; @@ -785,9 +782,15 @@ operclass "sra" { needoper; }; -operator "jilles" { +/* 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. */ + +/* ---------- Operators ---------- */ +/* Bootstrap SRA account - used to grant SRA to others via OperServ SOPER. + * Once other SRAs are added, this can be changed or removed. */ +operator "${ATHEME_SRA_BOOTSTRAP_ACCOUNT}" { operclass = "sra"; - #password = "$1$3gJMO9by$0G60YE6GqmuHVH3AnFPor1"; }; /* ---------- Operclass and operator (include sras.conf etc. if needed) ---------- */ diff --git a/apps/atheme/docker-entrypoint.sh b/apps/atheme/docker-entrypoint.sh index 061b8da6..4e8b807b 100755 --- a/apps/atheme/docker-entrypoint.sh +++ b/apps/atheme/docker-entrypoint.sh @@ -13,7 +13,7 @@ if [ "$(id -u)" = "0" ]; then fi # Create directories with proper ownership -mkdir -p /usr/local/atheme/data /usr/local/atheme/logs /usr/local/atheme/var +mkdir -p /usr/local/atheme/data /usr/local/atheme/var # Validate configuration exists if [ ! -f "/usr/local/atheme/etc/atheme.conf" ]; then @@ -32,13 +32,6 @@ if [ ! -w "/usr/local/atheme/data" ]; then exit 1 fi -# Validate logs directory is writable -if [ ! -w "/usr/local/atheme/logs" ]; then - echo "ERROR: Logs directory is not writable" - echo "Please check volume mount permissions" - exit 1 -fi - # Check if this is first run (no database exists) if [ ! -f "/usr/local/atheme/data/services.db" ]; then echo "First run detected - creating initial database..." @@ -52,6 +45,6 @@ fi echo "Starting Atheme services as user $(id -un) (UID: $(id -u))..." echo "Configuration: /usr/local/atheme/etc/atheme.conf" echo "Data directory: /usr/local/atheme/data" -echo "Logs directory: /usr/local/atheme/logs" +echo "Logging: stdout (Docker)" exec /usr/local/atheme/bin/atheme-services -n -c /usr/local/atheme/etc/atheme.conf -D /usr/local/atheme/data "$@" diff --git a/apps/bridge/Containerfile b/apps/bridge/Containerfile index 3b3fb374..dc861ed0 100644 --- a/apps/bridge/Containerfile +++ b/apps/bridge/Containerfile @@ -33,7 +33,7 @@ FROM common AS build WORKDIR /app -COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv +COPY --from=ghcr.io/astral-sh/uv:0.9.18 /uv /usr/local/bin/uv # Build context is repo root; use workspace lockfile COPY pyproject.toml uv.lock ./ @@ -68,7 +68,6 @@ ENV VIRTUAL_ENV=/app/.venv \ PYTHONOPTIMIZE=1 COPY --from=build --chown=nonroot:nonroot /app/.venv /app/.venv -COPY --from=build --chown=nonroot:nonroot /app/pyproject.toml /app/pyproject.toml COPY --from=build --chown=nonroot:nonroot /app/VERSION /app/VERSION # /app/config.yaml is bind-mounted at runtime; /data for optional persistence @@ -77,6 +76,6 @@ RUN mkdir -p /data && chown nonroot:nonroot /data USER nonroot HEALTHCHECK --interval=30s --timeout=10s --start-period=20s --retries=3 \ - CMD python -c "import bridge; print('ok')" || exit 1 + CMD pgrep -f "bridge --config" > /dev/null || exit 1 CMD ["bridge", "--config", "/app/config.yaml"] 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/pyproject.toml b/apps/bridge/pyproject.toml index 7362151b..5e05dea7 100644 --- a/apps/bridge/pyproject.toml +++ b/apps/bridge/pyproject.toml @@ -44,11 +44,13 @@ test = [ "pytest-randomly", "pytest-xdist", "pytest-html", + "hypothesis", ] +lint = ["ruff"] types = ["types-pyyaml", "types-mock", "annotated-types"] [tool.uv] -default-groups = ["test", "types"] +default-groups = ["test", "lint", "types"] [tool.hatch.build.targets.wheel] packages = ["src/bridge"] 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/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): diff --git a/apps/docs/AGENTS.md b/apps/docs/AGENTS.md new file mode 100644 index 00000000..0486511a --- /dev/null +++ b/apps/docs/AGENTS.md @@ -0,0 +1,56 @@ +# Docs (Fumadocs) + +> Scope: Documentation site app. Inherits monorepo [AGENTS.md](../../AGENTS.md). + +Fumadocs-powered documentation site for atl.chat. Next.js 16 + MDX content, deployed to Cloudflare Workers via OpenNext + Alchemy. + +## Tech Stack + +Next.js 16 · React 19 · Fumadocs (MDX) · Tailwind CSS 4 · Cloudflare Workers (OpenNext) · Alchemy (IaC deploy) + +## Repository Structure + +``` +app/ +├── layout.tsx, global.css +├── api/search/route.ts +├── llms.txt/route.ts, llms-full.txt/route.ts +├── llms.mdx/docs/[[...slug]]/route.ts +└── [[...slug]]/layout.tsx, page.tsx + +components/ +├── ai/page-actions.tsx +└── mdx/mermaid.tsx + +content/docs/ # MDX documentation content +├── index.mdx +├── architecture/ # Data model, networking +├── development/ # Contributing, adding a service, testing +├── getting-started/ # Overview, local development +├── operations/ # Deployment, backups, monitoring, security, SSL, troubleshooting +├── reference/ # API, env vars, FAQ, glossary, ports +└── services/ # Per-service docs (atheme, bridge, irc, thelounge, web, webpanel, xmpp) + +lib/ # Utilities (cn, layout, source) +scripts/lint.ts # Link validation script +source.config.ts # Fumadocs source config +alchemy.run.ts # Alchemy IaC deployment script +wrangler.jsonc # Cloudflare Workers config +``` + +## Key Commands + +| Command | Purpose | +|---------|---------| +| `pnpm run dev` | Start dev server (port 3001) | +| `pnpm run build` | Build for production | +| `pnpm run deploy` | Build + deploy to Cloudflare Workers (staging) | +| `pnpm run deploy:prod` | Build + deploy to Cloudflare Workers (prod) | +| `pnpm run check-types` | Fumadocs MDX + Next.js typegen + tsc | +| `pnpm run lint:links` | Validate internal links | + +## Related + +- [Monorepo AGENTS.md](../../AGENTS.md) +- [docs-old/AGENTS.md](../../docs-old/AGENTS.md) — Legacy docs (pre-Fumadocs) +- [apps/web/AGENTS.md](../web/AGENTS.md) 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..8ad920bd --- /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: 'strict', + 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..4e40667a --- /dev/null +++ b/apps/docs/content/docs/architecture/data-model.mdx @@ -0,0 +1,140 @@ +--- +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"] + ircWebpanel["webpanel-data/\nWebPanel persistent state"] + end + + subgraph atheme["atheme/"] + athemeData["data/\nservices.db (SQLite)"] + end + + subgraph xmpp["xmpp/"] + xmppData["data/\nprosody.sqlite"] + 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 +│ └── webpanel-data/ # WebPanel persistent state +├── atheme/ +│ └── data/ # Atheme SQLite database (services.db) +├── xmpp/ +│ ├── data/ # Prosody SQLite database (prosody.sqlite) +│ └── 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/certs` | `/home/unrealircd/unrealircd/certs` (ro) | UnrealIRCd | `irc.yaml` | +| `data/atheme/data` | `/usr/local/atheme/data` | 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/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/` | Removed — logs go to stdout (Docker) | +| `logs/atl-irc-server/` | Removed — logs go to stdout (Docker) | +| `data/irc/logs/` | Removed — logs go to stdout (Docker) | +| `data/atheme/logs/` | Removed — logs go to stdout (Docker) | +| `data/xmpp/logs/` | Removed — logs go to stdout (Docker) | +| `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 data/irc/logs data/atheme/logs data/xmpp/logs +``` + +## 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..65993180 --- /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:5347" --> 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..b71d9163 --- /dev/null +++ b/apps/docs/content/docs/operations/backups.mdx @@ -0,0 +1,456 @@ +--- +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 | +| WebPanel | `data/irc/webpanel-data/` | WebPanel session and configuration data | +| Atheme | `data/atheme/data/` | Services database (`services.db`) — NickServ, ChanServ registrations | +| Prosody | `data/xmpp/data/` | XMPP user accounts, rosters, MUC history | +| 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) | + +> **Note:** All services log to stdout, captured by Docker's json-file logging driver. Logs are not stored in `data/` — use `docker compose logs ` to view them. + +> **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 + +- **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..c81d9c93 --- /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 destinations + +Most services log to stdout/stderr, captured by Docker's json-file logging driver. Use `docker compose logs` to view them. The Lounge is an exception — it writes logs to files under `/var/opt/thelounge/` (bind-mounted to `data/thelounge/` on the host): + +| Service | Log destination | Notes | +|---|---|---| +| UnrealIRCd | stdout (JSON format via `/dev/stdout`) | `docker compose logs atl-irc-server` | +| Atheme | stdout (via `/dev/stdout` logfile) | `docker compose logs atl-irc-services` | +| Prosody | stdout (console sink) | `docker compose logs atl-xmpp-server` | +| Bridge | stderr (loguru) | `docker compose logs atl-bridge` | +| The Lounge | `/var/opt/thelounge/` | `data/thelounge/` | +| WebPanel | nginx access/error logs | `docker compose logs atl-irc-webpanel` | + +### 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..b8a6218c --- /dev/null +++ b/apps/docs/content/docs/reference/environment-variables.mdx @@ -0,0 +1,511 @@ +--- +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)_ | +| `LOG_MAX_SIZE` | Maximum container log file size before rotation (Docker json-file driver) | No | `50m` | +| `LOG_MAX_FILES` | Number of rotated log files to retain per container | No | `5` | + +> **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 (used in atheme.conf) | Yes | `admin@allthingslinux.org` | +| `ATHEME_REGISTER_EMAIL` | From address for registration emails | Yes | `noreply@allthingslinux.org` | +| `ATHEME_SRA_BOOTSTRAP_ACCOUNT` | IRC nick to grant SRA (Services Root Admin) on first run | No | `admin` | +| `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` | +| `BRIDGE_IRC_TLS_VERIFY` | Verify IRC TLS certificate (`true`/`false`) — set `false` for dev self-signed certs | No | `true` (prod), `false` (dev) | + +> **Warning:** `BRIDGE_IRC_OPER_PASSWORD` grants IRC operator privileges to the bridge bot. Use a strong, unique password. + +### Relay and media + +| Variable | Description | Required | Default | +|---|---|---|---| +| `BRIDGE_RELAYMSG_CLEAN_NICKS` | Use clean nicks in RELAYMSG (no `/d` suffix); requires UnrealIRCd `require-separator no` | No | `false` | +| `XMPP_AVATAR_BASE_URL` | Internal base URL for XMPP avatar HEAD-checks (Docker hostname, not public) | No | _(empty)_ | +| `XMPP_UPLOAD_FETCH_URL` | Internal base URL for rewriting XMPP upload URLs when fetching media for Discord | No | _(empty)_ | + +### Logging + +| Variable | Description | Required | Default | +|---|---|---|---| +| `LOG_LEVEL` | Bridge log verbosity (`DEBUG`, `INFO`, `WARNING`, `ERROR`) | No | `INFO` | + +## 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..9cc93f9c --- /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 logs to stdout via the `/dev/stdout` path, so all output is captured by Docker's json-file logging driver: + +```c +logfile "/dev/stdout" { debug; }; +``` + +View logs with `docker compose logs atl-irc-services`. 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, 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..1d04f9fd --- /dev/null +++ b/apps/docs/content/docs/services/atheme/index.mdx @@ -0,0 +1,99 @@ +--- +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 | + +> **Note:** Atheme logs to stdout via `/dev/stdout`. Logs are captured by Docker's json-file driver — use `docker compose logs atl-irc-services` to view them. + +## 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..08fc6c37 --- /dev/null +++ b/apps/docs/content/docs/services/atheme/operations.mdx @@ -0,0 +1,457 @@ +--- +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 | + +> **Note:** Atheme logs to stdout via `/dev/stdout`. Use `docker compose logs atl-irc-services` to view logs. + +## 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 +``` + +### 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 +docker compose logs atl-irc-services 2>&1 | grep -iE "error|fatal|warning" | 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..40e34a5e --- /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.tools +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..cf78750f --- /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 to stdout (console sink), captured by Docker's json-file logging driver. View them with: + +```bash +# Follow Prosody container logs +docker compose logs --follow atl-xmpp-server + +# Show last 100 lines +docker compose logs --tail=100 atl-xmpp-server + +# Show recent error-level entries +docker compose logs atl-xmpp-server 2>&1 | grep -i "error" +``` + +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/apps/gamja/AGENTS.md b/apps/gamja/AGENTS.md index 1a3fed17..900b5e32 100644 --- a/apps/gamja/AGENTS.md +++ b/apps/gamja/AGENTS.md @@ -6,7 +6,7 @@ Gamja — lightweight IRC web client. Currently planned; Containerfile and confi ## Tech Stack -Gamja · Node.js · Docker +Gamja · Node.js 22 · Docker ## Repository Structure @@ -17,7 +17,7 @@ config/ default/ └── config.json # Default config template -Containerfile # Multi-stage: node-alpine, npm install +Containerfile # Multi-stage: node:22-alpine3.21, npm ci (non-root gamja:gamja user) ``` ## Related diff --git a/apps/gamja/Containerfile b/apps/gamja/Containerfile index abe16925..f198079c 100644 --- a/apps/gamja/Containerfile +++ b/apps/gamja/Containerfile @@ -1,5 +1,5 @@ # Setup phase -FROM node:current-alpine3.22@sha256:3e843c608bb5232f39ecb2b25e41214b958b0795914707374c8acc28487dea17 AS base +FROM node:22-alpine3.21 AS base LABEL maintainer="AllThingsLinux IRC Infrastructure" \ description="Gamja - Lightweight IRC web client" \ @@ -27,7 +27,7 @@ RUN wget -q "https://codeberg.org/emersion/gamja/archive/v${GAMJA_VERSION}.tar.g WORKDIR /var/www/html/gamja -RUN npm install --omit=dev +RUN npm ci --omit=dev # Development server phase FROM base AS dev @@ -37,4 +37,11 @@ COPY --chown=gamja:gamja ./config/config.json /var/www/html/gamja/config.json WORKDIR /var/www/html/gamja +USER gamja:gamja + EXPOSE 8080 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/ || exit 1 + +CMD ["npm", "start"] diff --git a/apps/prosody/AGENTS.md b/apps/prosody/AGENTS.md index 4f12aa97..864dd788 100644 --- a/apps/prosody/AGENTS.md +++ b/apps/prosody/AGENTS.md @@ -20,6 +20,9 @@ config/ └── prosody.cfg.lua # Main Prosody config www/ # Static assets +├── index.html +├── robots.txt +└── security.txt Containerfile docker-entrypoint.sh justfile # Loaded via: mod xmpp './apps/prosody' @@ -48,4 +51,4 @@ modules.list ## Related - [Monorepo AGENTS.md](../../AGENTS.md) -- [docs/services/xmpp/](../../docs/services/xmpp/) +- [docs-old/services/xmpp/](../../docs-old/services/xmpp/) diff --git a/apps/prosody/Containerfile b/apps/prosody/Containerfile index 14fc26c1..ba79875a 100644 --- a/apps/prosody/Containerfile +++ b/apps/prosody/Containerfile @@ -116,9 +116,18 @@ RUN hg clone https://hg.prosody.im/prosody-modules/ prosody-modules && \ RUN mkdir -p prosody-modules-enabled/lib/luarocks/rocks && \ printf '%s\n' 'commands = {}' 'dependencies = {}' 'modules = {}' 'repository = {}' > prosody-modules-enabled/lib/luarocks/rocks/manifest +# Strip mercurial metadata — not needed in runtime image (saves ~10 MB) +RUN rm -rf prosody-modules/.hg prosody-modules/.hgignore prosody-modules/.hgtags + # --- 3. Runtime stage: Minimal image --- FROM debian:bookworm-slim +LABEL org.opencontainers.image.source="https://github.com/allthingslinux/atl.chat" \ + org.opencontainers.image.description="Prosody XMPP server for atl.chat" \ + org.opencontainers.image.licenses="MIT" \ + org.opencontainers.image.vendor="All Things Linux" \ + org.opencontainers.image.title="atl-prosody" + # Set shell options for better error handling SHELL ["/bin/bash", "-o", "pipefail", "-c"] @@ -168,9 +177,8 @@ RUN groupadd -r prosody && \ COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh RUN chmod +x /usr/local/bin/docker-entrypoint.sh -# Copy prepared community modules from builder -COPY --from=builder /usr/local/lib/prosody/prosody-modules /usr/local/lib/prosody/prosody-modules -COPY --from=builder /usr/local/lib/prosody/prosody-modules-enabled /usr/local/lib/prosody/prosody-modules-enabled +# prosody-modules and prosody-modules-enabled are already included via +# COPY --from=builder /usr/local /usr/local above — no separate copy needed # --- Expose all relevant ports --- EXPOSE 5222 5223 5269 5270 5280 5281 5347 5000 @@ -178,9 +186,6 @@ EXPOSE 5222 5223 5269 5270 5280 5281 5347 5000 # --- Healthcheck, volumes, and final setup --- HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 CMD curl -sf http://localhost:5280/status || exit 1 -# Create volumes for persistent data -VOLUME ["/var/lib/prosody", "/var/log/prosody", "/certs"] - # Set Lua paths for modules and C libraries ENV LUA_PATH="/usr/local/lib/prosody/?.lua;/usr/local/lib/prosody/?/init.lua;/usr/local/lib/prosody/prosody-modules-enabled/?.lua;/usr/local/lib/prosody/prosody-modules-enabled/?/init.lua;/var/lib/prosody/custom_plugins/?.lua;/var/lib/prosody/custom_plugins/?/init.lua;;" \ LUA_CPATH="/usr/local/lib/prosody/?.so;;" diff --git a/apps/prosody/config/prosody.cfg.lua b/apps/prosody/config/prosody.cfg.lua index a484c34a..4a636d22 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) @@ -22,10 +22,11 @@ modules_enabled = { "presence", -- Presence information and subscriptions (RFC 6121) "message", -- Message routing and delivery (RFC 6120) "iq", -- Info/Query request-response semantics (RFC 6120) - "s2s_status", -- https://modules.prosody.im/mod_s2s_status.html + "s2s_status", -- S2S connection status tracking (modules.prosody.im/mod_s2s_status) "s2s_bidi", -- XEP-0288: Bidirectional Server-to-Server Connections - "s2s_keepalive", -- XEP-0199 pings to remote servers to keep s2s connections alive (modules.prosody.im/mod_s2s_keepalive) - "limits", -- =============================================== + "s2s_keepalive", -- XEP-0199 pings to keep s2s connections alive (modules.prosody.im/mod_s2s_keepalive) + "limits", -- Rate limiting for c2s, s2s, and component connections (prosody.im/doc/modules/mod_limits) + -- =============================================== -- DISCOVERY & CAPABILITIES -- =============================================== "version", -- Server version information (XEP-0092) @@ -59,11 +60,12 @@ modules_enabled = { -- PUSH NOTIFICATIONS -- =============================================== "cloud_notify", -- Push notifications for mobile devices (XEP-0357) - "cloud_notify_extensions", -- Enhanced push notification features + "cloud_notify_extensions", -- Enhanced push notification features for iOS clients (Siskin, Monal) -- =============================================== -- 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,21 +75,21 @@ 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) "mimicking", -- Prevent address spoofing "tombstones", -- Prevent re-registration of deleted usernames (protects MUC access) - "flags", -- Module to view and manage flags on user accounts via shell/API. + "flags", -- View and manage flags on user accounts via shell/API -- =============================================== -- ADMINISTRATIVE INTERFACES -- =============================================== "admin_adhoc", -- Administrative commands via XMPP (XEP-0050) "admin_shell", -- Administrative shell interface "announce", -- Server-wide announcements - "motd", -- Message of the day for users2 - "compliance_latest", -- Compliance tester + "motd", -- Message of the day for connecting users + "compliance_latest", -- XMPP Compliance Suite compliance checker -- =============================================== -- WEB SERVICES & HTTP -- =============================================== @@ -98,7 +100,7 @@ modules_enabled = { "websocket", -- WebSocket connections for web clients (RFC 7395) "http_files", -- Static file serving over HTTP "conversejs", -- Converse.js web client at /conversejs (auto-config from VirtualHost) - "http_status", -- HTTP status API for monitoring (XEP-0156) + "http_status", -- HTTP status API for monitoring -- "proxy65", -- Disabled here; provided via dedicated Component `proxy.atl.chat` "turn_external", -- External TURN server support (XEP-0215) -- =============================================== @@ -109,7 +111,7 @@ modules_enabled = { -- COMPLIANCE & CONTACT INFORMATION -- =============================================== "server_contact_info", -- Contact information advertisement (XEP-0157) - "server_info", -- Server information (XEP-0157) + "server_info", -- Server information advertisement (XEP-0157) -- =============================================== -- MONITORING & METRICS -- =============================================== @@ -191,75 +193,29 @@ archive_compression = Lua.os.getenv("PROSODY_ARCHIVE_COMPRESSION") ~= "false" - archive_store = Lua.os.getenv("PROSODY_ARCHIVE_STORE") or "archive" -- Storage backend for archives -- Query limits -max_archive_query_results = Lua.tonumber(Lua.os.getenv("PROSODY_ARCHIVE_MAX_QUERY_RESULTS")) or - 250 -- Limit results per query -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 --- } - --- =============================================== --- MOBILE CLIENT OPTIMIZATIONS --- =============================================== - --- Client detection patterns --- mobile_client_patterns = { --- "Conversations", --- "ChatSecure", --- "Monal", --- "Siskin", --- "Xabber", --- "Blabber", --- } - --- Client State Indication (XEP-0352) --- csi_config = { --- enabled = true, --- default_state = "active", --- queue_presence = true, -- Queue presence updates when inactive --- queue_chatstates = true, -- Queue chat state notifications --- queue_pep = false, -- Don't queue PEP events --- delivery_delay = 30, -- Delay before batching (seconds) --- max_delay = 300, -- Maximum delay (5 minutes) --- batch_stanzas = true, -- Batch multiple stanzas --- max_batch_size = 10, -- Maximum stanzas per batch --- batch_timeout = 60, -- Batch timeout (seconds) --- } - --- Stream Management (XEP-0198) --- smacks_config = { --- -- Session resumption timeouts --- resumption_timeout = 300, -- 5 minutes --- max_resumption_timeout = 3600, -- 1 hour maximum --- hibernation_timeout = 60, -- 1 minute --- max_hibernation_timeout = 300, -- 5 minutes maximum --- --- -- Queue management --- max_unacked_stanzas = 500, -- Maximum unacknowledged stanzas --- max_queue_size = 1000, -- Maximum queue size --- --- -- Acknowledgment settings --- ack_frequency = 5, -- Request ack every 5 stanzas --- ack_timeout = 60, -- Timeout for ack requests --- --- -- Mobile-specific settings --- mobile_resumption_timeout = 900, -- 15 minutes for mobile --- mobile_hibernation_timeout = 300, -- 5 minutes for mobile --- mobile_ack_frequency = 10, -- Less frequent acks for mobile --- } +max_archive_query_results = Lua.tonumber(Lua.os.getenv("PROSODY_ARCHIVE_MAX_QUERY_RESULTS")) or 250 +-- When true, archives only for contacts the user has enabled archiving for; +-- when false (default), archives all conversations automatically. +mam_smart_enable = Lua.os.getenv("PROSODY_MAM_SMART_ENABLE") == "true" + +-- Namespaces to exclude from archiving (typing indicators, call signaling) +dont_archive_namespaces = { + "http://jabber.org/protocol/chatstates", + "urn:xmpp:jingle-message:0", +} --- Lua garbage collection -lua_gc_step_size = Lua.tonumber(Lua.os.getenv("LUA_GC_STEP_SIZE")) or 13 -- GC step size -lua_gc_pause = Lua.tonumber(Lua.os.getenv("LUA_GC_PAUSE")) or 110 -- GC pause percentage +-- =============================================== +-- LUA GARBAGE COLLECTION +-- =============================================== --- Enhanced garbage collection +-- Classic incremental GC step parameters (Lua 5.1/5.2 semantics) +lua_gc_step_size = Lua.tonumber(Lua.os.getenv("LUA_GC_STEP_SIZE")) or 13 +lua_gc_pause = Lua.tonumber(Lua.os.getenv("LUA_GC_PAUSE")) or 110 + +-- Enhanced GC table (Prosody 0.12+ / Lua 5.4 generational GC) gc = { - speed = Lua.tonumber(Lua.os.getenv("LUA_GC_SPEED")) or 200, -- Collection speed - threshold = Lua.tonumber(Lua.os.getenv("LUA_GC_THRESHOLD")) or 120, -- Memory threshold percentage + speed = Lua.tonumber(Lua.os.getenv("LUA_GC_SPEED")) or 200, + threshold = Lua.tonumber(Lua.os.getenv("LUA_GC_THRESHOLD")) or 120, } -- =============================================== @@ -268,11 +224,8 @@ gc = { -- This file centralizes all network- and port-related settings. -- -- References: --- - Port & network configuration docs: -- https://prosody.im/doc/ports --- - HTTP server docs: -- https://prosody.im/doc/http --- - Config basics and advanced directives: -- https://prosody.im/doc/configure -- -- IMPORTANT: @@ -282,20 +235,17 @@ gc = { -- _interfaces (e.g., c2s_ports, s2s_interfaces, etc.). -- - Private services (e.g., components, console) default to loopback. -- =============================================== --- Default service ports (override as needed) --- =============================================== + -- Client-to-server (XMPP over TCP, STARTTLS-capable) c2s_ports = { 5222 } --- Client-to-server over direct TLS (XMPP over TLS) --- Available since Prosody 0.12+ +-- Client-to-server over direct TLS (available since Prosody 0.12+) c2s_direct_tls_ports = { 5223 } -- Server-to-server (federation) s2s_ports = { 5269 } --- Server-to-server over direct TLS --- Available since Prosody 0.12+ +-- Server-to-server over direct TLS (available since Prosody 0.12+) s2s_direct_tls_ports = { 5270 } -- External components (XEP-0114); listen on all interfaces so bridge container can connect @@ -309,19 +259,19 @@ http_ports = { 5280 } https_ports = (Lua.os.getenv("PROSODY_HTTPS_VIA_PROXY") == "true") and {} or { 5281 } -- =============================================== --- Interfaces +-- INTERFACES -- =============================================== -- By default Prosody listens on all interfaces. To restrict: -- interfaces = { "127.0.0.1", "::1" } -- The special "*" means all IPv4; "::" means all IPv6. -interfaces = { "127.0.0.1" } -- Restrict to loopback (default) +interfaces = { "127.0.0.1" } -- Restrict to loopback by default -- Expose XMPP services publicly; override per-service so HTTP can remain loopback c2s_interfaces = { "*" } c2s_direct_tls_interfaces = { "*" } s2s_interfaces = { "*" } s2s_direct_tls_interfaces = { "*" } -local_interfaces = { "127.0.0.1" } -- Private services bind here by default +local_interfaces = { "127.0.0.1" } -- Private services (components, console) bind here -- If you need to hint external/public addresses (behind NAT) external_addresses = {} @@ -333,13 +283,12 @@ external_addresses = {} use_ipv6 = false -- =============================================== --- Backend & performance tuning +-- BACKEND & PERFORMANCE TUNING -- =============================================== --- Available backends: "epoll" (default), "event" (libevent), "select" (legacy) --- The setting name for libevent backend is "event" for compatibility. +-- Available backends: "epoll" (recommended on Linux), "event" (libevent), "select" (legacy) network_backend = "event" --- Common advanced network settings. See docs for full list. +-- Common advanced network settings. See docs for full list: -- https://prosody.im/doc/ports#advanced network_settings = { read_timeout = 840 -- seconds; align with reverse proxy timeouts (~900s) @@ -349,7 +298,7 @@ network_settings = { } -- =============================================== --- Proxy65 (XEP-0065) port/interface overrides +-- PROXY65 (XEP-0065) PORT/INTERFACE OVERRIDES -- =============================================== -- Global port/interface options must be set here (not under Component) -- Docs: https://prosody.im/doc/modules/mod_proxy65 @@ -365,26 +314,24 @@ proxy65_interfaces = { "*" } -- External URL advertised to clients and components (Converse.js BOSH/WebSocket, etc.) -- When behind reverse proxy, omit port. For direct dev access on :5280, set PROSODY_HTTP_EXTERNAL_URL. local __http_host = Lua.os.getenv("PROSODY_HTTP_HOST") or - Lua.os.getenv("PROSODY_DOMAIN") or "localhost" + Lua.os.getenv("PROSODY_DOMAIN") or Lua.os.getenv("XMPP_DOMAIN") or "localhost" local __http_scheme = Lua.os.getenv("PROSODY_HTTP_SCHEME") or "http" -local __domain = Lua.os.getenv("PROSODY_DOMAIN") or "xmpp.localhost" +local __domain = Lua.os.getenv("PROSODY_DOMAIN") or Lua.os.getenv("XMPP_DOMAIN") or "xmpp.localhost" + -- Route requests for unknown hosts to main VirtualHost. Must match http_host when set (Prosody docs). http_default_host = __http_host http_external_url = Lua.os.getenv("PROSODY_HTTP_EXTERNAL_URL") or (__http_scheme .. "://" .. __http_host .. "/") --- Port/interface defaults per Prosody 0.12 docs: --- http_ports = { 5280 } (already set above) --- https_ports = { 5281 } (already set above) --- http binds to loopback by default; https binds publicly for reverse proxy http_interfaces = { "*" } https_interfaces = { "*" } -- Static file serving root (Prosody's web root; reverse proxy in front) 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" } +-- Trusted reverse proxies for X-Forwarded-* handling +-- 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 = { @@ -398,7 +345,6 @@ http_headers = { ["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", -- Allow Converse.js CDN and XMPP endpoints for mod_conversejs ["Content-Security-Policy"] = @@ -412,7 +358,7 @@ http_file_share_expire_after = 30 * 24 * 3600 -- 30 days expiration -- http_file_share_path: not set; storage.http_file_share = "sql" stores files in DB http_file_share_global_quota = 10 * 1024 * 1024 * 1024 -- 10GB global quota --- BOSH/WebSocket tuning +-- BOSH/WebSocket tuning (XEP-0124, RFC 7395) bosh_max_inactivity = 60 bosh_max_polling = 5 bosh_max_requests = 2 @@ -435,28 +381,20 @@ http_paths = { status = "/status" } --- HTTP Status API (mod_http_status) for monitoring --- Allow access from any IP for monitoring (accessible from anywhere) --- http_status_allow_ips = { "*" } - --- 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", "127.0.0.0/8", "::1" } -- =============================================== -- TURN/STUN EXTERNAL SERVICES (XEP-0215) -- =============================================== --- External TURN/STUN server configuration for audio/video calls --- These services are provided by the COTURN container +-- External TURN/STUN server configuration for audio/video calls. +-- These services are provided by the COTURN container. +-- Credentials are generated dynamically using the shared secret (RFC 8489). --- TURN external configuration (XEP-0215) --- A secret shared with the TURN server, used to dynamically generate credentials +-- Shared secret with the TURN server for dynamic credential generation turn_external_secret = Lua.os.getenv("TURN_SECRET") or "devsecret" -- DNS hostname of the TURN (and STUN) server --- Use dedicated TURN subdomain for clean separation turn_external_host = Lua.os.getenv("TURN_EXTERNAL_HOST") or "turn.atl.network" -- Port number used by TURN (and STUN) server @@ -465,12 +403,11 @@ turn_external_port = Lua.tonumber(Lua.os.getenv("TURN_PORT")) or 3478 -- How long the generated credentials are valid (default: 1 day) turn_external_ttl = 86400 --- Whether to announce TURN (and STUN) over TCP, in addition to UDP --- Note: Most clients prefer UDP, but TCP can help with restrictive firewalls +-- Whether to announce TURN over TCP in addition to UDP +-- Note: Most clients prefer UDP; TCP helps with restrictive firewalls turn_external_tcp = true --- Optional: Port offering TURN over TLS (if using TURNS) --- Enable TLS support for secure TURN connections +-- Port for TURN over TLS (TURNS) turn_external_tls_port = Lua.tonumber(Lua.os.getenv("TURNS_PORT")) or 5349 -- =============================================== @@ -479,18 +416,17 @@ turn_external_tls_port = Lua.tonumber(Lua.os.getenv("TURNS_PORT")) or 5349 log = { { levels = { min = Lua.os.getenv("PROSODY_LOG_LEVEL") or "info" }, to = "console" }, - { levels = { min = Lua.os.getenv("PROSODY_LOG_LEVEL") or "info" }, to = "file", filename = "/var/lib/prosody/logs/prosody.log" }, } statistics = Lua.os.getenv("PROSODY_STATISTICS") or "internal" statistics_interval = Lua.os.getenv("PROSODY_STATISTICS_INTERVAL") or "manual" --- By default restrict to loopback; allow-list is expanded via CIDR below +-- Restrict to loopback by default; allow-list is expanded via CIDR below openmetrics_allow_ips = { Lua.os.getenv("PROSODY_OPENMETRICS_IP") or "127.0.0.1", } --- Fixed CIDR allow-list for internal scraping +-- Fixed CIDR allow-list for internal scraping (Docker network range) openmetrics_allow_cidr = Lua.os.getenv("PROSODY_OPENMETRICS_CIDR") or "172.16.0.0/12" -- =============================================== @@ -526,14 +462,14 @@ anti_spam_services = { "xmppbl.org" } -- TLS/SSL SECURITY -- =============================================== -- 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" }, --- } +-- 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" }, +} -- Let's Encrypt certificate location (mounted into the container) certificates = "certs" @@ -553,14 +489,40 @@ s2s_require_encryption = Lua.os.getenv("PROSODY_S2S_REQUIRE_ENCRYPTION") ~= "fal s2s_secure_auth = Lua.os.getenv("PROSODY_S2S_SECURE_AUTH") ~= "false" allow_unencrypted_plain_auth = Lua.os.getenv("PROSODY_ALLOW_UNENCRYPTED_PLAIN_AUTH") == "true" --- Channel binding strengthens SASL against MITM +-- Channel binding strengthens SASL against MITM attacks tls_channel_binding = Lua.os.getenv("PROSODY_TLS_CHANNEL_BINDING") ~= "false" --- Recommended privacy defaults for push notifications +-- Privacy defaults for push notifications: don't send message body/sender to push gateway -- See https://modules.prosody.im/mod_cloud_notify.html 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. +-- Tokens are also usable for OAUTHBEARER SASL auth. +allowed_oauth2_grant_types = { + "authorization_code", + "device_code", + -- "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 = true -- Enforce PKCE for security + +-- Dynamic client registration key (required; fail loudly if unset or still placeholder in prod) +local __oauth2_key = Lua.os.getenv("PROSODY_OAUTH2_REGISTRATION_KEY") +local __env = Lua.os.getenv("ATL_ENVIRONMENT") or + (Lua.os.getenv("XMPP_DOMAIN") == "xmpp.localhost" and "dev") or "prod" +if not __oauth2_key or __oauth2_key == "" or (__env ~= "dev" and __oauth2_key:match("^change_me_")) then + error("PROSODY_OAUTH2_REGISTRATION_KEY must be set to a secure value in .env") +end +oauth2_registration_key = __oauth2_key + -- =============================================== -- AUTHENTICATION & ACCOUNT POLICY -- =============================================== @@ -571,12 +533,6 @@ sasl_mechanisms = { "SCRAM-SHA-1", } --- Account lifecycle and registration hygiene --- user_account_management = { --- grace_period = Lua.tonumber(Lua.os.getenv("PROSODY_ACCOUNT_GRACE_PERIOD")) or (7 * 24 * 3600), --- deletion_confirmation = Lua.os.getenv("PROSODY_ACCOUNT_DELETION_CONFIRMATION") ~= "false", --- } - -- Disallow common/abusive usernames during registration block_registrations_users = { "administrator", @@ -593,113 +549,35 @@ block_registrations_users = { } block_registrations_require = Lua.os.getenv("PROSODY_BLOCK_REGISTRATIONS_REQUIRE") or "^[a-zA-Z0-9_.-]+$" --- Inline firewall rules for mod_firewall --- firewall_rules = [=[ --- %ZONE spam: log=debug --- RATE: 10 (burst 15) on full-jid --- TO: spam --- DROP. - --- %LENGTH > 262144 --- BOUNCE: policy-violation (Stanza too large) --- ]=] - -- =============================================== --- PUSH NOTIFICATIONS CONFIGURATION --- =============================================== --- Configuration for mod_cloud_notify and mod_cloud_notify_extensions --- XEP-0357: Push Notifications for mobile devices --- https://modules.prosody.im/mod_cloud_notify.html - --- =============================================== --- CLOUD NOTIFY CORE MODULE (XEP-0357) +-- PUSH NOTIFICATIONS (XEP-0357) -- =============================================== +-- Configuration for mod_cloud_notify and mod_cloud_notify_extensions. +-- Privacy settings (push_notification_with_body/sender) are in the TLS/Security section above. +-- See https://modules.prosody.im/mod_cloud_notify.html --- Body text for important messages when real body cannot be sent --- Used when messages are encrypted or have no body +-- Body text shown for important messages when the real body cannot be sent (e.g. encrypted) push_notification_important_body = Lua.os.getenv("PROSODY_PUSH_IMPORTANT_BODY") or "New Message!" -- Maximum persistent push errors before disabling notifications for a device --- Default: 16, Production: 16-32 depending on tolerance push_max_errors = Lua.tonumber(Lua.os.getenv("PROSODY_PUSH_MAX_ERRORS")) or 16 --- Maximum number of devices per user --- Default: 5, Production: 5-10 depending on user needs +-- Maximum number of registered push devices per user push_max_devices = Lua.tonumber(Lua.os.getenv("PROSODY_PUSH_MAX_DEVICES")) or 5 --- Extend smacks timeout if no push was triggered yet --- Default: 259200 (72 hours), Production: 259200-604800 (3-7 days) +-- Extend smacks hibernation timeout if no push was triggered yet (seconds; default 72h) push_max_hibernation_timeout = Lua.tonumber(Lua.os.getenv("PROSODY_PUSH_MAX_HIBERNATION_TIMEOUT")) or 259200 --- Privacy settings (configured in 21-security.cfg.lua) --- push_notification_with_body = false -- Don't send message body to push gateway --- push_notification_with_sender = false -- Don't send sender info to push gateway - --- =============================================== --- CLOUD NOTIFY EXTENSIONS (iOS CLIENT SUPPORT) --- =============================================== --- Enhanced push notification features for Siskin, Snikket iOS, and other clients --- that require additional extensions beyond XEP-0357 - --- Enable iOS-specific push notification features --- This module provides enhanced support for: --- - Siskin (iOS XMPP client) --- - Snikket (iOS XMPP client) --- - Other iOS clients with extended push requirements - --- =============================================== --- INTEGRATION WITH OTHER MODULES --- =============================================== - --- This module works with: --- - mod_smacks: Stream Management for connection resumption --- - mod_mam: Message Archive Management for offline messages --- - mod_carbons: Message Carbons for multi-device sync --- - mod_csi: Client State Indication for mobile optimization - --- =============================================== --- BUSINESS RULES AND MESSAGE HANDLING --- =============================================== --- The module automatically handles: --- - Offline messages stored by mod_offline --- - Messages stored by mod_mam (Message Archive Management) --- - Messages waiting in the smacks queue --- - Hibernated sessions via mod_smacks --- - Delayed acknowledgements via mod_smacks - --- =============================================== --- MONITORING AND DEBUGGING --- =============================================== --- To monitor push notification activity: --- - Check Prosody logs for "cloud_notify" entries --- - Monitor for push errors and device registration --- - Use prosodyctl shell to inspect push registrations - --- =============================================== --- CLIENT COMPATIBILITY --- =============================================== --- Supported clients include: --- - Conversations (Android) --- - Monal (iOS) --- - Siskin (iOS) - requires mod_cloud_notify_extensions --- - Snikket (iOS) - requires mod_cloud_notify_extensions --- - ChatSecure (iOS) --- - Xabber (Android) --- - Blabber (Android) - --- Note: Some iOS clients require mod_cloud_notify_extensions for full functionality --- as they use extensions not currently defined in XEP-0357 - -- =============================================== -- VIRTUAL HOSTS + COMPONENTS -- =============================================== --- Domain and registration settings -- Users are provisioned via Portal (mod_http_admin_api); disable self-registration. -local domain = Lua.os.getenv("PROSODY_DOMAIN") or "atl.chat" +local domain = Lua.os.getenv("PROSODY_DOMAIN") or Lua.os.getenv("XMPP_DOMAIN") or "atl.chat" allow_registration = Lua.os.getenv("PROSODY_ALLOW_REGISTRATION") == "true" -- Single VirtualHost --- http_host: map HTTP Host header (e.g. xmpp.atl.chat) to this VirtualHost; set PROSODY_HTTP_HOST when different from domain +-- http_host: map HTTP Host header (e.g. xmpp.atl.chat) to this VirtualHost; +-- set PROSODY_HTTP_HOST when different from domain VirtualHost(domain) http_host = __http_host @@ -791,56 +669,26 @@ default_mucs = { } -- MUC push notification configuration --- Ensure MUC messages trigger push notifications for offline users muc_notifications = Lua.os.getenv("PROSODY_MUC_NOTIFICATIONS") ~= "false" muc_offline_delivery = Lua.os.getenv("PROSODY_MUC_OFFLINE_DELIVERY") ~= "false" -restrict_room_creation = Lua.os.getenv("PROSODY_RESTRICT_ROOM_CREATION") == - "true" +restrict_room_creation = Lua.os.getenv("PROSODY_RESTRICT_ROOM_CREATION") == "true" muc_room_default_public = Lua.os.getenv("PROSODY_MUC_DEFAULT_PUBLIC") ~= "false" -muc_room_default_persistent = Lua.os.getenv("PROSODY_MUC_DEFAULT_PERSISTENT") ~= - "false" +muc_room_default_persistent = Lua.os.getenv("PROSODY_MUC_DEFAULT_PERSISTENT") ~= "false" muc_room_locking = Lua.os.getenv("PROSODY_MUC_LOCKING") == "true" -muc_room_default_public_jids = - Lua.os.getenv("PROSODY_MUC_DEFAULT_PUBLIC_JIDS") ~= "false" --- vcard_to_pep = true - --- General MUC configuration --- max_history_messages = 50 --- muc_room_lock_timeout = 300 --- muc_tombstones = true --- muc_room_cache_size = 1000 --- muc_room_default_public = true --- muc_room_default_members_only = false --- muc_room_default_moderated = false --- muc_room_default_persistent = true --- muc_room_default_language = "en" --- muc_room_default_change_subject = true +muc_room_default_public_jids = Lua.os.getenv("PROSODY_MUC_DEFAULT_PUBLIC_JIDS") ~= "false" -- MUC Message Archive Management (MAM) muc_log_by_default = Lua.os.getenv("PROSODY_MUC_LOG_BY_DEFAULT") ~= "false" muc_log_presences = Lua.os.getenv("PROSODY_MUC_LOG_PRESENCES") == "true" log_all_rooms = Lua.os.getenv("PROSODY_MUC_LOG_ALL_ROOMS") == "true" muc_log_expires_after = Lua.os.getenv("PROSODY_MUC_LOG_EXPIRES_AFTER") or "1y" -muc_log_cleanup_interval = Lua.tonumber(Lua.os.getenv( - "PROSODY_MUC_LOG_CLEANUP_INTERVAL")) or - 86400 -muc_max_archive_query_results = Lua.tonumber(Lua.os.getenv( - "PROSODY_MUC_MAX_ARCHIVE_QUERY_RESULTS")) or - 100 +muc_log_cleanup_interval = Lua.tonumber(Lua.os.getenv("PROSODY_MUC_LOG_CLEANUP_INTERVAL")) or 86400 +muc_max_archive_query_results = Lua.tonumber(Lua.os.getenv("PROSODY_MUC_MAX_ARCHIVE_QUERY_RESULTS")) or 100 muc_log_store = Lua.os.getenv("PROSODY_MUC_LOG_STORE") or "muc_log" muc_log_compression = Lua.os.getenv("PROSODY_MUC_LOG_COMPRESSION") ~= "false" muc_mam_smart_enable = Lua.os.getenv("PROSODY_MUC_MAM_SMART_ENABLE") == "true" --- muc_dont_archive_namespaces = { --- "http://jabber.org/protocol/chatstates", --- "urn:xmpp:jingle-message:0", --- "http://jabber.org/protocol/muc#user", --- } - --- muc_archive_policy = "all" --- muc_log_notification = true - -- Pastebin settings (mod_pastebin; pastes at /paste) pastebin_threshold = 800 pastebin_line_threshold = 6 @@ -870,7 +718,7 @@ http_host = __http_host http_external_url = Lua.os.getenv("PROSODY_UPLOAD_EXTERNAL_URL") or ("https://upload." .. domain .. "/") --- SOCKS5 Proxy component +-- SOCKS5 Proxy component (XEP-0065) Component("proxy." .. domain) "proxy65" ssl = { key = Lua.os.getenv("PROSODY_SSL_KEY") or @@ -900,7 +748,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" @@ -908,7 +756,6 @@ component_secret = Lua.os.getenv("BRIDGE_XMPP_COMPONENT_SECRET") or Lua.os.geten -- CONTACT INFO, ROLES, ACCOUNT CLEANUP -- =============================================== --- Domain and contact configuration (domain from VirtualHosts section above) local admin_email = Lua.os.getenv("PROSODY_ADMIN_EMAIL") or ("admin@" .. domain) local admin_jid = Lua.os.getenv("PROSODY_ADMIN_JID") or ("admin@" .. domain) 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 diff --git a/apps/unrealircd/AGENTS.md b/apps/unrealircd/AGENTS.md index 27719cf1..2efb0e71 100644 --- a/apps/unrealircd/AGENTS.md +++ b/apps/unrealircd/AGENTS.md @@ -18,7 +18,7 @@ config/ └── modules.sources.list contrib/relaymsg/ # relaymsg-atl.c (atl.chat fork, built as third/relaymsg-atl) -scripts/ # Docker entrypoint helpers +scripts/ # Docker entrypoint helpers, generate-oper-password, manage-modules, module-config config.settings # UnrealIRCd build settings third-party-modules.list # third/showwebirc, third/relaymsg-atl, etc. Containerfile # Docker image @@ -33,10 +33,28 @@ justfile # Loaded via: mod irc './apps/unrealircd' | `just irc shell` | Bash into IRC server container | | `just irc reload` | Send HUP to UnrealIRCd (reload config) | | `just irc test` | Run root pytest (../../tests/) | +| `just irc test-unit` | Run unit tests only | +| `just irc test-integration` | Run integration tests only | +| `just irc test-e2e` | Run end-to-end tests | +| `just irc test-protocol` | Run protocol tests | +| `just irc test-env` | Environment validation tests | +| `just irc test-irc` | IRC functionality + protocol tests | +| `just irc test-quick` | Quick environment check (compose config + status) | | `just irc ssl-setup` | Start cert-manager (Lego) | | `just irc ssl-status` | Show SSL cert status | +| `just irc ssl-renew` | Restart cert-manager for renewal check | +| `just irc ssl-logs` | Follow cert-manager logs | +| `just irc ssl-stop` | Stop cert-manager | +| `just irc ssl-clean` | Delete SSL certificates (destructive) | | `just irc modules-list` | List available modules | +| `just irc modules-installed` | List installed modules | | `just irc gencloak` | Run gencloak-update-env.sh | +| `just irc generate-password` | Generate IRC operator password | +| `just irc sra-bootstrap ` | Bootstrap first Atheme SRA account | +| `just irc lint` | Lint scripts (shellcheck) and Containerfiles (hadolint) | +| `just irc clean` | Stop services, prune images | +| `just irc reset` | Full reset (destructive: removes data, volumes, images) | +| `just irc info` | Show system info (Docker, disk, memory) | ## Third-Party Modules @@ -53,4 +71,4 @@ Config is generated by `scripts/prepare-config.sh` (run via `just init`). Templa ## Related - [Monorepo AGENTS.md](../../AGENTS.md) -- [docs/services/irc/](../../docs/services/irc/) +- [docs-old/services/irc/](../../docs-old/services/irc/) diff --git a/apps/unrealircd/Containerfile b/apps/unrealircd/Containerfile index 7f4c87a7..3aeaa96e 100644 --- a/apps/unrealircd/Containerfile +++ b/apps/unrealircd/Containerfile @@ -11,7 +11,7 @@ ARG GID=0 # Install build dependencies # hadolint ignore=DL3018 -RUN apk update && apk add --no-cache \ +RUN apk add --no-cache \ build-base \ wget \ pkgconfig \ @@ -87,9 +87,15 @@ FROM alpine:3.23@sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314 ARG UID=0 ARG GID=0 +LABEL org.opencontainers.image.source="https://github.com/allthingslinux/atl.chat" \ + org.opencontainers.image.description="UnrealIRCd IRC server for atl.chat" \ + org.opencontainers.image.licenses="MIT" \ + org.opencontainers.image.vendor="All Things Linux" \ + org.opencontainers.image.title="atl-unrealircd" + # Install runtime dependencies only (no build toolchain) # hadolint ignore=DL3018 -RUN apk update && apk add --no-cache \ +RUN apk add --no-cache \ openssl \ pcre2 \ c-ares \ @@ -115,14 +121,9 @@ RUN if [ "${UID}" = "0" ]; then \ # Copy built application from builder (includes pre-compiled third-party modules) COPY --from=builder /home/unrealircd/unrealircd /home/unrealircd/unrealircd -# Set ownership based on UID/GID arguments -RUN if [ "${UID}" = "0" ]; then \ - chown -R 1000:1000 /home/unrealircd/unrealircd; \ - else \ - chown -R ${UID}:${GID} /home/unrealircd/unrealircd; \ - fi - # Create directory structure with proper permissions +# Logging goes to stdout (/dev/stdout) — no logs volume mount needed, +# but UnrealIRCd expects the compiled LOGDIR to exist at runtime RUN mkdir -p /home/unrealircd/unrealircd/config \ /home/unrealircd/unrealircd/logs \ /home/unrealircd/unrealircd/data \ diff --git a/apps/unrealircd/config/unrealircd.conf.template b/apps/unrealircd/config/unrealircd.conf.template index 0742964f..67cd6042 100644 --- a/apps/unrealircd/config/unrealircd.conf.template +++ b/apps/unrealircd/config/unrealircd.conf.template @@ -3,9 +3,10 @@ * ============================================================================ * All modules and RPC config inlined here. On upgrade: diff against the new * release's modules.default.conf and modules.optional.conf to pick up changes. + * Docs: https://www.unrealircd.org/docs/Configuration * ============================================================================ */ -/* ---------- Core modules (from modules.default.conf) ---------- */ +/* ---------- Core user commands ---------- */ loadmodule "admin"; loadmodule "away"; loadmodule "invite"; @@ -34,6 +35,8 @@ loadmodule "watch"; loadmodule "whox"; loadmodule "whois"; loadmodule "whowas"; + +/* ---------- Additional client features ---------- */ loadmodule "botmotd"; loadmodule "cap"; loadmodule "cycle"; @@ -49,6 +52,8 @@ loadmodule "time"; loadmodule "userip"; loadmodule "vhost"; loadmodule "history"; + +/* ---------- IRCOp commands ---------- */ loadmodule "addmotd"; loadmodule "addomotd"; loadmodule "chghost"; @@ -78,6 +83,8 @@ loadmodule "trace"; loadmodule "tsctl"; loadmodule "unsqline"; loadmodule "jumpserver"; + +/* ---------- Server-to-server protocol ---------- */ loadmodule "eos"; loadmodule "md"; loadmodule "netinfo"; @@ -94,6 +101,8 @@ loadmodule "sreply"; loadmodule "unreal_server_compat"; loadmodule "sendsno"; loadmodule "sendumode"; + +/* ---------- Services protocol (SVS* commands) ---------- */ loadmodule "svsjoin"; loadmodule "svskill"; loadmodule "svslusers"; @@ -109,6 +118,8 @@ loadmodule "svssno"; loadmodule "svswatch"; loadmodule "svso"; loadmodule "svslogin"; + +/* ---------- Channel modes ---------- */ loadmodule "chanmodes/chanowner"; loadmodule "chanmodes/chanadmin"; loadmodule "chanmodes/chanop"; @@ -142,6 +153,8 @@ loadmodule "chanmodes/nokick"; loadmodule "chanmodes/regonlyspeak"; loadmodule "chanmodes/secureonly"; loadmodule "chanmodes/history"; + +/* ---------- User modes ---------- */ loadmodule "usermodes/wallops"; loadmodule "usermodes/bot"; loadmodule "usermodes/servicebot"; @@ -153,6 +166,8 @@ loadmodule "usermodes/nokick"; loadmodule "usermodes/regonlymsg"; loadmodule "usermodes/secureonlymsg"; loadmodule "usermodes/privdeaf"; + +/* ---------- Extended bans ---------- */ loadmodule "extbans/join"; loadmodule "extbans/quiet"; loadmodule "extbans/nickchange"; @@ -170,6 +185,8 @@ loadmodule "extbans/msgbypass"; loadmodule "extbans/flood"; loadmodule "extbans/asn"; loadmodule "extbans/inherit"; + +/* ---------- IRCv3 capabilities & message tags ---------- */ loadmodule "account-notify"; loadmodule "message-tags"; loadmodule "batch"; @@ -183,6 +200,8 @@ loadmodule "typing-indicator"; loadmodule "channel-context"; loadmodule "reply-tag"; loadmodule "clienttagdeny"; + +/* ---------- Security & TLS policy ---------- */ loadmodule "sts"; loadmodule "link-security"; loadmodule "plaintext-policy"; @@ -191,6 +210,8 @@ loadmodule "monitor"; loadmodule "extended-monitor"; loadmodule "standard-replies"; loadmodule "no-implicit-names"; + +/* ---------- JSON-RPC (remote management) ---------- */ loadmodule "rpc/rpc"; loadmodule "rpc/stats"; loadmodule "rpc/user"; @@ -202,6 +223,8 @@ loadmodule "rpc/name_ban"; loadmodule "rpc/spamfilter"; loadmodule "rpc/log"; loadmodule "rpc/whowas"; + +/* ---------- Security & anti-abuse ---------- */ loadmodule "ident_lookup"; loadmodule "certfp"; loadmodule "tls_cipher"; @@ -234,6 +257,8 @@ loadmodule "spamreport"; loadmodule "crule"; loadmodule "maxperip"; loadmodule "utf8functions"; + +/* ---------- GeoIP (classic MaxMind database, auto-refreshed) ---------- */ loadmodule "geoip_classic"; @if module-loaded("geoip_classic") set { @@ -246,13 +271,16 @@ set { } @endif -/* ---------- Optional modules (ircops, staff, websocket, antirandom, etc.) ---------- */ +/* ---------- Optional modules ---------- */ loadmodule "ircops"; loadmodule "staff"; @if module-loaded("staff") set { staff-file "network.staff"; } @endif loadmodule "nocodes"; + +/* antirandom: blocks connections with random-looking nick/ident/realname (bot detection). + * Thresholds are tuned conservatively; trusted IPs (Docker network) are exempt. */ loadmodule "antirandom"; @if module-loaded("antirandom") set { @@ -265,13 +293,16 @@ set { show-failedconnects yes; except { webirc yes; - ip { 192.168.*; 127.*; 172.17.*; } + ip { 192.168.*; 127.*; 172.16.0.0/12; } } } } @endif + loadmodule "webserver"; loadmodule "websocket"; + +/* antimixedutf8: blocks messages mixing scripts (e.g. Latin+Cyrillic) used for spam/homoglyph attacks */ loadmodule "antimixedutf8"; @if module-loaded("antimixedutf8") set { @@ -284,12 +315,14 @@ set { } @endif -/* ---------- RPC: Unix socket + memory log + rpc-class ---------- */ +/* ---------- RPC Unix socket (for local management tools) ---------- */ listen { file "/home/unrealircd/unrealircd/data/rpc.socket"; mode 0770; options { rpc; } } + +/* In-memory log buffer (accessible via JSON-RPC; excludes verbose join/part/kick events) */ log { source { all; @@ -308,9 +341,12 @@ log { } } } + +/* ---------- RPC permission classes ---------- */ rpc-class full { permissions { all; } } + rpc-class read-only { permissions { rpc; @@ -327,7 +363,6 @@ rpc-class read-only { } } -/* ---------- Includes (help, badwords, etc.) ---------- */ include "help/help.conf"; include "badwords.conf"; include "spamfilter.conf"; @@ -335,7 +370,7 @@ include "dccallow.conf"; include "operclass.default.conf"; include "snomasks.default.conf"; -/* ---------- Project-specific ---------- */ +/* ---------- Third-party / custom modules ---------- */ loadmodule "cloak_sha256"; loadmodule "third/showwebirc"; loadmodule "third/metadata"; @@ -366,13 +401,32 @@ me { sid "001"; } -/* Support Proxy Protocol from NPM on the same network */ -webirc { - mask ${ATL_GATEWAY_IP}/32; - password "change_me_webirc_password"; +/* Proxy blocks - pass real client IPs to UnrealIRCd from web gateways and reverse proxies. + * Docs: https://www.unrealircd.org/docs/Proxy_block */ + +/* NPM/gateway WEBIRC: passes real client IP via WEBIRC protocol. + * Used by web gateways (KiwiIRC, The Lounge, etc.) connecting through NPM. */ +proxy npm-webirc { + type webirc; + match { ip ${ATL_GATEWAY_IP}/32; } + password "${ATL_WEBIRC_PASSWORD}"; } -/* The Lounge web client - passes real user IP via WEBIRC */ +/* NPM WebSocket reverse proxy: trust X-Forwarded-For headers from NPM/Tailscale. + * When NPM proxies WebSocket connections (port 8000), it sends X-Forwarded-For + * with the real client IP. Hosts matching this are auto-exempted from connect + * floods and blacklist checks (UnrealIRCd 6.1.8+). + * + * 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 the 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 IRC client: passes real user IP via WEBIRC. + * Connects from the Docker network. */ proxy thelounge { type webirc; match { ip 172.16.0.0/12; } @@ -406,29 +460,36 @@ class servers { recvq 8000; } +/* Unix socket for Atheme services — shares network namespace with IRC server + * (atl-irc-services uses network_mode: service:atl-irc-server). + * Atheme connects here via 127.0.0.1 port 6901 as configured in atheme.conf. */ listen { file "/home/unrealircd/unrealircd/data/services.sock"; mode 0770; -} /* Unix socket for services - exempt from plaintext security */ +} +/* Standard IRC over TLS — primary client connection port */ listen { ip *; port 6697; - options { tls; } /* Support direct TLS. Proxy Protocol handled via webirc block. */ + options { tls; } } +/* Server-to-server over TLS — used for linking remote IRC servers */ listen { ip *; port 6900; options { tls; serversonly; } } +/* Server-to-server plaintext — used by Atheme services (localhost only via shared network namespace) */ listen { ip *; port 6901; options { serversonly; } } +/* JSON-RPC over TLS — for webpanel and management tools */ listen { ip *; port 8600; @@ -439,11 +500,12 @@ listen { } } +/* WebSocket port — TLS terminated at NPM; UnrealIRCd receives plaintext WebSocket */ listen { ip *; port 8000; options { - /* tls; */ /* Disabled because SSL is terminated at NPM */ + /* tls; */ /* Disabled: SSL is terminated at NPM */ websocket { type text; } } /* tls-options { @@ -455,13 +517,6 @@ listen { } */ } -/* ============================================================================ - * TLS/SSL CONFIGURATION - Enhanced Security Settings - * ============================================================================ - * Based on UnrealIRCd 6.x security recommendations and Let's Encrypt best practices - * See: https://www.unrealircd.org/docs/SSL/TLS - * ============================================================================ */ - set { tls { /* Certificate configuration - data/certs mounted at certs/, live// layout */ @@ -470,98 +525,87 @@ set { /* CA certificate bundle for certificate validation */ trusted-ca-file "/home/unrealircd/unrealircd/config/tls/curl-ca-bundle.crt"; - /* Modern TLS protocols - TLS 1.2 and 1.3 only (no older insecure versions) */ + /* TLS 1.2 and 1.3 only — matches UnrealIRCd 6.1.9+ defaults */ protocols "TLSv1.2,+TLSv1.3"; - /* Strong TLS 1.2 ciphers with Forward Secrecy - ECDHE provides perfect forward secrecy */ + /* TLS 1.2 cipher suites — ECDHE only (forward secrecy; matches UnrealIRCd 6.1.9+ defaults) */ ciphers "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"; - /* TLS 1.3 cipher suites - modern, secure, and fast */ + /* TLS 1.3 cipher suites (matches UnrealIRCd 6.1.9+ defaults) */ ciphersuites "TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256"; - /* Modern ECDH curves for better security */ - /* ECDH groups - updated for UnrealIRCd 6.2.0+ */ - /* Note: ecdh-curves is deprecated, use 'groups' instead */ - /* X25519MLKEM768: Post-quantum cryptography hybrid group (requires OpenSSL 3.5.0+) */ - /* X25519: Standard curve used by most clients today */ + /* Key exchange groups — includes post-quantum hybrid X25519MLKEM768 (UnrealIRCd 6.2.0+). + * X25519MLKEM768 requires OpenSSL 3.5.0+; falls back gracefully on older builds. + * Docs: https://www.unrealircd.org/docs/TLS_Ciphers_and_protocols */ groups "X25519MLKEM768:X25519:secp521r1:secp384r1:prime256v1"; - /* Legacy setting for compatibility */ + /* Legacy fallback for OpenSSL builds that don't support the 'groups' directive */ ecdh-curves "X25519:secp521r1:secp384r1:prime256v1"; - /* Additional TLS options for enhanced security */ options { - /* Don't require client certificates by default */ no-client-certificate; } - /* Strict Transport Security (STS) - Automatically redirect users to TLS */ + /* Strict Transport Security (STS) — redirects clients to TLS automatically. + * Clients cache this for 'duration' time; start low and increase gradually: + * 1m -> 1d -> 30d -> 180d + * WARNING: Cached by clients for the full duration period. */ sts-policy { - /* Redirect to secure port 6697 */ port 6697; - /* Conservative duration - start low and gradually increase - * Recommended progression: 1m -> 1d -> 30d -> 180d - * WARNING: Users cache this setting for 'duration' time! - */ duration ${IRC_STS_DURATION}; - /* Preload in client (optional, for advanced security) */ preload ${IRC_STS_PRELOAD}; } - /* Certificate expiry notifications */ certificate-expiry-notification yes; } - /* Plaintext policy - control who can use non-TLS connections */ + /* Plaintext policy — control who can use non-TLS connections. + * Docs: https://www.unrealircd.org/docs/Set_block#set::plaintext-policy */ plaintext-policy { - /* Allow localhost server connections (Atheme) - localhost is exempt from link security */ + /* Allow localhost server connections (Atheme) — localhost is exempt from link security */ server allow; - /* Allow users for now, but they'll be redirected to TLS via STS if client supports it */ + /* Allow users for now; STS will redirect TLS-capable clients automatically */ user allow; - /* IRCOps should always use TLS for security */ + /* IRCOps must always use TLS */ oper deny; - /* Helpful message for non-STS clients when we want to enforce TLS */ user-message "For security, please connect using TLS on port 6697. Your client will be automatically redirected if it supports STS."; oper-message "IRCOps must use TLS connections. Please connect to port 6697 with SSL/TLS enabled."; } - /* Outdated TLS policy - warn users about insecure TLS protocols/ciphers */ + /* Outdated TLS policy — warn or block connections using old TLS protocols/ciphers */ outdated-tls-policy { - /* Warn users about outdated TLS (but still allow connection) */ user warn; - /* Deny IRCOps with outdated TLS */ oper deny; - /* Deny server links with outdated TLS */ server deny; - /* Custom messages for better user experience */ user-message "Your IRC client is using an outdated TLS protocol or cipher. Please update your IRC client for better security."; oper-message "IRCOps must use modern TLS. Please update your IRC client."; } } -/* Link block for services - localhost connections are exempt from link security */ +/* Link block for Atheme 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}"; class servers; } -/* U-lines give services special privileges */ +/* U-lines give services special privileges (mode enforcement, nick protection, etc.) */ ulines { ${IRC_SERVICES_SERVER}; } -/* Allow Configuration - Who can connect */ +/* Allow configuration — who can connect */ allow { mask *@*; class clients; maxperip 5; } -/* Authentication Requirements - Uncomment and customize as needed */ +/* Authentication Requirements — uncomment and customize as needed. + * Docs: https://www.unrealircd.org/docs/Require_authentication_block */ /* Example 1: Require authentication for users from specific ISPs/countries require authentication { @@ -587,7 +631,7 @@ require authentication { }; */ -/* Ban nicknames that are reserved for services */ +/* Ban nicknames reserved for services or system use */ ban nick { mask "*ChanServ*"; reason "Reserved for Services"; @@ -688,7 +732,8 @@ ban nick { reason "Unknown user not allowed"; } -/* Blacklist configuration for security */ +/* DNSBL blacklists — block known proxies, drones, and TOR exit nodes. + * Docs: https://www.unrealircd.org/docs/Blacklist_block */ blacklist dronebl { dns { name dnsbl.dronebl.org; @@ -722,6 +767,14 @@ 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}"; @@ -730,38 +783,43 @@ set { stats-server "${IRC_DOMAIN}"; sasl-server "${IRC_SERVICES_SERVER}"; - /* Normal defaults */ help-channel "#support"; prefix-quit "quit"; - /* Cloak keys - from .env; generate with: just irc gencloak */ + /* Cloak keys — from .env; generate new keys with: just irc gencloak */ cloak-keys { "${IRC_CLOAK_KEY_1}"; "${IRC_CLOAK_KEY_2}"; "${IRC_CLOAK_KEY_3}"; } - /* Hidden host prefix - must match cloak-prefix on all servers */ + /* Hidden host prefix — must match cloak-prefix on all servers in the network */ hiddenhost-prefix "${IRC_CLOAK_PREFIX}"; - /* Cloaking method - use IP style for better privacy (optional but recommended) */ + /* Cloak method: "ip" hashes the full IP address for better privacy */ cloak-method ip; } -/* Operator Configuration */ -oper admin { +/* Die/Restart password: protects /DIE and /RESTART from accidental or unauthorized use */ +drpass { + restart "${IRC_DRPASS}"; + die "${IRC_DRPASS}"; +} + +/* Staff oper block — network staff with full privileges and vhost */ +oper staff { class opers; - mask *@*; + mask *; password "${IRC_OPER_PASSWORD}"; operclass netadmin-with-override; - swhois "is the Network Administrator"; - vhost "${IRC_STAFF_VHOST}"; - require-modes ""; + swhois "is a member of the Network Staff"; + vhost "${DOLLAR}account@${IRC_STAFF_VHOST}"; + require-modes "z"; } -/* Bridge oper: creates channels and sets +P (permanent) so they persist when empty. - * Needs channel override so IRCOp can set +P without being chanop. - * relaymsg: allows RELAYMSG for stateless bridging (third/relaymsg-atl, atl.chat fork). */ +/* Bridge operclass — minimal privileges needed for the bridge bot: + * - channel override: allows setting +P (permanent) without being chanop + * - relaymsg: allows RELAYMSG for stateless bridging (third/relaymsg-atl) */ operclass bridge-oper { parent locop; permissions { @@ -770,49 +828,33 @@ operclass bridge-oper { } } -oper atl-bridge { +oper bridge { class opers; - mask *@*atl-bridge*; + mask *@*bridge*; password "${BRIDGE_IRC_OPER_PASSWORD}"; operclass bridge-oper; + vhost "bridge@bridge.atl.chat"; } -/* RPC Configuration for Webpanel */ +/* RPC user for webpanel — restricted to Docker network and localhost */ 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}"; } -/* Automatic vhost for registered users */ +/* Automatic vhost for registered users — applied at login via services */ vhost { auto-login yes; mask { identified yes; } - vhost ${IRC_DOMAIN}; -} - -/* Logging Configuration */ - -/* Traditional text logging - human readable */ -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 "${IRC_LOG_PATH}/ircd.log" { maxsize 100M; } - } + vhost "${DOLLAR}account@${IRC_ROOT_DOMAIN}"; } -/* JSON logging - machine readable with detailed information */ +/* Log to stdout (JSON) — captured by Docker json-file driver with rotation. + * No file-based logging; use `docker compose logs atl-irc-server` to read. + * Verbose join/part/kick events excluded to reduce noise. */ log { source { all; @@ -825,53 +867,73 @@ log { !kick.REMOTE_CLIENT_KICK; } destination { - file "${IRC_LOG_PATH}/ircd.json.log" { maxsize 250M; type json; } + file "/dev/stdout" { type json; } } } -/* 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-join "+nt"; /* default channel modes when a new channel is created */ + modes-on-connect "+ixw"; /* user modes set automatically on connect */ + modes-on-oper "+xwsH"; /* additional modes set when someone becomes IRCOp */ + snomask-on-oper "+bBcdfkqsSo"; /* server notice mask for opers */ + modes-on-join "+nt"; /* default channel modes for newly created channels */ - /* Restrict user modes - prevent users from disabling cloaking for security */ - restrict-usermodes "x"; /* Users cannot remove +x (cloaking) */ + /* Prevent users from disabling their cloak (+x) for privacy */ + restrict-usermodes "x"; - /* Configure who can see webirc and websocket information in WHOIS */ + /* Control WHOIS visibility of webirc/websocket metadata */ whois-details { webirc { everyone none; self full; oper full; }; websocket { everyone none; self full; oper full; }; } - oper-auto-join "#mod-chat"; /* IRCOps are auto-joined to this channel */ + oper-auto-join "#mod-chat"; /* IRCOps are auto-joined to this channel on oper-up */ options { hide-ulines; /* hide U-lines in /MAP and /LINKS */ show-connect-info; /* show "looking up your hostname" messages on connect */ } - /* Authentication prompt is enabled by default via the authprompt module */ + /* Authentication prompt enabled by default via the authprompt module */ 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. - */ + /* Restrict /STATS to safe statistics 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. + * Docs: https://www.unrealircd.org/docs/Connthrottle */ + 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; + } + } + anti-spam-quit-message-time 10s; - /* Channel history configuration - enables history playback for channels - * The defaults are already good for most setups. This enables the feature. - * Users can set +H on channels to store history (e.g. /MODE #channel +H 50:1d) - * See: https://www.unrealircd.org/docs/Channel_history - */ history { channel { - /* Use defaults - 200 lines max, 31 days retention */ + /* Use defaults — 200 lines max, 31 days retention */ } }; - /* Restrict commands for new users (security hardening) */ + /* restrict-commands: delay certain commands for new/untrusted users. + * Identified users and those with reputation score ≥24 are exempt. + * Docs: https://www.unrealircd.org/docs/Set_block#set::restrict-commands */ restrict-commands { list { except { identified yes; reputation-score 24; } @@ -881,36 +943,47 @@ 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 */ + /* Anti-flood settings — controls flood protection for channels and users. + * 'known-users' = identified to services OR IP connected for >2h in past X days. + * 'unknown-users' = everyone else (stricter limits). + * Docs: https://www.unrealircd.org/docs/Anti-flood_settings */ anti-flood { - /* Channel anti-flood protection (UnrealIRCd 6.2.0+) */ + /* Channel flood profiles — used with channel mode +F. + * Format: [c#C,j#R,k#K,m#M,n#N]: */ channel { - /* Default profile for all channels - can be overridden with +F mode */ default-profile normal; - /* Custom profiles - these override the built-in ones if needed */ profile very-strict { flood-mode "[7c#C15,10j#R10,10k#K15,30m#M10,10n#N15]:15"; } - profile strict { flood-mode "[7c#C15,15j#R10,10k#K15,40m#M10,10n#N15]:15"; } - profile normal { flood-mode "[7c#C15,30j#R10,10k#K15,40m#M10,10n#N15]:15"; } - profile relaxed { flood-mode "[7c#C15,45j#R10,10k#K15,60m#M10,10n#N15]:15"; } + profile strict { flood-mode "[7c#C15,15j#R10,10k#K15,40m#M10,10n#N15]:15"; } + profile normal { flood-mode "[7c#C15,30j#R10,10k#K15,40m#M10,10n#N15]:15"; } + profile relaxed { flood-mode "[7c#C15,45j#R10,10k#K15,60m#M10,10n#N15]:15"; } profile very-relaxed { flood-mode "[7c#C15,60j#R10,10k#K15,90m#M10,10n#N15]:15"; } } - /* Settings that apply to everyone */ + /* Settings that apply to all users regardless of group */ everyone { - /* Connection flood protection - relaxed for testing: 20 connections per 10 seconds per IP */ - connect-flood 20:10; + connect-flood 5:60; /* max 5 connection attempts per IP per 60 seconds */ - /* Handshake data flood protection */ + /* Handshake data flood — blocks clients sending >4k during IRC handshake */ handshake-data-flood { amount 4k; ban-action zline; ban-time 10m; } - /* Target flood protection - prevents high-rate flooding of channels/users */ + /* Target flood — limits total message rate to a channel/user. + * This is NOT per-sender; it limits the aggregate rate to the target. + * Drop (not kill) is used because innocent users may send the Nth message. */ target-flood { channel-privmsg 45:5; channel-notice 15:5; @@ -921,242 +994,36 @@ set { } } - /* Settings for known users (registered/trusted users) */ + /* Known users (identified/trusted) — higher limits and lower lag penalties */ known-users { - nick-flood 4:60; /* 4 nick changes per 60 seconds */ - join-flood 4:90; /* 4 rejoins to same channel per 90 seconds */ - away-flood 4:120; /* 4 away changes per 120 seconds */ - invite-flood 5:60; /* 5 invites per 60 seconds */ - knock-flood 4:120; /* 4 knocks per 120 seconds */ - vhost-flood 3:90; /* 3 vhost changes per 90 seconds */ + nick-flood 4:60; + join-flood 4:90; + away-flood 4:120; + invite-flood 5:60; + knock-flood 4:120; + vhost-flood 3:90; max-concurrent-conversations { - users 12; /* Can message 12 different users */ - new-user-every 10s; /* Then 1 new user every 10 seconds */ + users 12; /* can message 12 different users before rate limiting */ + new-user-every 10s; /* then 1 new conversation target every 10 seconds */ } - lag-penalty 600; /* Lower lag penalty for trusted users */ - lag-penalty-bytes 200; /* More bytes allowed before extra penalty */ + lag-penalty 600; /* fake lag per command (ms); lower = more permissive */ + lag-penalty-bytes 200; /* bytes before extra lag penalty; higher = more permissive */ } - /* Settings for unknown users (new/untrusted users) */ + /* Unknown users (new/untrusted) — stricter limits */ unknown-users { - nick-flood 2:60; /* 2 nick changes per 60 seconds */ - join-flood 2:90; /* 2 rejoins to same channel per 90 seconds */ - away-flood 3:120; /* 3 away changes per 120 seconds */ - invite-flood 2:60; /* 2 invites per 60 seconds */ - knock-flood 2:120; /* 2 knocks per 120 seconds */ - vhost-flood 2:90; /* 2 vhost changes per 90 seconds */ + nick-flood 2:60; + join-flood 2:90; + away-flood 3:120; + invite-flood 2:60; + knock-flood 2:120; + vhost-flood 2:90; max-concurrent-conversations { - users 4; /* Can message 4 different users */ - new-user-every 20s; /* Then 1 new user every 20 seconds */ + users 4; /* can message 4 different users before rate limiting */ + new-user-every 20s; /* then 1 new conversation target every 20 seconds */ } - lag-penalty 1200; /* Higher lag penalty for unknown users */ - lag-penalty-bytes 80; /* Fewer bytes allowed before extra penalty */ + lag-penalty 1200; /* higher fake lag penalty for unknown users */ + lag-penalty-bytes 80; /* stricter byte threshold before extra lag penalty */ } } } - -/* - * ANTI-FLOOD PROTECTION OVERVIEW - * =============================== - * - * The anti-flood settings above provide comprehensive protection against various types of flooding: - * - * CONNECTION FLOOD PROTECTION: - * - Limits connections per IP address to prevent connection bombing - * - Default: 3 connections per 60 seconds per IP - * - * HANDSHAKE DATA FLOOD PROTECTION: - * - Prevents abuse during IRC protocol handshake - * - Blocks excessive data during connection establishment - * - Results in 10-minute Z-line for violators - * - * TARGET FLOOD PROTECTION: - * - Prevents high-rate flooding of channels and users - * - Limits total messages to any target (channel/user) regardless of source count - * - Protects against coordinated flooding attacks - * - * USER-BASED FLOOD PROTECTION: - * - Different limits for known-users vs unknown-users - * - Known users: Registered with services OR reputation score 25+ - * - Unknown users: New/unregistered users with lower trust - * - * COMMAND RATE LIMITING: - * - Nick changes, channel joins, away messages, invites, knocks - * - Private message conversation limits to prevent spam bots - * - Lag penalty system prevents command flooding - * - * CHANNEL ANTI-FLOOD PROTECTION (UnrealIRCd 6.2.0+): - * - Default "normal" profile applied to all channels automatically - * - Channel operators can override with MODE +F : - * +F normal - Standard protection (default) - * +F relaxed - For busy channels with lots of activity - * +F strict - For channels needing extra protection - * +F off - Disable channel anti-flood (not recommended) - * - Use MODE #channel F to see current effective settings - * - Protection covers: messages, notices, joins, parts, nick changes, kicks, CTCPs - * - * SECURITY PHILOSOPHY: - * - Balanced approach: Strict enough to prevent abuse, lenient enough for normal use - * - Graduated penalties: Unknown users have stricter limits than known users - * - Multiple protection layers: Per-IP, per-user, per-target, and per-channel limits - */ - -/* - * STRICT TRANSPORT SECURITY (STS) SECURITY PROGRESSION - * ==================================================== - * - * STS automatically redirects capable clients from plaintext (port 6667) to TLS (port 6697). - * - * PHASE 1 - TESTING (First week): - * - IRC_STS_DURATION=1m (1 minute cache) - * - IRC_STS_PRELOAD=no - * - plaintext-policy user=allow (users can still use plaintext if STS fails) - * - * PHASE 2 - GRADUAL ROLLOUT (After 1 week): - * - IRC_STS_DURATION=1d (1 day cache) - * - Monitor for any user connection issues - * - * PHASE 3 - MATURE NETWORK (After 1 month): - * - IRC_STS_DURATION=30d (30 days cache) - * - Consider IRC_STS_PRELOAD=yes for advanced security - * - * PHASE 4 - MAXIMUM SECURITY (After 6 months): - * - IRC_STS_DURATION=180d (6 months cache) - * - Optional: Set plaintext-policy user=deny to force TLS for all users - * - Optional: Remove port 6667 listen block entirely - * - * SUPPORTED CLIENTS: mIRC, AdiIRC, IRCCloud, The Lounge, Ambassador, GLirc, - * CoreIRC, BitBot, Limnoria, and many others. - * - * NON-STS CLIENTS: Will continue using plaintext unless plaintext-policy denies them. - */ - -/* - * CHANNEL ANTI-FLOOD RECOMMENDATIONS - * ================================== - * - * Operators can use channel mode +f for automatic flood protection: - * - * Recommended for large channels (50+ users): - * /MODE #channel +f [50j#i10,75m#m10,7c#C15,10n#N15,30k#K10]:15 - * - * Recommended for medium channels (10-50 users): - * /MODE #channel +f [30j#i10,40m#m10,7c#C15,10n#N15,30k#K10]:15 - * - * Recommended for small channels (<10 users): - * /MODE #channel +f [15j#i5,20m#m5,5c#C10,7n#N10,15k#K5]:15 - * - * Format explanation: - * - 30j#i10 = 30 joins per 15 seconds, set +i for 10 minutes if exceeded - * - 40m#m10 = 40 messages per 15 seconds, set +m for 10 minutes if exceeded - * - 7c#C15 = 7 CTCPs per 15 seconds, set +C for 15 minutes if exceeded - * - 10n#N15 = 10 nick changes per 15 seconds, set +N for 15 minutes if exceeded - * - 30k#K10 = 30 knocks per 15 seconds, set +K for 10 minutes if exceeded - * - * Emergency flood protection modes for operators: - * - +M = Only registered users can talk - * - +N = No nick changes allowed - * - +C = No CTCPs allowed - * - +K = No /KNOCK allowed - * - +i = Invite only - * - +m = Moderated (only voiced+ can talk) - */ - -/* - * SASL AUTHENTICATION GUIDE - * ========================= - * - * SASL allows users to authenticate to services before they are fully online. - * This configuration enables SASL support with modern SCRAM-SHA-256 authentication. - * - * BENEFITS: - * - Universal identification method (no need for /NS IDENTIFY) - * - Early authentication receives proper vhost and modes - * - Can join registered-only channels immediately - * - Works during connection throttling attacks - * - Enables selective authentication requirements - * - * CLIENT SETUP EXAMPLES: - * - * mIRC: - * - File -> Select Server -> Edit -> Login Method: SASL (/CAP) - * - Enter your services password - * - * irssi: - * - /NETWORK ADD -sasl_username yourname -sasl_password yourpass -sasl_mechanism SCRAM-SHA-256 NetworkName - * - * HexChat: - * - Network List -> Edit -> Login method: SASL (username + password) - * - Enter your services username and password - * - * TESTING SASL: - * - Connect to server and run: /QUOTE CAP LS - * - Look for 'sasl' in the capability list - * - If present, SASL is working correctly - * - * SOFT BANS (Dynamic Authentication Requirements): - * IRCOps can require authentication dynamically using soft bans: - * - /GLINE %*@*.suspicious-domain.com 0 Please authenticate to access this server - * - /KLINE %*@192.168.1.* 0 Authentication required from your IP range - * The % prefix makes the ban only affect unauthenticated users. - * - * AUTHENTICATION TYPES SUPPORTED: - * - SCRAM-SHA-256 (most secure, recommended) - * - SCRAM-SHA-1 (legacy compatibility) - * - PLAIN (basic, use only over TLS) - * - * SECURITY NOTES: - * - Always use TLS when possible to protect credentials - * - SCRAM mechanisms are much more secure than PLAIN - * - Consider requiring authentication for proxy/VPN users - * - Monitor for authentication bypass attempts - */ - -/* - * CHANNEL HISTORY GUIDE - * ===================== - * - * Channel history allows users to see previous messages when joining channels. - * This server supports multiple methods for retrieving channel history. - * - * ENABLING HISTORY: - * To enable history for a channel, set channel mode +H: - * - /MODE #channel +H 50:1d (store 50 lines for 1 day) - * - /MODE #channel +H 100:7d (store 100 lines for 7 days) - * - /MODE #channel +H 1000:31d (store 1000 lines for 31 days) - * - * HISTORY RETRIEVAL METHODS: - * 1. ON-JOIN PLAYBACK: Automatic history shown when joining (max 15 lines) - * 2. HISTORY COMMAND: Manual retrieval via /HISTORY #channel [lines] - * 3. CHATHISTORY (IRCv3): Advanced client-driven history fetching - * - * CLIENT SUPPORT: - * - * FULL CHATHISTORY SUPPORT (unlimited history): - * - KiwiIRC, Gamja, Goguma, Senpai - * - * PARTIAL SUPPORT (on-join + HISTORY command): - * - AdiIRC, Colloquy, HexChat, IceChat, irssi (1.2.3+) - * - Konversation, KVIrc, mIRC (7.34+), Quassel - * - Textual, WeeChat (3.3+), Swirc, Glirc - * - IRCCloud, The Lounge - * - * NO SUPPORT: - * - LimeChat, Mibbit - * - * REQUIREMENTS: - * - Client must support IRCv3 'server-time' capability - * - Channel must have +H mode set by channel operators - * - History is lost on server restart (stored in memory only) - * - * USAGE EXAMPLES: - * - /MODE #general +H 50:1d # Store 50 lines for 1 day - * - /HISTORY #general # Get recent history - * - /HISTORY #general 25 # Get last 25 lines - * - * OPERATOR NOTES: - * - History can prevent bots from re-triggering on old messages - * - Use +P (permanent) mode to prevent history loss when channel empties - * - Consider privacy implications when enabling history - * - History storage limits are configured server-wide - */ diff --git a/apps/unrealircd/docker-entrypoint.sh b/apps/unrealircd/docker-entrypoint.sh index 905279b9..7cada5d6 100755 --- a/apps/unrealircd/docker-entrypoint.sh +++ b/apps/unrealircd/docker-entrypoint.sh @@ -11,10 +11,10 @@ USER_ID=${PUID:-1000} GROUP_ID=${PGID:-1000} # Create directories with proper ownership -mkdir -p /home/unrealircd/unrealircd/data /home/unrealircd/unrealircd/logs /home/unrealircd/unrealircd/tmp +mkdir -p /home/unrealircd/unrealircd/data /home/unrealircd/unrealircd/tmp # Fix ownership of directories (important for rootless Docker) -chown -R "${USER_ID}:${GROUP_ID}" /home/unrealircd/unrealircd/data /home/unrealircd/unrealircd/logs /home/unrealircd/unrealircd/tmp 2> /dev/null || true +chown -R "${USER_ID}:${GROUP_ID}" /home/unrealircd/unrealircd/data /home/unrealircd/unrealircd/tmp 2> /dev/null || true # Ensure data directory has proper permissions for control socket chmod 755 /home/unrealircd/unrealircd/data 2> /dev/null || true diff --git a/apps/unrealircd/justfile b/apps/unrealircd/justfile index e81e1b63..80ff7ae4 100644 --- a/apps/unrealircd/justfile +++ b/apps/unrealircd/justfile @@ -144,6 +144,18 @@ generate-password: @echo "Generating IRC Operator Password" @./scripts/generate-oper-password.sh +# Bootstrap the first Services Root Administrator (SRA) account +[group('Maintenance')] +sra-bootstrap nick: + @echo "SRA Bootstrap Process:" + @echo "1. Connect to IRC with nick '{{ nick }}'" + @echo "2. Register your account: /msg NickServ REGISTER " + @echo "3. Update .env: set ATHEME_SRA_BOOTSTRAP_ACCOUNT={{ nick }}" + @echo "4. Reload services: just irc reload" + @echo "5. You will now have SRA privileges via the static bootstrap block." + @echo "6. Use /msg OperServ SOPER ADD sra to grant access to others." + @echo "7. Once finished, you can remove the bootstrap account from .env for security." + [group('Maintenance')] lint: @echo "Linting Scripts" diff --git a/apps/web/.gitignore b/apps/web/.gitignore new file mode 100644 index 00000000..2965e5f2 --- /dev/null +++ b/apps/web/.gitignore @@ -0,0 +1,14 @@ +# Next.js +.next/ +out/ + +# Dependencies +node_modules/ + +# Build output +dist/ + +# Environment +.env +.env.* +!.env.example diff --git a/apps/web/AGENTS.md b/apps/web/AGENTS.md index c00d77a8..4a89cd23 100644 --- a/apps/web/AGENTS.md +++ b/apps/web/AGENTS.md @@ -37,4 +37,4 @@ Env vars for dev: `NEXT_PUBLIC_IRC_WS_URL`, `NEXT_PUBLIC_XMPP_BOSH_URL` (set in ## Related - [Monorepo AGENTS.md](../../AGENTS.md) -- [docs/services/web/](../../docs/services/web/) +- [docs-old/services/web/](../../docs-old/services/web/) diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index fb5d6610..f14b656b 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -3,77 +3,77 @@ @tailwind utilities; body { - font-family: Arial, Helvetica, sans-serif; + font-family: Arial, Helvetica, sans-serif; } @layer utilities { - .text-balance { - text-wrap: balance; - } + .text-balance { + text-wrap: balance; + } } @layer base { - :root { - --background: 240 23% 9%; - --foreground: 210 40% 98%; - --card: 240 21% 12%; - --card-foreground: 222.2 84% 4.9%; - --popover: 0 0% 100%; - --popover-foreground: 222.2 84% 4.9%; - --primary: 221.2 83.2% 53.3%; - --primary-foreground: 210 40% 98%; - --secondary: 210 40% 96.1%; - --secondary-foreground: 222.2 47.4% 11.2%; - --muted: 210 40% 96.1%; - --muted-foreground: 215.4 16.3% 46.9%; - --accent: 210 40% 96.1%; - --accent-foreground: 222.2 47.4% 11.2%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 210 40% 98%; - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; - --ring: 221.2 83.2% 53.3%; - --radius: 0rem; - --chart-1: 12 76% 61%; - --chart-2: 173 58% 39%; - --chart-3: 197 37% 24%; - --chart-4: 43 74% 66%; - --chart-5: 27 87% 67%; - } + :root { + --background: 240 23% 9%; + --foreground: 210 40% 98%; + --card: 240 21% 12%; + --card-foreground: 222.2 84% 4.9%; + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + --primary: 221.2 83.2% 53.3%; + --primary-foreground: 210 40% 98%; + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 221.2 83.2% 53.3%; + --radius: 0rem; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + } - .dark { - --background: 240 23% 9%; - --foreground: 210 40% 98%; - --card: 222.2 84% 4.9%; - --card-foreground: 210 40% 98%; - --popover: 222.2 84% 4.9%; - --popover-foreground: 210 40% 98%; - --primary: 217.2 91.2% 59.8%; - --primary-foreground: 222.2 47.4% 11.2%; - --secondary: 217.2 32.6% 17.5%; - --secondary-foreground: 210 40% 98%; - --muted: 217.2 32.6% 17.5%; - --muted-foreground: 215 20.2% 65.1%; - --accent: 217.2 32.6% 17.5%; - --accent-foreground: 210 40% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 210 40% 98%; - --border: 217.2 32.6% 17.5%; - --input: 217.2 32.6% 17.5%; - --ring: 224.3 76.3% 48%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; - } + .dark { + --background: 240 23% 9%; + --foreground: 210 40% 98%; + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + --primary: 217.2 91.2% 59.8%; + --primary-foreground: 222.2 47.4% 11.2%; + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 224.3 76.3% 48%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + } } @layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } } diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index a898bd96..84b9d4aa 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -3,32 +3,32 @@ import localFont from "next/font/local"; import "./globals.css"; const geistSans = localFont({ - src: "./fonts/GeistVF.woff", - variable: "--font-geist-sans", - weight: "100 900", + src: "./fonts/GeistVF.woff", + variable: "--font-geist-sans", + weight: "100 900", }); const geistMono = localFont({ - src: "./fonts/GeistMonoVF.woff", - variable: "--font-geist-mono", - weight: "100 900", + src: "./fonts/GeistMonoVF.woff", + variable: "--font-geist-mono", + weight: "100 900", }); export const metadata: Metadata = { - title: "atl.chat", + title: "atl.chat", }; export default function RootLayout({ - children, + children, }: Readonly<{ - children: React.ReactNode; + children: React.ReactNode; }>) { - return ( - - - {children} - - - ); + return ( + + + {children} + + + ); } diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 4cbf7a06..3ff47959 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -1,87 +1,117 @@ -import { Button } from "@/components/ui/button" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" -import { Badge } from "@/components/ui/badge" -import { DiscIcon as Discord, MessageSquare, Signal, Share2, MessageCircle } from 'lucide-react' -import Link from "next/link" +import { + DiscIcon as Discord, + MessageCircle, + MessageSquare, + Share2, + Signal, +} from "lucide-react"; +import Link from "next/link"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; export default function Home() { - return ( -
-
- - - atl.chat - -
-
-
-

Connect with All Things Linux

-

- Join our vibrant Linux community across multiple chat platforms -

- -
-
- - - - - IRC - - Classic real-time chat protocol - - -

Connect via your favorite IRC client

- irc.atl.chat/6697 #general -
-
- - - - - XMPP - - Open communication protocol - - -

Join our XMPP chatroom

- general@muc.atl.chat -
-
- - - - - Signal - - Secure messaging platform - - -

Join our Signal group

- https://signal.atl.chat -
-
-
-
-

Someday Maybe?

-
- Mastodon - Matrix -
-
-
-
-
-

- © {new Date().getFullYear()} All Things Linux. All rights reserved. -

-
-
-
- ) + return ( +
+
+ + + atl.chat + +
+
+
+

+ Connect with All Things Linux +

+

+ Join our vibrant Linux community across multiple chat platforms +

+ +
+
+ + + + + IRC + + Classic real-time chat protocol + + +

+ Connect via your favorite IRC client +

+ + irc.atl.chat/6697 #general + +
+
+ + + + + XMPP + + Open communication protocol + + +

+ Join our XMPP chatroom +

+ + general@muc.atl.chat + +
+
+ + + + + Signal + + Secure messaging platform + + +

+ Join our Signal group +

+ + https://signal.atl.chat + +
+
+
+
+

Someday Maybe?

+
+ + Mastodon + + + Matrix + +
+
+
+
+
+

+ © {new Date().getFullYear()} All Things Linux. All rights reserved. +

+
+
+
+ ); } diff --git a/apps/web/biome.jsonc b/apps/web/biome.jsonc index c8687d89..495b7d6c 100644 --- a/apps/web/biome.jsonc +++ b/apps/web/biome.jsonc @@ -1,7 +1,72 @@ { "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", - "extends": [ - "ultracite/biome/core", - "ultracite/biome/next" - ] + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": false, + "includes": [ + "!**/node_modules", + "!**/.next", + "!**/dist", + "!**/components/ui" + ] + }, + "formatter": { + "enabled": true, + "indentStyle": "tab" + }, + "assist": { + "actions": { + "source": { + "organizeImports": "on" + } + } + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "useExhaustiveDependencies": "info" + }, + "suspicious": { + "noUnknownAtRules": "off" + }, + "performance": { + "noBarrelFile": "off", + "noReExportAll": "off", + "noNamespaceImport": "off" + }, + "nursery": { + "useSortedClasses": { + "level": "warn", + "fix": "safe", + "options": { + "functions": ["clsx", "cva", "cn"] + } + } + }, + "style": { + "noParameterAssign": "error", + "useAsConstAssertion": "error", + "useDefaultParameterLast": "error", + "useEnumInitializers": "error", + "useSelfClosingElements": "error", + "useSingleVarDeclarator": "error", + "noUnusedTemplateLiteral": "error", + "useNumberNamespace": "error", + "noInferrableTypes": "error", + "noUselessElse": "error" + } + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + }, + "extends": ["ultracite/biome/core", "ultracite/biome/next"] } diff --git a/apps/web/components/ui/badge.tsx b/apps/web/components/ui/badge.tsx index f000e3ef..a8560724 100644 --- a/apps/web/components/ui/badge.tsx +++ b/apps/web/components/ui/badge.tsx @@ -1,10 +1,10 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" +import { cva, type VariantProps } from "class-variance-authority"; +import type * as React from "react"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const badgeVariants = cva( - "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + "inline-flex items-center rounded-full border px-2.5 py-0.5 font-semibold text-xs transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", { variants: { variant: { @@ -21,7 +21,7 @@ const badgeVariants = cva( variant: "default", }, } -) +); export interface BadgeProps extends React.HTMLAttributes, @@ -30,7 +30,7 @@ export interface BadgeProps function Badge({ className, variant, ...props }: BadgeProps) { return (
- ) + ); } -export { Badge, badgeVariants } +export { Badge, badgeVariants }; diff --git a/apps/web/components/ui/button.tsx b/apps/web/components/ui/button.tsx index 36496a28..c6402bf0 100644 --- a/apps/web/components/ui/button.tsx +++ b/apps/web/components/ui/button.tsx @@ -1,11 +1,11 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; +import * as React from "react"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const buttonVariants = cva( - "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", { variants: { variant: { @@ -31,26 +31,26 @@ const buttonVariants = cva( size: "default", }, } -) +); export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { - asChild?: boolean + asChild?: boolean; } const Button = React.forwardRef( ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button" + const Comp = asChild ? Slot : "button"; return ( - ) + ); } -) -Button.displayName = "Button" +); +Button.displayName = "Button"; -export { Button, buttonVariants } +export { Button, buttonVariants }; diff --git a/apps/web/components/ui/card.tsx b/apps/web/components/ui/card.tsx index f62edea5..5056212a 100644 --- a/apps/web/components/ui/card.tsx +++ b/apps/web/components/ui/card.tsx @@ -1,79 +1,86 @@ -import * as React from "react" +import * as React from "react"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const Card = React.forwardRef< HTMLDivElement, React.HTMLAttributes >(({ className, ...props }, ref) => (
-)) -Card.displayName = "Card" +)); +Card.displayName = "Card"; const CardHeader = React.forwardRef< HTMLDivElement, React.HTMLAttributes >(({ className, ...props }, ref) => (
-)) -CardHeader.displayName = "CardHeader" +)); +CardHeader.displayName = "CardHeader"; const CardTitle = React.forwardRef< HTMLDivElement, React.HTMLAttributes >(({ className, ...props }, ref) => (
-)) -CardTitle.displayName = "CardTitle" +)); +CardTitle.displayName = "CardTitle"; const CardDescription = React.forwardRef< HTMLDivElement, React.HTMLAttributes >(({ className, ...props }, ref) => (
-)) -CardDescription.displayName = "CardDescription" +)); +CardDescription.displayName = "CardDescription"; const CardContent = React.forwardRef< HTMLDivElement, React.HTMLAttributes >(({ className, ...props }, ref) => ( -
-)) -CardContent.displayName = "CardContent" +
+)); +CardContent.displayName = "CardContent"; const CardFooter = React.forwardRef< HTMLDivElement, React.HTMLAttributes >(({ className, ...props }, ref) => (
-)) -CardFooter.displayName = "CardFooter" +)); +CardFooter.displayName = "CardFooter"; -export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardDescription, + CardContent, +}; diff --git a/apps/web/lib/utils.ts b/apps/web/lib/utils.ts index bd0c391d..ac680b30 100644 --- a/apps/web/lib/utils.ts +++ b/apps/web/lib/utils.ts @@ -1,6 +1,6 @@ -import { clsx, type ClassValue } from "clsx" -import { twMerge } from "tailwind-merge" +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) + return twMerge(clsx(inputs)); } diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs index 2448ca9c..04ac67dc 100644 --- a/apps/web/next.config.mjs +++ b/apps/web/next.config.mjs @@ -1,16 +1,16 @@ // @ts-check -import { setupDevPlatform } from '@cloudflare/next-on-pages/next-dev'; +import { setupDevPlatform } from "@cloudflare/next-on-pages/next-dev"; /** @type {import('next').NextConfig} */ const nextConfig = { - reactStrictMode: true, - poweredByHeader: false, - pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'], + reactStrictMode: true, + poweredByHeader: false, + pageExtensions: ["js", "jsx", "ts", "tsx", "md", "mdx"], }; -if (process.env.NODE_ENV === 'development') { - await setupDevPlatform(); +if (process.env.NODE_ENV === "development") { + await setupDevPlatform(); } export default nextConfig; diff --git a/apps/web/postcss.config.mjs b/apps/web/postcss.config.mjs index 1a69fd2a..f6c3605a 100644 --- a/apps/web/postcss.config.mjs +++ b/apps/web/postcss.config.mjs @@ -1,8 +1,8 @@ /** @type {import('postcss-load-config').Config} */ const config = { - plugins: { - tailwindcss: {}, - }, + plugins: { + tailwindcss: {}, + }, }; export default config; diff --git a/apps/web/tailwind.config.ts b/apps/web/tailwind.config.ts index 98d0095d..5efb3507 100644 --- a/apps/web/tailwind.config.ts +++ b/apps/web/tailwind.config.ts @@ -1,65 +1,65 @@ -import type { Config } from 'tailwindcss'; +import type { Config } from "tailwindcss"; -import animate from 'tailwindcss-animate'; +import animate from "tailwindcss-animate"; const config: Config = { - darkMode: ['class'], - content: [ - './pages/**/*.{js,ts,jsx,tsx,mdx}', - './components/**/*.{js,ts,jsx,tsx,mdx}', - './app/**/*.{js,ts,jsx,tsx,mdx}', - ], - theme: { - extend: { - colors: { - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', - card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))', - }, - popover: { - DEFAULT: 'hsl(var(--popover))', - foreground: 'hsl(var(--popover-foreground))', - }, - primary: { - DEFAULT: 'hsl(var(--primary))', - foreground: 'hsl(var(--primary-foreground))', - }, - secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))', - }, - muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))', - }, - accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))', - }, - destructive: { - DEFAULT: 'hsl(var(--destructive))', - foreground: 'hsl(var(--destructive-foreground))', - }, - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - chart: { - '1': 'hsl(var(--chart-1))', - '2': 'hsl(var(--chart-2))', - '3': 'hsl(var(--chart-3))', - '4': 'hsl(var(--chart-4))', - '5': 'hsl(var(--chart-5))', - }, - }, - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)', - }, - }, - }, - plugins: [animate], + darkMode: ["class"], + content: [ + "./pages/**/*.{js,ts,jsx,tsx,mdx}", + "./components/**/*.{js,ts,jsx,tsx,mdx}", + "./app/**/*.{js,ts,jsx,tsx,mdx}", + ], + theme: { + extend: { + colors: { + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + chart: { + "1": "hsl(var(--chart-1))", + "2": "hsl(var(--chart-2))", + "3": "hsl(var(--chart-3))", + "4": "hsl(var(--chart-4))", + "5": "hsl(var(--chart-5))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + }, + }, + plugins: [animate], } satisfies Config; export default config; diff --git a/apps/webpanel/AGENTS.md b/apps/webpanel/AGENTS.md index dcb12634..e3545d0b 100644 --- a/apps/webpanel/AGENTS.md +++ b/apps/webpanel/AGENTS.md @@ -18,13 +18,14 @@ README.md # User docs, access, troubleshooting ## Key Facts -- **Upstream:** [unrealircd/unrealircd-webpanel](https://github.com/unrealircd/unrealircd-webpanel) +- **Upstream:** [unrealircd/unrealircd-webpanel](https://github.com/unrealircd/unrealircd-webpanel) — pinned to branch `0.9.1` - **Access:** (dev) - **RPC:** UnrealIRCd JSON-RPC on port 8600 -- **Data:** `data/irc/webpanel-data` → `/var/www/html/unrealircd-webpanel/data` +- **Data:** `data/irc/webpanel-data` → `/var/www/html/data` +- **Security:** `cap_drop: ALL` + `cap_add: SETUID, SETGID` in compose ## Related - [Monorepo AGENTS.md](../../AGENTS.md) - [apps/unrealircd/AGENTS.md](../unrealircd/AGENTS.md) -- [docs/services/irc/WEBPANEL.md](../../docs/services/irc/WEBPANEL.md) +- [docs-old/services/irc/WEBPANEL.md](../../docs-old/services/irc/WEBPANEL.md) diff --git a/apps/webpanel/Containerfile b/apps/webpanel/Containerfile index 429f1b22..9286732c 100644 --- a/apps/webpanel/Containerfile +++ b/apps/webpanel/Containerfile @@ -1,19 +1,27 @@ -# hadolint ignore=DL3006 -FROM composer/composer AS builder +FROM composer:2 AS builder WORKDIR /app # Clone the webpanel source and install dependencies -RUN git clone --depth 1 https://github.com/unrealircd/unrealircd-webpanel.git . && \ +RUN git clone --depth 1 --branch 0.9.1 https://github.com/unrealircd/unrealircd-webpanel.git . && \ composer install --no-dev --optimize-autoloader -# hadolint ignore=DL3006 -FROM trafex/php-nginx +FROM trafex/php-nginx:3.10.0 + +LABEL org.opencontainers.image.source="https://github.com/allthingslinux/atl.chat" \ + org.opencontainers.image.description="UnrealIRCd web administration panel for atl.chat" \ + org.opencontainers.image.licenses="MIT" \ + org.opencontainers.image.vendor="All Things Linux" \ + org.opencontainers.image.title="atl-webpanel" USER root # hadolint ignore=DL3018 -RUN apk add --no-cache php84-sodium +RUN apk add --no-cache \ + php84-curl \ + php84-mbstring \ + php84-sodium \ + php84-zip USER nobody @@ -29,3 +37,8 @@ RUN chown -R nobody:nobody /var/www/html && \ chmod -R 755 /var/www/html/data /var/www/html/config /var/www/html/logs USER nobody + +EXPOSE 8080 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/ || exit 1 diff --git a/apps/webpanel/nginx.conf b/apps/webpanel/nginx.conf deleted file mode 100644 index ce55215b..00000000 --- a/apps/webpanel/nginx.conf +++ /dev/null @@ -1,44 +0,0 @@ -server { - listen 80; - server_name _; - root /var/www/html/unrealircd-webpanel; - index index.php; - - # Logging - access_log /var/log/nginx/webpanel_access.log; - error_log /var/log/nginx/webpanel_error.log; - - # Security headers - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-Content-Type-Options "nosniff" always; - add_header X-XSS-Protection "1; mode=block" always; - - # Handle PHP files - location ~ \.php$ { - fastcgi_pass 127.0.0.1:9000; - fastcgi_index index.php; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - include fastcgi_params; - fastcgi_read_timeout 300; - } - - # Handle static files - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - } - - # Main location block - location / { - try_files $uri $uri/ /index.php?$query_string; - } - - # Deny access to sensitive files - location ~ /\. { - deny all; - } - - location ~ /(data|config) { - deny all; - } -} diff --git a/compose.yaml b/compose.yaml index 528e3dc2..e9b64f68 100644 --- a/compose.yaml +++ b/compose.yaml @@ -29,6 +29,12 @@ services: - "${DOZZLE_PORT:-8082}:8080" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro + logging: + driver: json-file + options: + max-size: "${LOG_MAX_SIZE:-50m}" + max-file: "${LOG_MAX_FILES:-5}" + compress: "true" environment: - DOZZLE_FILTER=name=atl-* networks: diff --git a/docs/AGENTS.md b/docs/AGENTS.md deleted file mode 100644 index 26c8cad0..00000000 --- a/docs/AGENTS.md +++ /dev/null @@ -1,28 +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 | -| `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/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/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 ca37a213..00000000 --- a/docs/services/irc/CI_CD.md +++ /dev/null @@ -1,820 +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 staging and production environments. - -### Triggers - -```yaml -on: - release: - types: [published] - workflow_dispatch: - inputs: - environment: - description: 'Target environment' - required: true - default: 'staging' - options: [staging, production] -``` - -### Deployment Strategy - -#### Environment-Based Deployment - -```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' - 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 4b1bdfd0..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=staging.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