Files
ulicraft-server-v1/CLAUDE.md
Oier Bravo Urtasun 037b1777d6 feat(landing): rename crew to "El valle" + source downloads from launcher.json
Rename all "crew" mentions (en/es/eu) to the untranslated proper name
"El valle" across hero, members, and features copy in i18n/ui.ts.

Rewire the homepage launcher download list to the custom-launcher build
output launcher.json (productName/version/files[]) instead of the
never-produced launcher-manifest.json shape. site.ts now reads launcher.json
and normalizes files[] -> internal builds[]; launcher.example.json is the
committed fallback. Drop the stale launcher-manifest.example/schema, update
.gitignore and docs (CLAUDE.md, plan/18).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 01:55:05 +02:00

185 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Ulicraft Server — Project Context
> Self-hosted modded Minecraft (NeoForge 1.21.1) with self-hosted auth/skins
> (Drasl), a distribution-fed modpack, avatar rendering, a guest landing page, and uptime
> monitoring. Public, internet-present deployment behind the host's nginx + TLS.
**`plan/` is the source of truth for the design.** Start at `plan/00-overview.md`.
This file is a fast orientation + the durable decisions/gotchas; when it disagrees
with `plan/` or the code, those win.
## Goals & Constraints
- **Players**: 410 friends
- **Modpack vibe**: Kitchen-sink (tech + magic + exploration + QoL)
- **Pack approach**: Manually curated (~50100 mods), NOT a forked existing pack
- **Minecraft version**: 1.21.1, **NeoForge** (not classic Forge)
- **Auth/skins**: Drasl (Yggdrasil-compatible), **password login** — no OIDC
- **DNS**: handled OUTSIDE this repo; point `${BASE_DOMAIN}` + subdomains at the host
- **Persistence horizon**: long-term service, not disposable
## Architecture
One unified `docker-compose.yml`. The host's own nginx terminates TLS (Let's
Encrypt) and reverse-proxies every vhost to caddy (published localhost-only) by
Host header. caddy is the internal router; containers reach the stack's own names
via caddy's `mcnet` aliases (`auth.`, `pack.`).
```
Internet ── host nginx (TLS, LE certs, HSTS) ──► caddy (127.0.0.1:8880)
│ routes by Host:
${BASE_DOMAIN} ─► /srv/www landing + /launcher/ downloads
auth.${BASE_DOMAIN} ─► drasl (Yggdrasil API + web UI)
avatar.${BASE_DOMAIN} ─► nmsr (skin/avatar renderer, Drasl-backed)
status.${BASE_DOMAIN} ─► uptime-kuma (status page + monitors)
distribution.${BASE} ─► static site from ANOTHER repo (DISTRIBUTION_WEB_ROOT)
minecraft :25565 (+ 24454/udp Simple Voice Chat)
└ ONLINE_MODE=false, -javaagent authlib-injector → http://auth.${BASE_DOMAIN}/authlib-injector
└ mods from ./server-mods volume (/mods, REMOVE_OLD_MODS) — see plan/04-mods.md
mc-backup ── RCON → minecraft (6h interval, prune 14d, world-only)
```
Bring-up: render configs → sync server mods → build landing → fetch launcher → `docker compose up -d --build`.
See `plan/12-build-order.md` and `plan/14-deploy.md`. Deploy to prod host `cochi`
via the `deploy` skill.
## Key Decisions
- **NeoForge over Forge** — the 1.21.x kitchen-sink ecosystem is overwhelmingly
NeoForge; the Forge team migrated. `itzg/minecraft-server` via `TYPE=NEOFORGE`.
- **Drasl over Ely.by / vanilla offline** — self-hosted Go service, Yggdrasil
drop-in, skins + capes, actively maintained. Run with **password login**
(no OIDC/Keycloak in this stack).
- **Manual curation** (user choice, flagged expensive) — if it gets painful,
fork-and-trim Leaking Kitchen Sink is still on the table (closest vibe match).
- **itzg/minecraft-server** base image — de-facto standard, auto-installs NeoForge.
- **Distribution-fed mod volume (replaced packwiz)** — the modset's source of
truth is the HeliosLauncher distribution repo (`$DISTRIBUTION_WEB_ROOT`).
Clients install the full set via `distribution.json`. The SERVER loads a
filtered subset (`both`/`server`) from a mounted `./server-mods` volume
(`REMOVE_OLD_MODS=TRUE`). See `plan/04-mods.md`.
## Critical Gotchas
### Minecraft 1.21+ secure profile
- Server: `ENFORCE_SECURE_PROFILE=FALSE` (`enforce-secure-profile=false`)
- Drasl: `SignPublicKeys = false`
- Linked. Drasl docs: *"Mixed authentication does not work with
`SignPublicKeys = true` on Minecraft 1.21+."*
### authlib-injector JVM agent
- Syntax: `-javaagent:/extras/authlib-injector.jar=<yggdrasil-api-url>`
- URL after `=` is the **API root** = `<BaseURL>/authlib-injector`. For this
stack: `http://auth.${BASE_DOMAIN}/authlib-injector` (internal, over mcnet).
- **Must match between server and client** (clients use the public
`https://auth.${BASE_DOMAIN}/authlib-injector`), else session validation fails
silently with "Invalid session".
- Releases: https://github.com/yushijinhun/authlib-injector — jar lives in
`runtime/` (mounted at `/extras/authlib-injector.jar`).
### NeoForge version pinning
Mods are picky about exact minor versions. Compose pins `NEOFORGE_VERSION:
"21.1.233"`**must match the distribution's NeoForge** (`distribution.json`),
since mods are built against it (e.g. ferritecore≥218, farmersdelight≥219).
**Verify against https://projects.neoforged.net/neoforged/neoforge before
deploying.** Never `"latest"` for a stable server.
### Mod source + server filtering
The full modset lives in the distribution repo (`$DISTRIBUTION_WEB_ROOT/servers/
ulicraft-1.21.1/forgemods/required/*.jar`), managed by Nebula. Clients get
everything via `distribution.json`. The server runs a **filtered subset**:
`tooling/classify-mods.py` reads each jar's `neoforge.mods.toml` with tomllib and
seeds `mods-sides.json` (`client|server|both`, default `both`), then
`tooling/sync-server-mods.sh` copies the `both`/`server` jars into `./server-mods`
(mounted at `/mods`). Hard client-only mods (sodium/iris) classify as `client`
automatically; cosmetic-client mods that declare `BOTH` stay `both` until you
hand-edit `mods-sides.json` to `client`. No Modrinth/packwiz, no network at
classify/sync time. See `plan/04-mods.md`.
### Memory sizing
~50100 mods, 8 players, 1.21.1: `INIT_MEMORY: 4G`, `MAX_MEMORY: 10G`,
`USE_AIKAR_FLAGS: TRUE`.
## Config (.env)
`BASE_DOMAIN`, `RCON_PASSWORD`, `CADDY_HTTP_PORT`/`CADDY_HTTP_BIND`,
`DISTRIBUTION_WEB_ROOT` (source of the modset + caddy `distribution.` mount), the
Let's Encrypt block (`LE_EMAIL`, `LE_SUBDOMAINS`, `OVH_*`). Rendered configs
(drasl, nmsr) come from `*.tmpl` via `tooling/render-config.sh` (substitutes
`${BASE_DOMAIN}` only). Server mods are synced (not rendered) by
`tooling/sync-server-mods.sh`.
## Client Distribution (guests)
Launcher: **FjordLauncherUnlocked** (Prism fork, first-class authlib-injector),
mirrored at apex `/launcher/`. Per guest:
1. Download from `https://${BASE_DOMAIN}``/launcher/`.
2. Add an authlib-injector account, URL
`https://auth.${BASE_DOMAIN}/authlib-injector` (register/login at
`https://auth.${BASE_DOMAIN}`).
3. The launcher installs the modpack from the distribution (`distribution.json`).
(NOTE: packwiz/`pack.` removed — the landing join step still references a
packwiz URL and needs reworking around the launcher/distribution flow.)
4. Connect to `${BASE_DOMAIN}` (`:25565`).
## Open / Pending Work
### Roadmap — mod migration follow-ups (post packwiz→04-mods)
- [ ] **Curate `mods-sides.json` cosmetic-client mods.** tomllib auto-flagged 11
hard client-only mods; the other 65 default `both`, including client-cosmetic
ones (BetterF3, entityculling, MouseTweaks, ponderjs, fancymenu, drippy, melody,
welcomescreen, Searchables, JustEnoughResources, TravelersTitles, yeetus…) that
load harmlessly on the server but waste RAM. Hand-edit them to `client` so
`sync-server-mods.sh` drops them from `./server-mods`. (Also review `appleskin`,
flagged `client` — flip to `both` if server-side food data wanted.)
### Landing rework + mod list (PLANNED — see `plan/18-landing-rework.md`)
Full design settled across WS1WS6; all tasks unchecked, ready to execute.
- [ ] **WS1 — Join flow off packwiz.** Homepage = UlicraftLauncher, 3 steps
(register → download launcher → join); drop `packwizUrl` + packwiz step.
Per-OS downloads from a synced `launcher.json` (shape
`productName/version/files[]`, the build output of the `custom-launcher`
repo; `launcher.example.json` committed as the documented fallback). Fjord
moves to its own `/fjord` page.
- [ ] **WS2/3 — Player rosters.** Online (mc-status, live) + all-registered (new
`mc-status /api/mcstatus/players`, admin-login → Drasl `GET /players`, ~60s
cache). Shared avatar tile, `AVATAR_MODE` env (default `headiso`). Admin creds
→ mc-status only.
- [ ] **WS4 — Account page** (`/account`): skin CRUD via browser-direct Drasl
(reuses `register.astro` skin JS). Skins only, in-memory token, `players[0]`.
- [ ] **WS5 — Footer status link** to `status.${BASE_DOMAIN}` (nav unchanged).
- [ ] **WS6 — Mod shortlist + list component (`plan/17-mod-shortlist.md`).**
Light gap doc: current 76-jar pack is vanilla+ QoL/perf; **empty** on tech &
magic, thin worldgen, no map; orphan deps `ponderjs`/`Necronomicon`. Landing
mod-list component = auto-generated `mods.json` + extracted logos from the
distribution forgemods (`tooling/build-modlist.py`), flat alphabetical, pretty
names, no categories/links; feeds the "50+" stat tile.
- [ ] Server-side perf mods (C2ME, etc.).
- [ ] **Verify NeoForge version** against current stable before first deploy.
- [x] **Bootstrap Drasl admin** account (`admin`, 2026-06-10). Key is
`DefaultAdmins` (not `DefaultAdminUsernames`); first admin needs an invite-flip.
See `plan/03-drasl.md` gotchas.
## Reference URLs
- Drasl: https://github.com/unmojang/drasl — config docs:
https://github.com/unmojang/drasl/blob/master/doc/configuration.md
- authlib-injector: https://github.com/yushijinhun/authlib-injector
- itzg/minecraft-server: https://github.com/itzg/docker-minecraft-server
(NeoForge: …/docs/types-and-platforms/server-types/forge.md)
- itzg/mc-backup: https://github.com/itzg/docker-mc-backup
- NeoForge versions: https://projects.neoforged.net/neoforged/neoforge
- NMSR: https://github.com/NickAcPT/nmsr-rs
- Uptime Kuma: https://github.com/louislam/uptime-kuma
- FjordLauncherUnlocked: https://github.com/hero-persson/FjordLauncherUnlocked
- Reference packs (inspiration, NOT forking): ATM10, Leaking Kitchen Sink.
## Communication Preferences (carry-over)
- Concise and direct; diagnose + resolve without demanding repro steps.
- Prefer iterative single-change updates over large rewrites.
- Cautious scripts that halt on ambiguity rather than proceeding silently.
- Avoid over-engineering when a simple answer suffices.
- Spanish or English both fine.