Owner review-pass rework of the landing page: - Rename the per-language Telegram link build var VITE_TELEGRAM_LINK_EN/_RU -> VITE_TELEGRAM_GAME_CHANNEL_NAME_EN/_RU (it carries a channel username; the landing builds https://t.me/<name> -- the same channels the connector posts to via TELEGRAM_GAME_CHANNEL_ID_*). - Language switcher -> a globe icon dropdown (flags + names), saved + synced to the app prefs. - Theme switcher -> a sun/moon icon toggle, ephemeral (follows the system scheme, no auto, never persisted) -- galaxy-game style. - Drop the "Play in browser" CTA (no standalone-web onboarding yet). Docs: FUNCTIONAL(+ru), PLAN, deploy + ui READMEs.
scrabble-game
Multiplatform Scrabble game. Players arrive from a platform (Telegram first; later VK/MAX/iOS/Android) or from standalone web (email / guest). The game supports English Scrabble, Russian Scrabble and Эрудит.
Components
gateway— the only public ingress: anti-abuse, platform authentication (resolves the player and injectsX-User-ID), routing tobackend, and an admin surface behind Basic Auth. (added in a later stage)backend— internal-only service that owns every domain concern and embeds thescrabble-solverengine library in-process.ui— pure-HTML5 client (plain Svelte 5 + TypeScript + Vite) over Connect-RPC- FlatBuffers, embeddable in platform webviews and packageable to native via
Capacitor. See
ui/README.md.
- FlatBuffers, embeddable in platform webviews and packageable to native via
Capacitor. See
platform/*— per-platform side-services (e.g. the Telegram bot). (added in a later stage)
Documentation (sources of truth)
docs/ARCHITECTURE.md— global architecture, transport, security, cross-service contracts.docs/FUNCTIONAL.md(+_ru) — per-domain user stories.docs/TESTING.md— test layers and the per-stage CI gate.PLAN.md— the staged implementation plan and stage tracker.CLAUDE.md— project guide and the mandatory per-stage workflow.
Build & test
go build ./backend/... ./pkg/... ./gateway/... # per module (the workspace spans several)
go vet ./backend/... ./pkg/... ./gateway/...
gofmt -l . # must print nothing
go test -count=1 ./backend/... ./pkg/... ./gateway/... # unit tests
go test -tags=integration -count=1 -p=1 ./backend/... # + Postgres (needs Docker)
The integration-tagged tests start a throwaway postgres:17-alpine container
via testcontainers-go and require a reachable Docker daemon; they live in the
backend module. The wire contracts in pkg and the Connect edge in gateway
have committed generated code (regenerate dev-time with make -C pkg gen /
make -C gateway gen).
Run the backend locally
The backend now owns persistence, so it needs Postgres and applies its embedded migrations at startup:
docker run -d --name scrabble-pg -e POSTGRES_PASSWORD=dev -p 5432:5432 postgres:17-alpine
BACKEND_POSTGRES_DSN='postgres://postgres:dev@localhost:5432/postgres?search_path=backend&sslmode=disable' \
go run ./backend/cmd/backend # HTTP API + probes on :8080, push gRPC on :9090
Run the gateway locally
The gateway is the public edge; point it at a running backend:
GATEWAY_BACKEND_HTTP_URL=http://localhost:8080 \
GATEWAY_BACKEND_GRPC_ADDR=localhost:9090 \
go run ./gateway/cmd/gateway # Connect/h2c edge on :8081
Key environment: BACKEND_HTTP_ADDR (default :8080), BACKEND_LOG_LEVEL
(debug|info|warn|error, default info), BACKEND_POSTGRES_DSN (required).
The full configuration surface and the go-jet regeneration step live in
backend/README.md.
Run the UI locally
cd ui && pnpm install
pnpm start # mock mode: lobby -> game with no backend, on http://localhost:5173
pnpm dev # against a running gateway (Vite proxies the RPC path to :8081)
pnpm check (type-check), pnpm test:unit (Vitest), pnpm test:e2e (Playwright
smoke vs the mock), pnpm build (static bundle). Details — including the committed
edge codegen (pnpm codegen) — are in ui/README.md.
Deploy (deploy/)
The full contour is deploy/docker-compose.yml:
backend + gateway (with the UI embedded via go:embed, baked in by its node
build stage) + Postgres + the Telegram connector (with a VPN sidecar) + an
observability stack (OTel Collector → Prometheus + Tempo → Grafana) + a front
caddy that owns a single /_gm Basic-Auth (admin console + Grafana). The Go
services build from multi-stage distroless */Dockerfile.
docker build -f backend/Dockerfile -t scrabble-backend . # pulls the DAWG release artifact
docker build -f gateway/Dockerfile -t scrabble-gateway . # node stage builds + embeds the UI
docker compose -f deploy/docker-compose.yml config # validate (needs the TEST_/PROD_ env)
CI auto-deploys the test contour on a PR into — or push to — development
(.gitea/workflows/ci.yaml); the prod contour is a manual deploy after
development → master (Stage 18). Env reference: deploy/.env.example;
the topology and the two-contour model are in
docs/ARCHITECTURE.md §13.