You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Fill password fields: from the Keychain by reference, or agent-supplied text with approval #117
Decision (user, 2026-10-17). Closes #75's gap 2, which unblocks gaps 3 and 4.
Primary: fill from the Keychain by reference.
The agent names a Keychain item and never sees the secret.
The host reads the item with SecItemCopyMatching, so macOS's own Keychain prompt gates each item.
The host fills the item only into an AXSecureTextField in the snapshot. For an internet password, the field's web area URL host must match the item's server.
Fallback: agent-supplied text, with explicit approval.
The menu bar app shows a confirmation for each fill, naming the caller, the app and the site.
A setting, "Always allow agent-supplied passwords", skips that confirmation. It is off by default.
The secret is never logged, echoed in results, or kept in Recent Activity. Activity records only that a fill happened, with the caller and the target.
A fill is completed only on a readback that doesn't reveal the secret, such as the character count. Otherwise it is unknown.
Live spike first (the screen is unlocked now):
Does typed input or an AXValue set reach a secure field while secure keyboard entry is on? Test in a native NSSecureTextField and in a Chrome password input.
Does SecurityAgent (the admin prompt) accept synthetic input? Type a dummy value, then cancel. Never submit.
Are Wi-Fi passwords reachable, given they're in the System keychain?
off (secure input is on only while the field is focused in the active app)
delivered (field length 0 → 1)
refused by Peekaboo policy ("Direct value setting is not allowed for secure text fields") before dispatch
NSSecureTextField, app frontmost
on
delivered (0 → 1)
refused by policy, as above
Chrome password input, Chrome in background while another app held secure input
on (held by the other app)
delivered: length 1 and one input event on the page
desktop_type took the AX set route and was refused by policy. It came back as unknown with "incomplete exact-window identity", although nothing was dispatched (a labeling bug).
Chrome password input, Chrome frontmost
on (Chrome's own)
delivered: length 2 and two input events
not retried
Secure keyboard entry doesn't block the host's window-targeted events, in either app, whether foreground or background. It blocks other processes from reading keystrokes, not this delivery.
AX set at the OS level is untested. Peekaboo refuses it by policy in several places (BackgroundInputDriver, ActionInputDriver, TypeService, DialogService). The key-event route works everywhere tested, so the fill tool doesn't need AX set.
Characters outside the keyboard layout:desktop_key maps letters through the layout. A fill must send each character as a Unicode key event, as web typing already does, so symbols and non-layout characters work. I tested letters only.
2. SecurityAgent (admin prompt)
Trigger:security find-generic-password -D "AirPort network password" -w /Library/Keychains/System.keychain, with stdout sent to /dev/null. It opened "security wants to use the “System” keychain" with User Name, Password, Deny and Allow.
Readable: it's an ordinary exact window of SecurityAgent, combined_eligible, with all controls in Accessibility.
Input: clicking the password field focused it. One desktop_key "x" was delivered: its value went from "" to one mask character.
Cancelled: I clicked Deny. security exited with 128 (cancelled), and no secret was read. Enter and Allow were never sent.
Conclusion: SecurityAgent accepts synthetic input from the host. Filling an admin prompt is therefore technically possible, so the policy has to decide it. An admin password is the riskiest thing to fill, so it's worth a separate decision from website passwords.
3. Wi-Fi passwords (System keychain)
/Library/Keychains/System.keychain holds 15 "AirPort network password" items. Their attributes (SSID and so on) can be listed without authentication.
Reading an item's data opens the admin prompt above, every time. That's the System keychain ACL, which only airportd may read.
SecItemCopyMatching against the data-protection keychain doesn't reach the System keychain; that needs the legacy file-keychain API. A fill by reference for Wi-Fi would therefore cost the user an admin password prompt per read.
Recommendation: don't support Wi-Fi by reference in v1. Known networks join on their own. For a new network, use a login-keychain item the user created, or agent-supplied text with approval.
The fill tool must never fill a SecurityAgent prompt its own Keychain read raised. That would answer macOS's gate for the user.
4. Readback that doesn't reveal the secret
Field
AXValue seen by another process
Readback
NSSecureTextField (AppKit)
one U+F79A (private-use mask) per character
character count
SecurityAgent
one U+F79A per character
character count
Chrome <input type=password>
one U+2022 (•) per character
character count
AXNumberOfCharacters stayed 0 in the AppKit field, so don't use it.
Plan: a fill is completed when the field's mask count goes from the value before to that value plus the filled length, after replacing the selection or clearing the field first. Otherwise it's unknown.
Secure detection is incomplete today: the inspected role is AXTextField, and the captured attributes carry no subrole. Only the localized roleDescription says "secure text field". The client's secure checks (role == "AXSecureTextField" || subrole == … in read.swift, elements.swift and table.swift) therefore never match.
Inspect and read currently pass the mask through, which reveals only the length.
The fill tool must identify secure fields host-side by reading AXSubrole directly, and the read paths should do the same.
Shipped in 0.14.0 by #124 (desktop_fill_password: Keychain item by reference, or agent-supplied text with a per-fill confirmation). #124 said "Implements" rather than "Closes", so this stayed open.
Activity
iamnbutler commentedon Oct 17, 2026
Live spike findings (2026-10-17)
Setup
NSSecureTextField.--user-data-dir, as in chore: run the Chromium live checks in an isolated Chrome, and add the #111 read check #116) on a local page with<input type=password>.1. Delivery into secure fields
desktop_key(window-targeted key events, background)inputevent on the pagedesktop_typetook the AX set route and was refused by policy. It came back asunknownwith "incomplete exact-window identity", although nothing was dispatched (a labeling bug).inputeventsdesktop_keymaps letters through the layout. A fill must send each character as a Unicode key event, as web typing already does, so symbols and non-layout characters work. I tested letters only.2. SecurityAgent (admin prompt)
security find-generic-password -D "AirPort network password" -w /Library/Keychains/System.keychain, with stdout sent to /dev/null. It opened "security wants to use the “System” keychain" with User Name, Password, Deny and Allow.SecurityAgent,combined_eligible, with all controls in Accessibility.desktop_key"x" was delivered: its value went from""to one mask character.securityexited with 128 (cancelled), and no secret was read. Enter and Allow were never sent.3. Wi-Fi passwords (System keychain)
/Library/Keychains/System.keychainholds 15 "AirPort network password" items. Their attributes (SSID and so on) can be listed without authentication.SecItemCopyMatchingagainst the data-protection keychain doesn't reach the System keychain; that needs the legacy file-keychain API. A fill by reference for Wi-Fi would therefore cost the user an admin password prompt per read.4. Readback that doesn't reveal the secret
AXValueseen by another process<input type=password>AXNumberOfCharactersstayed 0 in the AppKit field, so don't use it.completedwhen the field's mask count goes from the value before to that value plus the filled length, after replacing the selection or clearing the field first. Otherwise it'sunknown.AXTextField, and the captured attributes carry nosubrole. Only the localizedroleDescriptionsays "secure text field". The client's secure checks (role == "AXSecureTextField" || subrole == …in read.swift, elements.swift and table.swift) therefore never match.AXSubroledirectly, and the read paths should do the same.Also seen (separate from #117)
unknown(feat: turn on Chromium and Electron web accessibility before inspect and read, once per app launch #107): inspecting an isolated Chrome (temporary profile, chore: run the Chromium live checks in an isolated Chrome, and add the #111 read check #116's launch) on 0.13.0 returnedweb_accessibility: "unknown"twice, and the page had no web elements. With--force-renderer-accessibility, the page tree appeared. Worth checking whether chore: run the Chromium live checks in an isolated Chrome, and add the #111 read check #116's feat: turn on Chromium and Electron web accessibility before inspect and read, once per app launch #107 check passes on the installed host.Proposed fill design (for review)
AXSubroleisAXSecureTextField, checked host-side, in a fresh snapshot.desktop_key, but with the text decoded and typed host-side.feat: fill password fields from the Keychain or with approved agent t…
feat: fill password fields from the Keychain or with approved agent t…
feat: fill password fields from the Keychain or with approved agent t…
feat: fill password fields from the Keychain or with approved agent t…
iamnbutler commentedon Oct 20, 2026
Shipped in 0.14.0 by #124 (
desktop_fill_password: Keychain item by reference, or agent-supplied text with a per-fill confirmation). #124 said "Implements" rather than "Closes", so this stayed open.