Two config keys were silently ignored by drasl (logged as "unknown config
option"), so neither took effect in production:
- `DefaultAdminUsernames` is not a drasl option; the correct top-level key is
`DefaultAdmins`. With the wrong key, the `admin` user never got promoted —
admin-only API routes (e.g. GET /players for the registered-players roster)
returned 403.
- `CORSAllowOrigins` sat after the `[RegistrationNewPlayer]` table header, so
TOML parsed it as `RegistrationNewPlayer.CORSAllowOrigins` (ignored → no CORS
headers → landing register/account browser calls blocked). Moved it top-level,
before any [Section], with a comment warning against re-nesting.
Verified against drasl master configuration.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a static /register page (en/es/eu) that calls the Drasl REST API v2
directly from the browser: register -> login -> optional skin upload, all
in the pixel design. Guests never touch Drasl's own web UI.
Drasl config gains the pieces this needs:
- CORSAllowOrigins scoped to the apex (the API has no CORS until set;
never "*").
- [RateLimit] for the now public-facing anonymous POST /users.
- [RegistrationNewPlayer] with RequireInvite driven by a new
REGISTRATION_MODE (invite|open) .env flag.
REGISTRATION_MODE has two consumers from one key: render-config.sh derives
the TOML boolean for Drasl (drasl is TOML-only, no env), and the landing
build reads it to show/hide the invite-code field. render-config.sh halts
on any value other than invite/open.
Security verified against drasl source: anonymous POST /users cannot set
privileged fields (isAdmin/isLocked/chosenUuid/maxPlayerCount are gated on
callerIsAdmin in CreateUser), so browser-direct registration is safe.
Docs: plan/16-landing-registration.md captures the design + the B1 vs fork
decision; build-order, deploy, and the deploy skill wire REGISTRATION_MODE
and the landing-rebuild requirement (www/ is gitignored, not updated by a
git pull).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Public scheme is HTTPS (host nginx terminates TLS in front of caddy). Drasl
builds absolute URLs — texture URLs and the authlib-injector API location —
from BaseURL, so an http:// value caused mixed-content blocking and broken
login on the https origin. Point BaseURL at https://auth.${BASE_DOMAIN}.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>