Commit graph

4 commits

Author SHA1 Message Date
ae3e4316fb
web: stamp each zebra-report page with build date + md5/sha256
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.
2026-05-28 17:50:12 -04:00
7c39f3537a
test: node web protocol suite (no devices) + exponential retransmit backoff
Add test/web-protocol.test.js — runs the real protocol code from chat.html in
Node (unit: crc/frame/ACK/HELLO codec, Hamming, Gray; integration: multi-level
modem roundtrip + FEC recovery of off-by-one symbol errors; functional: full
frame -> modem -> FEC -> assembler -> parse + ACK roundtrip). 3348 assertions.
Wired as `make test-web` (also in test-all). Lets us QA the modem without two
devices. Also: retransmit now uses exponential backoff so a lost ACK spaces out
retries instead of hammering the channel.
2026-05-28 12:44:20 -04:00
c8a755513f add zebrad: pulseaudio→websocket introspector for chat.html
single C file (~620 LOC), no third-party deps beyond libpulse.
closes a receive loop for web/chat.html: a browser tab cannot read
another tab's PulseAudio state, so each peer runs zebrad locally
to forward decoded frames to ws://127.0.0.1:7777.

components inline:
  * SHA-1 (RFC 3174) + base64 encode — for the WebSocket handshake;
    verified against the canonical RFC 6455 example
    (key dGhlIHNhbXBsZSBub25jZQ== → accept s3pPLMBiTxaQ9kYGzzhZRbK+xOo=)
  * CRC-32 (IEEE 802.3) — verifies DATA + HELLO frame payloads
  * UART symbol decoder with adaptive threshold:
    running peak with 2-second half-life decay, threshold = 50% of peak,
    falling-edge start detect, sample at 1.5..8.5 × sps from edge
  * frame assembler: magic-sync ZB, type dispatch OFFER/READY/DATA/HELLO,
    overflow + corrupt-length guards, resync on bad checksum
  * embedded WebSocket server: TCP listener on 127.0.0.1:7777,
    handshake responder, binary frames only (opcode 0x82), no masking
    (server→client), any inbound data closes the client (browser
    auto-reconnects); max 16 concurrent clients
  * pulseaudio: pa_mainloop integration, record stream on @DEFAULT_MONITOR@
    by default, PA_SAMPLE_FLOAT32LE mono @ 8 kHz, 10 ms fragsize, peak
    energy extracted from |abs(sample)|

adaptive baud: starts at ZEBRA_BAUD_HANDSHAKE (50). On valid READY frame
locks in negotiated baud from payload. Matches the chat.html send path.

binds 127.0.0.1 only — never reachable from the LAN, even though the
chat.html page itself may be served over HTTPS from a public domain.

smoke tested: PA connect + stream ready + WS handshake (RFC example
verified) + clean shutdown. -Wall -Wextra clean.

Makefile: zebrad now part of `make all`; clean target updated.
README: new section documenting it.
2026-05-27 15:57:22 -04:00
b77da42bbe phase 1: unfirehose reconstruction from session JSONL ingest
Source: ~/.unfirehose/unfirehose.db (project_id=81, 4 sessions covering
2026-03-29 through 2026-04-05). Reconstructed via chronological replay
of Write/Edit tool_input on file_paths under /home/fox/zebra-report/.

stats:
  files reconstructed:    20
  writes baselined:       all (zero missing)
  edits applied:          68
  edits unapplied:        8 (1 SKIP pre-baseline, 6 FAIL old_string drift, 1 AMBIGUOUS)

unapplied edits represent small drift in 6 files; baseline content for
each is intact. quality verification deferred to phase 2.

recovered tree:
  CLAUDE.md, Makefile
  src/{tx,rx,pulse,carrier,chat,bt}.c
  include/{modem,zebra}.h
  test/{functional,integration,unit}.c, test/test.h
  web/{index,kernel}.html, web/blog/style.css
  blog/build.py, blog/posts/{001-volume-modem,002-sse-chatroom}.md

report: /tmp/zebra_recover_report.txt
script: /tmp/zebra_recover.py
2026-05-27 13:51:14 -04:00