Conversation
Hold a global hotkey, an always-on-top overlay tracks the mic, whisper.cpp transcribes on-device and the text is pasted into whatever window had focus before the recording started. Audio never leaves the machine. The Rust core covers capture (cpal), resampling, the model catalog and download manager, snippet/vocabulary post-processing, focus-aware clipboard paste, a 30 min/day free quota, offline Ed25519 entitlement verification, a loopback HTTP bridge for the MCP companion, and the `agent-voice mcp` stdio server exposing ask_user_by_voice. The React front end drives a settings window plus the overlay from one bundle, selected by window label. Standalone package with its own bun.lock so the monorepo build and the root workspaces stay untouched. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
Pro is a one-time purchase, so the only thing standing between a key and a working app is a small worker: it accepts Paddle webhooks, records which license keys are active, hands out Ed25519-signed entitlement tokens, and caps each license at three devices. Tokens are the same format the Rust app already verifies offline, so the worker's signing key is the only secret it holds and the app never needs a network round-trip to know it is Pro. A refund or chargeback revokes the license and frees the device slots; webhook signatures are checked with HMAC-SHA256 before anything is written. KV is used for both license records and a best-effort fixed-window rate limit on /activate; the eventual-consistency caveat is documented rather than hidden. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
Users should not have to hunt for a new build, so the Tauri updater plugin is wired up behind a "Check for updates" action; signatures are verified against the minisign key baked into the bundle config, and the version comparison refuses to offer a downgrade. The docs cover what a first-time user needs (install, hotkey, MCP config, privacy) and what an operator needs (worker deployment, Paddle webhook events, entitlement token format and key rotation). CI runs the frontend typecheck, tests and build, plus fmt, clippy and tests for the Rust core on Linux and Windows. agent-voice keeps its own workflow because it is deliberately outside the monorepo workspaces. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
The entitlement token is the one artifact two languages agree on, and until now nothing enforced that agreement: a drift in either implementation would have shown up only as "Pro key rejected" in the field. Both sides now assert the same token, minted by the worker's TypeScript code with the dev signing key, byte for byte. The Rust test also proves the embedded dev public key still matches, so the pair cannot rot silently. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
The title tag still read "Tauri + React + Typescript", which is what shows in the taskbar and in the browser tab during development. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
Paddle Billing issues no license keys and has no refund or chargeback events, so the fulfillment model the worker assumed could not work on this platform: there is no license_key_created event to listen for and nothing for the customer to paste. The transaction id becomes the license code instead. The worker activates on transaction.completed and revokes on adjustment.created when the action is a refund or a chargeback, using an allowlist so a goodwill credit note or a dispute warning never costs a paying customer their license. The app reads the code from checkout.completed, accepts it pasted from a receipt, and shows it with a copy button so a license can move between machines. The default worker URL becomes the project's own deployment, served from a custom domain rather than a workers.dev address.
Paddle signs `timestamp:raw body` and carries no event-type header, but the worker was signing `eventType:body` and rejecting any delivery that lacked that header. Every real webhook would have failed verification — the local tests passed only because they reproduced the same wrong assumption, so nothing caught it until Paddle's own simulator delivered a genuine signature and got a 400. Verify over the raw bytes before parsing, read `event_type` from the payload, compare digests in constant time, and reject a timestamp outside a five-minute window to blunt replay. The simulator now returns 200 and the license record is created.
A refund starts as pending_approval, so acting on adjustment.created alone stripped Pro from a paying customer while Paddle still had the refund under review — and a rejected refund never came back. Only an approved refund or chargeback revokes; a rejected one restores the license, and credits, reversals and chargeback warnings are ignored. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
|
The engineering here looks careful — tests, clippy, a documented entitlement format, device-cap logic — but this isn't a PR a maintainer can "port in" in the sense the mirror process expects. It is an entire new commercial product: a Tauri app with its own payment processor integration (Paddle), its own license-minting backend (a Cloudflare Worker), Ed25519 entitlement tokens, and a The repo explicitly keeps Separately: at +15238/-0 across 84 files, with its own If the underlying idea — local push-to-talk dictation via whisper.cpp, pasted into the focused window, with an MCP |
The setup steps said "exactly two events" while listing three, and bundling adjustment.updated with adjustment.created implied Paddle adds it for you. It does not — events are ticked individually, so a destination subscribed only to adjustment.created would never deliver the event that restores a license after a rejected refund. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
What
A standalone desktop app at
Apps/agent-voice/— Tauri v2 + React 19 + TypeScript, its ownbun.lock, deliberately not a root workspace member so the monorepo build and the modified rootpackage.json/bun.lockare untouched.Hold
Ctrl/Cmd+Shift+Space, an always-on-top overlay shows the mic level, whisper.cpp transcribes on-device, and the text is pasted into the window that had focus before recording started. Audio never leaves the machine.Also included: an MCP server (
agent-voice mcp) exposingask_user_by_voice, so a coding agent can put a question on the overlay and read the spoken answer back.Tiers
ask_user_by_voice.Entitlements are Ed25519-signed tokens the app verifies offline; a Cloudflare Worker mints them from Paddle webhooks and caps each license at three devices.
Structure
src-tauri/src/—hotkey(push-to-talk orchestration, free-quota gate),audio(cpal → downmix → 16 kHz),asr+models(whisper-rs, streamed download with sha256/GGML check),text(snippets/vocabulary),paste/focus,usage,entitlement/license,bridge(loopback HTTP for the MCP companion),mcp,updates.src/— one bundle, two windows chosen by label: settings shell + overlay; Zustand store; Paddle checkout overlay withagentvoice://deep-link return.worker/— Cloudflare Worker:/activate,/deactivate,/webhook,/health; HMAC-verified Paddle webhooks; refunds revoke; device cap; rate limiting.docs/entitlement.md— token format, key generation, rotation.Verification
cargo testcargo clippy --all-targets -- -D warningscargo fmt --checkcargo build --bin agent-voicebun run typecheck(app + worker)bun testbun run buildagent-voice mcpover stdinask_user_by_voiceis listed and invokedNot verified
No end-to-end run with a real microphone/model/GUI, no signed release bundle, and the Paddle + Cloudflare deployment path is documented but not executed against the live accounts.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff noreply@codebuff.com