Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions docs/adr/0007-remote-device-leases.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,16 +173,19 @@ A `macos-app` lease's device key is a bundle id, optionally pinned to one proces
only a host administrator allocates one, over the loopback `/admin/leases` route that uses the daemon
token like host holds; tenant `lease_allocate` refuses the backend. The host picks the lease id, a
repeated PUT renews it, and a PUT naming another scope for an existing id is refused rather than
rewritten. Heartbeat, expiry, release, and the loss on daemon restart are those of any lease, except
that a tenant heartbeat or request cannot renew it for longer than the window of the host's last PUT.
rewritten. Heartbeat, expiry, and the loss on daemon restart are those of any lease, except that a
tenant heartbeat or request cannot renew it for longer than the window of the host's last PUT, and
a tenant cannot release it: `lease_release` is refused with `MACOS_APP_LEASE_HOST_OWNED`. The lease
ends by the host's `DELETE /admin/leases/<lease-id>`, by expiry, by the loss on daemon restart, or
when a session it holds closes and the host set `retainOnClose` to false.

Request admission confines every request admitted under the lease, so `batch` steps and `replay`
actions are confined when they re-enter it: an allow list of commands, the ones whose command
registry descriptor declares `appLease: 'allowed'` (later commands are refused, and of the commands
lease admission otherwise exempts only `lease_heartbeat` and `lease_release` declare it),
`open` and `close` of the leased bundle only, the `app` surface only, window-only screenshots, no
input that names a host path or launches beside the app, and an existing session that is the leased
app for every request but `open`, the `batch` envelope, and the lease's heartbeat and release, so a
app for every request but `open` and the `batch` envelope, so a
request naming no session cannot fall back to the host Mac. `open` requires the native app backend (ADR 0031), because XCTest
posts screen events that can land outside the app's window. A pid-pinned lease is checked against the
running process before each admitted request. A session opened under the lease holds its app, not
Expand Down
9 changes: 2 additions & 7 deletions src/daemon/macos-app-lease.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,9 @@ export function parseMacOsAppLeaseKey(deviceKey: string | undefined): MacOsAppLe

/**
* The requests that run without the leased app's session: `open` creates it, each `batch` step is
* admitted again when it runs, and a heartbeat acts on the lease alone, and a release is refused (the host ends the lease).
* admitted again when it runs.
*/
const SESSIONLESS_COMMANDS: ReadonlySet<string> = new Set([
'open',
'batch',
'lease_heartbeat',
'lease_release',
]);
const SESSIONLESS_COMMANDS: ReadonlySet<string> = new Set(['open', 'batch']);

type MacOsAppLeaseRule =
| 'command'
Expand Down
8 changes: 5 additions & 3 deletions website/docs/docs/remote-proxy.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,10 @@ that names another scope for an existing id is refused. The lease stays allocate
client's `close` unless the body sets `retainOnClose: false`, and DELETE revokes it at once. It
expires after `ttlMs` without a renewal or a client request, like any lease. A client heartbeat can
shorten that window but never extend it past the `ttlMs` of the last PUT. A client cannot release
it: `disconnect` drops only its local connection state, and a tenant `lease_release` is refused
with `MACOS_APP_LEASE_HOST_OWNED`.
it: `disconnect` drops only its local connection state, and a tenant `lease_release` is refused with
`MACOS_APP_LEASE_HOST_OWNED`. The lease ends by the host's `DELETE /admin/leases/<lease-id>`, by
expiry, by daemon restart, or when a session it holds closes and the body set `retainOnClose` to
false.

The client connects with a remote config that names the lease, and runs `open <bundleId>`:

Expand All @@ -164,7 +166,7 @@ The client connects with a remote config that names the lease, and runs `open <b

Requests under a `macos-app` lease are limited to `open`, `close`, `snapshot`, `wait`,
`find`, `get`, `is`, `click`, `fill`, `press`, `type`, `focus`, `scroll`, `screenshot`, and `batch`,
plus the lease's own heartbeat and release; `doctor`, `devices`, `session list` and the other
plus the lease's own heartbeat; `doctor`, `devices`, `session list` and the other
inventory commands are refused too.
`open` and `close` accept only the leased bundle id, only the `app` surface is allowed, screenshots
capture only the app window, inputs that name a host path or a launch (`--save-script`,
Expand Down
Loading