summaryrefslogtreecommitdiffstats
path: root/docs/adr/0001-testable-client-server-cores.md
blob: c19c01a83d46e63dcf6bc34eefb0c38549ab2ac3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 0001: Testable client and server cores

Status: Accepted (2026-10-28)

## Context

The client and server started as binaries with their game logic and I/O mixed
together. That made the TUI hard to reason about: a spin appended results below
the board, a later bet change redrew at the wrong line, and none of it could be
tested without a terminal. The server handler had the same problem: it owned
static database and session state, so its security-sensitive paths could not be
exercised from `tests/`.

## Decision

- The library (`src/lib.rs`) owns shared logic. `src/bin/client` and
  `src/bin/server` are thin adapters for crossterm, TLS, environment variables,
  and interactive prompts.
- `client::core::ClientCore` owns commands, game state, the current screen, and
  pending replies. It returns `Effect`s (`Print`, `Render(View)`, `ClearRegion`,
  `SendLine`, `Disconnect`, `Quit`). The binary applies them; the tests apply
  them to a headless screen. The prompt buffer, history and key mapping live in
  `client::input`, and `client::shell` parses a finished line, so the core never
  sees a `Key` or a raw command line.
- `server::core::ServerCore` owns message handling and receives an injected
  `Connection`, `GameRegistry`, and bcrypt cost. The binary only owns the TLS
  listener and per-connection thread.
- Tests live in `tests/` and exercise the library. Pure UI math is tested
  directly; terminal ANSI output is checked manually with `docs/TESTING.md`.
  There are no PTY tests because they cannot cover Windows and `RULES.md`
  requires identical behaviour on all platforms.
- The client keeps sending its SHA-256 credential. The server stores
  `bcrypt(credential)` and upgrades legacy SHA-256 rows on the first successful
  login. This protects a stolen server database without changing the wire
  protocol, but the client-side credential remains replayable if `client.db`
  leaks.
- Each settled spin writes a durable `plays` row in the same transaction as the
  balance updates. The in-memory session is a transient guard and is removed
  as soon as the spin settles or fails.

## Consequences

- Client flows and server security rules are testable without a terminal or a
  network. A visual bug becomes an assertion about the headless screen.
- The binaries have an extra translation layer (`Effect` application), and the
  server opens a SQLite connection per client.
- Terminal rendering still needs a manual Linux/Windows pass; the checklist is
  in `docs/TESTING.md`.
- Password storage is stronger, but not a full PAKE. A future MAJOR protocol
  change can replace the replayable credential.