neiam
/oh-my-ex-oh-ircd
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
Log in to mark this repository.
better defaults
1bba52e · gmorell · 2026-05-21 01:08
Files changed
modified
config/runtime.exs
+25
−4
| 40 | 40 | config :oh_my_ex_oh_ircd, OhMyExOhIrcd.IRC.Listener, network_name: net |
| 41 | 41 | end |
| 42 | 42 | |
| 43 | −# Only override allow_anonymous when explicitly set, so compile-time config | |
| 44 | −# (config/dev.exs etc.) wins by default. | |
| 45 | −if anon = System.get_env("IRC_ALLOW_ANONYMOUS") do | |
| 43 | +# Anonymous IRC access — default ON in prod releases (set | |
| 44 | +# IRC_ALLOW_ANONYMOUS=false to lock it down). Dev/test compile-time | |
| 45 | +# config wins outside prod. | |
| 46 | +if config_env() == :prod do | |
| 47 | + allow_anonymous = | |
| 48 | + case System.get_env("IRC_ALLOW_ANONYMOUS") do | |
| 49 | + nil -> true | |
| 50 | + val -> val in ~w(1 true yes on) | |
| 51 | + end | |
| 52 | + | |
| 46 | 53 | config :oh_my_ex_oh_ircd, OhMyExOhIrcd.IRC.Listener, |
| 47 | − allow_anonymous: anon in ~w(1 true yes on) | |
| 54 | + allow_anonymous: allow_anonymous | |
| 55 | +end | |
| 56 | + | |
| 57 | +# Web user registration mode. Default invite-only at runtime (prod | |
| 58 | +# releases). Explicit env-gating so dev/test configs win when set. | |
| 59 | +# Mint invite tokens with `mix invites.create` (or `bin/… eval` in a | |
| 60 | +# running release). | |
| 61 | +if config_env() == :prod do | |
| 62 | + registration_mode = | |
| 63 | + case System.get_env("REGISTRATION_MODE") do | |
| 64 | + "open" -> :open | |
| 65 | + _ -> :invite_only | |
| 66 | + end | |
| 67 | + | |
| 68 | + config :oh_my_ex_oh_ircd, :registration_mode, registration_mode | |
| 48 | 69 | end |
| 49 | 70 | |
| 50 | 71 | # TLS listener cert paths from env. In the :kubernetes build these point at |
modified
config/test.exs
+5
−0
| 3 | 3 | # Only in tests, remove the complexity from the password hashing algorithm |
| 4 | 4 | config :bcrypt_elixir, :log_rounds, 1 |
| 5 | 5 | |
| 6 | +# Keep existing user-registration tests working — runtime.exs defaults | |
| 7 | +# to `:invite_only`, but the test suite was written against open signup. | |
| 8 | +# Tests that exercise the invite gate can `Application.put_env/3` per-test. | |
| 9 | +config :oh_my_ex_oh_ircd, :registration_mode, :open | |
| 10 | + | |
| 6 | 11 | # Bind the app-level IRC listener to an ephemeral port during tests so |
| 7 | 12 | # `mix test` never fights with whatever's on 6667 (or a previous test run |
| 8 | 13 | # that leaked the socket). Real assertions all spin up their own listener |
added
deploy/argo/neiam-ircd/app.yml
+212
−0
| 1 | +apiVersion: apps/v1 | |
| 2 | +kind: Deployment | |
| 3 | +metadata: | |
| 4 | + name: ircd | |
| 5 | + namespace: neiam-ircd | |
| 6 | + labels: | |
| 7 | + name: ircd | |
| 8 | + keel.sh/policy: force | |
| 9 | + keel.sh/trigger: poll | |
| 10 | +spec: | |
| 11 | + replicas: 3 | |
| 12 | + selector: | |
| 13 | + matchLabels: | |
| 14 | + app: ircd | |
| 15 | + strategy: | |
| 16 | + type: RollingUpdate | |
| 17 | + rollingUpdate: | |
| 18 | + maxUnavailable: 0 | |
| 19 | + maxSurge: 1 | |
| 20 | + template: | |
| 21 | + metadata: | |
| 22 | + labels: | |
| 23 | + app: ircd | |
| 24 | + spec: | |
| 25 | + serviceAccountName: ircd | |
| 26 | + terminationGracePeriodSeconds: 30 | |
| 27 | + containers: | |
| 28 | + - name: ircd | |
| 29 | + image: r.g.gmp.io/neiam/ohmyxo:kubernetes-master | |
| 30 | + imagePullPolicy: Always | |
| 31 | + ports: | |
| 32 | + - { name: http, containerPort: 4000 } | |
| 33 | + - { name: irc, containerPort: 6667 } | |
| 34 | + - { name: ircs, containerPort: 6697 } | |
| 35 | + - { name: epmd, containerPort: 4369 } | |
| 36 | + - { name: dist, containerPort: 9100 } | |
| 37 | + env: | |
| 38 | + # Phoenix bring-up | |
| 39 | + - { name: PHX_SERVER, value: "true" } | |
| 40 | + - { name: PHX_HOST, value: "irc.skyjeep.club" } | |
| 41 | + - { name: PORT, value: "4000" } | |
| 42 | + | |
| 43 | + # IRC config | |
| 44 | + - { name: IRC_PORT, value: "6667" } | |
| 45 | + - { name: IRC_SERVER_NAME, value: "irc.skyjeep.club" } | |
| 46 | + - { name: IRC_NETWORK_NAME, value: "SkyjeepIRC" } | |
| 47 | + # Defaults in runtime.exs: anonymous IRC ON, web registration | |
| 48 | + # invite-only. Uncomment to override: | |
| 49 | + # - { name: IRC_ALLOW_ANONYMOUS, value: "false" } | |
| 50 | + # - { name: REGISTRATION_MODE, value: "open" } | |
| 51 | + | |
| 52 | + # TLS — cert files come from the cert-manager Secret below. | |
| 53 | + - { name: IRC_TLS_ENABLED, value: "true" } | |
| 54 | + - { name: IRC_TLS_PORT, value: "6697" } | |
| 55 | + - { name: IRC_TLS_CERT_PATH, value: "/etc/tls/tls.crt" } | |
| 56 | + - { name: IRC_TLS_KEY_PATH, value: "/etc/tls/tls.key" } | |
| 57 | + | |
| 58 | + # BEAM cluster | |
| 59 | + - { name: CLUSTER_STRATEGY, value: "kubernetes" } | |
| 60 | + - { name: CLUSTER_K8S_NAMESPACE, value: "neiam-ircd" } | |
| 61 | + - { name: CLUSTER_K8S_SERVICE, value: "ircd-headless" } | |
| 62 | + - { name: CLUSTER_K8S_APP_NAME, value: "ircd" } | |
| 63 | + - { name: CLUSTER_K8S_SELECTOR, value: "app=ircd" } | |
| 64 | + - name: POD_IP | |
| 65 | + valueFrom: { fieldRef: { fieldPath: status.podIP } } | |
| 66 | + | |
| 67 | + # StackGres-generated DB credentials | |
| 68 | + - name: POSTGRES_USER | |
| 69 | + valueFrom: | |
| 70 | + secretKeyRef: { name: postgres-ircd, key: superuser-username } | |
| 71 | + - name: POSTGRES_PASSWORD | |
| 72 | + valueFrom: | |
| 73 | + secretKeyRef: { name: postgres-ircd, key: superuser-password } | |
| 74 | + - { name: POSTGRES_HOST, value: "postgres-ircd" } | |
| 75 | + - { name: POSTGRES_PORT, value: "5432" } | |
| 76 | + - name: DATABASE_URL | |
| 77 | + value: ecto://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@$(POSTGRES_HOST):$(POSTGRES_PORT)/ircd | |
| 78 | + | |
| 79 | + # App secrets | |
| 80 | + - name: SECRET_KEY_BASE | |
| 81 | + valueFrom: { secretKeyRef: { name: ircd-secrets, key: SECRET_KEY_BASE } } | |
| 82 | + - name: RELEASE_COOKIE | |
| 83 | + valueFrom: { secretKeyRef: { name: ircd-secrets, key: RELEASE_COOKIE } } | |
| 84 | + | |
| 85 | + volumeMounts: | |
| 86 | + - { name: tls, mountPath: /etc/tls, readOnly: true } | |
| 87 | + | |
| 88 | + readinessProbe: | |
| 89 | + httpGet: { path: /, port: http } | |
| 90 | + initialDelaySeconds: 10 | |
| 91 | + periodSeconds: 5 | |
| 92 | + livenessProbe: | |
| 93 | + tcpSocket: { port: irc } | |
| 94 | + initialDelaySeconds: 30 | |
| 95 | + periodSeconds: 15 | |
| 96 | + | |
| 97 | + resources: | |
| 98 | + requests: { cpu: 100m, memory: 256Mi } | |
| 99 | + limits: { cpu: 1, memory: 1Gi } | |
| 100 | + | |
| 101 | + imagePullSecrets: | |
| 102 | + - name: gitlabgmp | |
| 103 | + volumes: | |
| 104 | + - name: tls | |
| 105 | + secret: | |
| 106 | + secretName: ircd-tls | |
| 107 | +--- | |
| 108 | +# Headless Service for libcluster pod discovery. | |
| 109 | +apiVersion: v1 | |
| 110 | +kind: Service | |
| 111 | +metadata: | |
| 112 | + name: ircd-headless | |
| 113 | + namespace: neiam-ircd | |
| 114 | +spec: | |
| 115 | + clusterIP: None | |
| 116 | + publishNotReadyAddresses: true | |
| 117 | + selector: | |
| 118 | + app: ircd | |
| 119 | + ports: | |
| 120 | + - { name: epmd, port: 4369, targetPort: 4369 } | |
| 121 | + - { name: dist, port: 9100, targetPort: 9100 } | |
| 122 | +--- | |
| 123 | +# Web UI service, fronted by Traefik via the Ingress below. | |
| 124 | +apiVersion: v1 | |
| 125 | +kind: Service | |
| 126 | +metadata: | |
| 127 | + name: ircd-web | |
| 128 | + namespace: neiam-ircd | |
| 129 | +spec: | |
| 130 | + selector: | |
| 131 | + app: ircd | |
| 132 | + ports: | |
| 133 | + - { name: http, port: 80, targetPort: 4000 } | |
| 134 | +--- | |
| 135 | +# Plain + TLS IRC service. Traefik routes TCP from public entryPoints | |
| 136 | +# to these ports via the IngressRouteTCPs below. | |
| 137 | +apiVersion: v1 | |
| 138 | +kind: Service | |
| 139 | +metadata: | |
| 140 | + name: ircd-irc | |
| 141 | + namespace: neiam-ircd | |
| 142 | +spec: | |
| 143 | + selector: | |
| 144 | + app: ircd | |
| 145 | + ports: | |
| 146 | + - { name: irc, port: 6667, targetPort: 6667 } | |
| 147 | + - { name: ircs, port: 6697, targetPort: 6697 } | |
| 148 | +--- | |
| 149 | +# HTTPS Ingress for the Phoenix LiveView / web UI. | |
| 150 | +apiVersion: networking.k8s.io/v1 | |
| 151 | +kind: Ingress | |
| 152 | +metadata: | |
| 153 | + name: ircd-web | |
| 154 | + namespace: neiam-ircd | |
| 155 | + annotations: | |
| 156 | + cert-manager.io/cluster-issuer: letsencrypt-prod | |
| 157 | + kubernetes.io/ingress.class: traefik | |
| 158 | +spec: | |
| 159 | + tls: | |
| 160 | + - hosts: [irc.skyjeep.club] | |
| 161 | + secretName: ircd-tls | |
| 162 | + rules: | |
| 163 | + - host: irc.skyjeep.club | |
| 164 | + http: | |
| 165 | + paths: | |
| 166 | + - path: / | |
| 167 | + pathType: Prefix | |
| 168 | + backend: | |
| 169 | + service: | |
| 170 | + name: ircd-web | |
| 171 | + port: { number: 80 } | |
| 172 | +--- | |
| 173 | +# Traefik raw-TCP routing for the IRC plain port. Assumes a Traefik | |
| 174 | +# entryPoint named `irc` listening on 6667 — add to your Traefik | |
| 175 | +# static config: | |
| 176 | +# | |
| 177 | +# entryPoints: | |
| 178 | +# irc: | |
| 179 | +# address: ":6667" | |
| 180 | +apiVersion: traefik.io/v1alpha1 | |
| 181 | +kind: IngressRouteTCP | |
| 182 | +metadata: | |
| 183 | + name: ircd-irc | |
| 184 | + namespace: neiam-ircd | |
| 185 | +spec: | |
| 186 | + entryPoints: [ircd] | |
| 187 | + routes: | |
| 188 | + - match: HostSNI(`*`) | |
| 189 | + services: | |
| 190 | + - name: ircd-irc | |
| 191 | + port: 6667 | |
| 192 | +--- | |
| 193 | +# Traefik TLS-passthrough for IRC-over-TLS. Termination happens inside | |
| 194 | +# the IRC pod using the cert-manager-issued cert. Add to Traefik: | |
| 195 | +# | |
| 196 | +# entryPoints: | |
| 197 | +# ircs: | |
| 198 | +# address: ":6697" | |
| 199 | +apiVersion: traefik.io/v1alpha1 | |
| 200 | +kind: IngressRouteTCP | |
| 201 | +metadata: | |
| 202 | + name: ircd-ircs | |
| 203 | + namespace: neiam-ircd | |
| 204 | +spec: | |
| 205 | + entryPoints: [ircds] | |
| 206 | + routes: | |
| 207 | + - match: HostSNI(`*`) | |
| 208 | + services: | |
| 209 | + - name: ircd-irc | |
| 210 | + port: 6697 | |
| 211 | + tls: | |
| 212 | + passthrough: true |
added
deploy/argo/neiam-ircd/cert.yml
+19
−0
| 1 | +# cert-manager Certificate — both the Ingress (web UI) and the IRC TLS | |
| 2 | +# listener mount the resulting `ircd-tls` Secret. cert-manager rotates | |
| 3 | +# in place; kubelet refreshes the projected volume; `:ssl` re-reads the | |
| 4 | +# files on every new handshake. | |
| 5 | +apiVersion: cert-manager.io/v1 | |
| 6 | +kind: Certificate | |
| 7 | +metadata: | |
| 8 | + name: ircd-tls | |
| 9 | + namespace: neiam-ircd | |
| 10 | +spec: | |
| 11 | + secretName: ircd-tls | |
| 12 | + duration: 2160h # 90d | |
| 13 | + renewBefore: 360h # 15d | |
| 14 | + issuerRef: | |
| 15 | + name: letsencrypt-prod | |
| 16 | + kind: ClusterIssuer | |
| 17 | + commonName: irc.skyjeep.club | |
| 18 | + dnsNames: | |
| 19 | + - irc.skyjeep.club |
added
deploy/argo/neiam-ircd/ns.yml
+4
−0
| 1 | +apiVersion: v1 | |
| 2 | +kind: Namespace | |
| 3 | +metadata: | |
| 4 | + name: neiam-ircd |
added
deploy/argo/neiam-ircd/pg.yml
+14
−0
Click to load diff…
added
deploy/argo/neiam-ircd/rbac.yml
+31
−0
Click to load diff…
added
deploy/argo/neiam-ircd/secrets.example.yml
+20
−0
Click to load diff…
added
lib/mix/tasks/invites.ex
+77
−0
Click to load diff…
modified
lib/oh_my_ex_oh_ircd/accounts.ex
+61
−0
Click to load diff…
added
lib/oh_my_ex_oh_ircd/accounts/invite.ex
+46
−0
Click to load diff…
modified
lib/oh_my_ex_oh_ircd_web/live/user_live/registration.ex
+77
−26
Click to load diff…
added
priv/repo/migrations/20260525000000_create_user_invites.exs
+19
−0
Click to load diff…
added
test/oh_my_ex_oh_ircd_web/live/user_live/registration_invite_test.exs
+57
−0
Click to load diff…
Parents: 0429b57