Unified system access

System access is a host capability boundary. It is separate from model tool approval, workspace permission rules, account credentials and provider authorization. The product has one capability registry, one live report, one explicit setup path, one wait representation and one recovery policy. A status read never opens a native prompt. A model or a remote client cannot grant an operating-system capability.

Current implementation and verified gaps

The base runtime used two hard-coded macOS rows, screen_recording and accessibility. The backend report, native probe, request route, GUI preflight and Web settings each contained part of that list, while package declarations for other categories lived separately. This created drift and allowed a reconnect to look like an unsolicited permission request.

The implemented registry removes those duplicated capability definitions and keeps ordinary chat, browser and headless use independent from optional desktop access. The old two-capability API remains wire-compatible; its rows are generated by the registry. Package usage descriptions, entitlements, report schema, identity metadata, GUI preflight, settings UI and wait manifests now consume the same declarations.

Reference corpus and boundaries

ReferenceAdopted ruleBoundary
Apple Screen & System Audio RecordingmacOS owns the grant and the user controls it in System Settings.The product can open the relevant pane and verify the executing identity; it cannot silently enable the grant.
AXIsProcessTrustedWithOptionsAccessibility is queried separately and can be requested only from an explicit local-owner action.Trust is for the named runtime executor; another application or an anonymous Python process is not evidence.
Apple privacy controlsEach privacy category has its own consent and revocation lifecycle.“Configured in the plist” and “signed with an entitlement” are prerequisites, not grants.
macOS file and access controlsFile access is reported as an operation-specific capability.Full Disk Access cannot be inferred from a successful read of one path and is never requested as a blanket workaround.
Electron systemPreferencesQuery and request are separate operations.Electron APIs do not remove macOS consent or target-specific Apple Events authorization.

Linux portals and Windows desktop integrity rules remain platform-specific. The registry represents their state without pretending that a macOS setting exists on those platforms. Browser and VM surfaces use their own provider/session boundary and do not inspect local macOS permissions.

Capability registry

openprogram.system_access owns an immutable registry. Every entry has a stable id, category, display label, platform, required/optional policy, target identity, non-prompting check, explicit request mode, settings destination, package declaration, operation mappings, and recovery policy. Backend report, CLI/TUI doctor, HTTP setup, Web settings, wait manifests and tests consume this registry instead of defining capability ids independently. All macOS native checks and requests run through the signed runtime bundle identity ai.openprogram.runtime; a source checkout may fall back to its development interpreter, but an installed app never registers a bare python3 process.

FieldMeaning
idStable protocol id, for example screen_recording, accessibility, apple_events, calendar, reminders, file_read, file_write, microphone and camera.
subjectIdentity checked by the platform: runtime, containing_app, target_app, user_selected_path or provider_session.
checkNon-prompting probe. It returns granted, not_granted, unknown, unavailable or unsupported.
request_modenative for a supported OS request, settings for a settings-only capability, targeted when a target application must be supplied, operation when only the real operation can verify it, and none when the platform has no request API.
settingsFixed, capability-specific System Settings URL or a documented instruction. It is opened only after an owner clicks the action.
identityBundle id and designated requirement, or provider/path identity. Anonymous python3, node and stale cache paths never become OpenProgram identities.
operationsTool and operation names that require the capability. GUI desktop requires screen recording and accessibility; browser, VM and ordinary CLI operations do not.

The registry is validated at import time: ids are unique, every native entry has a non-prompting check and an explicit request style, scoped entries specify their status semantics, every settings action has a fixed destination, and every operation reference points to a declared capability. Package usage descriptions and entitlements are checked against the same ids in build tests.

State and API contract

Each report contains schema, platform, host, executing identity, timestamp, and a capability array. Rows contain id, category, status, detail, instruction, request mode, settings availability, required operations, and recovery metadata. Status means:

GET /api/system/access returns the registry-derived report. POST /api/system/access/setup is the owner-only, loopback-only first-run action: it requests every registered native runtime capability through the signed runtime and returns one consolidated report. POST /api/system/access/{id} is the explicit per-row fallback; it rechecks, optionally performs the declared native request, and returns a fresh row. open_settings=true only opens the declared settings destination and never claims a grant. Existing /waits records use the same ids and schema. Responses are no-store and contain no secrets.

First-run setup and operation flow

  1. On first use, the Web settings page displays the registry grouped by product area. It explains why each capability is needed and performs one read-only check for all rows when the page loads.
  2. The user chooses “Set up all OpenProgram access” for the first-run native group, or chooses “Request authorization” / “Open System Settings” for a row. The UI never sends a native request during page mount, reconnect, status polling, history replay or model output rendering.
  3. The backend serializes one native request per executing identity. It uses the named runtime for the native check and request, then performs a fresh check. A macOS dialog may still be separate for separate privacy categories or target applications; the UI reports each result instead of claiming that one click can authorize unknown future targets.
  4. Before an operation, the operation declares its required capabilities. The worker runs one fresh report and creates a durable system_access wait only for missing required capabilities. Browser and VM operations bypass this host check. The wait stores the original checkpoint and continues the same execution after every required row is granted.
  5. After the user returns to the app, focus/visibility causes a read-only refresh. Granting access resolves the wait through the worker reconciliation path; it does not submit a fake answer, invoke a retry function, or create a new execution.

Recovery and identity

Ordinary status reads, startup reconciliation, retries and duplicate events never run tccutil and never reset another application. A verified stale receipt can authorize one explicit migration for the fixed OpenProgram bundle identity; the marker is written before the OS call and prevents loops. Unverified code, unknown checks, unchanged requirements, Accessibility migration and all other applications require the user to use System Settings. The named runtime and containing application retain stable bundle ids and a persistent local signing requirement. A build or cache path labelled python3 or node is not an accepted identity.

Third-party Apple Events authorization is target-specific. Calendar and Reminders use EventKit-style category consent when the native backend supports it. File access is scoped to the user-selected path or security-scoped bookmark. Microphone and camera are declared and requested only by operations that use them. The product cannot pre-authorize an application it has never targeted, a path the user has never selected, or a permission the user has revoked.

Trust, privacy and concurrency

Only the local owner on the execution host can request or open settings. Loopback and Origin checks remain mandatory; forwarded requests and remote clients are rejected. The model can describe a missing capability but cannot invoke the setup endpoint. Native probes run in a fresh bounded process, share one report per reconciliation pass, and do not enumerate screen contents. Requests are serialized per executor. Wait resolution is guarded by execution id, generation, checkpoint version and cancellation state. A failed probe keeps the wait open; a terminal or cancelled execution cannot resume.

Compatibility and migration

Old clients may continue sending screen_recording and accessibility. The API keeps those ids and adds registry metadata. Existing receipts are read with the legacy screen-recording shape. Unknown ids return a client error without opening settings. Existing permission rules and Auto mode are unchanged. No additional application profile, worker or port is created.

Acceptance criteria

Implementation status

This document is the canonical design for the unified registry. The unified registry, native EventKit/AVFoundation checks and requests, schema/versioned report, identity metadata, package consistency checks, explicit setup UI, no-auto-request recovery, operation preflight parity, and terminal handling for unavailable or unsupported desktop access are implemented. Remaining acceptance is installation-specific: verify the installed default /Applications/OpenProgram.app on port 18100 and its current macOS grants; those grants remain controlled by macOS.