diff --git a/apps-gx10/irc/irc.yaml b/apps-gx10/irc/irc.yaml
new file mode 100644
index 0000000..c517ff7
--- /dev/null
+++ b/apps-gx10/irc/irc.yaml
@@ -0,0 +1,1354 @@
+# ============================================================================
+# IRC stack rehomed from OLD RKE2 cluster (infra-irc, ns irc) to GX10 K8s.
+# UnrealIRCd + Anope IRC Services + The Lounge web client.
+#
+# GX10 adaptations vs OLD bluejay-infra/apps/irc/irc.yaml:
+# - OnePasswordItem REPLACED by directly-copied Secrets (no OnePassword operator on GX10).
+# irc-credentials is applied separately; cloak keys are injected from unrealircd-cloak-keys.
+# - unrealircd image -> localhost/fc-unrealircd:6.1.9.1-arm64 (built on GX10 from
+# DjLegolas/unrealircd-docker, alpine:3.19 base, UnrealIRCd 6.1.9.1 from source;
+# djlegolas/unrealircd:6.1.9.1 on Docker Hub is amd64-ONLY -> would crashloop on arm64).
+# imagePullPolicy: Never (image is ctr-imported, not in a registry).
+# - anope image PINNED anope/anope:2.0.15 (multi-arch incl arm64; was :latest = arm64 too
+# but pinned for reproducibility).
+# - thelounge ghcr.io/thelounge/thelounge:4.4.3 (multi-arch incl arm64) — unchanged.
+# - PVCs -> storageClassName: local-path (GX10 default; OLD was longhorn).
+# - IRC TCP 6667/6697 exposed via a dedicated MetalLB LoadBalancer on the PROD pool
+# (10.0.57.204) instead of OLD's Traefik irc/irctls entryPoints (GX10 Traefik has no
+# such entryPoints). 8067 (anope uplink) stays internal on a separate ClusterIP svc.
+# - Stripped ArgoCD instance labels / status / server metadata.
+# ============================================================================
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: irc
+ labels:
+ app.kubernetes.io/part-of: gx10-migration
+---
+# TLS Certificate for IRC (server SNI irc.iamworkin.lan)
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ name: irc-tls
+ namespace: irc
+spec:
+ secretName: irc-tls
+ issuerRef:
+ name: step-ca-acme
+ kind: ClusterIssuer
+ dnsNames:
+ - irc.iamworkin.lan
+---
+# TLS Certificate for The Lounge web IRC
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ name: webirc-tls
+ namespace: irc
+spec:
+ secretName: webirc-tls
+ issuerRef:
+ name: step-ca-acme
+ kind: ClusterIssuer
+ dnsNames:
+ - webirc.iamworkin.lan
+---
+# The Lounge configuration
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: thelounge-config
+ namespace: irc
+data:
+ config.js: |
+ "use strict";
+
+ module.exports = {
+ public: true,
+ host: "0.0.0.0",
+ port: 9000,
+ reverseProxy: true,
+ maxHistory: 2500,
+ theme: "thelounge-theme-flowercore",
+ prefetch: false,
+ disableMediaPreview: true,
+ fileUpload: {
+ enable: false
+ },
+ defaults: {
+ name: "BlueJayIRC",
+ host: "unrealircd.irc.svc",
+ port: 6667,
+ password: "",
+ tls: false,
+ rejectUnauthorized: true,
+ nick: "BlueJayWeb%%",
+ username: "bluejayweb",
+ realname: "BlueJay Web IRC",
+ join: "#general"
+ },
+ lockNetwork: true,
+ leaveMessage: "BlueJay Web IRC"
+ };
+---
+# FlowerCore / Blue Jay theme package for The Lounge
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: thelounge-flowercore-theme
+ namespace: irc
+data:
+ package.json: |
+ {
+ "name": "thelounge-theme-flowercore",
+ "version": "1.0.0",
+ "description": "FlowerCore Blue Jay theme for The Lounge",
+ "main": "package.json",
+ "keywords": [
+ "thelounge",
+ "thelounge-theme"
+ ],
+ "thelounge": {
+ "type": "theme",
+ "name": "FlowerCore Blue Jay",
+ "css": "theme.css",
+ "files": [
+ "bluejay-logo.svg",
+ "bluejay-bg.svg"
+ ]
+ }
+ }
+ theme.css: |
+ :root {
+ --fc-bg: #0a1628;
+ --fc-surface: #111d33;
+ --fc-surface-2: #162844;
+ --fc-border: #1e3a5f;
+ --fc-accent: #2b8aff;
+ --fc-accent-soft: rgba(43, 138, 255, 0.22);
+ --fc-gold: #ffb300;
+ --fc-text: #e8edf5;
+ --fc-text-muted: #9db1c8;
+ --fc-success: #3db86a;
+ --fc-danger: #e84545;
+ --body-color: var(--fc-text);
+ --body-color-muted: var(--fc-text-muted);
+ --body-bg-color: var(--fc-bg);
+ --button-color: var(--fc-accent);
+ --button-text-color-hover: #ffffff;
+ --overlay-bg-color: rgba(5, 12, 22, 0.84);
+ --link-color: #8bc3ff;
+ --window-bg-color: var(--fc-surface);
+ --window-heading-color: #f5f8ff;
+ --date-marker-color: rgba(43, 138, 255, 0.45);
+ --unread-marker-color: rgba(255, 179, 0, 0.6);
+ --highlight-bg-color: rgba(43, 138, 255, 0.12);
+ --highlight-border-color: var(--fc-gold);
+ --upload-progressbar-color: var(--fc-gold);
+ }
+
+ body {
+ background:
+ radial-gradient(circle at top right, rgba(43, 138, 255, 0.22), transparent 30%),
+ linear-gradient(145deg, rgba(10, 22, 40, 0.98), rgba(12, 25, 46, 0.98)),
+ url("/packages/thelounge-theme-flowercore/bluejay-bg.svg");
+ color: var(--fc-text);
+ font-family: "Trebuchet MS", "Segoe UI", Verdana, sans-serif;
+ }
+
+ a,
+ a:focus,
+ a:hover {
+ color: var(--link-color);
+ }
+
+ .window,
+ #confirm-dialog,
+ #context-menu,
+ .mentions-popup,
+ .textcomplete-menu {
+ background: rgba(17, 29, 51, 0.97);
+ border: 1px solid var(--fc-border);
+ box-shadow: 0 24px 60px rgba(3, 10, 18, 0.55);
+ }
+
+ #loading .window,
+ #confirm-dialog {
+ background:
+ linear-gradient(180deg, rgba(14, 30, 54, 0.98), rgba(17, 29, 51, 0.98)),
+ url("/packages/thelounge-theme-flowercore/bluejay-bg.svg");
+ border-color: rgba(43, 138, 255, 0.4);
+ }
+
+ #loading .logo,
+ #loading .logo-inverted,
+ #sidebar .logo,
+ #sidebar .logo-inverted {
+ display: none !important;
+ }
+
+ #loading-status-container,
+ #sidebar .logo-container {
+ position: relative;
+ }
+
+ #loading-status-container::before {
+ content: "";
+ display: block;
+ width: 96px;
+ height: 96px;
+ margin: 0 auto 16px;
+ background: url("/packages/thelounge-theme-flowercore/bluejay-logo.svg") center / contain no-repeat;
+ filter: drop-shadow(0 10px 24px rgba(0, 0, 0, 0.45));
+ }
+
+ #loading-page-message::before {
+ content: "FlowerCore IRC";
+ display: block;
+ margin-bottom: 10px;
+ color: #ffffff;
+ font-size: 30px;
+ font-weight: 700;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+ }
+
+ #loading-page-message::after {
+ content: "Blue Jay web chat for iamworkin.lan";
+ display: block;
+ margin-top: 10px;
+ color: var(--fc-text-muted);
+ font-size: 14px;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ }
+
+ #loading-page-message {
+ color: var(--fc-text);
+ font-size: 15px;
+ line-height: 1.7;
+ max-width: 30rem;
+ text-align: center;
+ }
+
+ #sidebar {
+ background:
+ linear-gradient(180deg, rgba(10, 22, 40, 0.98), rgba(17, 29, 51, 0.98)),
+ url("/packages/thelounge-theme-flowercore/bluejay-bg.svg");
+ border-right: 1px solid var(--fc-border);
+ color: #d5e3f5;
+ }
+
+ #sidebar .logo-container {
+ padding: 20px 14px 8px;
+ }
+
+ #sidebar .logo-container::before {
+ content: "";
+ display: block;
+ width: 72px;
+ height: 72px;
+ margin: 0 auto 10px;
+ background: url("/packages/thelounge-theme-flowercore/bluejay-logo.svg") center / contain no-repeat;
+ filter: drop-shadow(0 8px 18px rgba(0, 0, 0, 0.35));
+ }
+
+ #sidebar .logo-container::after {
+ content: "FlowerCore IRC";
+ display: block;
+ color: #ffffff;
+ font-size: 18px;
+ font-weight: 700;
+ letter-spacing: 0.06em;
+ text-align: center;
+ text-transform: uppercase;
+ }
+
+ #sidebar .network {
+ margin-bottom: 16px;
+ }
+
+ .channel-list-item,
+ #footer button {
+ border-radius: 10px;
+ transition: background-color 0.2s ease, box-shadow 0.2s ease, color 0.2s ease;
+ }
+
+ .channel-list-item:hover,
+ #footer button:hover {
+ background: rgba(43, 138, 255, 0.12);
+ color: #ffffff;
+ }
+
+ .channel-list-item.active,
+ #footer button.active {
+ background: linear-gradient(90deg, rgba(43, 138, 255, 0.24), rgba(22, 40, 68, 0.92));
+ box-shadow: inset 3px 0 0 var(--fc-gold);
+ color: #ffffff;
+ }
+
+ .channel-list-item[data-type="lobby"] {
+ color: #8bc3ff;
+ }
+
+ .channel-list-item .badge {
+ background: rgba(255, 255, 255, 0.08);
+ color: var(--fc-text-muted);
+ }
+
+ .channel-list-item .badge.highlight {
+ background: var(--fc-gold);
+ color: #08111e;
+ font-weight: 700;
+ }
+
+ #footer {
+ background: rgba(10, 22, 40, 0.92);
+ border-top: 1px solid var(--fc-border);
+ }
+
+ #viewport .lt,
+ #viewport .rt,
+ #chat button.close,
+ #chat button.menu,
+ #chat button.mentions,
+ #chat button.search,
+ #form #submit,
+ #form #upload,
+ .password-container .reveal-password span {
+ color: #8bc3ff;
+ }
+
+ #viewport .lt:hover,
+ #viewport .rt:hover,
+ #chat button.close:hover,
+ #chat button.menu:hover,
+ #chat button.mentions:hover,
+ #chat button.search:hover,
+ #form #submit:hover,
+ #form #upload:hover,
+ .password-container .reveal-password span:hover {
+ background: rgba(43, 138, 255, 0.16);
+ border-radius: 8px;
+ color: #ffffff;
+ opacity: 1;
+ }
+
+ #chat .header {
+ background: linear-gradient(135deg, #0e1e36, #1a3a6a, #2b8aff);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
+ color: #ffffff;
+ }
+
+ .header .title,
+ .header .topic,
+ #chat .header button {
+ color: #ffffff;
+ text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
+ }
+
+ #chat .messages {
+ background:
+ linear-gradient(180deg, rgba(17, 29, 51, 0.98), rgba(11, 21, 39, 0.98)),
+ radial-gradient(circle at top right, rgba(43, 138, 255, 0.08), transparent 35%);
+ }
+
+ #chat .msg {
+ border-radius: 6px;
+ transition: background-color 0.2s ease;
+ }
+
+ #chat .msg:hover {
+ background: rgba(255, 255, 255, 0.03);
+ }
+
+ #chat .chat-view[data-type="channel"] .msg.highlight,
+ .mentions-popup .msg .content {
+ background: linear-gradient(90deg, rgba(43, 138, 255, 0.16), rgba(255, 179, 0, 0.1));
+ box-shadow: inset 3px 0 0 var(--fc-gold);
+ }
+
+ #chat .msg-statusmsg {
+ background: rgba(255, 179, 0, 0.18);
+ color: #ffe4a0;
+ }
+
+ #chat .msg[data-type="monospace_block"] .text {
+ background: rgba(8, 17, 30, 0.94);
+ border: 1px solid rgba(43, 138, 255, 0.22);
+ border-radius: 10px;
+ box-shadow: inset 3px 0 0 var(--fc-gold);
+ color: var(--fc-text);
+ display: inline-block;
+ line-height: 1.7;
+ max-width: min(100%, 44rem);
+ padding: 10px 12px;
+ white-space: pre-wrap;
+ }
+
+ #chat .msg[data-command="motd"] .text {
+ background:
+ linear-gradient(180deg, rgba(14, 30, 54, 0.96), rgba(8, 17, 30, 0.96)),
+ url("/packages/thelounge-theme-flowercore/bluejay-bg.svg");
+ border-color: rgba(255, 179, 0, 0.28);
+ color: #f4f8ff;
+ }
+
+ #chat .msg[data-command="motd"] .from {
+ color: #9dd3ff;
+ font-weight: 700;
+ }
+
+ #chat .msg[data-command="motd"] a {
+ color: #8bc3ff;
+ font-weight: 700;
+ }
+
+ #chat .userlist,
+ #form,
+ .mentions-popup,
+ .textcomplete-menu,
+ #context-menu {
+ background: rgba(17, 29, 51, 0.98);
+ border-color: var(--fc-border);
+ }
+
+ #chat .userlist .count,
+ #chat .user-mode:before {
+ background: rgba(8, 17, 30, 0.95);
+ }
+
+ .input,
+ #connect input,
+ #connect select,
+ #settings input,
+ #settings select,
+ #settings textarea,
+ #form #nick,
+ #chat .userlist .search,
+ form.message-search input,
+ .jump-to-input .input,
+ .password-container input {
+ background: rgba(8, 17, 30, 0.72);
+ border: 1px solid var(--fc-border);
+ border-radius: 10px;
+ color: var(--fc-text);
+ }
+
+ #form {
+ border-top: 1px solid var(--fc-border);
+ padding: 8px;
+ }
+
+ #form #nick {
+ color: #8bc3ff;
+ line-height: 28px;
+ }
+
+ #form #input {
+ margin: 0 8px;
+ min-height: 36px;
+ padding: 8px 10px;
+ }
+
+ ::placeholder,
+ .jump-to-input .input::placeholder,
+ form.message-search input::placeholder {
+ color: rgba(232, 237, 245, 0.45);
+ }
+
+ .jump-to-input:before,
+ #chat .count:before {
+ color: rgba(232, 237, 245, 0.45);
+ }
+
+ .btn {
+ background: linear-gradient(180deg, rgba(24, 62, 112, 0.28), rgba(14, 30, 54, 0.38));
+ border-color: var(--fc-accent);
+ border-radius: 999px;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
+ color: var(--fc-text);
+ letter-spacing: 0.12em;
+ }
+
+ .btn:hover,
+ .btn:focus,
+ .btn:disabled {
+ background: linear-gradient(180deg, #2b8aff, #1c6fe3);
+ color: #ffffff;
+ }
+
+ .btn:active,
+ .btn:focus,
+ .input:focus {
+ box-shadow: 0 0 0 3px rgba(43, 138, 255, 0.26);
+ }
+
+ #version-checker,
+ #settings .settings-sync-panel,
+ #connect .connect-sasl-external {
+ background: rgba(8, 17, 30, 0.78);
+ border: 1px solid var(--fc-border);
+ color: var(--fc-text);
+ }
+
+ #version-checker.loading {
+ border-left: 3px solid var(--fc-accent);
+ color: #9dd3ff;
+ }
+
+ #version-checker.up-to-date {
+ border-left: 3px solid var(--fc-success);
+ color: #87e9a9;
+ }
+
+ #version-checker.new-packages,
+ #version-checker.new-version {
+ border-left: 3px solid var(--fc-gold);
+ color: #ffe4a0;
+ }
+
+ #version-checker.error,
+ #settings .error,
+ #sign-in .error {
+ border-left: 3px solid var(--fc-danger);
+ color: #ffb3b3;
+ }
+
+ #upload-progressbar {
+ box-shadow: 0 0 14px rgba(255, 179, 0, 0.75);
+ }
+
+ ::-webkit-scrollbar:hover {
+ background-color: rgba(255, 255, 255, 0.04);
+ }
+
+ ::-webkit-scrollbar-thumb:vertical {
+ background: linear-gradient(180deg, rgba(43, 138, 255, 0.72), rgba(30, 58, 95, 0.95));
+ }
+
+ ::-webkit-scrollbar-thumb:vertical:active {
+ background: linear-gradient(180deg, rgba(255, 179, 0, 0.85), rgba(43, 138, 255, 0.9));
+ }
+
+ @media (max-width: 768px) {
+ #sidebar {
+ box-shadow: 0 0 28px rgba(0, 0, 0, 0.5);
+ }
+
+ #chat .header {
+ padding-right: 6px;
+ }
+ }
+ bluejay-logo.svg: |
+
+ bluejay-bg.svg: |
+
+---
+# UnrealIRCd configuration template (passwords replaced by placeholders)
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: unrealircd-config-template
+ namespace: irc
+data:
+ unrealircd.conf: |
+ /* BlueJay Lab IRC - UnrealIRCd 6.x config */
+ /* Credentials injected from copied K8s secret at pod startup */
+
+ include "modules.default.conf";
+ include "help/help.conf";
+ include "operclass.default.conf";
+ include "snomasks.default.conf";
+
+ loadmodule "cloak_sha256";
+
+ me {
+ name "irc.iamworkin.lan";
+ info "BlueJay Lab IRC Server";
+ sid 001;
+ }
+
+ admin {
+ "BlueJay Lab IRC";
+ "admin@iamwork.in";
+ }
+
+ class clients {
+ pingfreq 90;
+ maxclients 500;
+ sendq 200k;
+ recvq 8000;
+ }
+
+ class opers {
+ pingfreq 90;
+ maxclients 50;
+ sendq 1M;
+ recvq 8000;
+ }
+
+ class servers {
+ pingfreq 60;
+ connfreq 15;
+ maxclients 10;
+ sendq 20M;
+ }
+
+ allow {
+ mask *;
+ class clients;
+ maxperip 5;
+ }
+
+ listen {
+ ip *;
+ port 6667;
+ }
+
+ listen {
+ ip *;
+ port 6697;
+ options { tls; }
+ tls-options {
+ certificate "/app/conf/tls/server.cert.pem";
+ key "/app/conf/tls/server.key.pem";
+ }
+ }
+
+ listen {
+ ip *;
+ port 8067;
+ }
+
+ oper bluejay {
+ mask *;
+ password "__OPER_PASSWORD__";
+ operclass netadmin-with-override;
+ class opers;
+ }
+
+ drpass {
+ restart "__OPER_PASSWORD__";
+ die "__OPER_PASSWORD__";
+ }
+
+ link services.iamworkin.lan {
+ incoming {
+ mask *;
+ }
+ password "__LINK_PASSWORD__";
+ class servers;
+ }
+
+ ulines {
+ services.iamworkin.lan;
+ }
+
+ log {
+ source {
+ all;
+ \\!debug;
+ }
+ destination {
+ channel "#ops";
+ }
+ }
+
+ set {
+ network-name "BlueJayIRC";
+ default-server "irc.iamworkin.lan";
+ services-server "services.iamworkin.lan";
+ stats-server "stats.iamworkin.lan";
+ help-channel "#general";
+ cloak-keys {
+ "__CLOAK_KEY_1__";
+ "__CLOAK_KEY_2__";
+ "__CLOAK_KEY_3__";
+ }
+ kline-address "admin@iamwork.in";
+ maxchannelsperuser 25;
+ anti-flood {
+ everyone {
+ connect-flood 3:60;
+ }
+ }
+ options {
+ hide-ulines;
+ show-connect-info;
+ }
+
+ /* TLS config */
+ tls {
+ certificate "/app/conf/tls/server.cert.pem";
+ key "/app/conf/tls/server.key.pem";
+ trusted-ca-file "/etc/ssl/certs/ca-certificates.crt";
+ }
+
+ /* Allow plaintext for server-to-server links (Anope is internal) */
+ plaintext-policy {
+ server allow;
+ }
+ }
+ ircd.motd: |
+ - BlueJay IRC -
+ Welcome to BlueJayIRC on iamworkin.lan.
+
+ Web IRC: https://webirc.iamworkin.lan
+ Channels: #general, #ops, #alerts
+
+ Keep it keyboard-first, practical, and kind.
+ Rehomed to GX10 (NVIDIA DGX Spark) K8s.
+---
+# Anope configuration template (passwords replaced by placeholders)
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: anope-config-template
+ namespace: irc
+data:
+ services.conf: |
+ define
+ {
+ name = "services.host"
+ value = "services.iamworkin.lan"
+ }
+
+ uplink
+ {
+ host = "unrealircd.irc.svc"
+ port = 8067
+ password = "__LINK_PASSWORD__"
+ }
+
+ serverinfo
+ {
+ name = "services.iamworkin.lan"
+ description = "BlueJay IRC Services"
+ pid = "/anope/data/services.pid"
+ motd = "/anope/data/services.motd"
+ }
+
+ module { name = "unreal4" }
+
+ networkinfo
+ {
+ networkname = "BlueJayIRC"
+ nicklen = 31
+ userlen = 10
+ hostlen = 64
+ chanlen = 32
+ }
+
+ options
+ {
+ casemap = "ascii"
+ strictpasswords = yes
+ readtimeout = 5s
+ warningtimeout = 4h
+ }
+
+ module { name = "enc_sha256" }
+
+ /* Service pseudo-client definitions */
+ service
+ {
+ nick = "NickServ"
+ user = "services"
+ host = "services.host"
+ gecos = "Nickname Registration Service"
+ }
+
+ service
+ {
+ nick = "ChanServ"
+ user = "services"
+ host = "services.host"
+ gecos = "Channel Registration Service"
+ }
+
+ service
+ {
+ nick = "OperServ"
+ user = "services"
+ host = "services.host"
+ gecos = "Operator Service"
+ }
+
+ service
+ {
+ nick = "BotServ"
+ user = "services"
+ host = "services.host"
+ gecos = "Bot Service"
+ }
+
+ service
+ {
+ nick = "HostServ"
+ user = "services"
+ host = "services.host"
+ gecos = "vHost Service"
+ }
+
+ service
+ {
+ nick = "MemoServ"
+ user = "services"
+ host = "services.host"
+ gecos = "Memo Service"
+ }
+
+ service
+ {
+ nick = "Global"
+ user = "services"
+ host = "services.host"
+ gecos = "Global Noticer"
+ }
+
+ /* Module configurations */
+ module
+ {
+ name = "nickserv"
+ client = "NickServ"
+ defaults = "kill_quick ns_secure ns_private hide_email"
+ registration = "none"
+ expire = 90d
+ }
+
+ module { name = "ns_identify" }
+ module { name = "ns_register" }
+ module { name = "ns_set" }
+ module { name = "ns_drop" }
+ module { name = "ns_recover" }
+ module { name = "ns_info" }
+ module { name = "ns_list" }
+ module { name = "ns_access" }
+ module { name = "ns_group" }
+
+ module
+ {
+ name = "chanserv"
+ client = "ChanServ"
+ defaults = "keeptopic peace cs_secure"
+ expire = 14d
+ }
+
+ module { name = "cs_register" }
+ module { name = "cs_set" }
+ module { name = "cs_access" }
+ module { name = "cs_ban" }
+ module { name = "cs_kick" }
+ module { name = "cs_mode" }
+ module { name = "cs_topic" }
+ module { name = "cs_info" }
+ module { name = "cs_list" }
+ module { name = "cs_drop" }
+
+ module
+ {
+ name = "operserv"
+ client = "OperServ"
+ }
+
+ module { name = "os_akill" }
+ module { name = "os_mode" }
+ module { name = "os_kick" }
+ module { name = "os_kill" }
+ module { name = "os_list" }
+ module { name = "os_stats" }
+ module { name = "os_reload" }
+ module { name = "os_shutdown" }
+
+ module
+ {
+ name = "botserv"
+ client = "BotServ"
+ defaults = "dontkickops fantasy greet"
+ }
+
+ module { name = "bs_bot" }
+ module { name = "bs_assign" }
+
+ module
+ {
+ name = "hostserv"
+ client = "HostServ"
+ }
+
+ module { name = "hs_set" }
+ module { name = "hs_request" }
+
+ module
+ {
+ name = "memoserv"
+ client = "MemoServ"
+ maxmemos = 20
+ }
+
+ module { name = "ms_send" }
+ module { name = "ms_read" }
+ module { name = "ms_del" }
+ module { name = "ms_list" }
+
+ module
+ {
+ name = "global"
+ client = "Global"
+ }
+
+ module { name = "gl_global" }
+
+ opertype
+ {
+ name = "Services Root"
+ commands = "*"
+ privs = "*"
+ }
+
+ oper
+ {
+ name = "bluejay"
+ type = "Services Root"
+ }
+
+ module
+ {
+ name = "db_flatfile"
+ database = "anope.db"
+ fork = no
+ }
+
+ log
+ {
+ target = "services.log"
+ admin = "*"
+ override = "chanserv/* nickserv/* operserv/*"
+ commands = "chanserv/* nickserv/* operserv/*"
+ servers = "*"
+ channels = "*"
+ users = "connect disconnect"
+ }
+---
+# UnrealIRCd PVC (local-path on GX10)
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: unrealircd-data
+ namespace: irc
+spec:
+ accessModes: [ReadWriteOnce]
+ storageClassName: local-path
+ resources:
+ requests:
+ storage: 1Gi
+---
+# Anope PVC (local-path on GX10) — holds anope.db (NickServ/ChanServ registrations)
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: anope-data
+ namespace: irc
+spec:
+ accessModes: [ReadWriteOnce]
+ storageClassName: local-path
+ resources:
+ requests:
+ storage: 1Gi
+---
+# UnrealIRCd Deployment
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: unrealircd
+ namespace: irc
+ labels:
+ app: unrealircd
+spec:
+ replicas: 1
+ strategy:
+ type: Recreate
+ selector:
+ matchLabels:
+ app: unrealircd
+ template:
+ metadata:
+ labels:
+ app: unrealircd
+ spec:
+ initContainers:
+ - name: inject-credentials
+ image: busybox:1.36
+ command: ["sh", "-c"]
+ args:
+ - |
+ OPER_PW=$(cat /secrets/password)
+ LINK_PW=$(cat /secrets/Link-Password)
+ CLOAK_KEY_1=$(cat /cloak-secrets/cloak-key-1)
+ CLOAK_KEY_2=$(cat /cloak-secrets/cloak-key-2)
+ CLOAK_KEY_3=$(cat /cloak-secrets/cloak-key-3)
+ sed -e "s|__OPER_PASSWORD__|${OPER_PW}|g" \
+ -e "s|__LINK_PASSWORD__|${LINK_PW}|g" \
+ -e "s|__CLOAK_KEY_1__|${CLOAK_KEY_1}|g" \
+ -e "s|__CLOAK_KEY_2__|${CLOAK_KEY_2}|g" \
+ -e "s|__CLOAK_KEY_3__|${CLOAK_KEY_3}|g" \
+ /config-template/unrealircd.conf > /injected-config/unrealircd.conf
+ echo "Credentials and cloak keys injected into unrealircd.conf"
+ volumeMounts:
+ - name: irc-credentials
+ mountPath: /secrets
+ readOnly: true
+ - name: unrealircd-cloak-keys
+ mountPath: /cloak-secrets
+ readOnly: true
+ - name: unrealircd-config-template
+ mountPath: /config-template
+ readOnly: true
+ - name: injected-config
+ mountPath: /injected-config
+ - name: copy-tls
+ image: busybox:1.36
+ command: ["sh", "-c"]
+ args:
+ - |
+ cp /tls-secret/tls.crt /tls/server.cert.pem
+ cp /tls-secret/tls.key /tls/server.key.pem
+ chmod 644 /tls/server.cert.pem
+ chmod 644 /tls/server.key.pem
+ chown 1000:1000 /tls/server.cert.pem /tls/server.key.pem 2>/dev/null || true
+ chmod 777 /data
+ volumeMounts:
+ - name: irc-tls-secret
+ mountPath: /tls-secret
+ readOnly: true
+ - name: irc-tls
+ mountPath: /tls
+ - name: unrealircd-data
+ mountPath: /data
+ containers:
+ - name: unrealircd
+ image: localhost/fc-unrealircd:6.1.9.1-arm64
+ imagePullPolicy: Never
+ ports:
+ - containerPort: 6667
+ name: irc-plain
+ - containerPort: 6697
+ name: irc-tls
+ - containerPort: 8067
+ name: services-link
+ volumeMounts:
+ - name: injected-config
+ mountPath: /app/conf/unrealircd.conf
+ subPath: unrealircd.conf
+ - name: unrealircd-config-template
+ mountPath: /app/conf/ircd.motd
+ subPath: ircd.motd
+ readOnly: true
+ - name: unrealircd-data
+ mountPath: /app/data
+ - name: irc-tls
+ mountPath: /app/conf/tls
+ resources:
+ requests:
+ memory: 64Mi
+ cpu: 50m
+ limits:
+ memory: 256Mi
+ cpu: 250m
+ volumes:
+ - name: irc-credentials
+ secret:
+ secretName: irc-credentials
+ - name: unrealircd-cloak-keys
+ secret:
+ secretName: unrealircd-cloak-keys
+ - name: unrealircd-config-template
+ configMap:
+ name: unrealircd-config-template
+ - name: injected-config
+ emptyDir: {}
+ - name: unrealircd-data
+ persistentVolumeClaim:
+ claimName: unrealircd-data
+ - name: irc-tls-secret
+ secret:
+ secretName: irc-tls
+ - name: irc-tls
+ emptyDir: {}
+---
+# Anope IRC Services Deployment
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: anope
+ namespace: irc
+ labels:
+ app: anope
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: anope
+ template:
+ metadata:
+ labels:
+ app: anope
+ spec:
+ initContainers:
+ - name: inject-credentials
+ image: busybox:1.36
+ command: ["sh", "-c"]
+ args:
+ - |
+ LINK_PW=$(cat /secrets/Link-Password)
+ sed -e "s|__LINK_PASSWORD__|${LINK_PW}|g" \
+ /config-template/services.conf > /injected-config/services.conf
+ echo "Credentials injected into services.conf"
+ volumeMounts:
+ - name: irc-credentials
+ mountPath: /secrets
+ readOnly: true
+ - name: anope-config-template
+ mountPath: /config-template
+ readOnly: true
+ - name: injected-config
+ mountPath: /injected-config
+ - name: fix-perms
+ image: busybox:1.36
+ command: ["sh", "-c"]
+ args:
+ - |
+ mkdir -p /data/db /data/logs /data/runtime
+ touch /data/anope.db /data/services.motd
+ chmod 666 /data/anope.db
+ chown -R 10000:10000 /data 2>/dev/null || chmod -R 777 /data
+ echo "Anope data dir prepared: $(ls -la /data/anope.db)"
+ volumeMounts:
+ - name: anope-data
+ mountPath: /data
+ containers:
+ - name: anope
+ image: anope/anope:2.0.15
+ volumeMounts:
+ - name: injected-config
+ mountPath: /anope/conf/services.conf
+ subPath: services.conf
+ - name: anope-data
+ mountPath: /anope/data
+ workingDir: /anope/data
+ resources:
+ requests:
+ memory: 64Mi
+ cpu: 25m
+ limits:
+ memory: 128Mi
+ cpu: 100m
+ volumes:
+ - name: irc-credentials
+ secret:
+ secretName: irc-credentials
+ - name: anope-config-template
+ configMap:
+ name: anope-config-template
+ - name: injected-config
+ emptyDir: {}
+ - name: anope-data
+ persistentVolumeClaim:
+ claimName: anope-data
+---
+# The Lounge web IRC Deployment
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: thelounge
+ namespace: irc
+ labels:
+ app: thelounge
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: thelounge
+ template:
+ metadata:
+ labels:
+ app: thelounge
+ spec:
+ initContainers:
+ - name: install-flowercore-theme
+ image: ghcr.io/thelounge/thelounge:4.4.3
+ command:
+ - sh
+ - -lc
+ - |
+ set -eu
+ THELOUNGE_HOME=/var/opt/thelounge thelounge install file:/flowercore-theme
+ volumeMounts:
+ - name: thelounge-config
+ mountPath: /var/opt/thelounge/config.js
+ subPath: config.js
+ - name: thelounge-packages
+ mountPath: /var/opt/thelounge/packages
+ - name: thelounge-flowercore-theme
+ mountPath: /flowercore-theme
+ containers:
+ - name: thelounge
+ image: ghcr.io/thelounge/thelounge:4.4.3
+ ports:
+ - containerPort: 9000
+ name: http
+ readinessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 10
+ periodSeconds: 10
+ livenessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 30
+ periodSeconds: 20
+ resources:
+ requests:
+ memory: 64Mi
+ cpu: 50m
+ limits:
+ memory: 256Mi
+ cpu: 250m
+ volumeMounts:
+ - name: thelounge-config
+ mountPath: /var/opt/thelounge/config.js
+ subPath: config.js
+ - name: thelounge-packages
+ mountPath: /var/opt/thelounge/packages
+ volumes:
+ - name: thelounge-config
+ configMap:
+ name: thelounge-config
+ - name: thelounge-packages
+ emptyDir: {}
+ - name: thelounge-flowercore-theme
+ configMap:
+ name: thelounge-flowercore-theme
+---
+# UnrealIRCd internal Service (anope uplink 8067 + in-cluster 6667 for thelounge)
+apiVersion: v1
+kind: Service
+metadata:
+ name: unrealircd
+ namespace: irc
+spec:
+ selector:
+ app: unrealircd
+ ports:
+ - port: 6667
+ targetPort: 6667
+ name: irc-plain
+ - port: 6697
+ targetPort: 6697
+ name: irc-tls
+ - port: 8067
+ targetPort: 8067
+ name: services-link
+---
+# UnrealIRCd external LoadBalancer — exposes IRC TCP 6667/6697 on the GX10 PROD MetalLB pool.
+# (Replaces OLD's Traefik irc/irctls entryPoints; GX10 Traefik has no such entryPoints.)
+apiVersion: v1
+kind: Service
+metadata:
+ name: unrealircd-lb
+ namespace: irc
+ annotations:
+ metallb.universe.tf/address-pool: prod-pool
+spec:
+ type: LoadBalancer
+ loadBalancerIP: 10.0.57.204
+ selector:
+ app: unrealircd
+ ports:
+ - port: 6667
+ targetPort: 6667
+ name: irc-plain
+ protocol: TCP
+ - port: 6697
+ targetPort: 6697
+ name: irc-tls
+ protocol: TCP
+---
+# Anope Service
+apiVersion: v1
+kind: Service
+metadata:
+ name: anope
+ namespace: irc
+spec:
+ selector:
+ app: anope
+ ports:
+ - port: 8067
+ targetPort: 8067
+ name: services-link
+---
+# The Lounge web IRC Service
+apiVersion: v1
+kind: Service
+metadata:
+ name: thelounge
+ namespace: irc
+spec:
+ selector:
+ app: thelounge
+ ports:
+ - port: 9000
+ targetPort: 9000
+ name: http
+---
+# Traefik IngressRoute - The Lounge web IRC (webirc.iamworkin.lan)
+apiVersion: traefik.io/v1alpha1
+kind: IngressRoute
+metadata:
+ name: webirc
+ namespace: irc
+spec:
+ entryPoints:
+ - websecure
+ routes:
+ - match: Host(`webirc.iamworkin.lan`)
+ kind: Rule
+ services:
+ - name: thelounge
+ port: 9000
+ tls:
+ secretName: webirc-tls
diff --git a/apps/irc/irc.yaml b/apps/irc/irc.yaml
index 26bc03a..7657682 100644
--- a/apps/irc/irc.yaml
+++ b/apps/irc/irc.yaml
@@ -685,9 +685,9 @@ data:
stats-server "stats.iamworkin.lan";
help-channel "#general";
cloak-keys {
- "ZWKeb8YevNiL45Xdh2p5u4tv2xksWgb8YPQSvmerBmNObyGbTDGnU4PNomZaLbZ1D9M2Cy6njM1XLJUkJhAx1oY3coBdZoPykEo7";
- "KqRaLeA6ijOnWDdCqYtJ6rb1VgR8lYnU9Sey7cbRhi3PsGzD5gZONJXyUdbJ7bD26QKCuiDydBsccUVKC3lYN0HJ9sGTlOYR3c2m";
- "2I4oopLDY79Fr4Mucy63EVOfkelVV23nESPWoqMnP1pUc8Yg0D4RK1mVtxyEhdTPpLFyKgG4fRlb6R33eHoQe7yi7moOu4W1Waw6";
+ "__CLOAK_KEY_1__";
+ "__CLOAK_KEY_2__";
+ "__CLOAK_KEY_3__";
}
kline-address "admin@iamwork.in";
maxchannelsperuser 25;
@@ -1006,14 +1006,23 @@ spec:
- |
OPER_PW=$(cat /secrets/password)
LINK_PW=$(cat /secrets/Link-Password)
+ CLOAK_KEY_1=$(cat /cloak-secrets/cloak-key-1)
+ CLOAK_KEY_2=$(cat /cloak-secrets/cloak-key-2)
+ CLOAK_KEY_3=$(cat /cloak-secrets/cloak-key-3)
sed -e "s|__OPER_PASSWORD__|${OPER_PW}|g" \
- -e "s|__LINK_PASSWORD__|${LINK_PW}|g" \
- /config-template/unrealircd.conf > /injected-config/unrealircd.conf
- echo "Credentials injected into unrealircd.conf"
+ -e "s|__LINK_PASSWORD__|${LINK_PW}|g" \
+ -e "s|__CLOAK_KEY_1__|${CLOAK_KEY_1}|g" \
+ -e "s|__CLOAK_KEY_2__|${CLOAK_KEY_2}|g" \
+ -e "s|__CLOAK_KEY_3__|${CLOAK_KEY_3}|g" \
+ /config-template/unrealircd.conf > /injected-config/unrealircd.conf
+ echo "Credentials and cloak keys injected into unrealircd.conf"
volumeMounts:
- name: irc-credentials
mountPath: /secrets
readOnly: true
+ - name: unrealircd-cloak-keys
+ mountPath: /cloak-secrets
+ readOnly: true
- name: unrealircd-config-template
mountPath: /config-template
readOnly: true
@@ -1071,6 +1080,9 @@ spec:
- name: irc-credentials
secret:
secretName: irc-credentials
+ - name: unrealircd-cloak-keys
+ secret:
+ secretName: unrealircd-cloak-keys
- name: unrealircd-config-template
configMap:
name: unrealircd-config-template