Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Build artifacts
*.o
*.so
*.a
*.lo
*.dylib
src/ircd
src/unrealircdctl
src/modules/*.so
src/modules/**/*.o

# Generated config
config.log
config.status
config.cache
autom4te.cache
config.h.in~
src/Makefile
src/modules/Makefile
src/modules/*/Makefile
src/aclocal.m4

# Local working state
config.settings
*.bak
*.swp

# Cloned client checkout used during cross-repo development
ObsidianIRC/

# Bundled .deb produced by Tauri builds
*.deb
*.rpm
*.AppImage

# CI / IDE noise
.git
.github/workflows
.vscode
.idea
.claude

# Doc artifacts
doc/doxygen/

# Existing Docker output
/docker-compose.override.yml
.env
58 changes: 58 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Copy to .env and edit before running `docker compose up -d`.

# --- identity ---
SERVER_NAME=irc.example.com
NETWORK_NAME=ObbyNetwork
ADMIN_EMAIL=admin@example.com
MOTD_TEXT=Welcome to ObbyNetwork!

# --- oper credentials ---
# OPER_NAME / OPER_PASSWORD become the network admin's IRC oper login.
# If OPER_PASSWORD is unset, a random one is generated on first run and
# saved to data/.oper_password (printed to the container log once).
# OPER_MASK restricts where the oper can /OPER from; "*" allows any
# host -- tighten this in production (e.g. *@10.0.0.0/8 or *@bastion).
OPER_NAME=admin
OPER_PASSWORD=
OPER_MASK=*

# --- ports ---
# SSL_PORT is the port inside the container. SSL_HOST_PORT is the
# port the host publishes; set them differently if you want a custom
# external port.
SSL_PORT=6697
SSL_HOST_PORT=6697

# Plain WebSocket -- always listens internally. Put a TLS-terminating
# reverse proxy in front for production (nginx, Traefik, Caddy).
WS_PORT=8080
WS_HOST_PORT=8080

# --- optional features ---
# Set FILEHOST_URL to enable the filehost { host "..."; }; block used
# by the filehost module for link-preview image rehosting.
FILEHOST_URL=

# Set RPC_PASSWORD to enable JSON-RPC over TCP. RPC_PORT is internal
# only; route via reverse proxy with TLS to expose it externally.
RPC_PASSWORD=
RPC_PORT=8600

# --- cloak keys ---
# Persistent cloak keys. If unset, random keys are generated on every
# fresh conf volume -- fine for a single-node deployment but bad for a
# linked network where cloaks should be stable across restarts and
# servers. Generate with: head -c 32 /dev/urandom | xxd -p -c 64
CLOAK_KEY1=
CLOAK_KEY2=
CLOAK_KEY3=

# --- bind-mount overrides (optional) ---
# Set any of these to an absolute host path to use a bind mount
# instead of the named volume. Useful when you need to know exactly
# where the data lives on disk (cert sync, off-host backups).
# CONF_BIND=/srv/obbyircd/conf
# DATA_BIND=/srv/obbyircd/data
# LOGS_BIND=/srv/obbyircd/logs
# TLS_BIND=/srv/obbyircd/tls
# CUSTOM_MODULES_BIND=/srv/obbyircd/custom-modules
38 changes: 38 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
services:
obbyircd:
build:
context: .
dockerfile: docker/Dockerfile
image: obbyircd:local
container_name: obbyircd
restart: unless-stopped
ports:
- "${SSL_HOST_PORT:-6697}:${SSL_PORT:-6697}"
- "${WS_HOST_PORT:-8080}:${WS_PORT:-8080}"
volumes:
- ${CONF_BIND:-obbyircd_conf}:/home/obbyircd/obby/conf
- ${DATA_BIND:-obbyircd_data}:/home/obbyircd/obby/data
- ${LOGS_BIND:-obbyircd_logs}:/home/obbyircd/obby/logs
- ${TLS_BIND:-obbyircd_tls}:/home/obbyircd/obby/tls
- ${CUSTOM_MODULES_BIND:-obbyircd_custom_modules}:/home/obbyircd/obby/custom-modules
env_file:
- path: .env
required: false
healthcheck:
test: ["CMD-SHELL", "nc -z localhost ${SSL_PORT:-6697}"]
interval: 5s
timeout: 5s
retries: 6
start_period: 15s

volumes:
obbyircd_conf:
driver: local
obbyircd_data:
driver: local
obbyircd_logs:
driver: local
obbyircd_tls:
driver: local
obbyircd_custom_modules:
driver: local
129 changes: 129 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# syntax=docker/dockerfile:1.6
#
# ObbyIRCd Docker image. Builds the IRCd straight from this repository,
# so every native module shipped with the fork (account-registration,
# persistence, metadata, filehost, authtoken, smtp, obbyscript, ...) is
# available out of the box.
#
# Build: docker build -f docker/Dockerfile -t obbyircd .
# Compose: docker compose up -d

FROM alpine:3.19

# Build + runtime dependencies. sqlite-dev is needed by the
# account-registration module; the rest cover the core IRCd, TLS, JSON
# logging, PCRE2 regexes, argon2 password hashing, libsodium for the
# RPC subsystem and gettext for the entrypoint's envsubst.
RUN apk add --no-cache \
build-base \
openssl-dev \
openssl \
pcre2-dev \
argon2-dev \
libsodium-dev \
jansson-dev \
curl-dev \
sqlite-dev \
pkgconf \
wget \
tar \
gettext \
su-exec \
netcat-openbsd \
&& rm -rf /var/cache/apk/*

RUN adduser -D -s /bin/sh obbyircd

# Copy the entire repository in. .dockerignore keeps build artifacts,
# the ObsidianIRC client checkout, and other dev junk out.
WORKDIR /tmp/obbyircd-source
COPY --chown=obbyircd:obbyircd . .

# Pre-seed config.settings so ./Config -quick can build non-interactively.
RUN install -d -o obbyircd -g obbyircd \
/home/obbyircd/obby \
/home/obbyircd/obby/bin \
/home/obbyircd/obby/data \
/home/obbyircd/obby/conf \
/home/obbyircd/obby/modules \
/home/obbyircd/obby/logs \
/home/obbyircd/obby/cache \
/home/obbyircd/obby/doc \
/home/obbyircd/obby/tmp \
/home/obbyircd/obby/lib \
&& cat > /tmp/obbyircd-source/config.settings <<'EOF'
BASEPATH="/home/obbyircd/obby"
BINDIR="/home/obbyircd/obby/bin"
DATADIR="/home/obbyircd/obby/data"
CONFDIR="/home/obbyircd/obby/conf"
MODULESDIR="/home/obbyircd/obby/modules"
LOGDIR="/home/obbyircd/obby/logs"
CACHEDIR="/home/obbyircd/obby/cache"
DOCDIR="/home/obbyircd/obby/doc"
TMPDIR="/home/obbyircd/obby/tmp"
PRIVATELIBDIR="/home/obbyircd/obby/lib"
MAXCONNECTIONS_REQUEST="auto"
NICKNAMEHISTORYLENGTH="2000"
GEOIP="none"
DEFPERM="0600"
SSLDIR=""
REMOTEINC=""
CURLDIR=""
NOOPEROVERRIDE=""
OPEROVERRIDEVERIFY=""
GENCERTIFICATE="0"
SANITIZER=""
EXTRAPARA=""
ADVANCED=""
EOF

RUN chmod +x Config \
&& chown -R obbyircd:obbyircd /tmp/obbyircd-source /home/obbyircd

USER obbyircd
RUN ./Config -quick && make -j"$(nproc)" && make install

USER root

# Snapshot the freshly-installed conf tree somewhere outside the volume
# mount points so first-run can repopulate a fresh volume. `make
# install` doesn't ship doc/conf/scripts/, so copy the obbyscript
# example separately -- the entrypoint also `mkdir -p`s the scripts
# directory on every start in case the operator wiped it.
RUN mkdir -p /etc/obbyircd/conf-defaults \
&& cp -r /home/obbyircd/obby/conf/. /etc/obbyircd/conf-defaults/ \
&& mkdir -p /etc/obbyircd/conf-defaults/scripts \
&& cp -n /tmp/obbyircd-source/doc/conf/scripts/*.js \
/etc/obbyircd/conf-defaults/scripts/ 2>/dev/null || true \
&& chmod -R a+rX /etc/obbyircd

RUN mkdir -p /home/obbyircd/obby/conf \
/home/obbyircd/obby/data \
/home/obbyircd/obby/logs \
/home/obbyircd/obby/tls \
/home/obbyircd/obby/tmp \
/home/obbyircd/obby/custom-modules \
&& chown -R obbyircd:obbyircd /home/obbyircd

COPY docker/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
COPY docker/obbyircd.conf.template /etc/obbyircd/obbyircd.conf.template
RUN chmod +x /usr/local/bin/docker-entrypoint.sh

ENV SERVER_NAME=irc.example.com \
NETWORK_NAME=ObbyNetwork \
ADMIN_EMAIL=admin@example.com \
SSL_PORT=6697 \
WS_PORT=8080 \
RPC_PORT=8600 \
MOTD_TEXT="Welcome to ObbyIRCd!"

VOLUME ["/home/obbyircd/obby/conf", \
"/home/obbyircd/obby/data", \
"/home/obbyircd/obby/logs", \
"/home/obbyircd/obby/tls", \
"/home/obbyircd/obby/custom-modules"]

WORKDIR /home/obbyircd/obby

ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["./bin/obbyircd", "-F"]
Loading