Files
ulicraft-server-v1/plan/12-build-order.md
Oier Bravo Urtasun cd79b0e540 docs(files): document filestash across plan/README/deploy skill
Fold files./filestash into the docs that carry the service list, the vhost
map and the deploy procedure — README stack table, 00-overview, 02-caddy,
12-build-order, 14-deploy, and the deploy skill.

Two real gaps the filestash deploy exposed, now guardrails in the skill:
- update-all.sh never touches host nginx, so a NEW SUBDOMAIN silently 404s
  after deploy. It needs issue-letsencrypt.sh + render-nginx.sh --install.
- a new service with ${FOO:?} guards fails the WHOLE compose file, so a
  missing .env var means `up` starts nothing — and --hard has already run
  `down`. Check the host's .env BEFORE tearing the stack down.

Also: filestash/config.json is a single-file bind mount re-rendered every
run, so a rolling update-all leaves it on a stale inode (same trap as the
caddy conf) — force-recreate after any FILES_* change.

Correct the cochi divergence section: `git diff HEAD` on the host is now
empty (verified this deploy — the ff-pull succeeded). The documented
docker-compose.yml/package.json divergence was converged; only untracked
dnsmasq/, caddy-root-ca.crt and a stale pack/ remain. The stash-pull-pop
procedure it prescribed is no longer needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 18:23:18 +02:00

3.4 KiB

Build order — one commit per task

Dependency-ordered. Each numbered item = one git commit. Operational steps (prep, acceptance test) need internet/hardware and are NOT commits — marked [ops].

Commit message convention: Conventional Commits, scope = service. Footer: Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>.

Phase 0 — baseline

  1. chore: baseline scaffolding — CLAUDE.md, plan/, docker-compose.yml, runtime/, .gitignore, .env.example.

Phase 1 — config foundation

  1. feat(env): add BASE_DOMAIN + RCON_PASSWORD + CADDY_* to .env.example
  2. feat(tooling): add render-config.sh (envsubst BASE_DOMAIN, halt on unset)

Phase 2 — ingress (caddy internal router)

  1. feat(caddy): add conf.d snippets (00-core auth+pack, 10-static apex+launcher)
  2. feat(compose): add caddy service (127.0.0.1:CADDY_HTTP_PORT) + network aliases

Phase 3 — auth

  1. feat(drasl): add config.toml.tmpl (password login, SignPublicKeys=false)
  2. refactor(compose): drasl internal-only behind caddy; authlib URL → auth.

Phase 4 — modpack

  1. feat(pack): packwiz init + initial mod curation
  2. feat(tooling): add render-pack.sh + add-custom-mod.sh
  3. refactor(compose): minecraft via PACKWIZ_URL

Phase 5 — landing

  1. feat(landing): Astro+TS onboarding site → www/
  2. chore(landing): wire logo + .gitignore for www/ 11b. feat(landing): /register page → Drasl API (B1) + REGISTRATION_MODE flag (CORS/RateLimit/RegistrationNewPlayer in drasl config.toml.tmpl; see 16-landing-registration.md).

Phase 6 — launcher distribution

  1. feat(tooling): add fetch-launcher.sh (all platforms + stable symlinks)

Phase 7 — extra services

  1. feat(avatar): add nmsr service (Drasl-backed) + 40-avatar.caddy
  2. feat(status): add uptime-kuma service + 50-status.caddy
  3. feat(distribution): add 30-distribution.caddy (static, another repo)

Phase 8 — backups

  1. feat(compose): add mc-backup service (RCON, 6h)

Phase 9 — production ingress / TLS

  1. feat(nginx): add ulicraft-caddy.conf.tmpl + render-nginx.sh
  2. feat(tls): add issue-letsencrypt.sh (OVH DNS-01) + LE env block

Phase 11 — file share (DONE, 2026-07-14)

  1. feat(files): add Filestash player file share at files.${BASE_DOMAIN} — compose service (digest-pinned) + 60-files.caddy + nginx vhost (2g uploads, /admin rate limit) + filestash/config.json.tmpl rendered :ro + FILES_* env block. One shared htpasswd login, writable. See 20-files.md.
    • [ops] files added to LE_SUBDOMAINSissue-letsencrypt.sh, then render-nginx.sh --install (a new subdomain needs BOTH; update-all.sh does not touch host nginx).

Phase 10 — operational (no commits)

  • [ops] Prep online (once): tooling/render-config.sh, build landing (cd landing && set -a && . ../.env && set +a && pnpm run build — sources .env so BASE_DOMAIN + REGISTRATION_MODE bake into the static output), tooling/fetch-launcher.sh, fetch authlib-injector into runtime/.
  • [ops] TLS: tooling/issue-letsencrypt.sh, then tooling/render-nginx.sh --install.
  • [ops] Bring up: docker compose up -d --build.
  • [ops] Acceptance: install Fjord, add drasl account, import pack, join.
  • [ops] If green: tag v1-ready.

Notes

  • Bring-up is a single command — no run modes, no override compose files.
  • Each commit should leave the tree in a non-broken state (compose still parses).