From 058432130628d00764663ecf819a9adc6793768d Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 8 Nov 2026 03:29:35 +0100 Subject: v5.0.0: protocol v5 and the browser frontend Protocol v5 replaces newline lines with bounded [u32 len][text] frames and REQ/RES/EVT envelopes. Requests carry correlation ids and the server replays the last 256 replies per connection, so a gateway retry cannot double-debit and a late reply cannot clobber a newer request. HELLO/PONG carry server time; crash rounds announce started_at and push coalesced 10 Hz ticks. Crash now commits sha256(seed) before a round and reveals the seed when it ends, so clients recompute the crash point. Adds the webd/ gateway: one blocking thread, one ClientCore and one pinned upstream TLS connection per session, an HttpOnly session cookie, takeover, and a 60-second grace window. The browser sends Command JSON and renders View JSON; it never speaks the game protocol or stores credentials. Adds the vanilla TypeScript browser app at /play/ (login, lobby, all four games, history, notices, crash verification), the nginx /play and /ws routes, and the headless DOM smoke test. webd and the app stay out of the TUI/GUI/server build graph. The client/server version moves to 5.0.0 in lockstep; v4 peers are refused at HELLO. --- webd/src/main.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 webd/src/main.rs (limited to 'webd/src/main.rs') diff --git a/webd/src/main.rs b/webd/src/main.rs new file mode 100644 index 0000000..c2cf0ff --- /dev/null +++ b/webd/src/main.rs @@ -0,0 +1,61 @@ +//! Browser gateway for zagan-bet. +//! +//! nginx terminates TLS, serves the static app and proxies `/ws` here. This +//! process owns one upstream TLS connection and one `ClientCore` per browser +//! session, so the browser never speaks the game wire protocol and never sees +//! a certificate. + +mod config; +mod session; + +use std::net::TcpListener; +use std::sync::Arc; +use std::thread; + +use config::Config; + +fn main() { + let config = match Config::from_env() { + Ok(config) => config, + Err(error) => { + eprintln!("webd: {error}"); + std::process::exit(2); + } + }; + + let listener = match TcpListener::bind(&config.bind) { + Ok(listener) => listener, + Err(error) => { + eprintln!("webd: could not bind {}: {error}", config.bind); + std::process::exit(1); + } + }; + + println!( + "webd listening on ws://{} for origin {}; upstream {} pinned to {}; grace {}s", + config.bind, + config.origin, + config.upstream, + config.fingerprint, + config.grace.as_secs() + ); + + let registry = session::new_registry(); + for stream in listener.incoming() { + let stream = match stream { + Ok(stream) => stream, + Err(error) => { + eprintln!("webd: accept failed: {error}"); + continue; + } + }; + let _ = stream.set_nodelay(true); + let config = config.clone(); + let registry = Arc::clone(®istry); + thread::spawn(move || { + if let Err(error) = session::accept(stream, config, registry) { + eprintln!("webd: {error}"); + } + }); + } +} -- cgit v1.2.3