C.W.K.
Stream
Lesson 01 of 05 · published

Two Terrifying Permissions

~11 min · permissions, tcc, accessibility, microphone

Level 0Unlit
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"Hear everything I say, and be able to type into every app I use." Read that back and notice it's the spec for both a dictation tool and a keylogger.

The Ask

Firekeeper needs two permissions that should make a thoughtful user pause. Microphone — it can hear you. Accessibility — it can read the focused element in other apps and act on them. Every dictation app needs exactly these, and so does malware. There is no clever architecture that removes the need; a tool that types what you say must be able to hear and to type. What you can control is asking for nothing beyond that, and behaving in a way that justifies the grant.

The Permission Surface Is a Design Output

Microphone         REQUIRED    -- it has to hear you
Accessibility      REQUIRED    -- selection, target context, robust insertion
Input Monitoring   AVOIDED     -- design chose flagsChanged over an event tap
Automation         AVOIDED     -- no AppleScript unless one app profile demands it
Network            SCOPED      -- only the provider you selected, never by default

Notice Input Monitoring is avoided, not accepted as a cost of doing business. That's the earlier hotkey decision paying a dividend here: because activation observes modifier flags instead of intercepting the event stream, the app never has to ask "can I watch every keystroke?" Permission surface isn't fixed by your feature list — it's an output of your architecture, and you can design it down.

Ask Honestly, and Early

Two habits make the ask fair. First, request the microphone at launch rather than ambushing the user mid-dictation — a permission prompt firing the instant you start talking is both startling and guarantees you miss the first words. Second, make the state legible: a Setup panel showing live permission status with buttons that open the right System Settings pane, so the user always knows what they've granted and can revoke it. An app that's cagey about what it holds has already lost the argument that it deserves to hold it.

Permission surface is designed, not discovered. Before accepting that a feature requires a scary grant, ask whether a different implementation gets the same interaction with a smaller ask. Firekeeper's bare-Fn hold and a keylogger-grade event tap deliver the same UX; only one of them requires 'watch every keystroke.' The interaction was worth keeping; the permission wasn't.
You are asking for the same capabilities as malware. That's not hyperbole and it's not a reason to be defensive — it's a reason to be rigorous. Everything in this track (no retention, secure-field refusal, visible recording, provider selection before send) exists because the honest response to 'this app could be spyware' is not reassurance, it's architecture the user can verify.

Code

Ask early, surface the state, and hold nothing extra·swift
func applicationDidFinishLaunching() {
    // Ask at launch — never ambush the user mid-utterance,
    // and never lose the first words to a permission sheet.
    AVCaptureDevice.requestAccess(for: .audio) { _ in }

    // Accessibility is required for selection + robust insertion.
    // Surface it; don't silently degrade and leave the user guessing.
    let trusted = AXIsProcessTrustedWithOptions(
        [kAXTrustedCheckOptionPrompt.takeUnretainedValue(): true] as CFDictionary)
    setupPanel.accessibilityGranted = trusted
}

// Note what's absent: no Input Monitoring request, no Automation.
// The hotkey design made those unnecessary.

External links

Exercise

List the permissions an app you use requests. For each, ask: is this genuinely required by the feature, or by the particular implementation the developer chose? Find one where a different design would have shrunk the ask — and one where the ask is truly irreducible.
Hint
Irreducible asks come straight from the product's nature (a dictation tool must hear you). Reducible ones come from implementation convenience — a broader API was easier than a narrower one. The distinction matters because users can't tell them apart from the outside, so every reducible permission you keep spends trust you didn't need to spend.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.