The retrospective log ring (RLog) is always on, has no user opt-in, and can be
exported by anyone at the machine via Save Retrospective Debug Logs. Recent
promotions of per-event handlers from DLog to RLog caused it to accumulate typed
characters (passwords included), command lines, credentials, URLs with embedded
secrets, keychain identifiers, and AI chat content with debug logging off.
Redact these on the ring path only. RLog feeds the live debug log when debug
logging is on (an explicit opt-in whose log should stay complete) and the ring
otherwise, so redaction is conditional on gDebugLogging:
- Swift: RLog now takes an RLogMessage with a custom string interpolation,
\(redacted: full, or: placeholder), that emits the full value when debug
logging is on and the placeholder when off (os_log's \(x, privacy:) pattern).
This removes the need to pair an RLog breadcrumb with a separate DLog carrying
the full value. RLog no longer accepts a bare String; a few string-concatenated
log calls became single interpolations to match.
- ObjC: RLogRedact(full, redacted) does the same for the macro-based RLog.
- Add -[NSEvent it_redactedDescription] (keycode and modifier flags, no typed
characters) and it_redactedDescription on NSURL and URL (scheme, host, and
path only, dropping userinfo, query, and fragment where secrets hide).
Command lines are the highest-volume category, so they are redacted wherever
they reach the ring. An object's -description can leak too: -[VT100ScreenMark
description] and -[iTermBackgroundCommandRunner description] embed the command
line, so logging one with %@ leaks it. Give those a -redactedDescription
(command omitted) and wrap every site that logs them with
RLogRedact(obj, obj.redactedDescription). Covered: shell-integration command
marks and command start/end, auto command history, session start (command, env,
substitutions), coprocess/launch/single-use/AppleScript/API-script commands and
arguments, the context-menu mark command, the git status-bar component, the
background command runner, the AI command-safety checker, command-started/
finished/running and script triggers, login-shell wrapped commands, the
constructed ssh command line and SSH conductor hook params, iTermCommandRunner
arguments, drag-drop/upload file paths, the SSH framer VT100 token, the AI
prompt template and an unsupported-subpart AI body, and the JSFetch error
response body and headers.
Three sites that log an actual plaintext user secret (snippet body, echoed
password during autofill, password-adapter login token) stay redacted on both
paths, since even opt-in debug logs travel with bug reports.
As defense in depth, scrub chars=/unmodchars= on the ring-write path so a future
RLog of a raw NSEvent cannot silently reintroduce the keystroke leak.