PRTOTYPE.COM · Production studioAudit · Build · Maintain
PRoTOTYPE.COM
◉
Pendant Rig
Case study · Local build

The cloud died. The recordings didn’t.

Pendant Rig drives a Limitless Pendant whose cloud service ended and a PLAUD NotePin whose app deletes what it syncs — entirely from one Windows PC. BLE capture lands in append-only journals, a fail-closed codec decodes it, and faster-whisper transcribes on the local GPU at roughly 25× realtime. No cloud, no accounts.

Type
Offline capture · Python
Backend
BLE · journals · faster-whisper
Status
Prototype · local
900 tests hold the line
~25× realtime transcription
No cloud · no accounts
127.0.0.1:8737
Pendant Rig dashboard: device cards, live sync stepper and today's recordings
The brief

Own the hardware, keep the transcripts.

The Limitless Pendant’s cloud no longer exists: Meta acquired Limitless, the UK service ended on 5 December 2025, and accounts in unsupported regions were deleted on 19 December 2025. The hardware still records; the upload destination vanished.

The PLAUD NotePin takes the other road to the same cliff: a required app account whose sync deletes recordings from the device, 300 free minutes a month, then $17.99–$29.99/month — on hardware that sells at $159 (GBP 125).

The rig’s premise: replace both vendors with one Windows PC. Wear the pendant or pocket the NotePin; when Bluetooth sees a device, the rig syncs by itself — each device at most every 60 minutes, plus a 03:00 sweep — pages land in the journal first, and only after a verified flush does the ACK free device flash. The GPU transcribes and files audio and transcript under the day, playable from the web app.

Proven moments: a spoken “baseline test, orange bicycle, 42” came back verbatim, and the first drain pulled 3,381 pages — about 86 minutes of audio — off a pendant whose cloud no longer existed. The rig has run nightly since 25 September 2026.

“Own the hardware outright, keep the transcripts forever, never create an account. The running cost is the electricity.”
900
Tests in the gate at HEAD
~25×
Realtime transcription · RTX 3050 fp16
2
Device families, one rig
112/114
PLAUD commands blocked at the allowlist
8×
Smaller audio · 1,246 MB → 156 MB
The product

A dashboard that reads like an instrument panel.

This is the rig’s real UI: a dark, terminal-adjacent panel on FastAPI and vanilla JS, served at 127.0.0.1:8737 to this PC only. A tour of what an operator actually sees.

127.0.0.1:8737 · devices
Device cards for the pendant and NotePin
01 / Device cards

Both devices, one dashboard.

Every paired device reports to one row of cards: battery level, charging state, pairing status, free space and the last successful sync. A pendant and a NotePin sit side by side — the NotePin added by nothing more than its serial number, with no vendor account anywhere.

Battery · free space · last syncNotePin added by serialUnified scan
127.0.0.1:8737 · live sync
Sync stepper mid-run: Copy, Prepare, Transcribe, Save
02 / The sync stepper

The rig syncs itself.

When Bluetooth sees a device, the run starts without anyone asking: Copy, Prepare, Transcribe, Save, live from the server’s event stream. Each device syncs at most every 60 minutes, with a 03:00 sweep for anything the day missed.

Copy → Prepare → Transcribe → SaveServer-sent events≤60 min cadence + 03:00 sweep
127.0.0.1:8737 · library
Day page with click-a-line-to-play transcript
03 / Day pages

Click a line, hear the moment.

Recordings file under their day as compiled audio with transcripts alongside. Click any line and playback seeks to that moment, streamed over HTTP Range requests — nothing leaves the PC. A spoken “baseline test, orange bicycle, 42” came back verbatim.

Click-a-line playbackHTTP Range streamingVerbatim test passed
127.0.0.1:8737 · the deletion gate
The gated Free-space-now deletion flow
04 / Free space now

Deletion, carrying proof.

Freeing device flash is the rig’s one irreversible action, so it is gated hardest. “Free space now” ACKs only up to the contiguous watermark already flushed to disk and verified; a gap halts the delete at gap−1, and a flush failure rolls back instead of ACKing.

fsync barrier before ACKGap halts at gap−1Rollback on flush failure
127.0.0.1:8737 · settings
Settings page with cadence, auto-ACK and auto-transcribe
05 / The dials

Every default, yours to set.

One page holds the operating dials: sync cadence, whether the ACK gate runs unattended, whether transcription follows each sync, and a per-device link-health row for the last 24 hours. Weak signal shows up as data here — the rig keeps receipts, seven consecutive failures at −92 dBm among them.

Cadence · auto-ACK · auto-transcribe24 h link-health rowsReceipts, not promises
protocol notes · ACK sequence
The fsync-gated ACK sequence diagram
06 / Under the hood

The pipeline, drawn out.

The rig can show its own wiring: an ACK sequence diagram rendered from the protocol notes, or a pendant diag readout from the CLI. The same facts the tests assert, made visible to whoever is watching the drain — no second documentation drifting out of date.

ACK sequence diagrampendant diagOne source of truth
Under the surface

Rigour enforced in code.

The rules a user never sees but always depends on: deletion that carries proof, decode that fails closed, and a journal that stays the single source of truth.

Proof-carrying deletion

An ACK permanently deletes the pendant’s stored pages, so it is sent only after an fsync barrier and only up to the verified contiguous watermark. Fifteen fault-injection tests attack exactly this rule.

15 fault-injection tests

Three-way PLAUD verification

A PLAUD session is complete only when the journaled bytes size-match the device, the session identity agrees across every record, and a disk replay reproduces the session — with an interpreted cmd-117 checksum on top.

verify_replay + cmd-117

Fail-closed codec

Decode is a replay from raw CRC32-framed journal bytes. Audio pages that yield zero frames are never surfaced and never ACKed; zero-frame marker pages still advance the watermark, exactly as the protocol specifies.

Opus/CELT replay

One isolated ML worker

faster-whisper runs as a subprocess on the GPU, so a native crash never takes the GUI down; any CUDA failure falls back to CPU int8 and one --cpu re-run.

distil-large-v3 · CUDA fp16

Vendor-independent by construction

Two undocumented BLE protocols, one rig. The PLAUD transmit surface is an explicit allowlist that blocks 112 of 114 commands, so nothing outside capture-and-read can ever be sent.

112/114 commands blocked

Self-healing rig

A cross-process lock means every journal user — GUI, CLI, scheduled task — queues instead of racing; hidden scheduled tasks restart the rig every 10 minutes; a redeploy lands only while idle and from a clean tree.

lock · 10-min restart · idle-only
Under the hood

Radio waves in, day pages out.

Six stages take a whisper in a room to a searchable transcript, and exactly one of them is irreversible — so that one is gated hardest.

01
BLE capture
bleak · WinRT · Windows stack
A background service keeps watch for two device families and pulls pages over BLE as they arrive, writing nothing but journal records. Auto-sync visits each device at most every 60 minutes; a 03:00 sweep catches what the day missed.
02
Raw journal
append-only · CRC32-framed records
Every page lands as a CRC32-framed record in an append-only journal, the source of truth for everything downstream. Decode is always a replay from these raw bytes; nothing decodes in place and discards the original.
03
Fail-closed decode
opuslib · vendored libopus
The replay decodes Opus/CELT audio through a vendored libopus. It fails closed: audio pages that yield zero frames are never surfaced, so they can never be ACKed — while zero-frame marker pages still advance the watermark, as the protocol intends.
04
fsync-gated ACK
contiguous flushed watermark · rollback
An ACK permanently deletes the device’s copy of those pages. It is sent only after an fsync barrier proves the bytes are on disk, only up to the contiguous flushed watermark, and a gap halts the ACK at gap−1.🔒 The one irreversible step
05
Local GPU transcription
faster-whisper distil-large-v3 · CUDA fp16
An isolated worker transcribes on a 6 GB RTX 3050 at about 25× realtime in float16. A native crash never kills the GUI, and any CUDA failure falls back to CPU int8 with one --cpu re-run.
06
Day pages & the web app
FastAPI · vanilla JS · 127.0.0.1:8737
Audio and transcript file under the day, playable in the local web app. The journal stays the source of truth; the app is a window onto it, served to this PC only.
pendant/drain/engine.py (the ACK watermark rule)
# pendant/drain/engine.py — the device deletes what an ACK covers,
# so an ACK may only target the journal's contiguous FLUSHED watermark.
def _ack_watermark(self) -> int | None:
    watermark = self._anchored_watermark()   # contiguous flushed prefix
    if watermark is None or self._stream_low is None:
        return None              # a gap below the run: nothing ACKable

    watermark = min(watermark, self._stream_high)  # never ahead of the stream

# _send_ack: fsync barrier first, then ACK the watermark — never beyond it,
# never twice. A flush failure rolls back: nothing is acked, nothing is lost.
Isolation by boundaries
Capture, journal, decode, ACK and transcription are separate processes with separate failures. A crashed transcriber leaves the journal intact; a failed flush leaves the device untouched.
Gates, not promises
The delete-safety rules are enforced in code and attacked by fault-injection tests, not stated in a README. Fifteen of them try to make the rig ACK audio it cannot prove it holds.
Local and account-free
No cloud, no accounts, no telemetry: the whole rig runs on one PC, and its web app serves 127.0.0.1 only. The stack must keep working with the network cable pulled.
The stack

One PC, no services.

Capture, decode, transcription and the web app all run on the machine they serve: the same box that wears the RTX 3050.

Python 3.12
Language
Typed, tested rig code from the BLE client to the web app.
bleak + WinRT
BLE stack
Windows-native Bluetooth LE for two undocumented device protocols.
opuslib + libopus
Audio codec
Opus/CELT decode through a vendored libopus — no system dependency.
Strict JSON journals
Storage of record
Append-only and CRC32-framed; decode is always a replay.
FastAPI + vanilla JS
Web app
Dashboard, library and settings, served to localhost only.
faster-whisper
Transcription
distil-large-v3 at CUDA float16 on an RTX 3050; CPU int8 fallback.
Windows Task Scheduler
Runtime
Hidden tasks run the rig at logon and re-check every 10 minutes.
PLAUD TX allowlist
Safety
An explicit command table; 112 of 114 commands are blocked.
Why it holds up

Deletion you can audit.

Gated
The ACK deletes only after an fsync barrier, up to the verified contiguous watermark; 15 fault-injection tests attack the rule.
Verified
PLAUD sessions complete only after size-match, session-identity and disk-replay checks, plus an interpreted cmd-117 checksum.
900 tests
pytest collects 900 tests at HEAD; ruff keeps the codebase lint-clean before anything lands.
Self-healing
A cross-process lock, hidden tasks restarting the rig every 10 minutes, and redeploys only while idle from a clean tree.
A working prototype

See it running.

There is no public URL, and that is the point: the rig serves one Windows PC at 127.0.0.1:8737, keeps its journals on that machine, and answers to no vendor. Everything on this page — capture, ACK gates, transcripts — runs from that box today.

Pendant Rig
Case study · Offline AI-wearable capture · Built & sponsored by PRTOTYPE.COM
Like what you see

Want hardware this independent?

The Pendant Rig replaced two vendors' clouds with one PC, a written protocol and a deletion rule enforced in code. If you have orphaned hardware or a fragile data pipeline that needs the same treatment, let's talk it through.

Fixed prices · Set against a written spec
Book a Production Consultation→