Build Superscribe

KINDAreplaces $38/mosaves $456/yrback to the verdict

0%0 of 17 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A dictation tool whose hard problems are capturing only while a key is held without becoming a keyboard observer, keeping an honest record of audio that leaves the machine for a transcription service, and inserting text into another application without stealing or losing the clipboard.

estimated effort weekendthe files for this build are in the project pack

RuntimeA native macOS menu bar application built with Swift Package ManagerHotkeyA registered system hotkey, not an event tap observing all inputCaptureAudio engine opened on key down and closed on key up, with the state shownTranscriptionOne streaming provider with a local model as an equal path, chosen per dictationInsertionPaste on release with the previous clipboard restored after a confirmed paste

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. have readyfree

    Why The permission prompts and secure input behaviour are the interesting part of this build and they only test properly on an account that has not already granted everything.

    Get it Create a fresh user account you can reset.

    Verify A first run on the clean account prompts for microphone access and nothing else until insertion is used

  2. installfree

    Why Builds the menu bar application without an Xcode project to maintain.

    Get it Install the command line developer tools.

    Verify swift build produces a runnable binary

  3. API keypay per minute of audio; the monthly ceiling in this build is what keeps it bounded

    Why Remote mode is the fast path, and this is the only component that costs money or sends audio anywhere.

    Get it Create a key with a provider whose retention policy you have read, and record what it says.

    Verify A held key produces a transcript and a transmissions row

  4. installfree

    Why It is a selectable mode rather than a fallback, and a local path nobody tested is the same as not having one.

    Get it Download a small model and a runtime that can transcribe a wav file offline.

    Verify Local mode produces a transcript with the network disconnected

  5. decidefree

    Why Audio, transcripts and retention windows are much easier to decide now than after a month of dictations exists on the disk.

    Get it Write down whether audio is kept at all, for how long, and what the redaction patterns are.

    Verify The settings match the written policy and the README states it

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- dictations(id, started_at, ended_at, duration_ms, mode, provider, model_id, transcript_path, cleanup_applied, target_bundle_id, cost_minor)
- transmissions(id, dictation_id, provider, endpoint_host, bytes_sent, seconds_sent, retention_note, at) · one row per time audio left the machine, written before the socket opens
- audio_files(dictation_id, path, bytes, delete_after) · optional, off by default, and deleted on a timer when on
- redactions(pattern, label, added_on) · applied to the transcript before it is stored or shown in history
- apps(bundle_id, first_seen_at, insertion_mode, blocked) · per-application behaviour is explicit rather than guessed
- settings(hotkey, provider, retain_audio, transcript_retention_days, monthly_ceiling_minor)
- There is no keystroke table, no window content and no audio outside the retention setting. A dictations row records that something happened; the transcript itself is optional.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
STT_API_KEYsecretoptionalsk-...redactedRemote transcription only. Unset means the application runs entirely locally.
STT_ENDPOINT_HOSToptionalapi.example.comRecorded on every transmission row so history can say where audio went.
LOCAL_MODEL_PATHrequired~/Models/speech-small.binThe local transcription mode. Required, because it is a mode rather than a fallback.
RETAIN_AUDIOrequiredfalseOff by default. When on, files are deleted at their deadline even across restarts.
TRANSCRIPT_RETENTION_DAYSrequired30Transcripts and their rows are removed together at this age.
MONTHLY_CEILINGrequired4.00Remote transcription stops at this figure and local mode takes over.
HISTORY_DIRrequired~/Library/Application Support/DictationTranscripts and the database. Redacted before anything is written here.

The build, in order

  1. A microphone that is shut unless you are holding the key

    Capture begins on key down and ends on key up, the application never observes other keystrokes, and the visible state always matches the hardware.

    1. The permission you are not asking for is the point.

      Files Sources/Hotkey/Register.swiftTests/NoEventTapTests.swift

    2. A flag and a microphone can disagree, and the flag is the one that lies.

      Files Sources/Capture/Engine.swift

    3. Every one of these has left a microphone open in some shipped app.

      Files Sources/Capture/Lifecycle.swift

    done when · tick each as it passes
    watch out
    • Implementing the hotkey with an event tap that watches all keyboard input gives your app the ability to read passwords, requires the permission the operating system grants most reluctantly, and stops working the moment any application enables secure keyboard entry, which you will then be tempted to work around. Register the hotkey through the supported interface, accept that it does not fire inside a secure input window, and say so in the interface rather than retrying.
  2. What left the machine, and when

    Every transmission to a remote service is recorded before it happens, and the local path works as well as the remote one.

    1. Afterwards is too late for the only case that matters.

      Files Sources/Transcribe/Remote.swiftTests/TransmissionRecordTests.swift

    2. A path nobody tests is a path that does not work.

      Files Sources/Transcribe/Local.swift

    3. The honest answer to where did that go is in the list.

      Files Sources/UI/HistoryView.swift

    done when · tick each as it passes
    watch out
    • Recording the transmission after the call returns means a crash, a cancellation or a network failure leaves audio that went to a third party with no local record of it having gone, which is precisely the case somebody will ask about. Write the row first, mark it complete afterwards, and treat a row without a completion as audio that probably left rather than audio that did not.
  3. Getting the text into the other application

    The transcript arrives in the focused application without stealing the clipboard or corrupting a field.

    1. A timer-based restore eats the dictation on a busy machine.

      Files Sources/Insert/Paste.swiftTests/ClipboardRestoreTests.swift

    2. Terminals, editors and browser fields all behave differently.

      Files Sources/Insert/AppPolicy.swift

    3. Live insertion into someone else's document is how text gets mangled.

      Files Sources/Insert/Gate.swift

    done when · tick each as it passes
    watch out
    • Restoring the clipboard on a fixed timer after issuing the paste means that on a slow or busy application the restore lands first and the user pastes whatever was there before, losing the dictation and looking like the tool dropped it. Confirm the paste landed before restoring, cap the wait, and if confirmation never arrives leave the transcript on the clipboard and say so rather than restoring over it.
  4. History is a liability, so treat it like one

    Transcripts and audio expire, redaction happens before storage, and deleting really deletes.

    1. The file is what other software reads.

      Files Sources/Store/Redact.swiftTests/RedactionOnDiskTests.swift

    2. A deadline that passes while the app is closed still passed.

      Files Sources/Store/Retention.swift

    3. A silent delete is indistinguishable from a broken one.

      Files Sources/UI/PrivacyPane.swift

    done when · tick each as it passes
    watch out
    • Redacting at display time leaves the unredacted transcript sitting in a plain file that any backup, any search index and any other application can read, so the redaction protects the interface rather than the user. Redact before the write, keep no shadow copy of the original, and test by grepping the transcript directory for the pattern after a dictation containing it.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more dictation.