From 4476eb0a065b3c25f91f7ee75513ef169a0f1eea Mon Sep 17 00:00:00 2001 From: MrLenin <909621+MrLenin@users.noreply.github.com> Date: Sun, 21 Jun 2026 01:01:11 -0400 Subject: [PATCH 01/10] docker: add ldap_writeback to the templatized config (completes LDAP-docker) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nickserv.c register gates ldap_do_add on ldap_enable && ldap_admin_dn && ldap_writeback. The LDAP-docker templatization (b38ab2e) set ldap_enable etc. but never ldap_writeback, so it defaulted off — registrations landed in saxdb only (credential-less) and LDAP-authoritative SASL failed. Add %X3_LDAP_WRITEBACK% placeholder + X3_LDAP_WRITEBACK:=0 default. Co-Authored-By: Claude Opus 4.8 (1M context) --- docker/dockerentrypoint.sh | 1 + docker/x3.conf-dist | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/docker/dockerentrypoint.sh b/docker/dockerentrypoint.sh index c91fc35f..a0165e8e 100755 --- a/docker/dockerentrypoint.sh +++ b/docker/dockerentrypoint.sh @@ -25,6 +25,7 @@ else # LDAP defaults (disabled unless overridden) : "${X3_LDAP_ENABLE:=0}" + : "${X3_LDAP_WRITEBACK:=0}" : "${X3_LDAP_URI:=ldap://localhost:389}" : "${X3_LDAP_BASE:=ou=users,dc=example,dc=net}" : "${X3_LDAP_DN_FMT:=uid=%s,ou=users,dc=example,dc=net}" diff --git a/docker/x3.conf-dist b/docker/x3.conf-dist index 29cccda2..0620bf76 100644 --- a/docker/x3.conf-dist +++ b/docker/x3.conf-dist @@ -250,6 +250,11 @@ // LDAP configuration // Uses inetOrgAnonAccount schema (x3/tools/ldap/inetorganon.schema) "ldap_enable" "%X3_LDAP_ENABLE%"; + // ldap_writeback: write new account registrations (incl. credentials) to LDAP. + // Required for SASL when LDAP is authoritative — without it nickserv.c gates out + // ldap_do_add and accounts land in saxdb only (credential-less). Was missing from + // the LDAP-docker templatization. + "ldap_writeback" "%X3_LDAP_WRITEBACK%"; "ldap_uri" "%X3_LDAP_URI%"; "ldap_base" "%X3_LDAP_BASE%"; "ldap_dn_fmt" "%X3_LDAP_DN_FMT%"; From 5014bf838c6ec15a700feef6f39f95f8d2b827dc Mon Sep 17 00:00:00 2001 From: MrLenin <909621+MrLenin@users.noreply.github.com> Date: Sat, 27 Jun 2026 17:18:55 -0400 Subject: [PATCH 02/10] docker: env-template OpServ untrusted_max (clone-G-line threshold) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit untrusted_max was hardcoded to 6 in x3.conf-dist, so any deployment using the docker template inherits the stock clone-G-line: a host exceeding 6 connections is auto-G-lined for clone_gline_duration (2h). On a testbed where a harness opens many connections from a single host, this auto-G-lines that host and the network appears dead (SASL/oper/link attempts all rejected) until the G-line expires — recurring on every heavy test run. Make it env-templated like the other X3_* settings: x3.conf-dist uses %X3_UNTRUSTED_MAX%, dockerentrypoint.sh defaults it to 6 (stock behavior preserved for normal deployments). A testbed sets X3_UNTRUSTED_MAX=0 (0 disables the clone-G-line entirely, per opserv.c) in its env so harness connection volume from one host is no longer auto-G-lined. Co-Authored-By: Claude Opus 4.8 (1M context) --- docker/dockerentrypoint.sh | 5 +++++ docker/x3.conf-dist | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docker/dockerentrypoint.sh b/docker/dockerentrypoint.sh index a0165e8e..cd874c91 100755 --- a/docker/dockerentrypoint.sh +++ b/docker/dockerentrypoint.sh @@ -23,6 +23,11 @@ else : "${X3_UPLINK_PORT:=8888}" : "${X3_UPLINK_PASSWORD:=changeme}" + # OpServ clone-G-line threshold (untrusted_max). Default 6 (stock). + # 0 disables it — testbeds set X3_UNTRUSTED_MAX=0 in .env.local so harness + # connection volume from a single host isn't auto-G-lined. + : "${X3_UNTRUSTED_MAX:=6}" + # LDAP defaults (disabled unless overridden) : "${X3_LDAP_ENABLE:=0}" : "${X3_LDAP_WRITEBACK:=0}" diff --git a/docker/x3.conf-dist b/docker/x3.conf-dist index 0620bf76..213fcf90 100644 --- a/docker/x3.conf-dist +++ b/docker/x3.conf-dist @@ -307,7 +307,7 @@ // when a server pings out and they reconnect before the old connection is noticed // to be dead by the server.. so set it at about twice the # you want to allow to // avoid false positives. - "untrusted_max" "6"; // 3 connections and 3 ghosts, 7th connection causes a gline. + "untrusted_max" "%X3_UNTRUSTED_MAX%"; // env-templated (default 6). 0 DISABLES the clone-G-line — set 0 on testbeds where a harness opens many connections from one host (else O3 auto-G-lines that host for clone_gline_duration and the bed appears dead). 3 connections and 3 ghosts, (N+1)th connection causes a gline. // how long of a g-line should be issued if the max hosts is exceeded? "clone_gline_duration" "2h"; // durations are smhdmy From 124b8701421c54ae382bff92b3754fb5392e2bac Mon Sep 17 00:00:00 2001 From: MrLenin <909621+MrLenin@users.noreply.github.com> Date: Sat, 1 Aug 2026 02:58:24 -0400 Subject: [PATCH 03/10] docker: escape backslash and the sed delimiter in template substitution The substitution loop uses '|' as the sed s/// delimiter but only escaped '/' and '&' in replacement values. A value containing '|' (any non-trivial LDAP filter, e.g. "(|(a=b)(c=d))") terminated the replacement early and corrupted the generated conf silently; a backslash was mis-handled too. Escape backslash FIRST (so the escapes added next are not re-escaped), then '&' and the '|' delimiter. '/' needs no escaping under a '|' delimiter. The empty-value warning behavior is unchanged, and the script stays bash -n clean. Co-Authored-By: Claude Fable 5 --- docker/dockerentrypoint.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docker/dockerentrypoint.sh b/docker/dockerentrypoint.sh index cd874c91..1fa19099 100755 --- a/docker/dockerentrypoint.sh +++ b/docker/dockerentrypoint.sh @@ -54,8 +54,11 @@ else # Only substitute if the variable is set if [ -n "$value" ]; then - # Escape special characters for sed (/, &, \) - escaped_value=$(printf '%s\n' "$value" | sed -e 's/[\/&]/\\&/g') + # Escape for use as a sed replacement with '|' as the s/// + # delimiter: backslash FIRST (so the escapes we add next are not + # themselves re-escaped), then '&' (whole-match backreference) + # and the '|' delimiter itself. '/' needs no escaping here. + escaped_value=$(printf '%s\n' "$value" | sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/|/\\|/g') sed -i "s|${placeholder}|${escaped_value}|g" "$BASECONF" else echo "Warning: No value set for ${varname}, leaving ${placeholder} unchanged" From ba78089d3991e6de16dc59d33b6accfa449a5c65 Mon Sep 17 00:00:00 2001 From: MrLenin <909621+MrLenin@users.noreply.github.com> Date: Sat, 1 Aug 2026 02:59:12 -0400 Subject: [PATCH 04/10] docker: env-template the server block (type, host-hiding keys, prefix) Templatize the remaining hardcoded server-block identity settings: type -> %X3_SERVER_TYPE% (default 8) hidden_host_type -> %X3_HIDDEN_HOST_TYPE% (default 1) key1/key2/key3 -> %X3_HIDDEN_HOST_KEY1/2/3% (defaults 45432/76934/98336) prefix -> %X3_HIDDEN_HOST_PREFIX% (default NETWORK) Defaults preserve the shipped literals, so generated output is unchanged apart from two new comments: the type line now notes 8 = Nefarious 1.3.x / 9 = Nefarious 2.0.x (must match your ircd), and the key lines warn that they MUST match the ircd's HOST_HIDING_KEY* F:lines and that stock keys make style-2 cloaks predictable. Co-Authored-By: Claude Fable 5 --- docker/dockerentrypoint.sh | 11 +++++++++++ docker/x3.conf-dist | 14 ++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/docker/dockerentrypoint.sh b/docker/dockerentrypoint.sh index 1fa19099..2743dbc0 100755 --- a/docker/dockerentrypoint.sh +++ b/docker/dockerentrypoint.sh @@ -23,6 +23,17 @@ else : "${X3_UPLINK_PORT:=8888}" : "${X3_UPLINK_PASSWORD:=changeme}" + # Server block + # 8 = Nefarious 1.3.x; 9 = Nefarious 2.0.x (nefarious2) — must match your ircd. + : "${X3_SERVER_TYPE:=8}" + : "${X3_HIDDEN_HOST_TYPE:=1}" + # MUST match the ircd's HOST_HIDING_KEY* F:lines; change in production — + # stock keys make style-2 cloaks predictable. + : "${X3_HIDDEN_HOST_KEY1:=45432}" + : "${X3_HIDDEN_HOST_KEY2:=76934}" + : "${X3_HIDDEN_HOST_KEY3:=98336}" + : "${X3_HIDDEN_HOST_PREFIX:=NETWORK}" + # OpServ clone-G-line threshold (untrusted_max). Default 6 (stock). # 0 disables it — testbeds set X3_UNTRUSTED_MAX=0 in .env.local so harness # connection volume from a single host isn't auto-G-lined. diff --git a/docker/x3.conf-dist b/docker/x3.conf-dist index 213fcf90..80fa971f 100644 --- a/docker/x3.conf-dist +++ b/docker/x3.conf-dist @@ -36,11 +36,13 @@ /* hidden_host should match the F:HIDDEN_HOST: line in your ircu's ircd.conf; * x3 does not set the host suffix for users, but must know it when making * things like bans, where it should not show the user's real hostname. */ - "hidden_host_type" "1"; // change this to 2 if you use Nefarious's style 2 host hiding. - "key1" "45432"; // Set these key values to the network KEY values you use - "key2" "76934"; // for host hiding style 2. - "key3" "98336"; // - "prefix" "NETWORK"; // If you use style 2 then this is the name that is prefixed to hosts. + "hidden_host_type" "%X3_HIDDEN_HOST_TYPE%"; // change this to 2 if you use Nefarious's style 2 host hiding. + // MUST match the ircd's HOST_HIDING_KEY* F:lines; change in production — + // stock keys make style-2 cloaks predictable. + "key1" "%X3_HIDDEN_HOST_KEY1%"; // Set these key values to the network KEY values you use + "key2" "%X3_HIDDEN_HOST_KEY2%"; // for host hiding style 2. + "key3" "%X3_HIDDEN_HOST_KEY3%"; // + "prefix" "%X3_HIDDEN_HOST_PREFIX%"; // If you use style 2 then this is the name that is prefixed to hosts. "numeric" "%X3_GENERAL_NUMERIC%"; // hint: If you get collisions on link, CHANGE THIS. /* Type handles some changes in Nefarious from version to version. * 4 - nefarious 0.4.x and other ircds @@ -50,7 +52,7 @@ * 8 - nefarious 1.3.0 and higher (Legacy Version) * 9 - nefarious 2.0.x and higher (Current Version) */ - "type" "8"; + "type" "%X3_SERVER_TYPE%"; // 8 = Nefarious 1.3.x; 9 = Nefarious 2.0.x (nefarious2) — must match your ircd. "host_in_topic" "1"; //Set to 1 if your Nefarious server have the HOST_IN_TOPIC F:line set to TRUE. "max_users" "256"; // You can save a little memory by setting this to a lower value. "force_n2k" "1"; // Use extended (5-digit) numnick for self, even if 3 are possible. From bc6efc650e4cbca9482d083351cfcb6aa5dcc85d Mon Sep 17 00:00:00 2001 From: MrLenin <909621+MrLenin@users.noreply.github.com> Date: Sat, 1 Aug 2026 03:00:25 -0400 Subject: [PATCH 05/10] docker: env-template nickserv settings and complete the LDAP set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Templatize the remaining hardcoded nickserv (AuthServ) settings: nick -> %X3_NICKSERV_NICK% (AuthServ) default_maxlogins -> %X3_DEFAULT_MAXLOGINS% (3) hard_maxlogins -> %X3_HARD_MAXLOGINS% (10) auto_oper -> %X3_AUTO_OPER% (+oxwgs) auto_oper_privs -> %X3_AUTO_OPER_PRIVS% (current literal, verbatim) password_min_length -> %X3_PASSWD_MIN_LENGTH% (4) password_min_digits/_upper/_lower -> %X3_PASSWD_MIN_DIGITS/UPPER/LOWER% (0/0/0) email_enabled -> %X3_EMAIL_ENABLED% (0) email_required -> %X3_EMAIL_REQUIRED% (0) cookie_timeout -> %X3_COOKIE_TIMEOUT% (2d) accounts_per_email -> %X3_ACCOUNTS_PER_EMAIL% (1) titlehost_suffix -> %X3_TITLEHOST_SUFFIX% (derives from X3_GENERAL_DOMAIN, replacing the hardcoded AfterNET.Org branding) ldap_autocreate -> %X3_LDAP_AUTOCREATE% (1) ldap_timeout -> %X3_LDAP_TIMEOUT% (10) Also ADD two LDAP keys that exist in x3.conf.example but were missing from the dist, so their env vars actually take effect: "ldap_field_oslevel" "%X3_LDAP_FIELD_OSLEVEL%"; (X3AccountLevel) "ldap_filter" "%X3_LDAP_FILTER%"; ((objectClass=inetOrgPerson)) Deliberately NOT added: ldap_oper_group_dn / ldap_oper_group_level / ldap_field_group_member — they are commented in the example and their empty-default semantics are unclear; adding them with a bogus default could enable oper-group writeback unintentionally. Co-Authored-By: Claude Fable 5 --- docker/dockerentrypoint.sh | 21 +++++++++++++++++++++ docker/x3.conf-dist | 34 ++++++++++++++++++---------------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/docker/dockerentrypoint.sh b/docker/dockerentrypoint.sh index 2743dbc0..1180daf8 100755 --- a/docker/dockerentrypoint.sh +++ b/docker/dockerentrypoint.sh @@ -39,6 +39,23 @@ else # connection volume from a single host isn't auto-G-lined. : "${X3_UNTRUSTED_MAX:=6}" + # NickServ (AuthServ) defaults + : "${X3_NICKSERV_NICK:=AuthServ}" + : "${X3_DEFAULT_MAXLOGINS:=3}" + : "${X3_HARD_MAXLOGINS:=10}" + : "${X3_AUTO_OPER:=+oxwgs}" + : "${X3_AUTO_OPER_PRIVS:=CHAN_LIMIT SHOW_INVIS SHOW_ALL_INVIS KILL LOCAL_KILL REHASH RESTART DIE JUPE LOCAL_JUPE OPMODE WHOX SEE_CHAN PROPAGATE DISPLAY SEE_OPERS WIDE_GLINE FORCE_OPMODE REMOTEREHASH CHECK SEE_SECRET_CHAN WIDE_SHUN WIDE_ZLINE LIST_CHAN}" + : "${X3_PASSWD_MIN_LENGTH:=4}" + : "${X3_PASSWD_MIN_DIGITS:=0}" + : "${X3_PASSWD_MIN_UPPER:=0}" + : "${X3_PASSWD_MIN_LOWER:=0}" + : "${X3_EMAIL_ENABLED:=0}" + : "${X3_EMAIL_REQUIRED:=0}" + : "${X3_COOKIE_TIMEOUT:=2d}" + : "${X3_ACCOUNTS_PER_EMAIL:=1}" + # Derived from X3_GENERAL_DOMAIN (set above) unless overridden. + : "${X3_TITLEHOST_SUFFIX:=${X3_GENERAL_DOMAIN}}" + # LDAP defaults (disabled unless overridden) : "${X3_LDAP_ENABLE:=0}" : "${X3_LDAP_WRITEBACK:=0}" @@ -50,6 +67,10 @@ else : "${X3_LDAP_FIELD_ACCOUNT:=uid}" : "${X3_LDAP_FIELD_PASSWORD:=userPassword}" : "${X3_LDAP_FIELD_EMAIL:=mail}" + : "${X3_LDAP_FIELD_OSLEVEL:=X3AccountLevel}" + : "${X3_LDAP_FILTER:=(objectClass=inetOrgPerson)}" + : "${X3_LDAP_AUTOCREATE:=1}" + : "${X3_LDAP_TIMEOUT:=10}" # Copy the template to the output location cp "$BASECONFDIST" "$BASECONF" diff --git a/docker/x3.conf-dist b/docker/x3.conf-dist index 80fa971f..06fb1895 100644 --- a/docker/x3.conf-dist +++ b/docker/x3.conf-dist @@ -98,7 +98,7 @@ * reserve nicks. */ "nickserv" { - "nick" "AuthServ"; // The bots nick on IRC + "nick" "%X3_NICKSERV_NICK%"; // The bots nick on IRC // If you want to have *@* as the default hostmask, set // default_hostmask. I highly reccomend this, and its required @@ -110,15 +110,15 @@ // default max number of logins allowed on new accounts. Users can set it // to something different using authserv commands. - "default_maxlogins" "3"; + "default_maxlogins" "%X3_DEFAULT_MAXLOGINS%"; // hard_maxlogins is the ammount the user cant override. - "hard_maxlogins" "10"; + "hard_maxlogins" "%X3_HARD_MAXLOGINS%"; //automatically set the following modes when opers auth: // - if it includes o, then are auto remote-opered. - "auto_oper" "+oxwgs"; - "auto_oper_privs" "CHAN_LIMIT SHOW_INVIS SHOW_ALL_INVIS KILL LOCAL_KILL REHASH RESTART DIE JUPE LOCAL_JUPE OPMODE WHOX SEE_CHAN PROPAGATE DISPLAY SEE_OPERS WIDE_GLINE FORCE_OPMODE REMOTEREHASH CHECK SEE_SECRET_CHAN WIDE_SHUN WIDE_ZLINE LIST_CHAN"; + "auto_oper" "%X3_AUTO_OPER%"; + "auto_oper_privs" "%X3_AUTO_OPER_PRIVS%"; // This names a file that contains easily guessed passwords. // It always contains "password", "" and the user's @@ -128,10 +128,10 @@ // Minimum number of various types of characters permitted in // a password. Authserv will enforce these. - "password_min_length" "4"; - "password_min_digits" "0"; - "password_min_upper" "0"; - "password_min_lower" "0"; + "password_min_length" "%X3_PASSWD_MIN_LENGTH%"; + "password_min_digits" "%X3_PASSWD_MIN_DIGITS%"; + "password_min_upper" "%X3_PASSWD_MIN_UPPER%"; + "password_min_lower" "%X3_PASSWD_MIN_LOWER%"; // What should valid account and nicks look like? // If valid_nick_regex is omitted, valid_account_regex is used @@ -224,14 +224,14 @@ // How to integrate with email cookies? // In order to use mail, mail must be enabled and configured // down below in the mail section of this config file. - "email_enabled" "0"; // Allow account verification and password reset by email. - "email_required" "0"; // if above is 1, require verification to authenticate. - "cookie_timeout" "2d"; // how long before we expire cookies? - "accounts_per_email" "1"; // How many people can use the same email account. + "email_enabled" "%X3_EMAIL_ENABLED%"; // Allow account verification and password reset by email. + "email_required" "%X3_EMAIL_REQUIRED%"; // if above is 1, require verification to authenticate. + "cookie_timeout" "%X3_COOKIE_TIMEOUT%"; // how long before we expire cookies? + "accounts_per_email" "%X3_ACCOUNTS_PER_EMAIL%"; // How many people can use the same email account. "email_search_level" "600"; // minimum OpServ level to search based on email address (search print email *foo*) "email_visible_level" "800"; // minimum OpServ level to see somebody's email address - "titlehost_suffix" "AfterNET.Org"; // 'USET title' sets a fake hostname of name.title.titlehost on a user. + "titlehost_suffix" "%X3_TITLEHOST_SUFFIX%"; // 'USET title' sets a fake hostname of name.title.titlehost on a user. "set_title_level" "900"; // Access to use 'uset title'. "set_fakehost_level" "1000"; //Access to set a freeform fakehost. (uset fakehost) @@ -260,14 +260,16 @@ "ldap_uri" "%X3_LDAP_URI%"; "ldap_base" "%X3_LDAP_BASE%"; "ldap_dn_fmt" "%X3_LDAP_DN_FMT%"; - "ldap_autocreate" "1"; + "ldap_autocreate" "%X3_LDAP_AUTOCREATE%"; "ldap_admin_dn" "%X3_LDAP_ADMIN_DN%"; "ldap_admin_pass" "%X3_LDAP_ADMIN_PASS%"; "ldap_object_classes" ( "top", "inetOrgAnonAccount" ); "ldap_field_account" "%X3_LDAP_FIELD_ACCOUNT%"; "ldap_field_password" "%X3_LDAP_FIELD_PASSWORD%"; "ldap_field_email" "%X3_LDAP_FIELD_EMAIL%"; - "ldap_timeout" "10"; + "ldap_field_oslevel" "%X3_LDAP_FIELD_OSLEVEL%"; + "ldap_filter" "%X3_LDAP_FILTER%"; + "ldap_timeout" "%X3_LDAP_TIMEOUT%"; }; From 6f98f183edbdb0bee6b38dec223ebb998f46c618 Mon Sep 17 00:00:00 2001 From: MrLenin <909621+MrLenin@users.noreply.github.com> Date: Sat, 1 Aug 2026 03:01:58 -0400 Subject: [PATCH 06/10] docker: env-template opserv/chanserv/global identity and limits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Templatize the remaining service-identity settings and their companion limits (defaults preserve the shipped literals; generated output is byte-identical): opserv nick -> %X3_OPSERV_NICK% (O3) opserv hostname -> %X3_OPSERV_HOSTNAME% (X3.Services) debug_channel -> %X3_DEBUG_CHANNEL% (#TheOps) alert_channel -> %X3_ALERT_CHANNEL% (#TheOps) staff_auth_channel -> %X3_STAFF_AUTH_CHANNEL% (#OperServ) clone_gline_duration -> %X3_CLONE_GLINE_DURATION% (2h) — companion to the already-templatized untrusted_max chanserv nick -> %X3_CHANSERV_NICK% (X3) off_channel -> %X3_OFF_CHANNEL% (no) max_owned -> %X3_MAX_OWNED% (2) max_chan_users -> %X3_MAX_CHAN_USERS% (512) support_channel -> (%X3_SUPPORT_CHANNEL%) ("#Operations", "#Help" — the value is the list body, channels stay quoted) global nick -> %X3_GLOBAL_NICK% (Global) The logs targets follow the channels they feed: irc:#TheOps -> irc:%X3_DEBUG_CHANNEL% and irc:#MrSnoopy -> irc:%X3_SNOOP_CHANNEL%. X3_SNOOP_CHANNEL's entrypoint default (#MrSnoopy) lands here so the logs reference resolves at this commit; the snoop module itself is templatized with the other modules. Channel MODES stay hardcoded. valid_channel_regex stays hardcoded on purpose: regex metacharacters through sed substitution are a foot-gun. Co-Authored-By: Claude Fable 5 --- docker/dockerentrypoint.sh | 17 +++++++++++++++++ docker/x3.conf-dist | 28 ++++++++++++++-------------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/docker/dockerentrypoint.sh b/docker/dockerentrypoint.sh index 1180daf8..8abe7d45 100755 --- a/docker/dockerentrypoint.sh +++ b/docker/dockerentrypoint.sh @@ -39,6 +39,23 @@ else # connection volume from a single host isn't auto-G-lined. : "${X3_UNTRUSTED_MAX:=6}" + # OpServ / ChanServ / Global identity + limits + : "${X3_OPSERV_NICK:=O3}" + : "${X3_OPSERV_HOSTNAME:=X3.Services}" + : "${X3_DEBUG_CHANNEL:=#TheOps}" + : "${X3_ALERT_CHANNEL:=#TheOps}" + : "${X3_STAFF_AUTH_CHANNEL:=#OperServ}" + : "${X3_CLONE_GLINE_DURATION:=2h}" + : "${X3_CHANSERV_NICK:=X3}" + : "${X3_OFF_CHANNEL:=no}" + : "${X3_MAX_OWNED:=2}" + : "${X3_MAX_CHAN_USERS:=512}" + # Value is a conf list body: each channel stays individually quoted. + : "${X3_SUPPORT_CHANNEL:=\"#Operations\", \"#Help\"}" + : "${X3_GLOBAL_NICK:=Global}" + # Also used by the snoop module (templatized with the other modules). + : "${X3_SNOOP_CHANNEL:=#MrSnoopy}" + # NickServ (AuthServ) defaults : "${X3_NICKSERV_NICK:=AuthServ}" : "${X3_DEFAULT_MAXLOGINS:=3}" diff --git a/docker/x3.conf-dist b/docker/x3.conf-dist index 06fb1895..ef658ecb 100644 --- a/docker/x3.conf-dist +++ b/docker/x3.conf-dist @@ -278,7 +278,7 @@ * Afternet uses the nickname "O3" for this as its easier to type. */ "opserv" { - "nick" "O3"; + "nick" "%X3_OPSERV_NICK%"; // should use of this service be limited to global opers? "privileged" "1"; @@ -286,21 +286,21 @@ "description" "Oper Service Bot"; // (for /whois) // hostname for service; only used if "description" is also set - "hostname" "X3.Services"; // (for /whois) + "hostname" "%X3_OPSERV_HOSTNAME%"; // (for /whois) // What channel should opserv send debug output to? // I don't have any idea what debug info goes here. You can configure // debugging logs in the log section to go to any channel. // Probably safest to set to your oper channel. - "debug_channel" "#TheOps"; // Bot will join this channel, also. + "debug_channel" "%X3_DEBUG_CHANNEL%"; // Bot will join this channel, also. "debug_channel_modes" "+tnOS"; // Modes get set every time X3 starts up // where to send general alerts (e.g. flood alerts)? - "alert_channel" "#TheOps"; // Bot will join this channel, also. + "alert_channel" "%X3_ALERT_CHANNEL%"; // Bot will join this channel, also. "alert_channel_modes" "+"; // Modes get set every time X3 starts up // who to tell about staff auths? - "staff_auth_channel" "#OperServ"; // Bot will join this channel, also. + "staff_auth_channel" "%X3_STAFF_AUTH_CHANNEL%"; // Bot will join this channel, also. "staff_auth_channel_modes" "+tnOs"; // modes get set every time X3 starts up // which channels should all services autojoin? @@ -314,7 +314,7 @@ "untrusted_max" "%X3_UNTRUSTED_MAX%"; // env-templated (default 6). 0 DISABLES the clone-G-line — set 0 on testbeds where a harness opens many connections from one host (else O3 auto-G-lines that host for clone_gline_duration and the bed appears dead). 3 connections and 3 ghosts, (N+1)th connection causes a gline. // how long of a g-line should be issued if the max hosts is exceeded? - "clone_gline_duration" "2h"; // durations are smhdmy + "clone_gline_duration" "%X3_CLONE_GLINE_DURATION%"; // durations are smhdmy // how long to g-line for ?block (or, by default, for trace gline)? "block_gline_duration" "12h"; @@ -417,7 +417,7 @@ }; "chanserv" { - "nick" "X3"; + "nick" "%X3_CHANSERV_NICK%"; // The umodes - add +d if you use nefarious 1.0 and you added 'b:lines' // to pass cmdchar through to chanserv anyway. @@ -431,7 +431,7 @@ // NOTE: +z mode, needed for this to work. X3 contains modifications to // try and prevent desynchs. If you use this mode do not use any other service // that uses this mode. - "off_channel" "no"; + "off_channel" "%X3_OFF_CHANNEL%"; // Infolines are sent when channel users join the channel. Users set them with USET INFO in X3. // how long should a person be unseen before resending infoline? @@ -443,7 +443,7 @@ "max_greetlen" "120"; // maximum users in a channel userlist - "max_chan_users" "512"; + "max_chan_users" "%X3_MAX_CHAN_USERS%"; // maximum bans on a channel banlist "max_chan_bans" "512"; // maximum length of a user's infoline @@ -510,10 +510,10 @@ // channel(s) that support helpers must be in to be helping // if this is a list, any one by itself will do - "support_channel" ("#Operations", "#Help"); + "support_channel" (%X3_SUPPORT_CHANNEL%); // maximum number of channels a user may have. ( FORCE can override ) - "max_owned" "2"; + "max_owned" "%X3_MAX_OWNED%"; // how long between automatic topic and userlist refreshes with TopicRefresh/Resync "refresh_period" "10h"; @@ -542,7 +542,7 @@ * like to set ours' nick to 'AfterNET', but some people use 'Global' */ "global" { - "nick" "Global"; + "nick" "%X3_GLOBAL_NICK%"; // should users get community announcements by default or not? // community announcements are a type of global that users may // opt into (or out of, depending on this setting) @@ -892,8 +892,8 @@ // list a target to log it -- this is because it is very rarely // useful. "*.*" ("std:out", "file:/x3/data/everything.log"); // does NOT suppress any defaults - "*.override,error,fatal" "irc:#TheOps"; // report all uses of staff commands - "*.staff" "irc:#MrSnoopy"; // report all uses of staff commands + "*.override,error,fatal" "irc:%X3_DEBUG_CHANNEL%"; // report all uses of staff commands + "*.staff" "irc:%X3_SNOOP_CHANNEL%"; // report all uses of staff commands "ChanServ.*" "file:/x3/data/chanserv.log"; // duplicates the default behavior "ProxyCheck.*" (); // stop it from logging anything }; From 8b0bf826f29bc1913e72b76c88269f6e34f1f6ef Mon Sep 17 00:00:00 2001 From: MrLenin <909621+MrLenin@users.noreply.github.com> Date: Sat, 1 Aug 2026 03:03:02 -0400 Subject: [PATCH 07/10] docker: env-template the modules block; kill the shipped qserver credential MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Templatize the module settings (defaults preserve shipped literals unless noted): sockcheck max_sockets -> %X3_SOCKCHECK_MAX_SOCKETS% (0) sockcheck gline_duration -> %X3_SOCKCHECK_GLINE_DURATION% (1d) snoop channel -> %X3_SNOOP_CHANNEL% (#MrSnoopy) snoop bot -> %X3_OPSERV_NICK% (reused) track channel -> %X3_TRACK_CHANNEL% (#MrPeanuts) memoserv bot -> %X3_MEMOSERV_BOT% (MemoServ) memoserv message_expiry -> %X3_MEMO_EXPIRY% (30d) memoserv limit -> %X3_MEMO_LIMIT% (30) qserver password -> %X3_QSERVER_PASSWORD% blacklist gline_duration -> %X3_BLACKLIST_GLINE_DURATION% (1h) The qserver password shipped as the literal credential "hello"; the entrypoint now generates a random 16-char password when the deployment doesn't provide one, so no image ever runs with a known qserver secret. The bogus sockcheck "bind_address" "192.168.0.10" is commented out — set only if you need a specific source IP; the shipped default was garbage that broke proxy checks on any host not owning that address. The webtv module is left alone (niche, mark-gated, nothing worth parameterizing). Co-Authored-By: Claude Fable 5 --- docker/dockerentrypoint.sh | 12 ++++++++++++ docker/x3.conf-dist | 23 ++++++++++++----------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/docker/dockerentrypoint.sh b/docker/dockerentrypoint.sh index 8abe7d45..5ff1e380 100755 --- a/docker/dockerentrypoint.sh +++ b/docker/dockerentrypoint.sh @@ -56,6 +56,18 @@ else # Also used by the snoop module (templatized with the other modules). : "${X3_SNOOP_CHANNEL:=#MrSnoopy}" + # Modules + : "${X3_SOCKCHECK_MAX_SOCKETS:=0}" + : "${X3_SOCKCHECK_GLINE_DURATION:=1d}" + : "${X3_TRACK_CHANNEL:=#MrPeanuts}" + : "${X3_MEMOSERV_BOT:=MemoServ}" + : "${X3_MEMO_EXPIRY:=30d}" + : "${X3_MEMO_LIMIT:=30}" + # qserver: no shipped literal credential — generate a random password + # when the deployment doesn't provide one. + : "${X3_QSERVER_PASSWORD:=$(tr -dc 'A-Za-z0-9' Date: Sat, 1 Aug 2026 03:03:41 -0400 Subject: [PATCH 08/10] docker: env-template the mail block; default mail OFF to match the image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Templatize the mail block and the mondo DB save interval: enable -> %X3_MAIL_ENABLE% (0 — BEHAVIOR CHANGE, see below) mailer -> %X3_MAIL_MAILER% (/usr/sbin/sendmail) smtp_server -> %X3_MAIL_SMTP_SERVER% (localhost) smtp_service -> %X3_MAIL_SMTP_SERVICE% (smtp) dbs mondo frequency -> %X3_DB_SAVE_FREQUENCY% (30m) The enable default flips 1 -> 0 deliberately: the image ships no MTA at /usr/sbin/sendmail, so mail was enabled-but-dead — every cookie/verify mail silently failed at mailer exec time. Deployments with a real mailer or SMTP relay set X3_MAIL_ENABLE=1 explicitly. Branding coherence: the mail body strings mixed %X3_GENERAL_DOMAIN% with leftover literals — "NET Support" / "NET IRC Network" now derive from the domain, and the odd "Network-Services: x3" extra header becomes a neutral "X-Mailer: X3-Services". Co-Authored-By: Claude Fable 5 --- docker/dockerentrypoint.sh | 12 ++++++++++++ docker/x3.conf-dist | 16 ++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docker/dockerentrypoint.sh b/docker/dockerentrypoint.sh index 5ff1e380..6cc48e10 100755 --- a/docker/dockerentrypoint.sh +++ b/docker/dockerentrypoint.sh @@ -68,6 +68,18 @@ else : "${X3_QSERVER_PASSWORD:=$(tr -dc 'A-Za-z0-9' Date: Sat, 1 Aug 2026 03:29:32 -0400 Subject: [PATCH 09/10] docker: regenerate generated configs on every start (marker-based) The entrypoint skipped generation whenever /x3/data/x3.conf existed, which freezes the config at first container start forever: with x3.conf on a persistent volume, every later env-var change, template update, or image rebuild was silently inert. (Deployments hit exactly this: a conf generated once in June still driving the container in August.) New policy, keyed on a marker comment stamped as the first line of every conf this script generates: - no x3.conf -> generate (with marker) - x3.conf WITH the marker -> regenerate: env + template are the source of truth, config changes land on restart - x3.conf WITHOUT the marker -> user-managed (volume-mounted, hand-rolled) -> never touched This preserves both documented workflows (env-templated and bring-your-own x3.conf) while making the env-templated one actually track its inputs. Generation substitutes into a temp file and stamps the marker on the final move, so a mid-generation crash can't leave a half-substituted marker-less conf that would then be mistaken for user-managed. Migration note: confs generated by the OLD entrypoint carry no marker and are therefore treated as user-managed after upgrade; delete the stale x3.conf once (the volume's x3.db is unaffected) to opt into regeneration. Co-Authored-By: Claude Fable 5 --- docker/dockerentrypoint.sh | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/docker/dockerentrypoint.sh b/docker/dockerentrypoint.sh index 6cc48e10..3826321a 100755 --- a/docker/dockerentrypoint.sh +++ b/docker/dockerentrypoint.sh @@ -7,11 +7,21 @@ BASECONFDIST=/x3/x3src/docker/x3.conf-dist BASECONF=/x3/data/x3.conf -# Only generate config if it doesn't already exist -if [ -f "$BASECONF" ]; then - echo "Found existing config at $BASECONF, skipping generation" +# Marker stamped as the first line of every conf THIS script generates. +# Regeneration policy: +# - no x3.conf -> generate (and stamp the marker) +# - x3.conf with the marker -> regenerate: env + template are the source +# of truth, so config changes actually land +# on restart instead of freezing at first +# container start +# - x3.conf without the marker -> user-managed (e.g. volume-mounted); +# never touched +GENMARKER='// GENERATED BY dockerentrypoint.sh from x3.conf-dist. Do not hand-edit: this file is REGENERATED ON EVERY CONTAINER START while this marker is the first line. To manage x3.conf yourself, replace it with a file that lacks this marker.' + +if [ -f "$BASECONF" ] && ! head -n 1 "$BASECONF" | grep -qF 'GENERATED BY dockerentrypoint.sh'; then + echo "Found user-managed config at $BASECONF (no generation marker), leaving it alone" else - echo "No existing config found, generating from template..." + echo "Generating $BASECONF from template..." # Set defaults for required variables (can be overridden by environment) : "${X3_GENERAL_NAME:=x3.network}" @@ -113,12 +123,15 @@ else : "${X3_LDAP_AUTOCREATE:=1}" : "${X3_LDAP_TIMEOUT:=10}" - # Copy the template to the output location - cp "$BASECONFDIST" "$BASECONF" + # Substitute into a temp copy, then stamp the marker and move into + # place, so a crash mid-generation never leaves a half-substituted + # marker-less (= treated as user-managed) conf behind. + GENTMP="$BASECONF.gen.$$" + cp "$BASECONFDIST" "$GENTMP" # Find all %VARIABLE% placeholders in the config and substitute them # with corresponding environment variable values - grep -oE '%[A-Za-z_][A-Za-z0-9_]*%' "$BASECONF" | sort -u | while read -r placeholder; do + grep -oE '%[A-Za-z_][A-Za-z0-9_]*%' "$GENTMP" | sort -u | while read -r placeholder; do # Extract variable name (remove the % signs) varname="${placeholder:1:-1}" @@ -132,12 +145,15 @@ else # themselves re-escaped), then '&' (whole-match backreference) # and the '|' delimiter itself. '/' needs no escaping here. escaped_value=$(printf '%s\n' "$value" | sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/|/\\|/g') - sed -i "s|${placeholder}|${escaped_value}|g" "$BASECONF" + sed -i "s|${placeholder}|${escaped_value}|g" "$GENTMP" else echo "Warning: No value set for ${varname}, leaving ${placeholder} unchanged" fi done + { printf '%s\n' "$GENMARKER"; cat "$GENTMP"; } > "$BASECONF" + rm -f "$GENTMP" + echo "Generated $BASECONF from template" fi From 9a566d06adba99cc7f5a8f907b324a06cc57d669 Mon Sep 17 00:00:00 2001 From: MrLenin <909621+MrLenin@users.noreply.github.com> Date: Mon, 3 Aug 2026 15:56:37 -0400 Subject: [PATCH 10/10] docker: add .env.example documenting every templated var MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The entrypoint already supplies a default for each %X3_*% placeholder, but those defaults lived only in dockerentrypoint.sh — setting up a .env.local meant reading the script (or the source) to learn what keys exist and what they should be. This adds a documented .env.example covering all 69 template variables: each with its exact entrypoint default, a one-to-two line description harvested from x3.conf.example / x3.conf-dist, grouped into setup-guide sections (server, uplink, service nicks, channels, cloaking, opserv/glines, chanserv, memoserv, mail, passwords, LDAP, misc). Secrets (uplink/LDAP/cloak keys) carry placeholder values and CHANGE-THIS markers; X3_QSERVER_PASSWORD notes the leave-unset auto-randomize behavior. Copy to .env.local and override only what you need — every value here is the default the entrypoint would apply anyway. Intended to become X3's canonical documented default set if config generation later reads .env* directly. Co-Authored-By: Claude Opus 4.8 --- .env.example | 332 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..aa3d3b29 --- /dev/null +++ b/.env.example @@ -0,0 +1,332 @@ +# ============================================================================ +# X3 IRC Services — Docker environment defaults (.env.example) +# ============================================================================ +# +# HOW TO USE +# 1. Copy this file to .env.local: cp .env.example .env.local +# 2. Edit .env.local and change ONLY the values you care about — every value +# shown here is already the built-in default the container entrypoint +# (docker/dockerentrypoint.sh) would use anyway. You never need to set a +# var just to keep its default; only OVERRIDE what you want to change. +# 3. At minimum, review the lines marked "CHANGE THIS — secret" below. +# +# HOW IT WORKS (regeneration policy) +# On container start, dockerentrypoint.sh reads docker/x3.conf-dist, replaces +# each %X3_*% placeholder with the matching environment variable (falling +# back to the defaults documented here), and writes /x3/data/x3.conf. +# It only (re)generates that file when the file is absent OR its first line +# carries the generator marker ("// GENERATED BY dockerentrypoint.sh ..."). +# A user-supplied x3.conf that LACKS that marker (e.g. a volume-mounted, +# hand-managed config) is left untouched and these variables are ignored. +# +# FORWARD-COMPATIBILITY +# This file is intended to eventually become X3's single canonical, documented +# source of default values — a future change may have X3 read .env* directly +# instead of templating through the entrypoint. It is therefore written to be +# complete (all 70 X3_* vars), accurate (exact entrypoint defaults), and +# self-contained (each var is understandable without reading source). +# +# Value syntax: shell-style KEY=value, one per line. Do not add quotes unless +# the value itself must contain them (see X3_SUPPORT_CHANNEL). +# ============================================================================ + + +# ---------------------------------------------------------------------------- +# Server identity (conf: "server" { } — how this X3 presents on the network) +# ---------------------------------------------------------------------------- + +# Server name X3 announces on link. This MUST be the name used in the ircd's +# Connect/C:line for X3. Shows in /links and /whois. +X3_GENERAL_NAME=x3.network + +# Free-text server description, shown in /links. +X3_GENERAL_DESCRIPTION=Network Services + +# Network domain. Used as the "network" name and woven into the hidden-host +# suffix (Users.), admin block, and all mail from/body templates. +X3_GENERAL_DOMAIN=example.com + +# Server numeric. Each server on a P10 network needs a unique numeric — +# if you get collisions on link, CHANGE THIS. +X3_GENERAL_NUMERIC=199 + +# Protocol/compat type — MUST match your ircd version: +# 8 = Nefarious 1.3.x and higher (legacy) +# 9 = Nefarious 2.0.x / nefarious2 (current) +# (Lower values 4-7 exist for old/obsolete ircds.) Default is 8; set 9 for +# a modern nefarious2 uplink. +X3_SERVER_TYPE=8 + +# LOCAL source IP X3 binds when connecting out to the uplink (uplink +# "bind_address"). Also becomes the default connect-from address. +X3_GENERAL_BIND_ADDRESS=127.0.0.1 + + +# ---------------------------------------------------------------------------- +# Uplink (conf: "uplinks"."Hub" — the IRC server X3 links to) +# ---------------------------------------------------------------------------- + +# IP/address of the ircd X3 connects to. +X3_UPLINK_ADDRESS=127.0.0.1 + +# TCP port of the uplink's server (C:line) listener. +X3_UPLINK_PORT=8888 + +# CHANGE THIS — secret. Link password; fills BOTH "password" and +# "uplink_password" and must match the pass in the ircd's Connect/C:line for X3. +X3_UPLINK_PASSWORD=changeme + + +# ---------------------------------------------------------------------------- +# Service bot nicks (conf: "services".."nick") +# ---------------------------------------------------------------------------- + +# Authentication service (NickServ/AuthServ) — register and auth here. +X3_NICKSERV_NICK=AuthServ + +# Operator service (OpServ) nick. Afternet uses "O3" (easier to type). +X3_OPSERV_NICK=O3 + +# OpServ /whois hostname (only used because OpServ also sets a description). +X3_OPSERV_HOSTNAME=X3.Services + +# Channel service (ChanServ) nick. +X3_CHANSERV_NICK=X3 + +# Global announcement bot nick. +X3_GLOBAL_NICK=Global + +# MemoServ bot nick (user-to-user memos module). +X3_MEMOSERV_BOT=MemoServ + + +# ---------------------------------------------------------------------------- +# Channels (service/report/autojoin channels) +# ---------------------------------------------------------------------------- + +# OpServ debug channel — bot joins it; also the target for override/error/fatal +# log events. +X3_DEBUG_CHANNEL=#TheOps + +# OpServ general-alert channel (e.g. flood alerts). Bot joins it. +X3_ALERT_CHANNEL=#TheOps + +# Channel where staff-auth notices are announced. Bot joins it. +X3_STAFF_AUTH_CHANNEL=#OperServ + +# Snoop module channel (connect/quit/join/part feed). Keep it secure — it +# exposes user activity. Also used as the log target for "staff" severity. +X3_SNOOP_CHANNEL=#MrSnoopy + +# Track module channel (per-user event tracking). NOTE: the track module is +# known-unstable and normally not compiled in; this only matters if it is. +X3_TRACK_CHANNEL=#MrPeanuts + +# ChanServ support_channel list — helpers must be in one of these to be "on +# duty". This is a conf LIST body: keep each channel individually quoted. +X3_SUPPORT_CHANNEL="#Operations", "#Help" + + +# ---------------------------------------------------------------------------- +# Hidden-host / cloaking (conf: "server" — must mirror the ircd's F:lines) +# ---------------------------------------------------------------------------- + +# Host-hiding style: 1 = simple (Users.), 2 = Nefarious style-2 keyed +# cloaking. If you use +x style-2 on the ircd, set 2 and the keys/prefix below. +X3_HIDDEN_HOST_TYPE=1 + +# CHANGE THIS — secret (style-2 only). These three keys MUST exactly match the +# ircd's HOST_HIDING_KEY1/2/3 F:lines. The shipped stock values make style-2 +# cloaks predictable — change them in production. +X3_HIDDEN_HOST_KEY1=45432 +X3_HIDDEN_HOST_KEY2=76934 +X3_HIDDEN_HOST_KEY3=98336 + +# Style-2 host prefix — the name prefixed onto cloaked hosts. +X3_HIDDEN_HOST_PREFIX=NETWORK + + +# ---------------------------------------------------------------------------- +# OpServ / G-line durations (clone control + module G-lines) +# ---------------------------------------------------------------------------- + +# OpServ clone threshold (untrusted_max): auto-G-line a host that exceeds this +# many connections. Default 6 (stock). 0 DISABLES it — testbeds set 0 in +# .env.local so a harness opening many connections from one host isn't +# auto-G-lined (which would make the network look dead). +X3_UNTRUSTED_MAX=6 + +# How long the clone/untrusted-max G-line lasts (duration units: s m h d m y). +X3_CLONE_GLINE_DURATION=2h + +# SockCheck (open-proxy scanner) max concurrent sockets. 0 = effectively off. +# Only enable proxy scanning with ISP permission — probes look like attacks. +X3_SOCKCHECK_MAX_SOCKETS=0 + +# G-line duration SockCheck issues when it flags an open proxy. +X3_SOCKCHECK_GLINE_DURATION=1d + +# G-line duration for blacklist (DNSBL/file) hits in the blacklist module. +X3_BLACKLIST_GLINE_DURATION=1h + + +# ---------------------------------------------------------------------------- +# ChanServ limits (conf: "services"."chanserv") +# ---------------------------------------------------------------------------- + +# off_channel mode: no/0 = ChanServ idles in registered channels; 1 = use the +# registered-channel mode and self-op; 2 = as 1 but ChanServ does NOT idle in +# the channel. Needs ircd +z support; do not run another +z service alongside. +X3_OFF_CHANNEL=no + +# Max channels one account may own (FORCE can override). +X3_MAX_OWNED=2 + +# Max users tracked in a channel userlist. +X3_MAX_CHAN_USERS=512 + + +# ---------------------------------------------------------------------------- +# MemoServ (conf: "modules"."memoserv") +# ---------------------------------------------------------------------------- + +# Age at which memos are auto-deleted; 0 disables memo expiry. +X3_MEMO_EXPIRY=30d + +# Max number of memos one account may hold. +X3_MEMO_LIMIT=30 + + +# ---------------------------------------------------------------------------- +# Mail / email (conf: "mail" + AuthServ email-cookie settings) +# ---------------------------------------------------------------------------- + +# Master mail switch. Disabled by default: the image ships NO MTA at +# /usr/sbin/sendmail, so "enabled" without a working mailer/SMTP just makes +# every cookie mail fail at exec time. Set 1 AND provide a mailer/SMTP to use it. +X3_MAIL_ENABLE=0 + +# Path to the sendmail-compatible binary (used when mail is enabled and you are +# not using the SMTP back-end). +X3_MAIL_MAILER=/usr/sbin/sendmail + +# SMTP server host (SMTP mail back-end). +X3_MAIL_SMTP_SERVER=localhost + +# SMTP service/port name (from /etc/services, e.g. "smtp" = 25). +X3_MAIL_SMTP_SERVICE=smtp + +# AuthServ: allow account verification and password reset by email. +# Requires mail (above) to be enabled and configured. +X3_EMAIL_ENABLED=0 + +# AuthServ: if email is enabled, require verification before an account may +# authenticate. +X3_EMAIL_REQUIRED=0 + +# How long an email verification/reset cookie stays valid. +X3_COOKIE_TIMEOUT=2d + +# How many accounts may share one email address. +X3_ACCOUNTS_PER_EMAIL=1 + + +# ---------------------------------------------------------------------------- +# Password / login policy (conf: "services"."nickserv") +# ---------------------------------------------------------------------------- + +# Minimum characters AuthServ enforces in a password. +X3_PASSWD_MIN_LENGTH=4 + +# Minimum digit characters required in a password (0 = no requirement). +X3_PASSWD_MIN_DIGITS=0 + +# Minimum uppercase characters required in a password (0 = no requirement). +X3_PASSWD_MIN_UPPER=0 + +# Minimum lowercase characters required in a password (0 = no requirement). +X3_PASSWD_MIN_LOWER=0 + +# Default max simultaneous logins on a NEW account (users can change their own). +X3_DEFAULT_MAXLOGINS=3 + +# Hard ceiling on simultaneous logins that a user cannot raise past. +X3_HARD_MAXLOGINS=10 + +# User modes auto-set when an oper auths. If it contains 'o' they are +# auto-remote-opered on auth. +X3_AUTO_OPER=+oxwgs + +# Oper privileges granted alongside X3_AUTO_OPER when an oper auths +# (space-separated privilege tokens). +X3_AUTO_OPER_PRIVS=CHAN_LIMIT SHOW_INVIS SHOW_ALL_INVIS KILL LOCAL_KILL REHASH RESTART DIE JUPE LOCAL_JUPE OPMODE WHOX SEE_CHAN PROPAGATE DISPLAY SEE_OPERS WIDE_GLINE FORCE_OPMODE REMOTEREHASH CHECK SEE_SECRET_CHAN WIDE_SHUN WIDE_ZLINE LIST_CHAN + + +# ---------------------------------------------------------------------------- +# LDAP (conf: "services"."nickserv" ldap_* — disabled by default) +# ---------------------------------------------------------------------------- + +# Master LDAP switch. 0 = off (accounts live only in saxdb). Set 1 to +# authenticate/store accounts against an LDAP directory. +X3_LDAP_ENABLE=0 + +# Write new registrations (including credentials) back to LDAP. REQUIRED for +# SASL when LDAP is authoritative — without it nickserv gates out ldap_do_add +# and new accounts land credential-less in saxdb only. +X3_LDAP_WRITEBACK=0 + +# LDAP server URI. +X3_LDAP_URI=ldap://localhost:389 + +# Base DN under which user entries live. +X3_LDAP_BASE=ou=users,dc=example,dc=net + +# DN format used to bind as a user; %s is replaced with the account name. +X3_LDAP_DN_FMT=uid=%s,ou=users,dc=example,dc=net + +# Admin/manager bind DN used for writes and searches. +X3_LDAP_ADMIN_DN=cn=admin,dc=example,dc=net + +# CHANGE THIS — secret. Password for the admin bind DN above. +X3_LDAP_ADMIN_PASS=changeme + +# Attribute holding the account name. +X3_LDAP_FIELD_ACCOUNT=uid + +# Attribute holding the (hashed) password. +X3_LDAP_FIELD_PASSWORD=userPassword + +# Attribute holding the email address. +X3_LDAP_FIELD_EMAIL=mail + +# Attribute holding the X3 OpServ level. +X3_LDAP_FIELD_OSLEVEL=X3AccountLevel + +# Search filter used to locate user entries. +X3_LDAP_FILTER=(objectClass=inetOrgPerson) + +# Auto-create an LDAP entry on first registration (1 = yes). +X3_LDAP_AUTOCREATE=1 + +# LDAP operation timeout, in seconds. +X3_LDAP_TIMEOUT=10 + + +# ---------------------------------------------------------------------------- +# Misc +# ---------------------------------------------------------------------------- + +# How often the mondo database (/x3/data/x3.db) is flushed to disk. +# Set 0 to disable automatic saves. +X3_DB_SAVE_FREQUENCY=30m + +# CHANGE THIS — secret. qserver (remote query interface) password. +# The entrypoint default is NOT a literal: if this var is left UNSET the +# entrypoint auto-generates a random 16-char password per boot. Set an explicit +# value here only if you need a stable, known qserver password. +X3_QSERVER_PASSWORD=changeme + +# AuthServ 'USET title' fakehost suffix (name.title.). The entrypoint +# default is DERIVED from X3_GENERAL_DOMAIN (i.e. equals your domain) unless you +# set an explicit value here. +X3_TITLEHOST_SUFFIX=example.com