Field report · macOS 26–27 · Apple silicon
Unlocked
OpenAI's Codex Mac agent doesn't click, and it doesn't see through the lock. It messages apps directly through the accessibility plane, and to run a locked Mac it briefly removes the lock — then puts it back.21
The finding, first
An agent that never touches the input stream.
The Codex computer-use service on this machine imports the entire accessibility read-and-write surface — AXUIElementPerformAction, AXUIElementSetAttributeValue, AXUIElementCreateSystemWide — plus ScreenCaptureKit for pixels. It imports no synthetic-event API at all: no CGEventPost, no CGEventCreateMouseEvent, no IOHIDPostEvent.1 All four research backends that read the architecture reached the same conclusion independently.87
That single absence explains almost everything people find surprising about it. An accessibility action is an inter-process message to the target application; a synthetic event is an injection into the one input stream the whole desktop shares. Message the process and you can press a button on a window that is not frontmost, on another Space, behind three other windows — without moving the pointer, without stealing focus, without the user seeing a thing.7 The "parallel cursors that don't foreground your apps" that Codex demonstrates are drawn overlays. There is one real pointer, and Codex is not using it.6
This page was written from a research panel of five backends, of which four returned; every report read end to end, anchored to first-party inspection of the machine it was written on. Where the backends disagreed, the disagreement is kept, not smoothed. Every claim below opens its source on hover, focus or tap.
Mechanism · two control planes
Two ways to move a Mac. Codex only uses one.
macOS has two entirely separate ways for software to drive the interface. They have different transport, different failure modes, and — the part that matters — different foreground rules.
AX + Apple Events
Sent to a specific process, or a specific element in it. Never enters the shared event queue. Works on background, occluded and off-Space windows without activation.7
CGEventPost
Injected into the WindowServer input stream and delivered wherever keyboard focus happens to point. To type into a window you must first bring it to the front.10
Whether every toolkit honours an AX action on an unfocused window is itself contested across the panel — see the disagreements below. But the plane Codex chose is the one that can, and the plane it declined is the one that structurally cannot.
The sharpest correction · locked operation
It does not see through the lock. It removes it.
While a Mac is genuinely locked, first-party logs show every real app returns cgWindowNotFound and only loginwindow answers.2 So the capability is not observation through the lock — it is a real unlock, brokered by a root plug-in that has rewritten this machine's unlock rule.1
Locked. Nothing reachable.
Session locked, Secure Input held by loginwindow. Chrome, Finder, every window: cgWindowNotFound or a 120-second timeout. Only loginwindow responds.2
Displays blank. The agent acts.
The session is now genuinely unlocked; the displays are covered so a passer-by sees nothing. Codex drives the normally-unlocked session through the same AX plane it always uses.8
Relock — and the rule stays.
When the turn ends it relocks. But removing the app does not restore the default unlock rule; the authorization database is system state, reverted only by hand.83 One report measured this path stalling a human unlock for 17.5 minutes versus ~73 ms once the rule was removed.3
This is a sanctioned Apple extension point — Jamf Connect, XCreds, Cisco Duo and Apple's own Platform SSO all participate in login or unlock the same way.7 What is unusual is a consumer AI agent using it on the unlock rule to run a Mac unattended. And the tightening that will eventually close third-party injection is already biting OpenAI itself: its plug-in is rejected by Library Validation on macOS 26.5 — "mapping process is a platform binary, but mapped file is not".4
The socket · what it is, and what it is not
World-writable, and fail-closed.
The unlock broker listens on /tmp/com.openai.sky.CUAService/LockScreenLoginAuthorization.sock, mode 0666 — any local process can open it. That permission bit reads alarming, and one backend flagged it as a channel through which "any local process could feed credentials" into the unlock flow.10
Probed directly on this machine, it isn't. Every unauthenticated request — a crafted one, and a silent connect with no bytes at all — returns a single word: DENY.1 The broker's own listener runs as the user, not root, and it authenticates its peer before it does anything. A second backend reasoned to the same place from first principles: a world-writable socket is not exploitable if the server checks who is on the other end.7
The honest limit of this test: it establishes fail-closed behaviour against an unauthenticated peer. It does not establish whether the authentication token itself could be forged — that would mean reversing the token scheme, which is a different exercise and edges toward attacking an unlock path rather than documenting one. So: world-writable but default-deny, and no exploitability claimed beyond that.
Reference · session-state capability matrix
What still works when the desktop isn't watching.
The reason the accessibility plane is the durable choice: it survives states that kill pixel capture and event injection outright. Compiled from the panel; cells the members split on are marked in the notes, not hidden.87
| Session state | AX read | AX action | Apple Events | SCK window | SCK display | CGEventPost |
|---|---|---|---|---|---|---|
| Window occluded | works | works | works | works | occluders | needs front |
| On another Space | works | degrades | works | degrades | wrong Space | fails |
| Window minimised | works | degrades | works | stale/none | fails | fails |
| Display asleep | works | works | works | may idle | may idle | wakes it |
| Screensaver | degrades | works | works | target-dep | saver only | dismisses |
| Session locked | loginwindow | not a state | if responsive | gone | lock pixels | secure UI |
| Fast-user-switched | fails | fails | same-session | fails | other console | fails |
| Headless / no display | works | works | works | needs surface | no display | undefined |
The single most operationally important row is another Space: a retained element reference keeps working while a fresh enumeration will not find the window at all. Build for observed-and-cached references, not per-step enumeration.8 And a captured background frame can be silently stale — SCFrameStatus.idle means "no new pixels," and an off-screen window may emit frames only when the mouse moves on its display.11
Auditability · the moving target
Its binary is a loader, not a capability list.
The Electron host ships a Node runtime and an Objective-C bridge, running a server-delivered kernel.js from a temp directory.1 That inverts the method this whole report rests on. Static import analysis tells you what the host linked; it cannot tell you what a given turn did, because the sequence of native calls is chosen by the server at runtime through the bridge.7
The pattern itself is now mainstream — Cloudflare's Code Mode and Anthropic's code-execution-with-MCP both hand the model generated code against a bounded binding surface.8 The open question, which no public source settles, is whether Codex's bridge is capability-allowlisted or open-ended. It is the highest-value thing left to determine, because notarisation and entitlement review are point-in-time attestations about a binary whose behaviour is refreshed from a server.7
The build question · agents that test UI
What you can actually build on macOS 26/27.
The same platform facts that shape Codex shape what a UI-testing toolchain can and cannot do. Four findings the panel agreed on, with the build each one implies.
Own your apps, or accept AX + pixels.
Every richer source — View Debugger transport, Metal capture, CoreText interposition — needs code injection the hardened runtime blocks on third-party apps.8 For apps you own, ship a #if DEBUG in-process reflector: the layer tree with resolved colour, font, radius, constraints and presentation values. A real computed-style analogue, no private API.7
Engineer determinism; the OS won't.
Core Animation timing runs in the out-of-process render server on the system clock, with no hook to slave it to a test clock.10 Determinism comes from settle-signals plus per-step state hashing — hash a normalised AX subtree and a frame region, and report the step of first divergence as your flake number.7
Parallelism is a hardware purchase.
Apple silicon caps concurrent macOS guests at two, via the kernel counter hv_apple_isa_vm_quota — the third returns VZErrorVirtualMachineLimitExceeded.12 Still enforced in the macOS 27 cycle; lifts only by disabling SIP.13 Meaningful parallel Mac UI testing means more Macs, not more VMs.8
An AX-driven suite tests a different app.
Electron and Chromium build no full tree until a client sets AXManualAccessibility; the flag is detectable by the app and changes its performance.7 So an AX-driven e2e suite measures the app in its assistive-technology configuration, not the one a human runs. State that in the methodology.9
And the pixel-free dream — drive apps through declared intents instead of the UI — doesn't hold at the OS level: App Intents has no third-party invocation API, the sanctioned callers are Shortcuts, Spotlight and Siri, and coverage across a real installed base is thin.15 Accessibility remains the only broad cross-app semantic surface, which is exactly why Codex falls back to it.8
Kept, not smoothed · where the panel split
The disagreements are the finding.
A panel exists to disagree. Support is counted in independent sources, not in how many backends nodded. Three splits worth carrying:
Does Electron honour AX actions on an unfocused window?
One backend reported Chromium/Electron discard AXPress when out of focus and require activation; two others reported they honour it once the tree is activated.98
Read: the confound is lazy-tree materialisation — an unactivated tree looks exactly like a refusal. Practically toolkit- and activation-dependent; don't assume either way without measuring your target.
Is the 2-VM cap real on macOS 26/27?
Two backends asserted a hard kernel cap; the most cautious member declined to confirm the number for the current OS from public API docs, marking it unverified.7
Resolved by hand: an independent check confirms the named kernel counter and error code, with a WWDC26 Q&A placing it in the macOS 27 cycle. The cap holds; the caution was over-conservatism.13
The single-member findings.
One backend alone reported the node_repl runtime leaking up to 271 unreaped processes and 10–13 GB;14 another alone reported the 17.5-minute unlock stall.3
Read: each is one member citing one issue. Real enough to record, not corroborated enough to assert — carried as attributed, single-source, exactly as they arrived.
Methods · what this page did and didn't do
How this was made.
- Panel
- Five research backends dispatched against one brief; four returned reports: Claude (local), Perplexity Sonar Deep Research, Gemini Deep Research (max), OpenAI gpt-5.6. The fifth, Codex CLI, produced nothing. The panel reported it as an argument-parsing failure; running the same command by hand showed the real cause was the CLI’s trusted-directory check, which is a different defect from the one that got logged.
- Cost
- Planned band $5.70–$20.00; actual $20.00 ($0 Claude-local, $4 Perplexity, $7 Gemini, $9 gpt-5.6). Reported before the spend and after.
- Read
- All four completed reports read end to end — not the outlines, not the merged distillation. The machine findings came first, by hand, and anchored the reading.
- Verified
- Citations checked across the two load-bearing members: no fabrications. One disputed load-bearing fact (the VM cap) re-checked with an independent search before it went on the page.
- First-party
- Binary imports, TCC grants, the authorization-database rule, running processes and a live benign socket probe — all read on the machine this page was written on.
- Human review
- A person chose the scope, the framing and the name, and reviewed the render before it was published. The socket was probed with explicit authorisation and no exploit was attempted.
- Could not establish
- Whether the Objective-C bridge is capability-allowlisted; the codex_chronicle binary's function; whether the socket's auth token is forgeable; and Codex's internal tool-selection order. All flagged inline, not papered over.
- Motion
- GSAP drives one scrubbed episode — the unlock timeline — because that sequence is a genuine mental comparison. No three.js: no claim on this page needs a third dimension. With motion off, every stage renders in full.
- Honest limit
- A scroll-driven cited page is measured to buy attention and perceived clarity — not comprehension. This page claims neither for itself.
Sources · openable, one per claim marker
The registry.
- First-partyDirect inspection of this machine — binary imports of SkyComputerUseService, TCC.db grants, the system.login.screensaver rule, running processes, and a benign live probe of the broker socket.
internal · capability inspection, 2026-08-12 - First-party bug reportopenai/codex #26743 — locked-session logs: cgWindowNotFound for real apps, only loginwindow reachable, AppleScript still works.
github.com/openai/codex/issues/26743 - First-party bug reportopenai/codex #29616 — the rewritten unlock rule contents, and a 17.5-minute unlock stall versus ~73 ms after removing it.
github.com/openai/codex/issues/29616 - First-party bug reportopenai/codex #24013 — Library Validation rejects the plug-in on macOS 26.5: "mapping process is a platform binary, but mapped file is not."
github.com/openai/codex/issues/24013 - Apple · primaryExtending authorization services with plug-ins — the sanctioned mechanism model behind the unlock rule.
developer.apple.com · authorization plug-ins - First-hand testingMacStories (Federico Viticci) — hands-on Codex Computer Use, documenting AX-primary observation and non-foregrounding parallel operation.
macstories.net - Panel · OpenAI gpt-5.6Dossier panel member, 50 sources — the most macOS-27-aware; source of the fleet-management, socket-authentication and own-app-reflector analysis.
panel report · docs/deep-research/openai-gpt56.md - Panel · Claude (local)Dossier panel member, 64 sources — best-sourced on the first-party GitHub evidence and the control-plane / capability-matrix detail.
panel report · docs/deep-research/claude-local.md - Panel · GeminiDossier panel member (max), 52 sources — source of the Electron-discards-AXPress reading and the single-member process-leak finding.
panel report · docs/deep-research/gemini.md - Panel · PerplexityDossier panel member (Sonar Deep Research), 20 sources — thorough, inference-heavy; the member that leaned toward the operate-through-lock and exploitable-socket readings the others corrected.
panel report · docs/deep-research/perplexity.md - Apple · primarySCFrameStatus / ScreenCaptureKit frame metadata — .idle means no new pixels; the basis for in-band stale-frame detection.
developer.apple.com · SCFrameStatus - Apple · primaryVirtualization framework — macOS guest save/restore (VZVirtualMachine); the isolation primitive for parallel sessions.
developer.apple.com · Virtualization - Web · verified by handThe 2-VM cap: kernel counter hv_apple_isa_vm_quota, VZErrorVirtualMachineLimitExceeded (code 6), macOS-guests-only, still enforced in the macOS 27 cycle.
khronokernel.com · Apple silicon VM limit - Bug report · single sourceopenai/codex #37672 — node_repl helper processes accumulating (up to 271, 10–13 GB). Reported by one panel member only; carried as attributed.
github.com/openai/codex/issues/37672 - Apple · primaryWWDC25 Session 275 — App Intents: invocation is system-mediated (Shortcuts, Spotlight, Siri); no third-party programmatic dispatch API.
developer.apple.com · WWDC25 275