Accuracy-first local meeting transcription for German and English. Audio never touches disk.
Project description
stenograf
Accuracy-first, fully local meeting transcription for German and English (one language per meeting), with speaker labels. Audio is processed entirely in memory — it never touches disk; only the transcript is persisted.
Built for Apple Silicon (M-series) first; Linux and Windows support is designed in from the start.
Status: pre-alpha, macOS only. The pipeline is complete end to end: live system-audio + microphone capture, live captions, and the high-accuracy speaker-labelled finalize pass. The local web UI, meeting notes, and Linux capture are not built yet. See PLAN.md.
Why another transcription tool?
- No audio on disk, ever. Live transcription of a meeting has far lighter legal requirements than recording it. stenograf keeps the session's audio in RAM only and writes nothing but text.
- Accuracy first. A two-pass design: fast live captions while the meeting runs, then a high-accuracy re-transcription of the full in-memory buffer the moment it ends. German is a first-class citizen, not an afterthought.
- Channel-aware speakers. Microphone and system audio are captured as separate streams, so local and remote voices never get confused; diarization handles the rest (2–8 speakers).
- Speakers, not headphones. Remote voices leaving your laptop speakers and re-entering the mic are cancelled in the audio domain (WebRTC AEC3, with the system channel as the far-end reference), so they are never transcribed as a local speaker.
Install
Requires macOS 14.4+ on Apple Silicon and uv. The wheel ships the signed capture helper — no toolchain needed.
uv tool install stenograf
steno doctor # environment checks
steno setup # one-time: mic + system-audio permission prompts, model downloads
macOS scopes the permission grant to the app the prompt came from, so run
steno setup once from each terminal app (or IDE) you'll start meetings from.
Pre-release channel: uv tool install git+https://github.com/daniel-om-weber/stenograf
installs the current main branch; building from the repository compiles the
capture helper on your machine, which needs the Xcode command-line tools
(xcode-select --install).
From a checkout
git clone https://github.com/daniel-om-weber/stenograf
cd stenograf
uv sync
sh native/helper/build.sh # builds + ad-hoc signs native/helper/stenocap
uv run steno doctor
uv run steno setup
Every command below is then uv run steno … from the repo.
Usage
uv run steno start # live captions, everything auto-detected
uv run steno start --lang de --local 3 --remote 2 # hybrid meeting, German
uv run steno transcribe recording.mov # batch-transcribe an existing file
steno start streams live captions while the meeting runs — a full-screen
TUI on a terminal, a plain line-by-line stream when piped — and replaces them
with the high-accuracy, speaker-labelled transcript the moment you stop
(Ctrl-C). The audio stays in RAM throughout; only the transcript is written.
Useful flags:
steno start --plain # plain caption stream instead of the TUI
steno start --no-live # skip live captions; just finalize on stop
steno start --title "Weekly sync" # name the meeting in the archive
steno start --flush-interval 60 # crash-checkpoint the captions every 60s
steno start --no-aec # disable echo cancellation (headphones)
steno start --record-audio # opt in to keeping a WAV (off by default)
steno start --replay mic.wav # dev: drive the live pass from a file
Both start and transcribe accept --format md,json,srt,vtt (default
md,json), --lang de|en, and --print to echo the transcript to stdout.
Your meeting archive
Transcripts are filed automatically into a managed archive at
~/Library/Application Support/stenograf/meetings/<id>/. Use --out DIR to
write somewhere else (still archived), or --no-archive to write loose files
next to the source and register nothing.
steno meetings list # every transcript, newest first
steno meetings show meeting-20260710-091500
steno meetings rm meeting-20260710-091500
Audio is stored only when you passed --record-audio; without it the archive
holds text alone.
Naming speakers across meetings
Enroll a voice once and every later meeting relabels that speaker automatically (cross-meeting re-identification):
steno profiles enroll Daniel daniel-sample.wav # a short clip of one speaker
steno profiles list # show enrolled voiceprints
steno profiles rename Daniel "Daniel W."
steno profiles remove Daniel
To name one person from a multi-speaker recording (e.g. a meeting saved with
--record-audio), diarize it and pick their cluster:
steno profiles enroll Anna meeting.wav --speakers 4 # lists the clusters
steno profiles enroll Anna meeting.wav --speakers 4 --speaker S2
Matching is on by default in steno start/transcribe and does nothing until
you enroll someone; disable it with --no-reid, or adjust the match strictness
with --reid-threshold (0–1, default 0.5). Voiceprints live in the platform data
dir (not the model cache) and are never uploaded.
Vocabulary
Domain terms and attendee names are corrected in the finalized transcript (the ASR has no decode-time biasing, so this is a post-correction pass):
steno transcribe rec.mov --attendee "Anja Müller" --glossary Kubernetes,gRPC
steno transcribe rec.mov --glossary-file terms.txt
A term and its transcription must share a word count — gRPC can fix G R P C
spoken as one word, but not a term split across word boundaries.
Development
Requires uv and Python ≥ 3.12.
uv sync
uv run pytest
uv run steno doctor
The test suite is label-free and runs without a meeting: model-gated and real-audio tests self-skip when their assets are absent.
See PLAN.md for the full architecture, model choices, and roadmap;
PLAN-AEC.md for the echo-cancellation design and its measurements;
native/README.md for the capture helper and its wire protocol; eval/README.md
for the model-evaluation and AEC-scoring harnesses.
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stenograf-0.1.0.tar.gz.
File metadata
- Download URL: stenograf-0.1.0.tar.gz
- Upload date:
- Size: 436.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
291c50d885fe5f76d31f85a6c6045c72122ebeb896da23669ccdabfbf47c0602
|
|
| MD5 |
a427607b914cc0af30d0187c77567575
|
|
| BLAKE2b-256 |
4063c004f06769bafe041ffe14e65bdd3317287ad9e1b090f9ef71ba3b3cf58a
|
Provenance
The following attestation bundles were made for stenograf-0.1.0.tar.gz:
Publisher:
release.yml on daniel-om-weber/stenograf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stenograf-0.1.0.tar.gz -
Subject digest:
291c50d885fe5f76d31f85a6c6045c72122ebeb896da23669ccdabfbf47c0602 - Sigstore transparency entry: 2136883487
- Sigstore integration time:
-
Permalink:
daniel-om-weber/stenograf@7d95b695e81e3201a024906c2a68e218cfa9bb87 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/daniel-om-weber
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7d95b695e81e3201a024906c2a68e218cfa9bb87 -
Trigger Event:
push
-
Statement type:
File details
Details for the file stenograf-0.1.0-py3-none-macosx_14_0_arm64.whl.
File metadata
- Download URL: stenograf-0.1.0-py3-none-macosx_14_0_arm64.whl
- Upload date:
- Size: 147.1 kB
- Tags: Python 3, macOS 14.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
66808464d17a40893aed0d00e809aabeb976cc520a4ec9afded0b91f7cac253b
|
|
| MD5 |
f3359467ee095ee34d90ad8855f1ba91
|
|
| BLAKE2b-256 |
aab5aa4482376e6341d455b2770c190b7cf660405e9435978c07f2ac9e1a33c4
|
Provenance
The following attestation bundles were made for stenograf-0.1.0-py3-none-macosx_14_0_arm64.whl:
Publisher:
release.yml on daniel-om-weber/stenograf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stenograf-0.1.0-py3-none-macosx_14_0_arm64.whl -
Subject digest:
66808464d17a40893aed0d00e809aabeb976cc520a4ec9afded0b91f7cac253b - Sigstore transparency entry: 2136883507
- Sigstore integration time:
-
Permalink:
daniel-om-weber/stenograf@7d95b695e81e3201a024906c2a68e218cfa9bb87 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/daniel-om-weber
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7d95b695e81e3201a024906c2a68e218cfa9bb87 -
Trigger Event:
push
-
Statement type:
File details
Details for the file stenograf-0.1.0-py3-none-any.whl.
File metadata
- Download URL: stenograf-0.1.0-py3-none-any.whl
- Upload date:
- Size: 110.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d463c574c6e0ca6e9f52d5e04b50faaa7ae3f19d2fe6ed0f6c321c0fffc7122a
|
|
| MD5 |
18ccc8f149afc830bda1500dc23b5607
|
|
| BLAKE2b-256 |
b1386670655ad876ea0564d680a0cc62a2d13ae51437cb5a4c4f1e5d05517be4
|
Provenance
The following attestation bundles were made for stenograf-0.1.0-py3-none-any.whl:
Publisher:
release.yml on daniel-om-weber/stenograf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stenograf-0.1.0-py3-none-any.whl -
Subject digest:
d463c574c6e0ca6e9f52d5e04b50faaa7ae3f19d2fe6ed0f6c321c0fffc7122a - Sigstore transparency entry: 2136883542
- Sigstore integration time:
-
Permalink:
daniel-om-weber/stenograf@7d95b695e81e3201a024906c2a68e218cfa9bb87 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/daniel-om-weber
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7d95b695e81e3201a024906c2a68e218cfa9bb87 -
Trigger Event:
push
-
Statement type: