Files
VerseVDI-Data-Plane/openspec/changes/phase-3d-macos-native-mvp/design.md
T

11 KiB

Context

Phase 3C freezes a gateway-only control/media baseline, but the Data Plane macOS target is still a template and the shared Protocol has no requested display mode, absolute pointer, or high-resolution scroll contract. The closed Server already owns broker persistence, allocation policy, manifest disclosure, and provider-work. The GPLv3 Data Plane owns the pure-Go gateway/Apollo translation plus the future Rust core and native client. The Protocol repository remains the only wire authority.

The implementation must preserve strict legacy JSON decoding, exact immutable Protocol pins, no direct client-to-provider path, encoded-media relay without gateway decode/transcode, and bounded release-all behavior. UI references are retained in the Planning repository as review evidence only.

Goals / Non-Goals

Goals:

  • Deliver one native Apple-Silicon SwiftUI/AppKit client for macOS 26 with macOS 15/14 compatibility, built against a deterministic fake core before real streaming integration.
  • Carry a client-requested display mode through Server allocation to provider work, while the Server remains the only policy/clamp authority and both requested/effective values remain visible to the client.
  • Add provider-neutral absolute pointer and high-resolution scroll events without changing the gateway-only route or coupling the public contract to Apollo packet shapes.
  • Keep lifecycle, input release, local preview storage, secrets, media, and diagnostics bounded and testable at their trust boundaries.
  • Preserve a small, sized/versioned Rust C ABI and a single Swift unsafe bridge.

Non-Goals:

  • Remote apps, manual PC/direct provider connections, live display renegotiation, multi-monitor remote topology, HDR, microphone, file/folder redirection, image clipboard, macros/timed sequences, Intel, provider-specific UI, or a plugin framework.
  • Gateway decode, encode, transcode, render, cgo, native sidecar, insecure retry, or silent capability fallback.
  • Public release, signing credentials, deployment, promotion, or agent access to the owner's Apollo host. Live Apollo/SudoMaker evidence remains owner E2E.

Decisions

1. Repository and compatibility ownership

  • Protocol adds a reusable DisplayMode object and optional request/response fields while retaining control wire version 1. Generated Go represents optional referenced objects as pointers so nil is omitted; Rust/Swift use optionals. A legacy request receives the legacy response shape, while a display-aware request requires display.request.v1 and a display-aware manifest contains the accepted value.
  • Protocol registers display.request.v1, input.absolute.v1, and input.scroll.v1 and extends VGI1 only behind those negotiated features. Old gateways continue to reject unknown kinds; clients therefore never send an unadvertised kind.
  • Protobuf remains unchanged unless an observed consumer or verification gate proves that the JSON control route actually depends on it. This avoids an unrelated second contract.
  • A verified-unused immutable Phase 3D Protocol RC is frozen before Server/Data pins advance together. No sibling replace or local wire fork is committed.

2. Server-owned display decision

  • Broker sessions gain nullable requested/effective width, height, and FPS columns plus the immutable policy-version identity needed to replay allocation/reconnect. Dedicated columns keep the decision queryable and avoid overloading the existing bandwidth policy JSON.
  • Request validation accepts width 320..16384, height 200..8640, and FPS 1..240. Requested mode participates in every idempotency identity, including waiting sessions.
  • Allocation selects the machine, resolves the immutable machine/pool/global policy, computes the accepted mode, and atomically persists the allocation and accepted values. Reconnect reuses the persisted result; it does not re-clamp against later policy.
  • Clamp is proportional and deterministic: scale = min(policyWidth/requestWidth, policyHeight/requestHeight, 1); effectiveWidth = floorToEven(requestWidth * scale); effectiveHeight = floorToEven(requestHeight * scale); effectiveFPS = min(requestedFPS, policyFPS).
  • Provider work uses the persisted effective dimensions/FPS and the selected immutable policy's codec/bitrate/audio. Legacy sessions retain today's exact policy dimensions.

3. Provider-neutral input

  • VGI1 kind 0x06 has exactly eight body bytes: big-endian unsigned x, y, viewport width, and viewport height. Width/height must be nonzero; x < width and y < height.
  • VGI1 kind 0x07 has exactly four body bytes: big-endian signed 16-bit vertical and horizontal high-resolution scroll deltas.
  • Protocol docs, fixtures, Go/Rust/Swift classifiers, Data encode/decode, fuzzing, transport, and Apollo translation share the same vectors. Apollo packet details remain private to the adapter and must be justified by pinned reference source before implementation.
  • Absolute/scroll events do not enter the pressed-state ledger. Keys, buttons, and controllers continue to release through the existing ledger on every authority/lifecycle exit.

4. Native workspace, display discovery, and input ownership

  • The client uses NavigationSplitView, native controls/materials, and standard macOS 26 Liquid Glass APIs conditionally. macOS 15/14 retain identical hierarchy with native materials; no custom glass framework is introduced.
  • Desktop cards remain 328 x 205 pt; an adaptive grid only changes column count. Card actions remain accessible without hover, and no direct-connect button or endpoint entry is added.
  • Automatic mode reads physical pixels for the screen where the session window opens and uses its maximum FPS. A defensible notch-safe built-in mode is preferred; otherwise Full Native is disclosed. A display move offers a new-session reconnect and never mutates the active stream.
  • The renderer aspect-fits. Absolute coordinates originate only inside the rendered viewport; letterbox bars produce no pointer event.
  • Native NSWindow fullscreen preserves top-edge system UI in absolute mode. Relative mode hides/captures the cursor. Reserved mode-toggle and emergency-release chords are handled before mapping and never cross the core boundary. Mode switches release pressed input and clamp cursor restoration to the rendered viewport.

5. State, Rust core, and platform boundaries

  • Swift actors own authentication, resources, brokerage, streaming, and settings; immutable projections reach MainActor views. One bridge owns every C pointer/callback/lifetime.
  • The Rust core owns tunnel negotiation, authenticated framing, packet reconstruction, encoded media delivery, input encoding, statistics, cancellation, and structured errors. Apple frameworks own decode/render/audio/device/input capture and product state.
  • The C ABI uses fixed-width values, explicit lengths, opaque handles, sized/versioned tables, a declared callback queue, caught panics, cancellation, and late-callback/destroy rules.
  • Fakes cover every external boundary before real core/media integration. No speculative adapter/factory/plugin layer is added beyond interfaces already needed for deterministic tests.

6. Preview and diagnostic privacy

  • Stable desktop previews are downscaled to 656 x 410, stored in the sandbox cache as mode 0600, and written only after an active session ends cleanly. Failed launch retains the prior preview. Pool cards use generic art.
  • Logout or entitlement loss clears affected previews. Hide/Clear settings are explicit. Backup, telemetry, OSLog, crashes, support bundles, and default diagnostics exclude preview and raw media bytes.
  • Diagnostics are allowlisted structured state/correlation/performance summaries; tokens, grants, provider endpoints, media, input, clipboard, and Windows secrets never enter them.

7. Delivery and evidence

  • Land capability-sized commits in order: Planning/OpenSpec, Protocol, Server, gateway input, fake-core native shell, Rust/core integration, platform media, then full qualification.
  • Every nontrivial boundary starts with a focused failing test/vector and ends with the narrow affected gate; one complete clean-source gate runs only after freeze.
  • macOS 26/15/14 native behavior, accessibility, lifecycle, privacy, and platform media require real target evidence. The owner later runs the only live Apollo/SudoMaker acceptance.

Risks / Trade-offs

  • [Strict legacy decoders reject additive response fields] -> Emit display fields only for a negotiated display-aware request; retain legacy shapes and cross-version fixtures.
  • [Requested dimensions exceed policy or change aspect] -> Apply one Server-owned proportional/even clamp, persist it atomically, and disclose both values.
  • [Absolute coordinates mismatch presentation] -> Normalize against the rendered viewport, reject letterbox coordinates, and test edge/scale/display cases.
  • [Relative capture strands local input] -> Keep an unremappable emergency release, release before every mode/authority transition, and test focus/sleep/cancel/revocation paths.
  • [Preview bytes leak user media] -> Minimize one local file, mode 0600, clean-session-only writes, lifecycle clearing, and explicit exclusion/secret-canary tests.
  • [Apollo input semantics are guessed] -> Stop until the pinned provider source establishes the exact packet; do not encode provider assumptions into VGI.
  • [macOS API rendering differs by release/accessibility setting] -> Test deterministic geometry and state automatically; use bounded manual material review without snapshots of OS-owned pixels as the sole gate.
  • [The full client scope is large] -> Keep serial capability commits and stop at the first failed contract/environment gate rather than introducing fallbacks or partial claims.

Migration Plan

  1. Freeze Planning/OpenSpec and visual references.
  2. Implement/freeze an immutable Protocol RC with legacy and new feature fixtures.
  3. Apply the forward-only Server migration and update Server/Data Protocol pins together.
  4. Deploy no schema or runtime automatically; validate clean install/upgrade/legacy rows and gateway fixtures locally/private CI first.
  5. Build the native shell against fakes, then integrate the frozen core/gateway surfaces.
  6. Roll back application binaries only while schema compatibility permits; nullable additive fields preserve legacy row behavior. Never down-migrate or rewrite applied migrations.
  7. Keep signing, publication, deployment, promotion, and live owner E2E under separate authorization.

Open Questions

None at proposal time. Exact Apollo absolute-pointer/scroll packet vectors must be established from the approved pinned source before the adapter step; absence of that evidence is an implementation hard stop, not a design choice to guess.