jonnify·codemojex /codemojex/the-live-surface

Codemojex · chapter C7

The live surface on Phoenix.

The surface is a thin Phoenix application, deliberately small. Every call goes through the Codemojex facade and the privacy-safe views; a guess returns accepted-and-on-its-way while the bus carries the scoring; and there is no process per room, so a large field of idle rooms costs nothing. The chapter opens at the seam cm.4 closed — identity is verified, never trusted: a signed Telegram initData is exchanged once for a shared SES session, and every later request and the socket present it.

Grounding — echo/…/codemojex_web/{router,auth,mini_app_auth,channels/*}.ex · init_data.ex · session.ex · view.ex · the cm.4 auth spec · design §The web surface / §Privacy and fairness · the privacy story.

One mint, many reads — select a surface
SES in Valkey :cm_sessions :tracking · sliding TTL verified initData HMAC-SHA-256 · key WebAppData ① mint (sole writer) Bearer <SES> the :auth plug ② resolve + slide conn.assigns.player the only identity read socket connect param session · off the query string ③ connect · or refuse assign :player a non-nil socket id/1
the handshake → POST /api/auth/:platform is the SOLE SES writer. Codemojex.InitData.verify checks the initData signature — HMAC-SHA-256 over the bot token keyed by the constant WebAppData, compared constant-time — then resolve_player_by_tg maps the verified tg_user_id to a PLR and Session.mint writes one SES row to Valkey. Trust is in the signature, not a session.
§1

Verified, never trusted

router.ex · the two /api scopes

The surface has exactly two trust tiers, and they are drawn in the router. Open routes — health, the handshake, the room lobby, a game view, the leaderboard — trust no caller-supplied identity and stay on the :api pipeline. Player-acting routes — join, guess, history, and the two key operations — add the :auth plug, which turns Authorization: Bearer <SES> into conn.assigns.player. The old POST /api/players — a free player mint — is retired; a player now exists only behind a verified handshake.

echo/…/codemojex_web/router.ex · the trust boundary source
# Open JSON routes — trust no caller-supplied identity.
scope "/api", CodemojexWeb do
  pipe_through :api

  get "/health", GameController, :health
  post "/auth/:platform", AuthController, :handshake   # the SOLE SES mint
  get "/rooms", GameController, :rooms
  get "/games/:id", GameController, :game
  get "/games/:id/leaderboard", GameController, :leaderboard
end

# Player-acting JSON routes — gated by the :auth plug.
scope "/api", CodemojexWeb do
  pipe_through [:api, :auth]

  post "/rooms/:id/join", GameController, :join
  post "/games/:id/guess", GameController, :guess
  get "/games/:id/history", GameController, :history
  post "/keys/buy", GameController, :buy_keys
  post "/keys/convert", GameController, :convert
end

The :api pipeline only accepts JSON; :auth adds one plug, CodemojexWeb.Auth, that reads the bearer, resolves the SES, and assigns the player — or halts with 401. Nothing downstream reads a caller-supplied id. The browser LiveView surface reaches the same session a different way (the signed Plug session), but the JSON API and the socket are pure bearer + connect-param.

§2

The three dives

C7.1 · C7.2 · C7.3

The order is the request’s: first the floor that mints identity (C7.1), then the JSON lifecycle every call rides (C7.2), then the live half that pushes results back without a per-room process (C7.3). The surface stays thin throughout; the engine is the chapters behind it.

§3

The doors

out of this chapter
§4

References

sources · related

Sources

Related