Proof of concept mechanical port of ircnet/ircd to Rust as part of a bit about C being insecure for network services
1

Configure Feed

Select the types of activity you want to include in your feed.

at master 1 folder 7 files
README.md

Example network: leveva + ircd, linked, on Kubernetes#

A minimal two-server IRC network in the vibecoding namespace:

  • leveva (irc.example.net, SID 0ABC) — the idiomatic-Rust daemon.
  • ircd (hub.example.net, SID 001A) — the faithful C-port daemon.

They are linked server-to-server. Each runs as its own Deployment + Service, and both read their config from one ConfigMap (generated by kustomize from cfg/) mounted at /cfg.

            ┌─────────────── namespace: vibecoding ───────────────┐
 client →   │  Service ircd:6667 ─→ ircd pod                       │
            │                         │  auto-connects (C-line)    │
            │                         ▼                            │
 client →   │  Service leveva:6667 ─→ leveva pod  ←─ :6067 (S2S)   │
            └──────────────────────────────────────────────────────┘

Layout#

File Purpose
kustomization.yaml namespace, resources, configMapGenerator, image pins
namespace.yaml the vibecoding namespace
leveva.yaml leveva Deployment + Service
ircd.yaml ircd Deployment + Service
cfg/leveva.kdl leveva config (KDL)
cfg/ircd.conf ircd config (legacy pipe-delimited)
cfg/leveva.motd leveva MOTD

Build and push the images#

The images: block in kustomization.yaml remaps the bare names that docker buildx bake produces to their registry location:

images:
    - { name: leveva, newName: reg.xeiaso.net/shitpost/leveva, newTag: latest }
    - { name: ircd, newName: reg.xeiaso.net/shitpost/ircd, newTag: latest }

So the cluster pulls reg.xeiaso.net/shitpost/{leveva,ircd}:latest. The bake file has a REGISTRY variable that stamps that same prefix, so build + push in one step from the repo root:

docker login reg.xeiaso.net
REGISTRY=reg.xeiaso.net/shitpost/ docker buildx bake --push
# → builds and pushes reg.xeiaso.net/shitpost/leveva:latest
#                  and reg.xeiaso.net/shitpost/ircd:latest

To point at a different registry/tag, edit the images: block (and pass a matching REGISTRY=/TAG= to bake). The Deployments set imagePullPolicy: Always because the tag is :latest; pin a digest or version tag for reproducible rollouts and drop that to IfNotPresent.

Private registry#

If reg.xeiaso.net/shitpost needs auth to pull, create a pull secret in the namespace and reference it. The namespace must exist first:

kubectl create namespace vibecoding
kubectl -n vibecoding create secret docker-registry regcred \
  --docker-server=reg.xeiaso.net \
  --docker-username=<user> --docker-password=<token>

Then add it to both Deployments — either edit the pod specs to add

      imagePullSecrets:
        - name: regcred

or, cluster-wide for the namespace, patch the default ServiceAccount:

kubectl -n vibecoding patch serviceaccount default \
  -p '{"imagePullSecrets":[{"name":"regcred"}]}'

Local cluster (no registry)#

To skip the registry on kind/minikube, build locally, load the images, and override the names back to bare :latest (with IfNotPresent):

docker buildx bake                       # builds leveva:latest, ircd:latest
kind load docker-image leveva:latest ircd:latest
# minikube image load leveva:latest && minikube image load ircd:latest

Apply#

kubectl apply -k docs/k8s/example-network
kubectl -n vibecoding get pods,svc

Editing any file under cfg/ and re-applying rolls both Deployments automatically — the ConfigMap name carries a content hash.

# leveva should report the linked server:
kubectl -n vibecoding logs deploy/leveva | grep -i "S2S link established"

# or talk to it directly:
kubectl -n vibecoding port-forward svc/leveva 6667:6667
#   then: nc localhost 6667  →  NICK x / USER x x x x  →  /LINKS , /MAP

Which side dials, and why#

The ircd is the active side: its C-line has a port, so try_connections() auto-dials leveva and retries on the server-class cadence (30 s). leveva is passive — it only initiates outbound links from an operator /CONNECT, so here it just listens on :6067 and authorises the inbound link in its connect block. The passwords mirror across the two configs:

  • ircd C-line password hubpass == leveva accept-password
  • leveva send-password levevapass == ircd N-line password

Caveat: the C ircd is IPv6-only#

The C port's sockets are AF_INET6, so when it auto-connects it resolves the leveva Service via an AAAA record. That works on a dual-stack or IPv6 cluster (the leveva Service is set ipFamilyPolicy: PreferDualStack). On a single-stack IPv4 cluster the Service has no AAAA, and the auto-connect can't resolve a destination. Options there:

  1. Enable dual-stack on the cluster (then the Service gets an IPv6 ClusterIP).
  2. Pin the leveva Service to a known IPv6 clusterIP and put that literal in the ircd C/N lines instead of the DNS name (isdigit-leading hosts go straight through inetpton(AF_INET6, …), skipping resolution entirely).

Reversing the dial direction (leveva → ircd via /CONNECT) doesn't help here: the ircd C/N host fields double as the accept mask, and they're set to leveva's Service name — which resolves to the ClusterIP, not the leveva pod's source IP — so ircd would reject the inbound match. If you want that direction, widen the ircd C/N host masks to * (it can no longer auto-dial then, since that same field is the dial target).

This example is intentionally plaintext (no TLS, no iauth ident probe) to keep it readable — don't ship the passwords as-is.