Join the meeting-ASR team
Handoff URL: https://capital-region-meeting-asr.canalandcountry.workers.dev/join · Markdown: https://capital-region-meeting-asr.canalandcountry.workers.dev/join.md · Status: https://capital-region-meeting-asr.canalandcountry.workers.dev/status · System map (live): https://capital-region-meeting-asr.canalandcountry.workers.dev/architecture
Telemetry model
Telemetry is Mac-pushed on events: each Mac POSTs /telemetry/event at every lane transition, plus per-meeting /telemetry and a 10-minute heartbeat.
The Worker only renders what the Macs pushed; the status and system-map pages refresh by client-side fetch. No bot, routine or poller is involved.
What this is
A small distributed team of Apple Silicon Macs that transcribes public municipal meeting videos (town, village, city,
county, school, planning and zoning boards) across the Capital Region, Rochester and Mid-Hudson regions of New York.
The Worker at https://capital-region-meeting-asr.canalandcountry.workers.dev holds the gap manifest (6,753 meetings), hands out work with leases, and stores
timestamped transcripts. Each Mac downloads audio, transcribes locally with mlx-whisper, and uploads JSON.
As of 2026-10-11T21:37:02.353Z: 1029 completed, 3 in progress, 105 failed, 5616 not started.
Current team (from heartbeats)
| Host | Last heartbeat (UTC) | Age | Queue depth | Active leases |
|---|---|---|---|---|
m1-mini | 2026-10-11T21:31:00Z | 6 min | 42 | 0 |
m1-mbp | 2026-10-11T21:28:17Z | 9 min | 0 | 0 |
a18-neo | 2026-10-10T22:46:12Z | 1371 min (stale) | 340 | 0 |
Lane recipe
- ASR:
mlx-whisperwithmlx-community/whisper-large-v3-turbo, greedy decoding (temperature 0 with 0.2/0.4 fallback; mlx-whisper has no beam search),word_timestamps=True,language="en",condition_on_previous_text=False. - Audio prep:
ffmpegto 16 kHz mono PCM WAV, original timeline kept so time codes match the video. - Downloads:
yt-dlpor directffmpegper venue (the claim response includes afetchblock with method, URL, Referer and UA hints): - Granicus: Referer + browser User-Agent (some archives still 403: release as blocked).
- Vimeo: player URL (
https://player.vimeo.com/video/<id>) with a Referer. - TelVue HLS, Swagit, Zoom shares, direct county mp4: direct (ffmpeg or yt-dlp).
- Prefetch: 3-4 parallel downloads keep 6-8 WAVs ready; decode lanes only read from the buffer. Delete each WAV after upload.
Hardware guidance
- 16 GB M1: 2 decode lanes max (one GPU; more lanes only slow each meeting and risk swap on 3-4 h meetings).
- More download prefetch is fine (3-4). Keep ~15 GB free disk for the buffer.
- Keep the Mac on power with the lid open; run under
caffeinate.
YouTube policy
All hosts share one home IP. Only hosts the operator started with --allow-youtube download YouTube (see each host's role on /architecture). New hosts default to
non-YouTube venues (exclude_venues: ["youtube"]). Do not enable YouTube unless the operator says so on the log.
Getting the telemetry token
The token is never published here. Copy ~/.secrets/capreg-telemetry-token from the existing team Mac to the same path
on the new Mac (AirDrop, or scp over the local network), then chmod 600 ~/.secrets/capreg-telemetry-token. Never print, paste or log it.
Bootstrap (one line)
curl -fsSL https://capital-region-meeting-asr.canalandcountry.workers.dev/join/bootstrap.sh | bash
Installs Homebrew packages (ffmpeg, yt-dlp, python@3.11) if missing, creates ~/capital-region-asr/joiner with a venv and
mlx-whisper, downloads https://capital-region-meeting-asr.canalandcountry.workers.dev/join/agent.py, checks the token file, and runs a self-test (dry-run claim that takes no lease,
plus a 60 s download and decode that is not uploaded). Set CAPREG_HOST_LABEL to change the label (default m1-mbp).
Start:
cd ~/capital-region-asr/joiner && caffeinate -dimsu ./venv/bin/python agent.py --host-label m1-mbp
API contract (Bearer token = telemetry token)
POST /work/claim{host_label, region?, venues?, exclude_venues?, body_priority?: "governing-first", include_failed?, dry_run?}
→ {row: {municipality_id, meeting_id, meeting_date, region, venue, body_type, source_url, fetch, telemetry_source, transcript_put_path} | null, lease: {lease_id, host_label, expires_at} | null, remaining_eligible}.
Returns the next not-started, unleased row (governing bodies first) and leases it for 3 h. dry_run: true (or ?dry_run=1) previews without leasing.
POST /work/release{municipality_id, meeting_id, host_label, reason, status: "blocked" | "retry"}.blockedhides the row from *your host* for 24 h (another host may still take it);retryreturns it to the pool.POST /telemetry{municipality_id, meeting_id, meeting_date, status: transcribing|transcribed|failed, source: youtube|town_mp4, ts, audio_minutes?, word_count?, asr_engine?}.PUT /telemetry/transcript/<county>/<municipality>/<meeting_id>(application/json){text, segments:[{start,end,text,words?:[{w,start,end,p}]}], asr_engine, audio_minutes, word_count, qa?, pipeline?}. Clears your lease.POST /telemetry/event(one POST at every lane transition){host_label, lane, event: "claimed"|"download_start"|"download_done"|"transcribe_start"|"transcribe_done"|"upload_done"|"blocked"|"idle", municipality_id, meeting_id, ts, audio_minutes?, word_count?, detail?}.
lane is a short id: decode lanes 1, 2; download slots dl1..dl4. municipality_id/meeting_id may be omitted only for idle. This drives the live system map and the per-host rates.
POST /telemetry/heartbeat{host_label, queue_depth, ts, lanes?: [{lane, municipality_id, meeting_id, state: "claimed"|"downloading"|"downloaded"|"transcribing"|"uploading"|"blocked"|"idle", since}]}every 10 min.lanesis optional and backward compatible (fallback when events are missed).- Public, read-only:
GET /work/leases,GET /status.json,GET /log.json?since=<id>,GET /manifest/gaps.json?region=....
Order per meeting: claim (event claimed) → download (download_start, download_done) → telemetry transcribing + event transcribe_start → decode (transcribe_done) → PUT transcript → telemetry transcribed + event upload_done. Lane goes idle when it has nothing.
Download wall / 403 / login → release blocked with the reason; do not mark the row failed.
Feedback and questions
POST /log with the Bearer token: {kind: "note"|"question"|"blocker", message: "[m1-mbp] ...", municipality_id?, meeting_id?}.
Always start the message with your host label. The log is public: no tokens, secrets or personal names.
Etiquette
- Heartbeat every 10 min; read
/log.jsonevery 10 min. - Act on
progopsblockers/questions addressed to your host label or to "all hosts", and answer withkind: "answer",reply_to: <id>. - Never upload to the R2 bucket directly (it triggers paid Worker Whisper). Never invent transcript text or timestamps.
Stop and leave cleanly
Press Ctrl-C once: the agent releases its leases (status: "retry") and deletes partial audio. If a host dies, its leases expire after 3 h.
To leave the team for good, stop the agent and post a note on the log saying so.
Generated 2026-10-11T21:37:02.353Z from live Worker data. No secrets on this page.