diff --git a/docs/adr/0007-remote-device-leases.md b/docs/adr/0007-remote-device-leases.md index 9585d2f299..dee137a90c 100644 --- a/docs/adr/0007-remote-device-leases.md +++ b/docs/adr/0007-remote-device-leases.md @@ -173,8 +173,11 @@ 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/`, 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 @@ -182,7 +185,7 @@ registry descriptor declares `appLease: 'allowed'` (later commands are refused, 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 diff --git a/src/daemon/macos-app-lease.ts b/src/daemon/macos-app-lease.ts index 8d54eab595..a739aed375 100644 --- a/src/daemon/macos-app-lease.ts +++ b/src/daemon/macos-app-lease.ts @@ -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 = new Set([ - 'open', - 'batch', - 'lease_heartbeat', - 'lease_release', -]); +const SESSIONLESS_COMMANDS: ReadonlySet = new Set(['open', 'batch']); type MacOsAppLeaseRule = | 'command' diff --git a/website/docs/docs/remote-proxy.md b/website/docs/docs/remote-proxy.md index c90ca7cbbd..d75dbbbe1b 100644 --- a/website/docs/docs/remote-proxy.md +++ b/website/docs/docs/remote-proxy.md @@ -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/`, 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 `: @@ -164,7 +166,7 @@ The client connects with a remote config that names the lease, and runs `open