- Android: MainActivity installs a WebViewClient whose onReceivedSslError
proceeds only when the server's SPKI SHA-256 is in TrustStore (pins of all
paired venues, SharedPreferences); XeniaTlsPlugin lets JS set that list.
Spike showed this one callback covers fetch/XHR, images AND WebSockets.
Pins are per key, not per address: IP changes, renewals and expiry never
need action; a different key is always refused.
- Pairing: QR key (k=) must equal the server's advertised pin, else refused;
typed addresses trust the advertised key on first use. Then the venue moves
to https://<ip>:<tls.port> (stays on HTTP if that port isn't reachable yet).
- upgradeToTls(): on every start, a plain-HTTP venue moves onto TLS once the
server offers it (never accepting a key different from the stored one).
- main.jsx pushes the venues' pins to native before the first request.
- Rediscovery made proactive: checks the saved address at start and every
minute while the live connection is down, instead of waiting for requests
to an unanswered IP to time out (minutes). HTTPS probes get 5s: the first
TLS connection in a fresh process takes ~2s (measured).
E2E on the emulator vs an isolated stack: wrong-key QR refused; pairing on
TLS; tables + wss live; impostor server with another key refused natively;
HTTP venue upgraded on start; server cert renewed (same key) - app keeps
working without re-pairing; rediscovery over TLS (11s). Step 5 HTTP
rediscovery (now 2.7s/4.8s) and cold-start login tests, and web modes, pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>