# gateway The Scrabble platform's only public ingress (module `scrabble/gateway`). It terminates the client's **Connect-RPC + FlatBuffers** traffic over HTTP/2 cleartext (`h2c`), authenticates the originating credential, mints/resolves a thin opaque session, rate-limits, injects `X-User-ID` when forwarding to the backend over REST/JSON, and bridges the backend's gRPC push stream to each client's in-app live channel. It also fronts the backend admin API behind HTTP Basic-Auth. See [`../docs/ARCHITECTURE.md`](../docs/ARCHITECTURE.md) §2, §3, §10, §12. ## Package layout ``` cmd/gateway/ # main: config -> backend client -> session cache -> # push hub -> Connect h2c server (+ admin) -> serve proto/edge/v1/ # Connect envelope contract (committed generated Go) internal/config/ # GATEWAY_* env config internal/backendclient/ # typed REST client (+ X-User-ID) and push gRPC client internal/session/ # in-memory session cache (LRU/TTL, backend fallback) internal/ratelimit/ # token-bucket limiter (golang.org/x/time/rate) internal/auth/ # Telegram initData HMAC validator (seam + fixtures) internal/push/ # live-event fan-out hub (per-user client streams) internal/transcode/ # FlatBuffers<->REST bridge + message_type registry internal/connectsrv/ # the Connect Gateway service over h2c internal/admin/ # Basic-Auth reverse proxy to the backend admin API ``` The FlatBuffers payloads and the backend push proto are the shared wire contracts in [`../pkg`](../pkg). ## Transport contract A single `Gateway` Connect service: `Execute(message_type, payload, request_id)` for unary operations and `Subscribe` for the live stream. The `payload` bytes are FlatBuffers tables (`scrabble/pkg/fbs`); the gateway transcodes them to and from the backend's JSON. The session token rides in `Authorization: Bearer`; `auth.*` operations are unauthenticated and return the minted token. A unary domain outcome rides back in `ExecuteResponse.result_code` (HTTP 200); only edge failures become Connect error codes. The Stage 6 message-type slice: `auth.telegram`, `auth.guest`, `auth.email.request`, `auth.email.login`, `profile.get`, `game.submit_play`, `game.state`, `lobby.enqueue`, `lobby.poll`, `chat.post`; live events `your_turn`, `opponent_moved`, `chat_message`, `nudge`, `match_found`. Stage 7 added the play-loop ops; **Stage 8** added the social/account/history ops — `friends.*` (list/incoming/request/respond/cancel/unfriend/code.issue/code.redeem), `blocks.*`, `invitation.*` (list/create/accept/decline/cancel), `profile.update`, `email.bind.*`, `stats.get`, `game.gcg`, and the `notify` live event — all via the identical transcode pattern (`transcode_social.go`). ## Configuration | Variable | Default | Notes | | --- | --- | --- | | `GATEWAY_HTTP_ADDR` | `:8081` | public Connect/h2c listener | | `GATEWAY_ADMIN_ADDR` | `:8082` | admin proxy listener (enabled only with creds) | | `GATEWAY_LOG_LEVEL` | `info` | zap level | | `GATEWAY_BACKEND_HTTP_URL` | `http://localhost:8080` | backend REST base URL | | `GATEWAY_BACKEND_GRPC_ADDR` | `localhost:9090` | backend push gRPC address | | `GATEWAY_BACKEND_TIMEOUT` | `5s` | per backend REST call | | `GATEWAY_ADMIN_USER` / `GATEWAY_ADMIN_PASSWORD` | unset | enable + guard the admin proxy | | `GATEWAY_TELEGRAM_BOT_TOKEN` | unset | enable the Telegram auth path | | `GATEWAY_SESSION_TTL` | `10m` | cached session lifetime | | `GATEWAY_SESSION_CACHE_MAX` | `50000` | cached session cap | | `GATEWAY_PUSH_HEARTBEAT_INTERVAL` | `15s` | live-stream keep-alive | Rate-limit defaults (built-in): public 30/min·IP (burst 10), authenticated 120/min·user (burst 40), admin 60/min·IP (burst 20), email-code 5/10 min·IP. ## Run ```sh GATEWAY_BACKEND_HTTP_URL=http://localhost:8080 \ GATEWAY_BACKEND_GRPC_ADDR=localhost:9090 \ go run ./gateway/cmd/gateway # Connect edge on :8081 ``` ## Generated code The Connect envelope Go is committed under `proto/edge/v1`. Regenerate after editing the `.proto` (dev-time, like `backend/cmd/jetgen`): ```sh make -C gateway tools # go install protoc-gen-go + protoc-gen-connect-go make -C gateway gen # buf generate (local plugins) ``` The FlatBuffers payloads are generated in [`../pkg`](../pkg) (`make -C pkg fbs`). ## Tests ```sh go test -count=1 ./gateway/... ``` All gateway tests are hermetic: no real network, a fake backend (`httptest`) and credential fixtures. There is no integration (Docker) suite — the gateway holds no database.