# Architecture ## Services ### Web application The Next.js application owns user onboarding, account management, admin configuration, server-side Minecraft profile validation, sessions, and the HTTP API used by Discord and Velocity integrations. User authentication begins with an opaque, short-lived, single-use token created for a Discord user. Only a cryptographic hash of the token is persisted. Admin authentication is a separate Keycloak OIDC flow and requires the `minecraft-account-manager-admin` role. ### Discord bot The bot creates private login links in response to `/register` and `/account`. Discord user IDs are the canonical Discord identity; mutable usernames are snapshots only. Nickname updates target the deployment guild configured by `DISCORD_GUILD_ID`; the public join button uses `DISCORD_INVITE_URL`. ### Velocity plugin Velocity sends the authenticated Java UUID, current username, source IP, server ID, request ID, and occurrence time. The API matches UUID first. Username fallback is allowed only when the stored account has no UUID, after which UUID and canonical username are updated. The decision is fail closed. Unknown players, invalid responses, expired requests, authentication failures, and unavailable API responses are denied with the configured registration message. ## Trust boundaries - Browser input is untrusted. Minecraft profile resolution occurs on the server. - Forwarded IP headers are accepted only from configured reverse proxies. - Discord IDs come from bot-authenticated requests or one-time-code records, not browser fields. - Velocity requests use hashed per-server bearer credentials, timestamps, and database-unique request IDs for authentication and replay prevention. - Session and one-time-code values are random and stored only as hashes. - Exact IP addresses are sensitive data and require an explicit retention policy before production deployment. ## Database invariants - A Discord user ID maps to one user. - An active Minecraft UUID or case-insensitive username maps to one account. - A user has at most one active primary Minecraft account. - Removed accounts are soft deleted to retain audit history. - Audit events are CloudEvents-shaped, append-only application records. - `published_at` reserves an outbox-style path for later Kafka publishing. ## IP intelligence IP observations and cached classifications are modeled independently from any provider. Until a provider is configured, addresses remain `unknown`; the application must not claim that VPN checks occurred. When enabled, account creation can require a `clear` classification and record denied attempts as events. ## Event naming Events use reverse-DNS names beneath `games.minecraft.account-manager`, including: - `games.minecraft.account-manager.ui.accessed` - `games.minecraft.account-manager.auth.magic-link.consumed` - `games.minecraft.account-manager.minecraft-account.added` - `games.minecraft.account-manager.minecraft-account.removed` - `games.minecraft.account-manager.discord.nickname.updated` - `games.minecraft.account-manager.network.vpn-blocked` - `games.minecraft.account-manager.game.login.allowed` - `games.minecraft.account-manager.game.login.denied`