Stage 10: admin console & dictionary ops (complaint review, hot-reload, broadcasts) (#11)
This commit was merged in pull request #11.
This commit is contained in:
+23
-14
@@ -14,8 +14,9 @@ Three executables plus per-platform side-services:
|
||||
- **`gateway`** — the only public ingress (module `scrabble/gateway`). Performs
|
||||
anti-abuse (rate limiting), authenticates the player against the originating
|
||||
platform (or an email/guest session), resolves the internal `user_id`, and
|
||||
forwards authenticated traffic to `backend` with an `X-User-ID` header. Hosts an
|
||||
admin surface behind HTTP Basic Auth. Bridges live events from `backend` to the
|
||||
forwards authenticated traffic to `backend` with an `X-User-ID` header. Serves the
|
||||
backend's admin console at `/_gm` on its public listener behind HTTP Basic Auth.
|
||||
Bridges live events from `backend` to the
|
||||
client. The shared wire contracts (the push proto and the FlatBuffers edge
|
||||
payloads) live in `scrabble/pkg`, imported by both `gateway` and `backend`.
|
||||
- **`backend`** — internal-only service that owns every domain concern:
|
||||
@@ -47,7 +48,7 @@ Three executables plus per-platform side-services:
|
||||
`scrabble/platform/telegram`). It is the only component holding the bot token: it
|
||||
runs the Bot API long-poll loop (Mini App launch + `/start` deep-links) and serves
|
||||
a gRPC API (`pkg/proto/telegram/v1`) that `gateway` (Mini App initData validation
|
||||
and out-of-app push) and `backend` (admin messaging — Stage 10) call over the
|
||||
and out-of-app push) and `backend` (operator broadcasts) call over the
|
||||
trusted internal network. Its generic delivery methods are **platform-agnostic**
|
||||
(keyed by the identity `external_id`), so a future VK/MAX connector reuses them; only
|
||||
initData validation is Telegram-specific. It runs in its own container, egressing to
|
||||
@@ -62,7 +63,7 @@ flowchart LR
|
||||
Backend -- pgx --> Postgres[(Postgres)]
|
||||
Backend -. embeds .- Solver[[scrabble-solver library]]
|
||||
Gateway -- gRPC (validate initData, out-of-app push) --> Telegram[Telegram connector]
|
||||
Backend -. admin gRPC, Stage 10 .-> Telegram
|
||||
Backend -. operator broadcasts (gRPC) .-> Telegram
|
||||
Telegram -- Bot API (via VPN sidecar) --> TgCloud((Telegram))
|
||||
```
|
||||
|
||||
@@ -166,11 +167,15 @@ Key points:
|
||||
word-check tool through `Registry.Lookup`.
|
||||
- **Dictionary versioning — pin per game.** A game records the `dict_version` it
|
||||
started on and finishes on that version; new games use the latest. Multiple
|
||||
versions may be resident at once. An admin reload *(planned, Stage 10)*
|
||||
registers a new version through `Registry.Load`; delivery is the DAWG file in
|
||||
the image / a volume mounted at the dictionary directory. (A future split of
|
||||
the solver into engine + dictionary generator with versioned artifacts is
|
||||
recorded in [`../PLAN.md`](../PLAN.md) TODO-2.)
|
||||
versions may be resident at once. The boot version loads from the flat
|
||||
`BACKEND_DICT_DIR`; the admin console **hot-reloads** a new version from a
|
||||
per-version subdirectory `BACKEND_DICT_DIR/<version>/` through
|
||||
`Registry.LoadAvailable` (only the variants whose DAWG is present there), and a
|
||||
restart re-loads every resident version via `engine.OpenWithVersions` (the flat
|
||||
boot version plus each subdirectory). In-flight games keep their pinned version;
|
||||
new games use the latest. (A future split of the solver into engine + dictionary
|
||||
generator with versioned artifacts is recorded in [`../PLAN.md`](../PLAN.md)
|
||||
TODO-2.)
|
||||
- Move generation/validation/scoring use `Solver.GenerateMoves` (ranked),
|
||||
`Solver.ValidatePlay` and `Solver.ScorePlay`; board mutation uses
|
||||
`scrabble.Apply`. The engine adds its own deterministic, seeded tile **bag**
|
||||
@@ -231,8 +236,11 @@ Key points:
|
||||
"no options" rather than "no hints left".
|
||||
- **Word-check tool**: unlimited dictionary lookups against the game's pinned
|
||||
dictionary; each result offers a **complaint** (complainant, game, variant,
|
||||
dict_version, word, the disputed result, an optional note) that lands in an
|
||||
admin review queue *(admin side planned, Stage 10)*.
|
||||
dict_version, word, the disputed result, an optional note) that lands in the admin
|
||||
review queue. An operator resolves it (`open → resolved`) with a **disposition** —
|
||||
reject, accept-add or accept-remove; the accepted ones form a derived
|
||||
**pending-changes** list that feeds the offline dictionary rebuild and is marked
|
||||
applied once the rebuilt version is hot-reloaded (§5, §12).
|
||||
|
||||
## 7. Robot opponent
|
||||
|
||||
@@ -439,7 +447,7 @@ promotions) is future work and would deliver short markdown messages (text + lin
|
||||
| Session minting; email-code / guest validation | gateway (with backend) |
|
||||
| Session → `user_id` resolution, `X-User-ID` injection | gateway |
|
||||
| Authorisation, ownership, state transitions | backend (`X-User-ID` is the sole identity input) |
|
||||
| Admin authentication | gateway validates HTTP Basic Auth (`GATEWAY_ADMIN_*`), then reverse-proxies to backend admin endpoints |
|
||||
| Admin authentication | gateway validates HTTP Basic Auth (`GATEWAY_ADMIN_*`) on the public `/_gm/*` path and reverse-proxies it **verbatim** to the backend's server-rendered admin console; the backend trusts the gateway (no admin principal) and guards its state-changing POSTs with a **same-origin** check — the console's CSRF defence. No operator identity is tracked |
|
||||
| backend ↔ gateway ↔ connector trust | the network (only gateway may reach backend; the connector serves unauthenticated gRPC on the internal segment) |
|
||||
|
||||
This is an explicit, accepted MVP risk: compromise of the gateway↔backend
|
||||
@@ -459,8 +467,9 @@ a dedicated redeem sub-limit or a longer code is the hardening step if abuse app
|
||||
|
||||
Single public origin, path-routed: a mini-landing at the root, the **Telegram Mini
|
||||
App under `/telegram/`** (the gateway serves the static UI build; outside Telegram
|
||||
that path redirects to the root), the gateway public surface and the admin surface
|
||||
share one host that terminates TLS. The **Telegram connector** runs as a separate
|
||||
that path redirects to the root), the gateway public surface and the **admin console
|
||||
at `/_gm`** (backend-rendered, Basic-Auth at the gateway) share one host that
|
||||
terminates TLS. The **Telegram connector** runs as a separate
|
||||
container with **no public ingress** — it long-polls Telegram and egresses through a
|
||||
VPN sidecar, answering only internal gRPC. MVP runs one `gateway`, one `backend`, one
|
||||
Postgres, plus the connector. The connector's Docker/compose ships now
|
||||
|
||||
Reference in New Issue
Block a user