#!/bin/sh # Dojo Bay container entrypoint: seeds first-run data, points the backend at # Archipelago's Tor SOCKS proxy, runs the 10-minute prober on a loop (in place # of the systemd timer the standalone deploy used), and supervises all three # processes (node backend, prober loop, nginx) so a SIGTERM from tini/podman # stops them all cleanly rather than leaving orphans for the hard-kill timeout. set -eu # ---- first-run data seeding ------------------------------------------------- # /app/data is a bind-mounted, host-persistent volume: empty on first install, # and shadows whatever was baked into the image at that path. Populate it from # the clean templates exactly once; a real seed.json/operator.json (once the # claim wizard or "Manage my Dojo" writes one) is never overwritten. for f in seed.json dojos.json history.json history-daily.json paynym-codes.json version.json; do if [ ! -f "/app/data/$f" ]; then cp "/app/data-template/$f" "/app/data/$f" fi done # ---- outbound Tor ----------------------------------------------------------- # The manifest generates /app/data/tor-proxy.conf with the archy-net bridge # gateway's SOCKS address (Archipelago's Tor binds a second SocksPort there # specifically for containers) — see docs/app-developer-guide.md's # {{NETWORK_GATEWAY}} placeholder. probe.mjs already reads TOR_SOCKS_HOST/PORT # (used for PayNym lookups, DNS-over-HTTPS domain checks, and probing every # listed Dojo), so no code change is needed, only wiring the env vars here. if [ -f /app/data/tor-proxy.conf ]; then TOR_PROXY_ADDR="$(cat /app/data/tor-proxy.conf)" export TOR_SOCKS_HOST="${TOR_PROXY_ADDR%:*}" export TOR_SOCKS_PORT="${TOR_PROXY_ADDR##*:}" fi # ---- the backend ------------------------------------------------------------- cd /app/server node index.mjs & NODE_PID=$! # ---- the 10-minute prober ---------------------------------------------------- # Replaces dojobay-update.timer: the same script, invoked on a loop instead of # by systemd. update.mjs itself is unchanged from upstream. Runs once shortly # after start (dojobay-update.timer's OnBootSec=2min counterpart — a fresh # install should not sit on an empty/stale list for a full ten minutes), then # every 10 minutes; a few seconds of random jitter on each wait, same reasoning # as the timer's RandomizedDelaySec (a fleet of instances should not all probe # the same nodes on the same wall-clock tick). ( sleep "$((25 + RANDOM % 30))" while true; do node /app/scripts/update.mjs || echo "[update] cycle failed, will retry in 10 minutes" >&2 sleep "$((570 + RANDOM % 60))" done ) & UPDATE_LOOP_PID=$! # ---- the web server ----------------------------------------------------------- # Backgrounded rather than exec'd: this script stays the live PID tini # supervises, so the trap below can actually run when SIGTERM arrives and # forward it to all three children. (exec'ing nginx here would replace this # script's process image, and a trap registered by a process that no longer # exists never fires — the other two would then only die on the container's # hard-kill timeout instead of shutting down cleanly.) # -e /dev/stderr: nginx's master process logs its very first startup lines # (before it has even parsed nginx.conf's own error_log directive) to a # compiled-in default path under /var/lib/nginx/logs — a symlink to # /var/log/nginx, which is not one of the paths this app asks Archipelago to # make writable under security.readonly_root. Overriding it here means # nothing ever depends on /var/log/nginx existing or being writable at all, # on this image or any other readonly-root host. nginx -e /dev/stderr -g "daemon off;" & NGINX_PID=$! cleanup() { kill -TERM "$NGINX_PID" "$NODE_PID" "$UPDATE_LOOP_PID" 2>/dev/null || true wait "$NGINX_PID" 2>/dev/null || true exit 0 } trap cleanup TERM INT wait "$NGINX_PID" kill "$NODE_PID" "$UPDATE_LOOP_PID" 2>/dev/null || true