TapScribe
Local transcription recorder and operator dashboard. TapScribe records one WAV per utterance per speaker over a WebSocket, runs Whisper / Voxtral / Parakeet batch transcription on demand, and supervises a WhisperLiveKit child process for live captions. Nothing leaves the machine.
Audio arrives via a bridge — usually a browser extension that taps the
meeting platform's audio tracks and streams raw PCM to the /tap WebSocket.
The included bridges/spacialchat-bridge/ targets spatial.chat; see
bridges/README.md for the wire protocol.
Beyond record + transcribe: a hallucination filter (substring / exact: /
re: rules; suppressed segments kept in an audit array), silence-stripping
(trimmed copies in <session>/stripped/, originals untouched), summarization
(bundled local model, operator CLI, or an OpenAI-compatible API), a People
registry (cross-session speaker identity → display name, with merge/detach),
and an end-of-meeting pipeline (strip → transcribe → summarize as one job a
Bridge can trigger and poll with the tap token — no operator in the loop).
CONTEXT.md has the domain model.
Dashboard
One operator console at / — the "Stages" UI. A slim left spine navigates the
global views (Taps · Sessions · People · Settings) and the per-session journey
(Capture → Recordings → Transcript → Summary); the active-taps rail follows you
across views. Live captions stream in Capture; silence-stripping and the
per-WAV files live in Recordings; the ▶ transcribe range button, the
meeting-languages declaration (ADR-0011), and the merged transcript live in
Transcript; the live and batch engine pickers live in Settings.
Captured live by the browser E2E test: the Apollo 11 fixture through the
bridge and a real faster-whisper tiny.en. Bigger models clean up the
self-flagged low-confidence output considerably.
Quick start (macOS / Linux)
bash start.sh # localhost only
bash start.sh --lan # bind 0.0.0.0
The script finds Python 3.12+, creates .venv, base-installs the package, and
launches the recorder on port 8001. Pick transcription models in the browser at
http://localhost:8001/setup (/ redirects there until a model is installed);
/setup doubles as the manage-models surface. Other flags: --non-interactive
(headless — install the saved/default model selection without the browser),
--no-mlx (skip MLX on Apple Silicon; live and batch both use MLX there by
default), --auto-live (boot with live captions running), --tls, --no-auth
(dev only).
Live captions are off by default: start whisperlivekit-server from the
dashboard. The recorder supervises it as a child on an ephemeral internal port
(pin one with SX_PORT_WLK); child logs are prefixed [wlk]. Ctrl+C stops the
recorder and any child.
On first run two secrets are generated and printed:
- a dashboard password for HTTP Basic auth, persisted to
.auth-password; - a
/tapbearer token for bridges, persisted to.tap-token— paste it into the bridge popup along with host and port.
Rotate with .venv/bin/tapscribe --rotate-password / --rotate-tap-token.
start.sh --tls serves https:// + wss:// with a self-signed cert generated
on first boot (.tapscribe-cert.pem, .tapscribe-key.pem) and reused after;
to supply your own, run tapscribe directly with --tls --cert <path> --key <path> (recorder flags, not start.sh flags).
Windows
Windows Bundle (no Python needed)
Download TapScribe-Setup-win-x64.exe from the
latest release and run
it. Per-user install (no admin prompt), carries its own interpreter; a tray
icon starts the dashboard and its Copy password item gives you the
generated login. The Bundle is unsigned, so SmartScreen warns — click More
info → Run anyway. Program files land in %LOCALAPPDATA%\Programs\TapScribe;
recordings, transcripts and settings live in %USERPROFILE%\TapScribe and
survive uninstall; model weights cache in %USERPROFILE%\.cache\huggingface.
See packaging/README.md and
ADR-0015.
From a checkout (developers)
.\start.ps1 # flags mirror start.sh: -Lan, -NoMlx, -AutoLive, -Tls, -NonInteractive
Other installs
- PyPI:
pip install tapscribe, thentapscribe. Models still install via/setup. - Docker (CPU-only):
docker run --rm -it -p 8001:8001 -v "$PWD/data:/data" ghcr.io/vortiago/tapscribe:latest
Configuration
Editable config files live under config/ and are re-read on every job; all
are editable from the dashboard's Settings view (further knobs — chunk sizes,
timeouts, model idle TTL — live under Settings → Advanced).
| File | Shapes |
|---|---|
config/prompt.txt / live-prompt.txt |
Batch initial_prompt / live --init-prompt (independent). Prose under ~150 words. |
config/hotwords.txt |
Batch hotwords — proper nouns; faster-whisper only. |
config/hallucinations.txt |
Suppression rules: substring, exact:, or re:. Ships with common YouTube-trained Whisper hallucinations. |
config/batch-model.txt / live-model.txt |
Default batch / live model ids. |
config/languages.txt |
Candidate languages (ADR-0010); default da,no,en. |
config/summarizer.json |
Default summarizer (source, command/model/base_url, prompt; api_key write-only). |
Architecture
One backend, one supervised child, N bridges. Audio flows in over WebSocket; captions and recordings come out.
flowchart LR
subgraph Meeting["Meeting platform (e.g. spatial.chat)"]
Bridge["Bridge<br/>(browser extension<br/>or native helper)"]
end
subgraph Host["TapScribe host"]
Backend["TapScribe backend<br/>FastAPI :8001<br/>/tap · /api · dashboard"]
WLK["whisperlivekit-server<br/>(supervised child, internal port)"]
WAVs[("recordings/<br/><session>/*.wav")]
end
Operator["Operator browser<br/>(dashboard)"]
Bridge -- "PCM 16k mono<br/>over WS /tap" --> Backend
Backend -- "forwards PCM" --> WLK
WLK -- "settled live captions" --> Backend
Backend -- "one WAV per utterance" --> WAVs
Operator <-- "HTTPS (500 ms /api/state poll)" --> Backend
One /tap WebSocket = one utterance. Each PCM frame is tee'd: appended to the
per-utterance WAV on disk and relayed to the WhisperLiveKit child for live
captions. The two sinks are independent — if WhisperLiveKit is down, recording
still works. Bridges never talk to the child directly.
Backends
| Family | Models | Backend | Languages | Notes |
|---|---|---|---|---|
| Whisper | tiny(.en)/base(.en)/small(.en)/medium(.en), large-v3, large-v3-turbo |
mlx-whisper (AS) / faster-whisper | English-only suffixes; else multilingual | large-v3-turbo is the default generalist (ADR-0010). |
| NB-Whisper | nb-whisper-tiny/base/small/medium/large |
faster-whisper on CT2 weights | Norwegian | Pulled from NbAiLab/nb-whisper-*/ct2/. No MLX. |
| Voxtral | voxtral-mini |
mlx-voxtral (AS) / HF transformers | EN/ES/FR/PT/HI/DE/NL/IT | Batch-only. First load downloads ~6 GB. |
| Parakeet | parakeet-tdt-0.6b-v3 |
parakeet-mlx (AS) / HF transformers | 25 EU languages, no Norwegian | Batch-only; replaced Canary (ADR-0006). |
| Moonshine | moonshine-tiny/base |
mlx-audio (AS) / ONNX-CPU | English | Live-only low-latency captions. Extras [moonshine-mlx] / [moonshine-cpu]. |
Tests
pip install -e ".[dev]"
python -m pytest -q
Unit + route tests (backends stubbed, under 20 s), an HTTP pipeline E2E over
real /tap WebSockets, a real-Whisper E2E on committed CC-licensed fixtures,
a Playwright dashboard-UI E2E, and a headed bridge-extension E2E. See
CONTRIBUTING.md for how to run each layer. GitHub Actions
runs the suite and ruff on every push and PR across Python 3.12-3.14 on
Ubuntu, macOS, and Windows.
License
MIT. See LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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 tapscribe-1.3.0.tar.gz.
File metadata
- Download URL: tapscribe-1.3.0.tar.gz
- Upload date:
- Size: 3.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4d7989248732161a447c9e1019fce32cc45245287e7a5779a74abe6647685f08
|
|
| MD5 |
b849cd7ecde353110b01c8346aaaf68a
|
|
| BLAKE2b-256 |
bd2078729bf0e69d4a7a69f047807bfc987d8311e53d59695c3ec1253a783914
|
Provenance
The following attestation bundles were made for tapscribe-1.3.0.tar.gz:
Publisher:
release.yml on Vortiago/TapScribe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tapscribe-1.3.0.tar.gz -
Subject digest:
4d7989248732161a447c9e1019fce32cc45245287e7a5779a74abe6647685f08 - Sigstore transparency entry: 2617995979
- Sigstore integration time:
-
Permalink:
Vortiago/TapScribe@2e40c435382ef12b5edac06e08c615029993490c -
Branch / Tag:
refs/tags/v1.3.0 - Owner: https://github.com/Vortiago
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2e40c435382ef12b5edac06e08c615029993490c -
Trigger Event:
push
-
Statement type:
File details
Details for the file tapscribe-1.3.0-py3-none-any.whl.
File metadata
- Download URL: tapscribe-1.3.0-py3-none-any.whl
- Upload date:
- Size: 2.8 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
442465590870eb2f4393fee63d39b730ac28a65bb4b2381378e3f8637fa7ad88
|
|
| MD5 |
3e780aa8aac566300570476bf16fa74f
|
|
| BLAKE2b-256 |
52fc3cacbf660c0e6b2961ba18455359ae6e99e91477c2fdf9ddc8cf43b44ea1
|
Provenance
The following attestation bundles were made for tapscribe-1.3.0-py3-none-any.whl:
Publisher:
release.yml on Vortiago/TapScribe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tapscribe-1.3.0-py3-none-any.whl -
Subject digest:
442465590870eb2f4393fee63d39b730ac28a65bb4b2381378e3f8637fa7ad88 - Sigstore transparency entry: 2617995986
- Sigstore integration time:
-
Permalink:
Vortiago/TapScribe@2e40c435382ef12b5edac06e08c615029993490c -
Branch / Tag:
refs/tags/v1.3.0 - Owner: https://github.com/Vortiago
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2e40c435382ef12b5edac06e08c615029993490c -
Trigger Event:
push
-
Statement type: