New page: web/zebra-spaces.html. Extends the rendezvous + WebRTC mesh
model from zebra-audio (1:1) to a room of up to ~8 speakers (host + 2
co-hosts + speakers) with listeners (deferred to v0.2 for SFU fan-out).
Identity: per-browser persistent Ed25519 keypair in localStorage,
plus a per-session UUID for in-room "whose who". Password-vault
backup/restore (PBKDF2 600k + AES-GCM, matches zebra-audio's crypto)
emits a self-contained 'zspc-id-v1|...' blob.
Roles: everyone joins as listener; mods (host + co-hosts) extend
mic-invites that listeners accept/decline; host promotes to co-host;
mods demote and boot. Co-hosts cannot remove the host. Every role
transition is signed by the acting mod's Ed25519 over a canonical
input bound to room_id + epoch, so a compromised relay cannot forge
promotions, only refuse to relay them.
Pairs with cmd/zebra-spaces-signal in proxy.unturf.com.
Nav: zebra-audio and host-your-own cross-link to spaces.
Makefile: stamp target now covers zebra-spaces.html (web/chat.html
and web/how-it-works.html stamps refreshed today as a side effect).
Add web/host-your-own.html and link it from the chat and how-it-works headers
and footers. make stamp now stamps it alongside the other pages. CLAUDE.md:
turn the deploy flow into an explicit push-BOTH-repos reminder (zebra-report
source + www.unturf.com served) and list host-your-own.html as a deployed page.
- chat + zebra-audio: treat ICE 'disconnected' as recoverable (grace
before acting), auto ICE-restart on 'failed'/timeout driven by the
offerer to avoid glare, and auto-rejoin the signaling socket if it
drops mid-call. Superseded-pc guard ignores late events from a
closed connection.
- zebra-audio: input-device dropdown that hot-swaps the mic via
replaceTrack (no renegotiation, mute preserved); refreshes on
devicechange so a plugged-in interface appears mid-call.
Add an integrity footer to chat.html, zebra-audio.html, and how-it-works.html
showing the build date (2026-05-28) and the page's own MD5 + SHA-256. A file
can't hold its own hash, so web/stamp.js (make stamp) computes the hashes with
the two hash fields zeroed, then writes the real values back — self-consistent
and idempotent. To verify a served page: blank the two fields and re-hash;
confirmed it reproduces the stamped value with plain sha256sum.
Standalone blog-style page (matching chunkfive/monospace b&w UX) explaining the
whole solution: amplitude-as-data, the no-copy-paste signaling relay, the
multi-level modem, the Opus->G.711 codec fix and 440 Hz beat, the ACK/outbox
reliability layer, and the Hamming(12,8)+Gray FEC. Linked from chat.html's
header.