Skip to content

fix(privacy): stop persisting dictated content to the disk log#403

Open
postoso wants to merge 2 commits into
altic-dev:mainfrom
postoso:fix/privacy-no-transcript-in-disk-logs
Open

fix(privacy): stop persisting dictated content to the disk log#403
postoso wants to merge 2 commits into
altic-dev:mainfrom
postoso:fix/privacy-no-transcript-in-disk-logs

Conversation

@postoso

@postoso postoso commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

DebugLogger persists every log line to ~/Library/Logs/Fluid/Fluid.log unconditionally: FileLogger.append runs before the loggingEnabled guard, so the "Enable debug logs" toggle only gates the in-memory UI panel, not the disk file. As a result, on release builds the dictated transcript, spoken prompts/instructions, selected text, and LLM request/response bodies were written to that log in plaintext. The dictation prompt-trace path was also on by default, so it logged the transcript + prompt + AI answer on every AI-enhanced dictation.

This redacts user content to metadata only (lengths, byte counts, model/provider names, status, response field names) at every affected site, keeping diagnostics useful. Full prompt traces are still available on the console behind FLUID_PROMPT_TRACE=1, but are never persisted to disk.

Scope

28 logging sites across 9 files (LLMClient, AIProvider, FunctionCallingProvider, ASRService, the two Apple speech providers, ContentView, RewriteModeService, TypingService). Includes LLMError.httpError's description, which embedded the raw provider error body and escaped to the log via downstream error.localizedDescription calls.

Testing

  • xcodebuild build: succeeds
  • swiftlint --strict: 0 violations
  • Completeness cross-checked (a remaining-leak re-grep + a cross-model review). No regression test added: the project has no SwiftPM test target and these surfaces are private, so a dedicated Xcode test target would be disproportionate; the completeness review is the gate.

DebugLogger.log() writes every line to a persistent plaintext file
(~/Library/Logs/Fluid/Fluid.log) unconditionally: FileLogger.append
runs before the `guard loggingEnabled` check, so the EnableDebugLogs
toggle only gates the in-memory UI panel, not the disk file. As a
result, multiple log call sites persisted the user's dictated
transcript, spoken prompts/instructions, selected text, and the
AI-processed result to disk in plaintext on release builds.

Redact user content at every leak site, logging metadata (lengths,
byte counts, model/provider names, status, response field names)
instead of the content itself, while keeping diagnostics useful:

- LLMClient: redact the cURL request body, the request-body debug
  log, the full streaming delta (now logs field names only), the
  streamed content chunk, and the non-streaming HTTP-error body.
- LLMClient: LLMError.httpError.errorDescription no longer embeds the
  raw provider error body, which callers persist via
  error.localizedDescription; expose status code and body size only.
- AIProvider / FunctionCallingProvider: redact request body, response
  body, error bodies, and tool-call arguments.
- ASRService: redact the final transcript, post-processed transcript,
  streaming partial, and boosted-term logs.
- AppleSpeechAnalyzerProvider / AppleSpeechProvider: redact
  recognized-speech logs.
- ContentView / RewriteModeService: the prompt-trace sinks
  (logDictationPromptTrace, logPromptTrace) persist only a redacted
  metadata line; the full trace remains available on the console
  behind the explicit FLUID_PROMPT_TRACE=1 env var.
- TypingService: drop the typed-text content previews; the adjacent
  length logs are retained.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant