No description
  • Swift 97.2%
  • Shell 2.4%
  • Python 0.4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Vadym Samoilenko 2d87fed339
Some checks failed
CI / build-and-test (push) Has been cancelled
chore(release): 0.1.46
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-04 21:03:34 +01:00
.github/workflows ci: build and test on every push and pull request 2026-07-01 23:56:16 +01:00
build chore(entitlements): drop unused screencapture mach-lookup exception 2026-06-22 09:52:17 +01:00
dist chore(release): notes for 0.1.45 2026-09-04 16:04:59 +01:00
Docs docs: gate chat and podcast features behind Pro license 2026-07-07 13:16:21 +01:00
output/imagegen chore: initial commit — baseline before audit fixes 2026-06-16 19:00:11 +01:00
Packaging chore(release): 0.1.46 2026-09-04 21:03:34 +01:00
Scripts chore(release): RELEASE_SKIP_UPLOAD=1 stops before rsync 2026-09-04 14:22:51 +01:00
Sources/LocalWhisper feat: rename meetings from the sidebar context menu 2026-09-04 17:05:25 +01:00
Tests/LocalWhisperTests feat: call participants feed the recognition vocabulary; hallucination corpus 2026-09-04 16:04:59 +01:00
tools/license feat(distribution): add signing, notarization, Sparkle updates, and licensing 2026-06-18 12:30:35 +01:00
Vendors fix(security): keep API credentials out of plaintext files and argv 2026-07-02 15:33:42 +01:00
.env.example feat(telemetry): add Sentry crash/error reporting 2026-06-24 19:22:50 +01:00
.gitignore chore(build): silence compiler warnings 2026-07-01 22:51:04 +01:00
CLAUDE.md docs: participant names, vocabulary correction, Voice Studio bridge; notes for 0.1.44 2026-09-04 15:50:30 +01:00
DESIGN.md feat(ui): design system and full UI/UX redesign for neurodivergent accessibility 2026-06-17 11:44:48 +01:00
Package.resolved feat(telemetry): add Sentry crash/error reporting 2026-06-24 19:22:50 +01:00
Package.swift feat(transcription): decode only speech blocks and keep word timings 2026-09-04 13:58:15 +01:00
PRODUCT.md feat(ui): design system and full UI/UX redesign for neurodivergent accessibility 2026-06-17 11:44:48 +01:00
README.md feat(distribution): add signing, notarization, Sparkle updates, and licensing 2026-06-18 12:30:35 +01:00

LocalWhisper

Local-first macOS transcription app built with SwiftUI and whisper.cpp.

Current Status

  • Native macOS SwiftUI shell.
  • Local file transcription via whisper-cli (audio conversion uses built-in AVFoundation).
  • Metal-enabled whisper.cpp backend.
  • Meeting detection banner with developer bypass mode.
  • Permission state dashboard.
  • Model manager shell.
  • Queue, history and transcript detail views.
  • Swift Testing coverage for parser, diarization mapping and bypass detector.

Build

cd /Users/ai_leed/Documents/Projects/LocalWhisper
DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer swift build

Test

cd /Users/ai_leed/Documents/Projects/LocalWhisper
DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer swift test

Run With Developer Permission Bypass

cd /Users/ai_leed/Documents/Projects/LocalWhisper
LOCALWHISPER_BYPASS_PERMISSIONS=1 DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer swift run LocalWhisper

Bypass mode only bypasses LocalWhisper's internal workflow gates. It does not grant macOS Microphone, Screen Recording, Accessibility or Calendar permissions.

Build App Bundle

cd /Users/ai_leed/Documents/Projects/LocalWhisper
./Scripts/build_app.sh

The app bundle is created at:

/Users/ai_leed/Documents/Projects/LocalWhisper/dist/LocalWhisper.app

Smoke-run the built app with developer bypass:

cd /Users/ai_leed/Documents/Projects/LocalWhisper
LOCALWHISPER_BYPASS_PERMISSIONS=1 dist/LocalWhisper.app/Contents/MacOS/LocalWhisper

Distribution

Three Key Pairs — Never Mix Them Up

# Key pair Purpose Private key lives Public key lives
1 Apple Developer ID codesign + Gatekeeper + notarization macOS Keychain / CI secret CERTIFICATE_P12_BASE64 Embedded by Apple in the signed binary (cert chain)
2 Sparkle EdDSA (ed25519) Update package integrity — Sparkle verifies each DMG before installing macOS Keychain (local) / CI secret SPARKLE_ED_PRIVATE_KEY SUPublicEDKey key in Packaging/Info.plist
3 License Ed25519 Offline license-payload verification Your server / your machine only — never in the repo Constant embeddedPublicKeyBase64 in Sources/LocalWhisper/LicenseService.swift

One-Time Setup (per machine)

# 1. Generate Sparkle EdDSA key pair (run once, store private key in Keychain)
#    Sparkle's generate_keys is in .build/artifacts after the first `swift build`
find .build/artifacts -name generate_keys | head -1 | xargs -I{} {}
# → prints public key → paste it as SUPublicEDKey in Packaging/Info.plist

# 2. Generate license signing key pair (run on your server or secure machine)
swift tools/license/license-tool.swift generate-keys
# → prints private key (store securely, never commit) and public key
# → paste public key as embeddedPublicKeyBase64 in Sources/LocalWhisper/LicenseService.swift

# 3. Store notarytool credentials in Keychain
xcrun notarytool store-credentials notarytool-creds \
  --apple-id "$APPLE_ID" \
  --team-id "$APPLE_TEAM_ID" \
  --password "$APPLE_APP_SPECIFIC_PASSWORD"

Signing & Notarization

# Prerequisites: .env filled in from .env.example, app bundle already built
source .env
./Scripts/build_dmg.sh            # build → dist/LocalWhisper-Installer.dmg
./Scripts/sign-and-notarize.sh    # sign (inside-out, no --deep) → notarize → staple → verify

The script signs in the correct inside-out order:

  1. dylibs in Contents/Frameworks/
  2. Sparkle.framework nested helpers (XPC services, Updater.app, AutoUpdate)
  3. Contents/Resources/whisper.cpp/bin/whisper-cli (stale build-tree rpaths stripped first)
  4. Contents/MacOS/LocalWhisper
  5. Outer .app bundle with build/entitlements.plist

Final checks run automatically: codesign --verify --strict, spctl -a -t exec -vvv, xcrun stapler validate.

Full Release (build → sign → notarize → Sparkle sign → upload)

source .env
./Scripts/release.sh 1.2.0    # bumps Info.plist, builds, signs, notarizes,
                               # signs update with Sparkle EdDSA, generates appcast.xml,
                               # rsyncs to $UPDATE_HOST_USER@$UPDATE_HOST:$UPDATE_HOST_PATH

CI (GitHub Actions)

Workflow at .github/workflows/release.yml. Trigger: workflow_dispatch with version input.

Secrets to configure in GitHub → Settings → Secrets:

Secret Description
CERTIFICATE_P12_BASE64 base64 of Developer ID .p12 (base64 -i cert.p12)
CERTIFICATE_P12_PASSWORD .p12 export password
DEVELOPER_ID_APP "Developer ID Application: Name (TEAMID)"
APPLE_ID Apple ID email
APPLE_TEAM_ID 10-char team ID
APPLE_APP_SPECIFIC_PASSWORD app-specific password from appleid.apple.com
SPARKLE_ED_PRIVATE_KEY Sparkle EdDSA private key (base64)
UPDATE_HOST_USER SSH user for rsync upload
UPDATE_HOST hostname for rsync upload
UPDATE_HOST_PATH remote path (e.g. /var/www/updates/)
SPARKLE_FEED_URL Full URL of appcast.xml (e.g. https://updates.localwhisper.dev/appcast.xml)

Key Rotation

  • Apple Developer ID: revoke old cert in Developer portal, issue new one, re-export .p12, update CI secret. No code change needed.
  • Sparkle EdDSA: run generate_keys again, update SUPublicEDKey in Packaging/Info.plist, update CI secret. Old updates already delivered are unaffected; new releases use new key.
  • License key pair: generate new pair with tools/license/license-tool.swift generate-keys, update embeddedPublicKeyBase64 in LicenseService.swift, ship a new app version. Old signed licenses signed with the old private key will stop verifying offline — issue replacement licenses to affected customers.

Licensing

Tiers: Free (cloud STT on BYO API key) · Pro (one-time, unlocks local pipeline) · Teams (multi-seat).

Users activate in Settings → License using a Lemon Squeezy license key.

# Issue a license offline (server-side, private key required)
swift tools/license/license-tool.swift issue \
  --tier pro --email [email protected] --seats 1 \
  --private-key "$LICENSE_SIGNING_PRIVATE_KEY"

Licenses are verified first offline (Ed25519 signature against the embedded public key) then periodically online with a 14-day grace period.

Local Whisper Backend

Expected files:

  • Vendors/whisper.cpp/build/bin/whisper-cli
  • Vendors/whisper.cpp/models/ggml-base.bin

Rebuild backend:

cd /Users/ai_leed/Documents/Projects/LocalWhisper/Vendors/whisper.cpp
cmake -B build -DCMAKE_BUILD_TYPE=Release -DGGML_METAL=ON
cmake --build build --config Release -j
./models/download-ggml-model.sh base