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
4 changes: 2 additions & 2 deletions docs/adr/0090-s16-watch-and-take-over-a-working-computer.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
status: implemented for DESK16-01 (#410), first slice; deterministic evidence; guest desktop session, desktop-browser egress and live host evidence pending (#410 follow-up, #431)
status: implemented for DESK16-01 (#410), first slice; deterministic evidence; guest desktop session and desktop-browser egress delivered by ADR 0091 (#453 and slice 2b); live host evidence pending (#431)
date: 2026-11-08
scope: DESK16-01 (#410); S16-AC05/AC07; FR-02/10/14/15/18/21/35/36/38/40; NFR-01/06/07/11/12/14
---
Expand Down Expand Up @@ -44,5 +44,5 @@ Option 3 keeps Rakazo's product concept (watch, take control with a lease, gener

## Pending (not delivered by this slice)

- Guest desktop session (X server on the virtual display, window manager, browser) and the desktop browser's egress: the VM proxy admits only per-session leases, so a desktop browser outside a work session needs a reviewed lease design before a trusted-site login can work in the VM. #442's remaining item depends on it.
- ~~Guest desktop session and the desktop browser's egress~~: delivered by [ADR 0091](0091-s16-working-computer-desktop-and-browser.md) (desktop lease in #453, desktop session in slice 2b). #442's remaining item can now build on it.
- Live evidence on `jit-vm` (QEMU `screendump` and input on the real host, VM15-03 file permissions, restart into the desktop profile) belongs to LIVE16-01 (#431).
6 changes: 4 additions & 2 deletions docs/adr/0091-s16-working-computer-desktop-and-browser.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
status: proposed for DESK16-01 (#410), second slice; for review before implementation
status: accepted for DESK16-01 (#410), second slice; implemented with deterministic evidence (desktop lease #453, desktop session slice 2b with #454); live host evidence pending (#431)
date: 2026-11-09
scope: DESK16-01 (#410); AUTH16-01 (#442) step 5; S16-AC07/AC11; FR-10/14/15/35/38/40; NFR-07/14
---
Expand Down Expand Up @@ -65,7 +65,9 @@ while the owner holds control, or per-site approval). What Rakazo does, read fro
6. **Pinned browser.** Ubuntu 26.04 ships Chromium only as a snap, which needs snapd and store egress. Proposed:
install a version-pinned Chromium build with a recorded SHA-256 for x86_64 and arm64 through the existing bounded
provisioning lease, like the Docker install. The alternative is Firefox ESR from Mozilla's apt repository with
its signing key pinned. Champ chose the pinned Chromium build on 2026-11-11.
its signing key pinned. Champ chose the pinned Chromium build on 2026-11-11. Implemented as Chrome for Testing
(Google's version-pinned Chromium build, published for both linux64 and linux-arm64, no auto-updater) from
`storage.googleapis.com/chrome-for-testing-public`, version and per-architecture SHA-256 in `qemuDesktopBrowser`. Its own `deb.deps` list drives the runtime packages.

## Consequences

Expand Down
4 changes: 3 additions & 1 deletion docs/contracts/s15-working-vm-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ Stop/revoke immediately fences further calls and late publication. Host SSH clos

The external guest network allows only a protected HTTP/CONNECT registry proxy. Its default exact hosts are exported as `defaultVmRegistryHosts`; owner settings can narrow/replace them at a fresh proxy incarnation. Public DNS answers are checked and the actual socket pins the checked address; private/control/metadata/IP-literal destinations are refused. Connections/bytes/time are finite, lease revocation closes active connections, and logs contain only session/host/byte metadata. **This is destination control, not content-level disclosure prevention.** Approved registries see request metadata and may receive workspace content; TLS cannot verify release-only/read-only semantics or block every publication at an allowed domain. Proxy attribution is cooperative against shared guest root. New destinations require policy; the exact HTTPS+SHA-256 bounded artifact path injects no credential and follows no redirect.

**Desktop browser lease (DESK16-01, ADR 0091 decisions 2, 3 and 5).** A local VM started with the explicit `desktop` opt-in gets one standing proxy lease, `desktop_<vm>_<random>`, issued by the same proxy after every boot and again when a restarted controller re-attaches a VM that survived (the restarted proxy holds no lease). It reaches the public web under the same rules as work sessions (public DNS name, 443 CONNECT or 80 GET/HEAD, checked public address, no injected credential, no registry disclosure lease), and in addition refuses every host for which `websiteSensitiveOnly("https://" + host)` holds (identity providers, primary email, banking, payment, `login.`/`sso.`/`auth.`/`accounts.` hosts). That filter runs on the requested name before DNS for every host on the lease, registry hosts included, and a refusal is logged as `denied`; a filter that throws refuses. Composition cannot build a desktop lease without the filter (`vm_invalid_setup`). The lease is written over SSH to `/etc/jit-desktop/proxy.env` (`0640 root:jit-desktop`, atomic replace) only when the guest has the desktop session's `jit-desktop` group; otherwise, or when delivery fails, it is revoked at once and the start still succeeds. A held lease is renewed every 10 minutes for 30 minutes while the VM runs, revoked at the next renewal if the VM is no longer running, and revoked before every protected stop (owner stop, idle stop, restore). It is separate from work-session leases: revoking a session never cuts it, and takeover does not touch it. Work-session leases keep the rules above unchanged. Residuals: guest root can read the desktop lease, so anything in the guest can reach any non-sensitive public host while the VM runs (ADR 0084, ADR 0091); the sensitive-host list is a heuristic.
**Desktop browser lease (DESK16-01, ADR 0091 decisions 2, 3 and 5).** A local VM started with the explicit `desktop` opt-in gets one standing proxy lease, `desktop_<vm>_<random>`, issued by the same proxy after every boot and again when a restarted controller re-attaches a VM that survived (the restarted proxy holds no lease). It reaches the public web under the same rules as work sessions (public DNS name, 443 CONNECT or 80 GET/HEAD, checked public address, no injected credential, no registry disclosure lease), and in addition refuses every host for which `websiteSensitiveOnly("https://" + host)` holds (identity providers, primary email, banking, payment, `login.`/`sso.`/`auth.`/`accounts.` hosts). That filter runs on the requested name before DNS for every host on the lease, registry hosts included, and a refusal is logged as `denied`; a filter that throws refuses. Composition cannot build a desktop lease without the filter (`vm_invalid_setup`). The lease is written over SSH to `/etc/jit-desktop/proxy.env` (`0640 root:jit-desktop`, atomic replace; a lease URL containing a newline, NUL, space, quote, backslash or `$` is refused, since systemd expands `$` in an environment file) only when the guest's desktop session is completely installed at the pinned version (`/opt/jit-desktop/ready-v1`, written last by the installer) and has the `jit-desktop` group, and only while `inspect()` proves the VM running with its `proxy-only` network profile; otherwise, or when issue or delivery fails (for example a full lease pool), it is revoked at once and the start still succeeds. Provisioning issues it again after cloud-init and the desktop install, so a first boot whose group did not exist yet still ends with a lease. One desktop beat runs every 10 minutes per boot: with the VM running it renews a held lease for 30 minutes, and each renewal starts a fresh byte allowance (`bytesPerSession`, 512 MiB by default, per renewal period), or issues a new lease when none is held; a transient `inspect()` failure (QMP busy with a live snapshot, a management timeout) skips that beat and keeps the lease, since its 30-minute expiry outlives one missed renewal; a VM proven not running, whose boundary is unproven (`vm_boundary_unproven`) or whose network profile is not proven (`network` other than `proxy-only`) has its lease revoked and gets none (fail closed), and a VM no longer running also ends the beat until its next protected start. The lease is revoked before every protected stop (owner stop, idle stop, restore).

**Desktop session (DESK16-01, ADR 0091 decisions 1 and 6).** With `desktop` on, provisioning (and, for a VM enrolled before `desktop` was on, the first desktop beat of a later boot) runs one fixed idempotent guest program through a separate bounded provisioning lease (unfiltered, like the Docker install; always closed afterwards). It creates the unprivileged system user and group `jit-desktop` (no sudo, home `/var/lib/jit-desktop`), downloads the pinned Chrome for Testing archive for the guest architecture (`qemuDesktopBrowser`: version `157.0.8103.50`, one recorded SHA-256 each for x86_64 and arm64) and refuses any other bytes, unpacks it to `/opt/jit-desktop/chrome-<version>`, installs its `deb.deps` plus Xorg (modesetting, 1280×800), libinput, xinit, fluxbox and fonts from Ubuntu's archive, and enables two units: `jit-desktop-proxy.service` (a loopback forwarder on `127.0.0.1:3129` that adds `Proxy-Authorization` from `proxy.env`, read per connection, because Chromium accepts no credentials in a proxy URL) and `jit-desktop.service` (Xorg, fluxbox and the browser on vt7 as `jit-desktop`, browser restarted if it exits, profile in the user's home, sandbox on through an AppArmor `userns` profile for that binary only). The install removes `ready-v1` first and writes it only after both units and the pinned browser have stayed up for three checks in a row, so a partial, failed or outdated install is installed again. A failed install is tried once per boot and leaves the VM working without a desktop lease. A headless controller installs nothing. It is separate from work-session leases: revoking a session never cuts it, and takeover does not touch it. Work-session leases keep the rules above unchanged. Residuals: guest root can read the desktop lease, and any guest process can use the loopback forwarder, so anything in the guest can reach any non-sensitive public host while the VM runs (ADR 0084, ADR 0091); the sensitive-host list is a heuristic.

Proxmox setup registers a VM-scoped API token (VM.Audit/PowerMgmt/Snapshot/Snapshot.Rollback), with read-only Sys.Audit at `/` and `/nodes/<node>`. The protected verifier reads all firewall enable levels, NIC/rules and direct guest egress probes. Missing audit or disabled/unproven controls refuses the `network-isolated` capability. Lima/QEMU verifies the actual protected process/config: plain mode, no shared mounts/guest agent/additional NIC, `restrict=on`, IPv6 disabled and only the fixed proxy guest-forward. Guest firewall settings are diagnostic only. Since revision 4 the default x64/arm64 working VM is the Jit-managed local VM below; Lima/Proxmox are the optional owner-supplied path.

Expand Down
2 changes: 1 addition & 1 deletion docs/contracts/s16-computer-screen-v1.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# s16-computer-screen/v1

Owner contract for watching a desktop working computer and taking exclusive control (DESK16-01, [ADR 0090](../adr/0090-s16-watch-and-take-over-a-working-computer.md)). Available when the installation enables `computers` and `desktop`; the bootstrap reports `capabilities.computers.screen: true`.
Owner contract for watching a desktop working computer and taking exclusive control (DESK16-01, [ADR 0090](../adr/0090-s16-watch-and-take-over-a-working-computer.md)). Available when the installation enables `computers` and `desktop`; the bootstrap reports `capabilities.computers.screen: true`. The screen shows the guest desktop session (Xorg, fluxbox and the pinned browser on the 1280×800 virtio display; ADR 0091, `s15-working-vm-v1` "Desktop session") once it is installed; before that, or on a VM whose install failed, it shows the guest's text console.

## Read the screen state

Expand Down
5 changes: 4 additions & 1 deletion src/s15/vm/local-control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,8 @@ export async function startLocalVmControl(store: VmStore, options: LocalVmContro
const probe = host.probe ?? (options.vmm ? vmmHostProbe(options.vmm) : undefined);
// DESK16-01: the desktop browser's standing lease always carries the sensitive-host filter (ADR 0091 decisions 2, 5).
const desktopLease = host.desktop && desktopDeny ? (sessionId: string, expiresAt: number) => proxy.lease(sessionId, expiresAt, undefined, { deny: desktopDeny }) : undefined;
return { host: { ...host, proxySocket, ...(probe ? { probe } : {}), ...(desktopLease ? { desktopLease } : {}) }, lease: (sessionId, expiresAt, authorize) => proxy.lease(sessionId, expiresAt, authorize), setHosts: hosts => proxy.setHosts(hosts), close: () => proxy.close() };
// A desktop VM enrolled before `desktop` was on gets its session installed from the beat through a bounded
// provisioning lease, the same unfiltered kind as the Docker install (ADR 0091 decisions 1, 6).
const provisionLease = desktopLease ? (sessionId: string, expiresAt: number) => proxy.lease(sessionId, expiresAt) : undefined;
return { host: { ...host, proxySocket, ...(probe ? { probe } : {}), ...(desktopLease ? { desktopLease } : {}), ...(provisionLease ? { provisionLease } : {}) }, lease: (sessionId, expiresAt, authorize) => proxy.lease(sessionId, expiresAt, authorize), setHosts: hosts => proxy.setHosts(hosts), close: () => proxy.close() };
}
Loading
Loading