Commit Graph
5 Commits
Author SHA1 Message Date
oierandClaude Opus 4.8 16523e4f62 feat(files): move filestash to upload., serve files. as read-only static browse
Filestash cannot be served under a subpath (its SPA hardcodes /api, /assets,
/login at root), so it moves to its own subdomain upload.${BASE_DOMAIN}. The
apex-level ask was a static, no-auth file browser at files.${BASE_DOMAIN}: caddy
now file_server browse's the same ${FILES_DATA_DIR} share read-only there.

- filestash general.host -> upload.; nginx upload. vhost keeps the 2g body +
  upload streaming + /admin throttle; new files. vhost is a plain static proxy.
- caddy: 60-files.caddy split into 60-upload.caddy (filestash) + 62-files.caddy
  (file_server browse of /srv/files); caddy mounts FILES_DATA_DIR :ro.
- LE_SUBDOMAINS += upload (both upload. and files. are vhosts needing certs).

Security: files. is unconditionally public read (no auth), independent of
FILES_AUTH. The htpasswd login only gates writes via upload. Documented.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 17:03:33 +02:00
oier b6b02398da feat(generators): offline data pack generators at generators.
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.
2026-07-21 18:59:01 +02:00
oierandClaude Opus 4.8 08fe8c9c53 docs(files): record the general.host trap + that a green monitor proves nothing
Propagate the bare-hostname fix (10b86ae) to the docs that still said "three
traps", and write down the meta-lesson it exposed.

The bug was invisible to every check we had: Filestash strips the scheme
before its own Host check, so `curl /` returned 200, the API answered, wrong
passwords were rejected, and the brand-new Uptime Kuma HTTP monitor stayed
green — while the SPA computed `http://https://files...` and no browser could
use the site. An HTTP monitor asserts "server returned 200", which is a much
weaker claim than "the app works".

- CLAUDE.md / README: four traps now, host-scheme first; add the post-change
  check (`/api/config` → origin must be the real https URL).
- 19-routes: login form only renders at ?action=redirect (a bare 303 is
  normal, not a bug); assert origin after config changes.
- 10-uptime-kuma: an HTTP monitor cannot see an SPA break — use a Keyword
  monitor if you want teeth, and don't read green as "users can log in".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 18:31:46 +02:00
oierandClaude Opus 4.8 25df9f9398 feat(files): add Filestash player file share at files.${BASE_DOMAIN}
One shared login (htpasswd) for all players, writable. Config is rendered
from a template and mounted read-only, so git stays authoritative — the
admin console loads but cannot save, by design.

Filestash's OIDC/SAML middlewares are enterprise-only, so Keycloak SSO is
out; FILES_AUTH keeps htpasswd/passthrough switchable for later. Auth-off
(passthrough) is only valid once the host is off the public internet —
render-config.sh warns loudly when rendering it.

Three traps, all found by testing against the pinned image:
- the `local` backend is admin-gated: without LOCAL_BACKEND_SECRET in the
  connection params every login fails with "backend error - Not Allowed"
- the htpasswd plugin only accepts $2a$ bcrypt, so a $2y$ hash from
  `htpasswd -B` fails every login silently — use `openssl passwd -6`.
  render-config.sh rejects the wrong format rather than shipping it
- APPLICATION_URL/ADMIN_PASSWORD env make filestash rewrite config.json at
  boot, which fails on the :ro mount — both live in the rendered config

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 18:08:46 +02:00
oierandClaude Opus 4.8 5d5602fbd7 feat(nmsr): cut avatar refresh lag to ~1min + document HTTP routes
NMSR has no per-request avatar refresh (the `?skin=` override is ignored by this
build), so the only lever for a changed skin to appear is the resolve cache.
Drop resolve_cache_duration 15m -> 60s so account-page skin changes propagate
within ~1 min; texture cache stays 48h (Drasl skin URLs are content-hashed, so a
new skin is always a new URL — never stale). Cost is one extra internal Drasl
profile lookup per avatar per minute, trivial for 4-10 players.

Update the now-stale "~15 min" copy (skinLead + skinPreviewNote, en/es/eu) and
code comments to "~1 min".

Add plan/19-routes.md: a reference for the nmsr / landing / auth HTTP routes
(public via caddy + browser->Drasl + internal service-to-service), indexed in
plan/00-overview.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 20:58:57 +02:00