This commit is contained in:
Samantha Atkins 2026-04-24 02:38:40 -04:00
parent 0dac270e53
commit 7649edba32
4 changed files with 375 additions and 132 deletions

7
.claude/settings.json Normal file
View file

@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"Read(//home/samantha/private/Knowledge/**)"
]
}
}

337
k3s/ejabberd/ejabberd.yaml Normal file
View file

@ -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='<password>' \
# --from-literal=erlang-cookie='<random-long-string>' \
# --from-literal=db-password='<postgres-password-for-ejabberd_user>'
#
# # Create DB + user + schema (one-shot, from a host with kubectl):
# kubectl exec -i deploy/postgres -- psql -U postgres <<SQL
# CREATE USER ejabberd_user WITH PASSWORD '<db-password>';
# 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 <ns>
#
# 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-password>
#
# Admin web UI: http://<any-node-wg-ip>:32382/admin
# (login: admin@xmpp.the-fulfillment.org / <admin-password>)
#
# 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 <user> xmpp.the-fulfillment.org <password>
---
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

View file

@ -1,29 +1,48 @@
# Monerod — Monero full node (pruned) # Monerod — Monero full node (pruned)
# NAS-backed PVC for blockchain data — unpinned, free to migrate across nodes # Pinned to game-worker-hdd — blockchain data lives on local disk at
# Ban list stored on NAS alongside blockchain data # /home/samantha/.bitmonero (LMDB/mmap is NFS-hostile, must stay local)
# Ban list stored alongside blockchain data
# NodePorts: 32379 (P2P), 32380 (restricted RPC) # NodePorts: 32379 (P2P), 32380 (restricted RPC)
# #
# Prerequisites: # Prerequisites:
# NAS share /volume1/samantha-private/.bitmonero must exist on Synology # /home/samantha/.bitmonero must exist on game-worker-hdd with blockchain data
# Copy ban list to NAS: /volume1/samantha-private/.bitmonero/ban_list.txt # ban_list.txt must exist in that directory
# nas-pv.yaml must be applied first
# nfs-common installed on all worker VMs
# #
# Deploy: # Deploy:
# kubectl apply -f ../storage/nas-pv.yaml # once, if not already applied
# kubectl apply -f monerod.yaml -n <ns> # kubectl apply -f monerod.yaml -n <ns>
---
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 apiVersion: v1
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
metadata: metadata:
name: monerod-pvc name: monerod-pvc
annotations:
nfs.io/storage-path: ".bitmonero"
spec: spec:
accessModes: accessModes:
- ReadWriteMany - ReadWriteOnce
storageClassName: nas-nfs storageClassName: local-storage
resources: resources:
requests: requests:
storage: 200Gi storage: 200Gi
@ -43,6 +62,7 @@ spec:
labels: labels:
app: monerod app: monerod
spec: spec:
nodeName: game-worker-hdd
containers: containers:
- name: monerod - name: monerod
image: ghcr.io/sethforprivacy/simple-monerod:latest image: ghcr.io/sethforprivacy/simple-monerod:latest

View file

@ -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 <ns>
#
# 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