diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..b5aa0db --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + "Read(//home/samantha/private/Knowledge/**)" + ] + } +} diff --git a/k3s/ejabberd/ejabberd.yaml b/k3s/ejabberd/ejabberd.yaml new file mode 100644 index 0000000..ac6c918 --- /dev/null +++ b/k3s/ejabberd/ejabberd.yaml @@ -0,0 +1,337 @@ +# ejabberd — XMPP server +# Vhost: xmpp.the-fulfillment.org +# Unpinned — scheduler places freely, local-path PVC for Mnesia data +# TLS terminated externally by Caddy at venture ingress VPS +# (HTTP admin/BOSH/websocket on 5280 is served plain; Caddy fronts it) +# (c2s/s2s use ejabberd's auto-generated self-signed cert for STARTTLS +# until real certs are mounted — see "TLS notes" below) +# No STUN/TURN — add later when available +# No MQTT — skipped +# +# Storage: +# Mnesia (on-disk, via PVC) — default backend for most modules +# PostgreSQL (cluster-local `postgres` service) — only mod_mam (archive + archive_prefs) +# DB schema loaded from /home/ejabberd/sql/pg.new.sql into ejabberd_db. +# SQL password lives in ejabberd-secret and is injected into the rendered +# config at pod start by the `render-config` init container (sed). +# +# NodePorts: +# 32378 — XMPP c2s (5222) +# 32381 — XMPP s2s federation (5269) +# 32382 — HTTP admin / BOSH / websocket / HTTP upload (5280) +# +# Deploy: +# kubectl create secret generic ejabberd-secret \ +# --from-literal=admin-password='' \ +# --from-literal=erlang-cookie='' \ +# --from-literal=db-password='' +# +# # Create DB + user + schema (one-shot, from a host with kubectl): +# kubectl exec -i deploy/postgres -- psql -U postgres <'; +# CREATE DATABASE ejabberd_db ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' +# TEMPLATE template0 OWNER ejabberd_user; +# SQL +# kubectl exec deploy/ejabberd -- cat /home/ejabberd/sql/pg.new.sql \ +# | kubectl exec -i deploy/postgres -- psql -U ejabberd_user -d ejabberd_db +# +# kubectl apply -f ejabberd.yaml -n +# +# First startup does NOT auto-register the admin (CTL_ON_CREATE entrypoint hook +# doesn't expand shell vars). Register manually: +# kubectl exec deploy/ejabberd -- ejabberdctl register admin xmpp.the-fulfillment.org +# +# Admin web UI: http://:32382/admin +# (login: admin@xmpp.the-fulfillment.org / ) +# +# TLS notes: +# For production federation, mount real certs for xmpp.the-fulfillment.org +# at /home/ejabberd/conf/server.pem (PEM with privkey + fullchain) and +# set certfiles in the ConfigMap to point at it. Many federating servers +# reject self-signed s2s peers. +# +# User registration is CLOSED by default — admin must create accounts via: +# kubectl exec deploy/ejabberd -- ejabberdctl register xmpp.the-fulfillment.org + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: ejabberd-data-pvc +spec: + accessModes: + - ReadWriteOnce + storageClassName: local-path + resources: + requests: + storage: 10Gi + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: ejabberd-config +data: + ejabberd.yml: | + hosts: + - "xmpp.the-fulfillment.org" + + loglevel: info + + ## ejabberd will auto-generate a self-signed cert on first run. + ## Replace with real certs for production federation. + certfiles: [] + + ## PostgreSQL — used only by mod_mam (see modules section). + ## Password is substituted into a rendered copy of this file at pod + ## start by the `render-config` init container. + sql_type: pgsql + sql_server: postgres + sql_port: 5432 + sql_database: ejabberd_db + sql_username: ejabberd_user + sql_password: "__SQL_PASSWORD__" + + listen: + - + port: 5222 + ip: "::" + module: ejabberd_c2s + max_stanza_size: 262144 + shaper: c2s_shaper + access: c2s + starttls: true + - + port: 5269 + ip: "::" + module: ejabberd_s2s_in + max_stanza_size: 524288 + - + port: 5280 + ip: "::" + module: ejabberd_http + request_handlers: + "/admin": ejabberd_web_admin + "/api": mod_http_api + "/bosh": mod_bosh + "/ws": ejabberd_http_ws + "/upload": mod_http_upload + + s2s_use_starttls: optional + + acl: + admin: + user: + - "admin@xmpp.the-fulfillment.org" + local: + user_regexp: "" + + access_rules: + local: + allow: local + c2s: + deny: blocked + allow: all + announce: + allow: admin + configure: + allow: admin + muc_create: + allow: local + pubsub_createnode: + allow: local + register: + deny: all + trusted_network: + allow: loopback + + api_permissions: + "console commands": + from: ejabberd_ctl + who: all + what: "*" + "admin access": + who: + access: + allow: + acl: admin + what: + - "*" + - "!stop" + - "!start" + + shaper: + normal: + rate: 3000 + burst_size: 20000 + fast: 100000 + + shaper_rules: + max_user_sessions: 10 + max_user_offline_messages: + 5000: admin + 100: all + c2s_shaper: + none: admin + normal: all + s2s_shaper: fast + + modules: + mod_adhoc: {} + mod_admin_extra: {} + mod_announce: + access: announce + mod_avatar: {} + mod_blocking: {} + mod_bosh: {} + mod_caps: {} + mod_carboncopy: {} + mod_client_state: {} + mod_configure: {} + mod_disco: {} + mod_http_api: {} + mod_http_upload: + put_url: "http://@HOST@:5280/upload" + max_size: 104857600 # 100 MB + mod_last: {} + mod_mam: + db_type: sql + assume_mam_usage: true + default: always + mod_muc: + access: + - allow + access_admin: + - allow: admin + access_create: muc_create + access_persistent: muc_create + default_room_options: + mam: true + mod_offline: + access_max_user_messages: max_user_offline_messages + mod_ping: {} + mod_privacy: {} + mod_private: {} + mod_pubsub: + access_createnode: pubsub_createnode + plugins: + - flat + - pep + mod_push: {} + mod_push_keepalive: {} + mod_roster: + versioning: true + mod_sic: {} + mod_stream_mgmt: + resend_on_timeout: if_offline + mod_vcard: {} + mod_vcard_xupdate: {} + mod_version: + show_os: false + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ejabberd +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: ejabberd + template: + metadata: + labels: + app: ejabberd + spec: + securityContext: + fsGroup: 9000 + initContainers: + ## Render ejabberd.yml from template, substituting the SQL password + ## from the ejabberd-secret. Avoids baking the password into the ConfigMap. + - name: render-config + image: busybox:1.37 + env: + - name: SQL_PASSWORD + valueFrom: + secretKeyRef: + name: ejabberd-secret + key: db-password + command: + - /bin/sh + - -c + - | + sed "s|__SQL_PASSWORD__|${SQL_PASSWORD}|" /template/ejabberd.yml > /rendered/ejabberd.yml + volumeMounts: + - name: config-template + mountPath: /template + - name: config-rendered + mountPath: /rendered + containers: + - name: ejabberd + image: ejabberd/ecs:latest + env: + - name: ERLANG_NODE + value: ejabberd@localhost + - name: ERLANG_COOKIE + valueFrom: + secretKeyRef: + name: ejabberd-secret + key: erlang-cookie + ports: + - name: c2s + containerPort: 5222 + - name: s2s + containerPort: 5269 + - name: http + containerPort: 5280 + volumeMounts: + - name: config-rendered + mountPath: /home/ejabberd/conf/ejabberd.yml + subPath: ejabberd.yml + - name: data + mountPath: /home/ejabberd/database + readinessProbe: + tcpSocket: + port: 5222 + initialDelaySeconds: 20 + periodSeconds: 10 + livenessProbe: + tcpSocket: + port: 5222 + initialDelaySeconds: 60 + periodSeconds: 30 + volumes: + - name: config-template + configMap: + name: ejabberd-config + - name: config-rendered + emptyDir: {} + - name: data + persistentVolumeClaim: + claimName: ejabberd-data-pvc + +--- +apiVersion: v1 +kind: Service +metadata: + name: ejabberd +spec: + selector: + app: ejabberd + ports: + - name: c2s + port: 5222 + targetPort: 5222 + nodePort: 32378 + - name: s2s + port: 5269 + targetPort: 5269 + nodePort: 32381 + - name: http + port: 5280 + targetPort: 5280 + nodePort: 32382 + type: NodePort diff --git a/k3s/monerod/monerod.yaml b/k3s/monerod/monerod.yaml index 650bbe6..d01e0da 100644 --- a/k3s/monerod/monerod.yaml +++ b/k3s/monerod/monerod.yaml @@ -1,29 +1,48 @@ # Monerod — Monero full node (pruned) -# NAS-backed PVC for blockchain data — unpinned, free to migrate across nodes -# Ban list stored on NAS alongside blockchain data +# Pinned to game-worker-hdd — blockchain data lives on local disk at +# /home/samantha/.bitmonero (LMDB/mmap is NFS-hostile, must stay local) +# Ban list stored alongside blockchain data # NodePorts: 32379 (P2P), 32380 (restricted RPC) # # Prerequisites: -# NAS share /volume1/samantha-private/.bitmonero must exist on Synology -# Copy ban list to NAS: /volume1/samantha-private/.bitmonero/ban_list.txt -# nas-pv.yaml must be applied first -# nfs-common installed on all worker VMs +# /home/samantha/.bitmonero must exist on game-worker-hdd with blockchain data +# ban_list.txt must exist in that directory # # Deploy: -# kubectl apply -f ../storage/nas-pv.yaml # once, if not already applied # kubectl apply -f monerod.yaml -n +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: monerod-pv +spec: + capacity: + storage: 200Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: local-storage + local: + path: /home/samantha/.bitmonero + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/hostname + operator: In + values: + - game-worker-hdd + --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: monerod-pvc - annotations: - nfs.io/storage-path: ".bitmonero" spec: accessModes: - - ReadWriteMany - storageClassName: nas-nfs + - ReadWriteOnce + storageClassName: local-storage resources: requests: storage: 200Gi @@ -43,6 +62,7 @@ spec: labels: app: monerod spec: + nodeName: game-worker-hdd containers: - name: monerod image: ghcr.io/sethforprivacy/simple-monerod:latest diff --git a/k3s/snikket/snikket.yaml b/k3s/snikket/snikket.yaml deleted file mode 100644 index 99e873c..0000000 --- a/k3s/snikket/snikket.yaml +++ /dev/null @@ -1,121 +0,0 @@ -# Snikket — XMPP server (Prosody-based) -# Unpinned — scheduler places freely, local-path PVC -# TLS terminated externally by Caddy at venture ingress VPS -# NodePorts: 32381 (web/admin), 32382 (XMPP client), 32383 (XMPP federation), 32384 (file transfer proxy) -# -# Deploy: -# kubectl apply -f snikket.yaml -n -# -# Caddy must proxy port 80 traffic (invites/admin portal) via NodePort 32381 -# XMPP client connections on 32382 must be reachable directly (not HTTP — raw TCP) -# XMPP federation on 32383 must be reachable directly (raw TCP) - ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: snikket-pvc -spec: - accessModes: - - ReadWriteOnce - storageClassName: local-path - resources: - requests: - storage: 10Gi - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: snikket-web -spec: - replicas: 1 - selector: - matchLabels: - app: snikket-web - template: - metadata: - labels: - app: snikket-web - spec: - containers: - - name: snikket-web - image: snikket/snikket-server:latest - command: ["web"] - ports: - - containerPort: 80 - volumeMounts: - - name: snikket-data - mountPath: /snikket - volumes: - - name: snikket-data - persistentVolumeClaim: - claimName: snikket-pvc - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: snikket-server -spec: - replicas: 1 - selector: - matchLabels: - app: snikket-server - template: - metadata: - labels: - app: snikket-server - spec: - containers: - - name: snikket-server - image: snikket/snikket-server:latest - command: ["server"] - ports: - - containerPort: 5222 - - containerPort: 5269 - - containerPort: 5000 - volumeMounts: - - name: snikket-data - mountPath: /snikket - volumes: - - name: snikket-data - persistentVolumeClaim: - claimName: snikket-pvc - ---- -apiVersion: v1 -kind: Service -metadata: - name: snikket-web -spec: - selector: - app: snikket-web - ports: - - port: 80 - targetPort: 80 - nodePort: 32381 - type: NodePort - ---- -apiVersion: v1 -kind: Service -metadata: - name: snikket -spec: - selector: - app: snikket-server - ports: - - name: xmpp-client - port: 5222 - targetPort: 5222 - nodePort: 32382 - - name: xmpp-federation - port: 5269 - targetPort: 5269 - nodePort: 32383 - - name: file-transfer - port: 5000 - targetPort: 5000 - nodePort: 32384 - type: NodePort