Skip to content

[KS] API: privileged helper, admin prompt, XPC (#138) - #441

Merged
sethwebster merged 2 commits into
seth/kitchen-sink-gatefrom
seth/ks-138-privileged-helper
Oct 13, 2026
Merged

sethwebster merged 2 commits into
seth/kitchen-sink-gatefrom
seth/ks-138-privileged-helper

Conversation

@sethwebster

@sethwebster sethwebster commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Refs #138

Adds @legendapp/spark/privileged-helper (macOS): a launch daemon the app ships, registered through SMAppService with its Login Items approval states; the system admin prompt (Authorization Services), whose rights travel to the helper, which checks them itself; and JSON over XPC with each side checking the other's code signature. Unsigned/ad-hoc builds report unsigned and stop before ServiceManagement. macos.privilegedHelpers opts helpers in; spark build writes their LaunchDaemons plists, nothing otherwise. Windows is typed unsupported (#440). Guide and threat model: docs/privileged-helper.md.

Security details:

  • The helper accepts only the app: team + app identifier, plus a Developer ID leaf when the helper is Developer ID signed; on connect it reads the client's signature by audit token and refuses clients without the hardened runtime or with get-task-allow.
  • The app requires the helper's team and its signing identifier (its launchd label; spark build/spark package sign it so, and spark package checks it). The app sends nothing until the helper's verification reply passes; interrupted connections are marked stale synchronously and verified again.
  • Admin rights: AdminAuthorization.expiresAt reports when system.privilege.admin's timeout lapses them (300 s); lapsed rights reject authorizationExpired (app-side, and helper-side before the handler) instead of a silent authorized: false. The helper checks with flags 0 (no interaction, no extending).

Kitchen Sink: Processes and IPC > Privileged helper, PROC checks, driver flows, a manual real-path flow (team-signed build with examples/kitchen-sink/helpers/privileged-helper), goldens.

Verification (run in this session, after rebasing onto the current base)

  • bun run typecheck: passes
  • bun run test: 1974 passed, 2 failed (accepted: node-runtime asdf shim; motion-native frames check)
  • tests/privileged-helper-native.test.ts: real XPC round trips against an anonymous listener in the test process (test binary signed ad hoc with the hardened runtime): both signature checks, the hardened-runtime/get-task-allow check (a non-hardened build of the binary fails it), a lapsed authorization rejected before the handler, helper errors, timeout, abort; the simulation; a build without the driver; the Kitchen Sink helper compiling. tests/packaging.test.ts: plists only on opt-in, helpers signed with their labels.
  • bun run e2e:lint: 0 problems
  • Driver flows under ks-lock native: all e2e/flows/processes-ipc macOS flows pass (Windows skipped, manual awaiting sign-off), accessibility/audit-processes-ipc (4) and infra/catalog-driver (4) pass; the catalog still reads 26 of 38 areas, so its goldens did not change after the rebase
  • security/entitlements.yaml (SEC-ENT-01) passes on a release build of this branch; it has no Contents/Library
  • Only covered by the manual flow (privileged-helper-real.yaml, needs Seth on a Developer ID build): the root-side authorization check with a granted right, the real admin prompt, SMAppService registration/approval, rights lapsing after 300 s, and the Developer ID client requirement. Automated runs never reach a granted right.
  • Not run: Windows

Screens

Light Dark Arabic

- @legendapp/spark/privileged-helper: availability, status, register,
  unregister, open Login Items, requestAdminAuthorization and
  sendPrivilegedHelperMessage, with typed PrivilegedHelperError reasons
- SMAppService launch daemons; requiresApproval is a state, not an error
- admin prompt via Authorization Services; the rights travel to the helper,
  which checks them itself without UI
- XPC both ways checked by code signature (team + app identifier on the
  helper's listener, team on the app's connection); the app sends nothing
  until the helper's verification reply passes its requirement
- helper side (helper/SparkPrivilegedHelperService.m) for app helpers
- unsigned/ad-hoc builds report unsigned and stop before ServiceManagement
- macos.privilegedHelpers opts helpers in; spark build writes their
  LaunchDaemons plists, nothing otherwise
- driver mode simulates registration, approval, the prompt and the
  connection: simulatePrivilegedHelper / testPrivilegedHelper
- Windows typed unsupported (#440); threat model in docs/privileged-helper.md
…goldens (#138)

Processes and IPC > Privileged helper shows availability and status,
registers and unregisters, opens Login Items, requests and releases admin
rights and sends whoami. Driver flows cover availability, approval states,
the prompt and the connection via the simulation; the real path is a manual
flow on a team-signed build with the Kitchen Sink helper. Goldens for
light/dark x en/ar; infra-shell-catalog goldens re-approved for the area count.
@sethwebster
sethwebster force-pushed the seth/ks-138-privileged-helper branch from dcc5535 to 1218aea Compare October 13, 2026 03:10
@sethwebster
sethwebster merged commit 51aec6b into seth/kitchen-sink-gate Oct 13, 2026
2 checks passed
@sethwebster
sethwebster deleted the seth/ks-138-privileged-helper branch October 13, 2026 03:47
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