← Devlog

M0.2 — five avatars, one world

This sprint’s goal had two halves, and we only got to claim one of them honestly.

The half that’s true

Five avatars now replicate between clients over a real wss:// connection to a server that’s actually deployed and reachable from off-box, not just localhost. Every Join is credential-checked — there is no unauthenticated fallback anywhere in the path, on purpose. An unauthorized join gets refused with a real Goodbye message and a closed connection, and that refusal is now verified in production, not just in a test suite.

The half that isn’t, yet

Nobody has actually played this with a friend. Not because the network doesn’t work — it does, and five avatars have been screenshotted standing in the same world, moving. It’s because there’s still nothing to do. Watching your own avatar stand next to four scripted ones isn’t a session; it’s a network diagnostic with legs. That half of the goal is carried forward honestly rather than quietly redefined as met, because there’s no verb to play with until the next sprint builds one.

The bug that only production could find

This is the interesting part, and it’s worth writing down in full because the failure mode is instructive.

The client’s WebSocket layer depends on a TLS library that, as of the version pinned in this project, compiles fine with no cryptography backend selected at all. That backend gets resolved at run time, the first time something actually tries to perform a TLS handshake — not at compile time, and not the moment the connection object is created.

Every test in the suite before this sprint spoke plain ws://. Every one of them passed, cleanly, because a plaintext connection never reaches the code path that needs a crypto backend. cargo test was green. cargo check was clean. Nothing in the local development loop ever exercised the one line of code that mattered.

Then the client tried to speak wss:// to the real deployed server for the first time, and the process panicked on the very first handshake attempt — “no process-level CryptoProvider available.” A perfectly compiling, perfectly tested binary, dead on first contact with the one environment that actually uses TLS.

The fix was small: declare a dependency purely to switch a feature flag on, non-wasm-gated so it costs the browser client nothing. The lesson is the bigger deliverable. A TLS code path is not exercised by a test suite that only ever speaks plaintext. If a connection type only gets used against a real deployed endpoint, it needs to be smoke-tested against a real deployed endpoint — no amount of local green checkmarks substitutes for that once.