scrybe 1.5.0

Local-first meeting recording, transcription, and notes for macOS.
# scrybe

[![Crates.io](https://img.shields.io/crates/v/scrybe.svg?label=version)](https://crates.io/crates/scrybe)
[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)

> 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: `v1.5.0`.

## What Works Today

The supported user path today is macOS:

- `scrybe init` writes the default local macOS profile on macOS.
- `scrybe init --profile default` writes the hermetic synthetic profile used by CI and cross-platform smoke tests.
- `scrybe record` creates a session folder with source-separated `audio.opus`, centered `playback.opus`, `transcript.md`, `notes.md`, and `meta.toml`.
- `--source synthetic` runs the hermetic smoke path used by CI.
- `--source mic` records Core Audio's default microphone resolved once at session start; `--input-device <uid>` or `[record].input_device` pins an exact macOS Core Audio device UID.
- `scrybe devices` lists macOS input-device UIDs and identifies the current default.
- `--source mic+system` records microphone plus macOS system audio through ScreenCaptureKit when built with `mic-capture,system-capture-mac` on macOS 13+. It requires the broader **Screen & System Audio Recording** permission.
- `--whisper-model <PATH>` enables local whisper.cpp transcription when built with `whisper-local`.
- `--llm openai-compat` enables real notes through Ollama, vLLM, OpenAI, Groq, Together, or any compatible `/chat/completions` endpoint when built with `llm-openai-compat`.
- `scrybe record TITLE --shell` enables 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 shared `Stop & save` path for the tray, pill, and global hotkey.
- `scrybe list`, `scrybe show <id>`, `scrybe doctor`, `scrybe repair <session>`, `scrybe notes <session>`, and `scrybe bench` are available in the CLI.
- `scrybe bench stt --corpus <MANIFEST> --whisper-model <FILE> --sherpa-model <DIR>` compares both local providers on a checksum-validated English paired corpus when built with `whisper-local,stt-sherpa` and an explicitly provisioned native runtime. [Manual acquisition and measurement scope](INSTALL.md#optional-streaming-zipformer-and-english-paired-stt-benchmark). 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 full macOS application from crates.io:

```sh
cargo install scrybe
scrybe doctor
```

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. Use `cargo install scrybe --locked` only when reproducing the exact dependency graph qualified for a release or troubleshooting a registry install; releases qualify both forms.

For a faster prebuilt installation:

```sh
curl --proto '=https' --tlsv1.2 -LsSf \
  https://github.com/Mathews-Tom/scrybe/releases/latest/download/scrybe-installer.sh | sh
scrybe doctor
```

The GitHub installer downloads the matching macOS tarball, verifies the release checksum manifest, and installs `scrybe` on `PATH`. Both installation paths provide the same production capabilities. On an interactive terminal, `scrybe doctor` reads the configured capture mode, explains the required macOS permission, and offers the applicable live probe. It does not modify the system or create signing identities without an explicit confirmation. Manual tarball installation, release verification, and source builds are documented in [`INSTALL.md`](INSTALL.md).

## First Local Recording Setup
```sh
mkdir -p ~/Library/Application\ Support/dev.scrybe.scrybe/models
curl -L -o ~/Library/Application\ Support/dev.scrybe.scrybe/models/ggml-small.en.bin \
  https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.en.bin

ollama pull gemma4:latest
scrybe init
scrybe doctor
```

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:

```sh
scrybe record "client-call" --shell
# Stop from the menu bar, floating panel, global hotkey, or Ctrl-C.
scrybe list
scrybe show <session-id>
```

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:

```sh
scrybe devices
scrybe rec --title "client-call" --source mic --input-device <uid>
```

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:

```text
~/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-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:

- `AudioCapture` for platform audio.
- `ContextProvider` for meeting metadata.
- `SttProvider` and `LlmProvider` for transcription and notes backends.
- `Diarizer` for speaker attribution.
- `Hook` for post-session actions.

The Tier-1 stability contract is documented in [`docs/system-design.md`](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` (feature `agent-access`, off by default) is a read-only local-agent surface over `~/scrybe/`: it exposes `list_recent_meetings`, `search_meetings`, `get_meeting`, `get_meeting_notes`, and `get_meeting_transcript` as MCP tools over stdio JSON-RPC. There is no write, delete, or mutate capability anywhere in the module, and no network listener — stdio only, reachable only by spawning it as a child process. The server refuses to start unless `[agent_access].enabled = true` is set explicitly in `config.toml`.

Run the egress audit locally:

```sh
cargo build -p scrybe --release --no-default-features
python3 scripts/check-egress-baseline.py
```

## Current Limitations

- macOS is the only polished binary distribution target today.
- `--source mic+system` defaults 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 `.app` bundle. `scrybe doctor` reports 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.
- The native recording shell requires the default `cli-shell` feature and explicit `--shell`; headless recording remains available. The `menu-bar-label` indicator 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.
- crates.io installation supports the polished macOS application. Linux and Windows recording remain parked until hardware-qualified release paths exist.
- Native macOS notarization and Windows Authenticode signing are out of scope for the v1 line. Release artifacts are verified with checksums and cosign provenance instead.

## Development

Required toolchain is pinned by the repository:

```sh
cargo fmt --all --check
cargo clippy --workspace --all-targets --no-default-features -- -D warnings
cargo check --workspace --all-targets --no-default-features
cargo test --workspace --all-targets --no-default-features
```

Additional gates used by CI include `cargo audit`, `cargo deny check`, coverage, LoC budget, egress audit, release planning, and advisory reproducibility checks.

Project docs:

- [`docs/pitch.md`](docs/pitch.md) — product framing and market position.
- [`docs/system-overview.md`](docs/system-overview.md) — user-facing system explanation.
- [`docs/system-design.md`](docs/system-design.md) — engineering contract and stability tiers.
- [`INSTALL.md`](INSTALL.md) — installation, source builds, and release verification.
- [`MAINTENANCE.md`](MAINTENANCE.md) — v1 maintenance commitments.
- [`CHANGELOG.md`](CHANGELOG.md) — release history.

## License

Apache-2.0. See [`LICENSE`](LICENSE).