Add generators.${BASE_DOMAIN}: a pinned fork of misode.github.io served
with its own mcmeta + vanilla-mcdoc mirror, so it works with zero internet
at the LAN party. Fork lives in ulicraft-generators (branch ulicraft),
pinned at upstream 2bfe5522.
The app is 100% client-side: every data fetch comes from the player's
browser, not from cochi. So offline capability is entirely about which
origins the bundle refers to, plus a mirror to serve them from. The vhost
sets Content-Security-Policy with connect-src 'self' as ENFORCEMENT: an
external origin that sneaks back in (or returns via an upstream merge)
then breaks loudly in prod, on an ordinary day, instead of silently at the
venue with no internet to debug it.
sync-generators-data.sh mirrors ~109M: the 1.21.1 summary/data/atlas tags,
assets-tiny (assets minus hundreds of MB of .ogg — hence no sounds
generator), vanilla-mcdoc symbols.json, and summary/versions/data.min.json,
which comes from a BRANCH head rather than a version tag and runs during
Spyglass init. It verifies a sentinel file per source instead of trusting a
clean clone exit, since a renamed upstream path would otherwise sync
"successfully" and only fail in the browser at the party.
The container serves the bundle and the mirror on one origin, so there is
no CORS, no second cert and no second DNS name.
DEPLOY ORDER MATTERS. GENERATORS_REPO must be set in cochi's .env BEFORE
pulling this: compose uses ${GENERATORS_REPO:?...}, and an unset required
variable makes docker compose fail to parse the file — taking down every
service including Minecraft, not just this one. Full runbook and the rest
of the prerequisites: plan/21-generators.md.
Verified offline in real Chromium with every non-origin request aborted:
zero off-origin attempts, zero same-origin failures, zero console errors,
generators rendering schemas and live JSON.
128 lines
6.4 KiB
Bash
128 lines
6.4 KiB
Bash
# Copy to .env and fill in real values
|
|
|
|
# Single base domain for the whole stack; every service is a subdomain of this.
|
|
# DNS is handled OUTSIDE this repo — point ${BASE_DOMAIN} and every subdomain
|
|
# (auth. avatar. status. distribution. www.) at the host running nginx.
|
|
BASE_DOMAIN=ulicraft.net
|
|
|
|
RCON_PASSWORD=change-me-to-something-random
|
|
|
|
# Registration mode (BACKEND gate) for self-hosted accounts (Drasl).
|
|
# invite = closed; guests need an admin-minted invite code (recommended on a
|
|
# public, internet-present host). Admin mints via POST /drasl/api/v2/invites.
|
|
# open = anyone can self-register with username+password (set [RateLimit]!).
|
|
# Consumed by render-config.sh (-> Drasl RegistrationNewPlayer.RequireInvite).
|
|
# Default: invite. NOTE: this no longer controls the landing invite FIELD — that's
|
|
# REGISTRATION_SHOW_INVITE below.
|
|
REGISTRATION_MODE=invite
|
|
|
|
# Show the invite-code FIELD on the landing register form. Read by the landing
|
|
# BUILD (data/site.ts) only; default false (hidden). Independent of the backend
|
|
# gate above. CAVEAT: if REGISTRATION_MODE=invite (Drasl requires a code) but this
|
|
# is false, public self-registration can't supply one — enable both together, or
|
|
# mint accounts admin-side. true | false.
|
|
REGISTRATION_SHOW_INVITE=false
|
|
|
|
# NMSR avatar render mode for the landing's avatar tiles. Read by the landing
|
|
# BUILD (data/site.ts) only. Avatar URL =
|
|
# https://avatar.${BASE_DOMAIN}/<mode>/<uuid>?size=N. e.g. headiso | head | fullbody.
|
|
# AVATAR_MODE → ServerCard online row + account preview (heads)
|
|
# ROSTER_AVATAR_MODE → Members grid (full body; default fullbodyiso)
|
|
AVATAR_MODE=headiso
|
|
ROSTER_AVATAR_MODE=fullbodyiso
|
|
|
|
# Drasl admin creds for the registered-players roster (/api/mcstatus/players).
|
|
# Consumed ONLY by the mc-status container (server-side login → sanitized
|
|
# [{uuid,name}] list); these NEVER reach the browser. Use a DEDICATED, rotatable
|
|
# admin account (Drasl has no read-only role) to limit blast radius. Leave blank
|
|
# to disable the roster (the Members grid then just shows its empty state).
|
|
DRASL_ADMIN_USERNAME=
|
|
DRASL_ADMIN_PASSWORD=
|
|
|
|
# caddy host-published port. The host's own nginx terminates TLS and
|
|
# reverse-proxies to caddy by Host header (nginx/ulicraft-caddy.conf.tmpl), so
|
|
# bind caddy to a high localhost-only port — only nginx should reach it.
|
|
CADDY_HTTP_PORT=8880
|
|
CADDY_HTTP_BIND=127.0.0.1
|
|
|
|
# distribution.${BASE_DOMAIN} static vhost. Absolute host path to the built
|
|
# static site — it lives in ANOTHER repo. Bind-mounted read-only into caddy.
|
|
DISTRIBUTION_WEB_ROOT=/home/oier/projects/mc-mods/ulicraft-group/ulicraft-distribution/dist
|
|
|
|
# ── files.${BASE_DOMAIN} — Filestash player file share ────────────────
|
|
# Screenshots / schematics / maps. ONE shared login for all players, writable.
|
|
# Full design + gotchas: plan/20-files.md.
|
|
|
|
# Host path holding the shared files. Keep it OUTSIDE the repo — guest-writable
|
|
# data must not live in a git tree we `git pull` into.
|
|
FILES_DATA_DIR=/srv/ulicraft-files
|
|
|
|
# Literal mount flag for FILES_DATA_DIR: rw | ro. `ro` makes the share
|
|
# read-only at the KERNEL, regardless of what the Filestash UI thinks. It is a
|
|
# mount flag rather than a boolean because compose has no conditionals — the
|
|
# value is substituted straight into the volume string.
|
|
FILES_MOUNT_MODE=rw
|
|
|
|
# Auth middleware (rendered into config.json by render-config.sh):
|
|
# htpasswd one shared user/pass — the only internet-safe value
|
|
# passthrough NO LOGIN. Anyone reaching the vhost is in.
|
|
# none alias of passthrough
|
|
# passthrough/none on a WRITABLE, internet-reachable share is an open upload
|
|
# relay (malware/phishing hosted on your domain, under your cert, with your
|
|
# bandwidth — and a domain blocklisting would take auth. and the apex down
|
|
# with it). Only set it once this host is unreachable from the internet.
|
|
FILES_AUTH=htpasswd
|
|
|
|
# The shared player credential (FILES_AUTH=htpasswd).
|
|
# FILES_PASSWORD_HASH: openssl passwd -6 '<password>'
|
|
# MUST be $6$ (sha512). The htpasswd plugin's bcrypt branch only accepts $2a$,
|
|
# so a $2y$ hash from `htpasswd -B` fails EVERY login, silently.
|
|
# render-config.sh rejects the wrong format rather than shipping it.
|
|
FILES_USER=uli
|
|
FILES_PASSWORD_HASH=
|
|
|
|
# Filestash /admin console password — bcrypt, and /admin IS publicly exposed, so
|
|
# use a 20+ char generated password. It is the only credential guarding config.
|
|
# docker run --rm caddy:alpine caddy hash-password --plaintext '<password>'
|
|
FILES_ADMIN_PASSWORD_HASH=
|
|
|
|
# Session key (openssl rand -hex 16). Must be non-empty: an empty secret_key
|
|
# makes filestash rewrite config.json at boot, which fails on the :ro mount.
|
|
FILES_SECRET_KEY=
|
|
|
|
# Unlocks the local storage backend, which filestash admin-gates: it refuses to
|
|
# init unless the connection params carry this secret. config.json supplies it
|
|
# server-side; players never see it. openssl rand -hex 24.
|
|
FILES_LOCAL_SECRET=
|
|
|
|
# ── generators.${BASE_DOMAIN} — offline Minecraft data pack generators ────
|
|
# A pinned fork of misode.github.io, built from source and served with its own
|
|
# data mirror so it works with ZERO internet at the LAN party. The app is 100%
|
|
# client-side, so the mirror is what makes it offline-capable — not the fact
|
|
# that the host has no WAN. Full design: plan/21-generators.md.
|
|
|
|
# Absolute host path to the ulicraft-generators checkout (the fork). This is the
|
|
# docker build context; the fork carries its own Dockerfile + Caddyfile.
|
|
GENERATORS_REPO=/home/oier/projects/mc-mods/ulicraft-group/ulicraft-generators
|
|
|
|
# Where tooling/sync-generators-data.sh puts the ~107M mcmeta + vanilla-mcdoc
|
|
# mirror. Gitignored, fetched deliberately with WAN, mounted read-only.
|
|
# Must be re-synced (with a matching MC_REF) if the pinned MC version moves.
|
|
GENERATORS_DATA_DIR=./generators-data
|
|
|
|
# Only needed if using CurseForge-exclusive mods:
|
|
# CF_API_KEY=your-curseforge-api-key
|
|
|
|
# ── Let's Encrypt via OVH DNS-01 (tooling/issue-letsencrypt.sh) ───────
|
|
# Only needed to issue real TLS certs. DNS-01 needs no inbound ports.
|
|
LE_EMAIL=you@example.com
|
|
# space-separated subdomains; apex ${BASE_DOMAIN} is always included
|
|
LE_SUBDOMAINS=auth distribution www avatar status files generators
|
|
# OVH API creds (DNS zone write). Create at https://eu.api.ovh.com/createToken/
|
|
# OVH_CK can be blank on first run — acme.sh prints an auth URL, then paste it.
|
|
OVH_END_POINT=ovh-eu
|
|
OVH_AK=
|
|
OVH_AS=
|
|
OVH_CK=
|
|
# LE_STAGING=1 # use the untrusted staging CA for dry runs
|