Files
ulicraft-server-v1/plan/19-routes.md
Oier Bravo Urtasun 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

4.3 KiB

19 — HTTP route reference (nmsr, landing, auth)

Quick map of the public + internal HTTP routes. caddy (caddy/conf.d/*.caddy) is the internal router; the host nginx terminates TLS and forwards each vhost to it by Host header. All ${BASE_DOMAIN} = ulicraft.net in prod.

Vhost → service (caddy)

Vhost caddy file Upstream
${BASE_DOMAIN} (apex) 10-static.caddy static /srv/www + /srv/launcher + mc-status
auth.${BASE_DOMAIN} 00-core.caddy drasl:25585
avatar.${BASE_DOMAIN} 40-avatar.caddy nmsr:8080
status.${BASE_DOMAIN} 50-status.caddy uptime-kuma:3001
distribution.${BASE_DOMAIN} 30-distribution.caddy static /srv/distribution (other repo)

NMSR — avatar.${BASE_DOMAIN} (skin/avatar renderer)

reverse_proxy nmsr:8080. Stateless renderer; pulls profiles+skins from Drasl over mcnet (no public round-trip).

  • GET /{mode}/{uuid}?size=N — render an avatar PNG.
    • mode = any NMSR render mode (headiso is the configured default via AVATAR_MODE; others incl. head, face, fullbody, fullbodyiso).
    • uuid = Drasl player UUID; size = px.
    • Landing usage: ${avatarUrl}/${avatarMode}/<uuid>?size=64|96.
  • No per-request refresh. A ?skin=<url> override is ignored by this build (tested). Skin changes propagate via the resolve cache only.
  • Caching (nmsr/config.toml): resolve_cache_duration = 60s (uuid→profile, bounds stale-avatar window), texture_cache_duration = 48h (safe — Drasl skin URLs are content-hashed, new skin = new URL). Instant global flush = restart nmsr. See plan/03-drasl.md for the Drasl-backed mojank wiring.

Landing — ${BASE_DOMAIN} (apex)

10-static.caddy. Three handlers:

  • handle_path /launcher/*/srv/launcher (file browse) — launcher downloads.
  • handle /api/mcstatus/*uri strip_prefix /api/mcstatusmc-status:8080:
    • /api/mcstatus/v2/status/java/<addr> → internal /v2/status/java/ — live server status JSON (mcstatus.io-compatible, CORS: *, <addr> ignored — it only pings MC_STATUS_TARGET). Landing statusApi.
    • /api/mcstatus/players → internal /players — registered-players roster, sanitized [{uuid,name}], ~60s cache (mc-status admin-logs-in to Drasl GET /players). Landing rosterApi.
    • /healthz — mc-status liveness (internal).
  • handle {}/srv/www static (Astro output). Pages: /, /es, /eu (index), /{lang}/register, /{lang}/account, /{lang}/fjord.

Browser → Drasl directly (CORS-scoped to apex via Drasl CORSAllowOrigins), base https://auth.${BASE_DOMAIN}/drasl/api/v2 (site.draslApiUrl):

  • register.astro: POST /users (register), POST /login, PATCH /players/{uuid} (set initial skin).
  • account.astro: POST /login, PATCH /players/{uuid} with {skinBase64,skinModel} (upload) or {deleteSkin:true} (reset).

Auth / Drasl — auth.${BASE_DOMAIN}

reverse_proxy drasl:25585. One service exposes three surfaces:

  • Web UI: / (login / register / profile pages), /web/texture/skin/<sha256>.png, /web/texture/cape/<sha256>.png.
  • Yggdrasil / authlib-injector: /authlib-injector (API root — server JVM agent + clients point here), /session/minecraft/..., mojang-compat /users/profiles/minecraft/{name} and /session/minecraft/profile/{uuid} (also what NMSR calls internally).
  • Drasl REST API v2 (/drasl/api/v2):
    • POST /login {username,password}{apiToken}
    • POST /users {username,password[,inviteCode]} — register (invite-gated when RequireInvite=true; admins from DefaultAdmins are promoted at startup, see plan/03-drasl.md)
    • GET /playersadmin only, full roster
    • PATCH /players/{uuid} — skin CRUD (own player, or admin)
    • POST /invites — admin, mint invite codes

Internal (service → service, over mcnet)

  • minecraft-javaagent authlib-injector=http://auth.${BASE_DOMAIN}/authlib-injector (caddy mcnet alias → drasl).
  • nmsrhttp://auth.${BASE_DOMAIN} (mojang-compat session/textures/api).
  • mc-statushttp://drasl:25585/drasl/api/v2 (DRASL_API_URL) for the roster; pings MC_STATUS_TARGET for live status.
  • mc-backup → RCON minecraft:25575.