scrybe
Local-first meeting transcription and notes. No bot, no account, no vendor cloud by default.
scrybe is an open-source meeting transcription tool built around one constraint: the meeting artifacts belong on the user's machine as ordinary files. It captures audio locally, transcribes it with either local Whisper or a user-configured OpenAI-compatible provider, generates Markdown notes, and writes everything under ~/scrybe/.
Current release: v2.1.0.
What Works Today
The supported user path today is macOS:
scrybe initwrites the default local macOS profile on macOS.scrybe init --profile defaultwrites the hermetic synthetic profile used by CI and cross-platform smoke tests.scrybe recordcreates a session folder with source-separatedaudio.opus, centeredplayback.opus,transcript.md,notes.md, andmeta.toml.--source syntheticruns the hermetic smoke path used by CI.--source micrecords Core Audio's default microphone resolved once at session start;--input-device <uid>or[record].input_devicepins an exact macOS Core Audio device UID.scrybe deviceslists macOS input-device UIDs and identifies the current default.--source mic+systemrecords microphone plus macOS system audio through ScreenCaptureKit when built withmic-capture,system-capture-macon macOS 13+. It requires the broader Screen & System Audio Recording permission.--whisper-model <PATH>enables local whisper.cpp transcription when built withwhisper-local.--llm openai-compatenables real notes through Ollama, vLLM, OpenAI, Groq, Together, or any compatible/chat/completionsendpoint when built withllm-openai-compat.scrybe record TITLE --shellenables the native macOS recording shell: a five-bar 4 Hz waveform cycling between the current macOS appearance foreground and recording red, an optional Scrybe image mark, a compact floating elapsed-time pill, and one sharedStop & savepath for the tray, pill, and global hotkey.scrybe list,scrybe show <id>,scrybe doctor,scrybe repair <session>,scrybe notes <session>, andscrybe benchare available in the CLI. A session is addressed by its folder name or an unambiguous session-ID prefix, never by a path: every session resolves beneath the configured storage root, and--rootis the only way to point a command at a different one.scrybe bench stt --corpus <MANIFEST> --whisper-model <FILE> --sherpa-model <DIR>compares both local providers on a checksum-validated English paired corpus when built withwhisper-local,stt-sherpaand an explicitly provisioned native runtime. Manual acquisition and measurement scope. Whisper remains the default; the historical multilingual corpus is Whisper-only.
Linux, Windows, and Android crates are present in the workspace as adapter surfaces and scaffolds. They are not the polished end-user install path yet. The project keeps those adapters in-tree so the trait contracts, config, tests, and packaging work stay cross-platform from the start.
Install
Install the native macOS application from the DMG attached to the latest GitHub release. Choose the Apple-silicon (aarch64) or Intel (x86_64) image, verify it against the signed checksum manifest, open the image, and drag Scrybe.app to /Applications.
The application uses Scrybe's stable self-signed community certificate. It is not signed with Apple Developer ID and is not notarized, so Gatekeeper rejects the first launch. Verify the release and certificate fingerprint, then use System Settings → Privacy & Security → Open Anyway. Do not disable Gatekeeper globally or remove quarantine metadata. Exact verification and installation steps are in INSTALL.md.
Install the command-line application from crates.io:
This builds Scrybe locally with microphone capture, ScreenCaptureKit system audio, Whisper, Opus, and OpenAI-compatible notes enabled. It requires Rust 1.95 and Xcode Command Line Tools. The prebuilt CLI installer remains available:
|
The native app, CLI archive verification, source builds, and community trust limitations are documented in INSTALL.md.
First Local Recording Setup
In the application
Open Scrybe. An installation that cannot record yet opens on guided setup, which walks through four steps and can be left at any point:
- Welcome — where recordings are kept, that no meeting bot joins a call, and that the transcription-model download is the only network request setup makes.
- Recording — the microphone, and what the Microphone and Screen & System Audio Recording permissions are for. macOS raises its own dialog the first time a recording needs one; if you have already refused a capability, each has a button that opens the System Settings pane where it is granted.
- Transcription and notes — the transcription model, shown with its source, upstream revision, licence, exact byte count, SHA-256, destination, and disk requirement before anything is requested. Nothing is fetched until you confirm it; the download is cancellable, and it is promoted to its final name only after its size and digest both match exactly. Local notes are checked separately, and setup can be finished without them.
- Ready — capture, transcription, notes, storage, and privacy reported separately. Capture is reported as not checked: Scrybe does not read whether macOS has granted microphone and system-audio recording, because no way to read it without prompting exists in this release. macOS asks the first time a recording needs the grant; if you have already refused it, macOS will not ask again, and step 2 above is where it is granted through System Settings instead.
Setup never asks for an account or an API key. Recording remains unavailable while transcription or storage is blocked; notes are optional and visibly so.
Settings carries the same model storage, the same readiness report, and a diagnostics list whose repairs run only when you choose one. Open advanced configuration opens config.toml for the settings no form models.
Reading what is already recorded
Sessions lists what is under the storage root, grouped by the day each meeting was recorded and newest first. A session that never finished, and one that can still be recovered, are marked as such in words as well as by the edge beside them; a session that never wrote a start time is filed under Undated rather than dropped. Only the first page is read, and the list says how many there are when there are more.
Search matches folder names and titles for every session, and notes and transcript text for completed ones. Searching again abandons the search still running, so a slower answer to an earlier question can never replace a newer one.
Opening a session shows what is actually on disk for it: its state, identity, times and length, how it was captured, which providers produced its text, and which of the five artifacts are present and which are not. Notes open first. The transcript is one button away and is read fifty lines at a time — the whole document never crosses into the window, so a long meeting costs the same to show as a short one.
Five actions, each offered only when the session accepts it and each saying why beside itself when it does not:
- Regenerate notes — replaces
notes.mdfrom the durable transcript, through the providerconfig.tomlnames. Offered only for a completed session that has a transcript. - Repair recording — completes a recording that was interrupted. Offered only when durable state survives for it to recover.
- Reveal in Finder — opens the session's folder. The folder is resolved in Rust beneath the storage root; the window never handles a path.
- Copy notes and Copy transcript — read the document and put it on the clipboard, both in Rust. The transcript is never assembled in the window, for the same reason it is never read there whole.
A session with a playback.opus gets a player, using the platform's own controls. One without gets no player at all rather than a control that does nothing: playback.opus is written only for a two-channel capture, so a mono session is complete, has audio, and has nothing to play. The audio reaches the window through a scheme that serves that one file and no other — a URL on it names a session and the word playback, and anything else is refused before any file is opened.
Nothing on these screens edits a note or a transcript, and nothing deletes, archives, or moves a session. The files on disk stay the source of truth; the window is a way to read them.
From the terminal
# expect c6138d6d58ecc8322097e0f987c32f1be8bb0a18532a3f88f734d1bbf9c41e5d
The URL is pinned to an immutable upstream revision, and the digest above is the one the application's own catalog (scrybe-application/models.toml) carries — so a file fetched by hand and one installed through the application are the same 487,614,201 bytes. A resolve/main URL names whatever that branch points at today and no checked-in digest can describe it; do not substitute one.
scrybe doctor reports whether the model is present and whether a configured local notes endpoint is answering. A missing model is reported before a recording starts rather than when transcription does.
On macOS, bare scrybe init writes the local recording profile:
[record].source = "mic+system"[record].input_device = "<Core Audio UID from scrybe devices>"(pins the meeting microphone instead of following later OS default changes)[record].llm = "openai-compat"[record].system_backend = "sck"(ScreenCaptureKit; use"tap"only for the macOS 14.4+ legacy Core Audio Tap recovery path)[stt].model = "small.en"and[stt].language = "en"[llm].model = "gemma4:latest"[shell].indicators = ["menu-bar-waveform", "menu-bar-label", "floating-window"](the default when[shell]is omitted)
The macOS platform data path is
~/Library/Application Support/dev.scrybe.scrybe/. Other platforms resolve
the model path through their native data directory convention. Pass
--profile default for the synthetic smoke-test profile, or override local
model choices with --whisper-model <PATH> and --llm-model <MODEL>.
If a config file already exists, scrybe init refuses to overwrite it; pass
--force only when you intentionally want to replace the existing config with
fresh profile defaults.
Record with the native macOS shell:
# Stop from the menu bar, floating panel, global hotkey, or Ctrl-C.
The ergonomic scrybe record TITLE resolves capture source, system-audio backend, Whisper model, and LLM kind from your config and platform probes. Add --shell to construct the configured indicators before capture starts; without it, recording remains headless. ScreenCaptureKit runs directly from the invoking terminal; the legacy tap backend auto-launches through the .app bundle so its Audio Capture TCC grant binds correctly. Run scrybe doctor after configuration to inspect the selected backend and accept or decline its live permission probe. Use scrybe rec --title TITLE --source … --system-backend … --whisper-model … --llm … --shell when you need explicit flag control (CI, debugging, alternate hardware setups).
Select a microphone explicitly:
The terminal prints each accepted transcript chunk while recording, then reports transcript flush, audio encoding, notes generation, and metadata-writing progress after the first stop request. During a shell recording, the menu and panel change to Saving, freeze the elapsed time, and disable Stop & save until finalization returns. A second Ctrl-C aborts finalization immediately. Run scrybe repair <session-folder> to recover unfinished audio or reconstruct missing metadata, then scrybe notes <session-folder> to regenerate missing notes.
For cloud or hosted-compatible LLMs, configure [llm] with a base URL, model, and an environment-variable name for the API key. Secrets stay in the environment, not in config.toml.
Storage Model
Every session is a directory:
~/scrybe/
└── 2026-05-02-1430-acme-discovery-01HXY7K9RZ/
├── audio.opus
├── playback.opus
├── transcript.md
├── notes.md
├── meta.toml
├── pid.lock
├── transcript.partial.jsonl
└── .stignore
The filesystem is the database. meta.toml, notes.md, audio.opus, and playback.opus use atomic replace. transcript.md is append-only. Audio is treated as the source of truth so failed or improved transcription can be regenerated later.
For --source mic+system, audio.opus preserves the source master with the user's microphone on the left channel and system audio on the right. playback.opus contains the same meeting as a centered stereo listening mix, preventing either speaker from playing in only one ear. The exact source layout remains recorded in meta.toml under [audio].layout as stereo:mic-l,system-r; downstream transcription and archival tools must use audio.opus, not the convenience playback mix. Mono sessions produce only audio.opus.
Architecture
scrybe is a Rust workspace with a small core and platform adapters:
| Package | Role |
|---|---|
scrybe |
Published application and scrybe binary (scrybe-cli/) |
scrybe-meeting-core |
Published session, storage, config, provider, hook, diarization, and pipeline library |
scrybe-meeting-application |
Published session, configuration, diagnostics, and recording services shared by every frontend (scrybe-application/) |
scrybe-meeting-capture-mac |
Published macOS microphone and system-audio adapter |
scrybe-meeting-capture-mic |
Published microphone adapter via cpal |
scrybe-capture-linux |
Private PipeWire/Pulse adapter surface |
scrybe-capture-win |
Private WASAPI adapter surface |
scrybe-android |
Private Android FFI adapter surface |
The important public seams are:
AudioCapturefor platform audio.ContextProviderfor meeting metadata.SttProviderandLlmProviderfor transcription and notes backends.Diarizerfor speaker attribution.Hookfor post-session actions.
The Tier-1 stability contract is documented in docs/system-design.md. In short: AudioCapture, MeetingContext, LifecycleEvent, ConsentAttestation, the meta.toml schema, storage invariants, and the Apache-2.0 license are frozen for the v1 series.
Privacy and Network Posture
- The published application includes OpenAI-compatible provider support, but no network provider runs unless selected in configuration.
- API keys are read from named environment variables.
- There is no account system, sync service, telemetry, hosted backend, or bot that joins calls.
- Courtesy notification is part of the recording flow and is recorded in
meta.toml. scrybe mcp(featureagent-access, off by default) is a read-only local-agent surface over~/scrybe/: it exposeslist_recent_meetings,search_meetings,get_meeting,get_meeting_notes, andget_meeting_transcriptas MCP tools over stdio JSON-RPC. It is handed a read-only view of the shared session services whose trait surface has no method that repairs, regenerates, writes, or deletes anything, so there is no mutation a tool handler could reach. There is no network listener either — stdio only, reachable only by spawning it as a child process. The server refuses to start unless[agent_access].enabled = trueis set explicitly inconfig.toml.
Run the egress audit locally:
Current Limitations
- macOS is the only polished binary distribution target today.
--source mic+systemdefaults to ScreenCaptureKit on macOS 13+ and requires Screen & System Audio Recording. This privacy permission covers screen recording in addition to system audio; deny it if that scope is unacceptable. ScreenCaptureKit runs from the invoking terminal and does not require an application bundle or signing identity.- The macOS 14.4+ Core Audio Tap backend remains available as
[record].system_backend = "tap"for recovery. It requires the narrower Audio Capture permission and a signed.appbundle.scrybe doctorreports bundle state and offers repair with the project self-signed identity; it never creates or auto-selects a signing identity. A failed or silent Tap switches once to ScreenCaptureKit after a 1.5 s startup window, so a quiet desktop can switch before external audio begins. - Reading a session offers no paging control: the first twenty are shown and the count says how many there are. Search is submitted rather than run per keystroke.
- The native recording shell requires the default
cli-shellfeature and explicit--shell; headless recording remains available. Themenu-bar-labelindicator is the Scrybe branding slot, and the status item never includes elapsed time. Configured indicators contain only recording state, elapsed time, and the stop control—never captured or generated meeting content. - Native application releases are self-signed with the stable Scrybe community certificate and are not Apple-notarized. Gatekeeper rejection is expected until the user chooses Open Anyway after verifying the release. Apple Developer Program enrollment remains deferred.
- Linux and Windows recording remain parked until hardware-qualified release paths exist.
Development
Required toolchain is pinned by the repository:
Additional gates used by CI include cargo audit, cargo deny check, coverage, LoC budget, egress audit, release planning, and advisory reproducibility checks.
The desktop application is qualified against the built bundle rather than against a test harness, which needs a real macOS login session and so runs locally rather than in CI:
Each drives the shipped shape of the application against a disposable storage root and configuration, and reports what it observed rather than what it assumed. library is the one that answers a question no unit test can: whether the webview may load a scrybe-audio:// URL at all. That is decided by the content security policy, and the policy check in that script compares the built artifact against a constant the same file declares — so it passes for any string written in both places. Driving the real webview at a real URL and reading back what the protocol handler served is what tells a correctly-admitted scheme from one admitted under a directive that does nothing.
recording answers a different one. Every surface a recording is stopped from is a platform event, and the dispatch behind each is reachable from a unit test — but that the built application wires them to it is not. A tray item built disabled, a command missing from a capability file, a permission absent from the generated manifest: each leaves the unit tests passing and the application inert. That scenario records a real session through the synthetic source and the stub providers, so it needs no device, no permission grant and no network, and asserts on the session the pipeline actually wrote.
installed copies the community-signed bundle out of the build tree and drives that copy. It verifies the running process came from the copy, records a session and reads the same session through the reader, reads both the Scrybe status item's name and its actions from the native accessibility tree, and checks the explicit community trust contract: stable certificate, expected Gatekeeper rejection, and no notarization ticket. The native accessibility observation requires a human-granted TCC permission for the invoking terminal. model-download-live is the release-only proof that the checked-in production catalog can complete its pinned Hugging Face transfer and promote the exact expected bytes inside a disposable model root.
Project docs:
docs/pitch.md— product framing and market position.docs/system-overview.md— user-facing system explanation.docs/system-design.md— engineering contract and stability tiers.docs/desktop-recording.md— how a recording starts and stops from the desktop application, and what each way of failing leaves behind.INSTALL.md— installation, source builds, and release verification.MAINTENANCE.md— v1 maintenance commitments.CHANGELOG.md— release history.
License
Apache-2.0. See LICENSE.