Skip to content

feat(opencomputers): native desktop consent and cross-platform host foundations - #275

Merged
robertohluna merged 12 commits into
mainfrom
fix/opencomputers-host-platform-20260912
Sep 12, 2026
Merged

robertohluna merged 12 commits into
mainfrom
fix/opencomputers-host-platform-20260912

Conversation

@robertohluna

@robertohluna robertohluna commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Purpose and relationship

Continues the OpenComputers platform work after #274 merged.
This branch is based on current main and does not replay the merged reconnect fix.
Keep subsequent OSA changes in this PR; the companion compute work remains https://github.com/Miosa-osa/miosa-compute/pull/1557.
This is a draft implementation checkpoint, not a claim that the entire cross-platform product is finished or deployed.

Included

  • Advertise implemented OSA job handlers instead of inferring VM capacity from the operating system.
  • Native physical desktop helpers and permission plumbing for macOS, Windows, Linux X11 and Linux Wayland.
  • Separate owner-bound control-plane desktop grants, explicit input consent, one-use sessions, bounded leases and revocation on connection loss.
  • Safe ETF protocol vocabulary for desktop frames without accepting arbitrary atoms.
  • Windows enrollment packaging that preserves an existing OSA installation and requires verified artifacts in the host flow.
  • Linux Wayland release packaging and runtime dependency checks.
  • A macOS Virtualization.framework helper with owned storage and identity boundaries for the companion native host runtime.

Evidence recorded by implementation owners

  • Latest OSA desktop/codec/router run: 89 tests, 0 failures, 2 excluded.
  • Expanded desktop/session run: 153 tests, 0 failures, 2 excluded.
  • Windows desktop protocol tests: 28 passing; independently rerun by integration owner.
  • Linux helper: native tests, Clippy, release build and eight packaging tests passing.
  • macOS helper build and synthetic lifecycle, permission, input-protocol and loopback TCP tests passing.
  • Whitespace validation passes after rebasing onto main.

These are targeted tests, not live capture/input or end-to-end VM acceptance.
The implementation preserves explicit unavailable results instead of inventing readiness or usage measurements.

Remaining acceptance and integration

  • Finish and test the companion compute ticket, database, relay and session-grant path.
  • Finish native VM guest networking, certified image delivery, metering and reconnect/reactivation.
  • Validate complete signed release assembly and a clean install, existing-install enrollment, retry and reboot on supported platforms.
  • Validate real native desktop capture, input consent and revocation on macOS, Windows and Wayland.
  • Validate actual sandbox, desktop VM, deployment and persistent volume lifecycle through the product APIs.
  • Complete dashboard integration and cross-repository acceptance.
  • Review the complete diff and run the applicable release checks before marking ready.

No merge or deployment is requested by this PR checkpoint.


Release-pipeline fixes (added during review)

Root cause of the failing linux-helper — fixed

Every run failed with:

libgstreamer1.0-dev : Depends: libunwind-dev
E: Unable to correct problems, you have held broken packages.

MEASURED on the real runner (ubuntu-22.04, image 20260907.292.1, diagnostic runs
34670409222 / 34670564638 / 34670653303): the image preinstalls
libunwind-14-dev
(1:14.0.0-1ubuntu1.1), which declares

Replaces: libunwind-dev
Breaks:   libunwind-dev

so installing libunwind-dev requires REMOVING libunwind-14-dev — and the
libc++-dev / libc++-14-dev that depend on it. apt will not remove an
installed package as a side effect of resolving a dependency inside a combined
transaction
, so asking for the dev packages alone dead-ends. Naming
libunwind-dev as an explicit target authorises the replacement.

This is a runner-image fact, not a Docker one. A bare ubuntu:22.04
container (both arm64 and amd64) does NOT ship libunwind-14-dev and installs
the dev packages in one transaction — which is why it could only ever reproduce
in CI.

Why it mattered beyond the helper: release.yml runs the same install on the
same image, so this would have failed build-linux-x64, after which the publish
gate refuses to publish and the tag ships with zero assets — the v1.0.191
failure mode.

Fixed in linux-wayland-helper.yml and release.yml. Verified end-to-end on the
real runner: the swap happened, pkg-config reported gstreamer-1.0 1.20.3 /
gstreamer-app-1.0 1.20.1, and the helper built (Finished release profile in 1m 07s, 4,182,136-byte ELF x86-64). Run 34671103633: success.

The Windows helper had no PR-time coverage — fixed

Coverage was asymmetric:

native target PR-exercised before
Linux Wayland helper linux-wayland-helper.yml
macOS capture native-capture.yml
Windows helper + enrollment bundle nothing — tag only

release.yml builds the Windows helper, validates its PE architecture, hashes it
into priv/helpers, runs the enrollment suite and assembles
osa-opencomputers-windows.zip — and the publish gate now REQUIRES that zip, so a
failure anywhere in that chain blocks the entire release, Linux and macOS
included. Its first execution was the release it was supposed to protect.

windows-desktop-helper.yml closes it: same runner label, same steps, plus a
bundle-content check (the runtime installer reads RELEASE_TAG and delegates to
install-runtime.ps1, so a bundle missing either is unusable for enrollment).
Uses a synthetic v0.0.0-ci tag so CI never presents itself as a release. Run
34671361337: success, Enrollment bundle OK: 26925 bytes.

Windows asset contract — kept, not weakened

docs/windows-onboarding.md states the release produces
osa-opencomputers-windows.zip and its SHA-256 sidecar, so requiring it is
consistent with the gate's "complete release or nothing" design. The gate was
not relaxed; the missing CI coverage was added instead.

Regression coverage — test/release_pipeline_contract_test.exs

Reads the workflows as DATA (following toolchain_pin_test.exs) and fails if:

  • either workflow installs the GStreamer dev packages without libunwind-dev
    first, or in a different order;
  • the two workflows install different GStreamer package sets — they must agree,
    because linux-helper exists to fail BEFORE the release does;
  • the publish gate requires an asset nothing in release.yml or the scripts it
    invokes produces;
  • a required asset matches no .sha256 sidecar glob (it would silently get no
    sidecar, then fail the gate);
  • the gate stops checking sidecars, the installers stop deriving asset names, or
    the enrollment bundle loses an input it copies.

Every assertion was confirmed to FAIL against the regressed state. The producer
search deliberately excludes the gate's own list — the first version of the test
passed with a bogus asset added, because the search matched the gate line and
every name "produced itself".

Final asset list — 14 files (was 12)

osa-linux-x64.tar.gz           + .sha256
osa-macos-arm64.tar.gz         + .sha256
osa-windows-x64.zip            + .sha256
osa-opencomputers-windows.zip  + .sha256   <- NEW
osagent-tui-linux-x64          + .sha256
osagent-tui-macos-arm64        + .sha256
osagent-tui-windows-x64.exe    + .sha256

Both installers derive asset names from a platform variable rather than
hardcoding a list, so the new asset does not break them.

Tag-time pre-checks

  • staged helper lands at lib/optimal_system_agent-<ver>/priv/helpers/… — matches
    the verifier predicate (verified PurePosixPath normalization);
  • prune_build_artifacts/1 only removes priv/rust/tui/target and stale lib
    dirs — it does not strip priv/helpers;
  • stage.sh runs before mix release on both Linux and Windows;
  • build.sh / stage.sh / verify-package.sh agree on CARGO_TARGET_DIR.

Not verified

The publish assembly itself — release.yml triggers only on a tag, and
running it means creating a public release. The contract test statically proves
every required asset has a producer and a sidecar glob, so the gate is
satisfiable.

Release ordering (unchanged)

miosa-compute #1557 remains the companion. Its Sec-WebSocket-Protocol
response-header fix must be deployed before a client release containing #274's
strict validation is published.

@github-actions github-actions Bot added documentation Improvements or additions to documentation area/docs Documentation content area/priv priv resources area/lib Core library code area/ci CI and workflow config labels Sep 12, 2026
`linux-helper` failed on every run with:

    libgstreamer1.0-dev : Depends: libunwind-dev
    E: Unable to correct problems, you have held broken packages.

Root cause, MEASURED on the real runner (ubuntu-22.04, image
20260907.292.1, diagnostic run 34670653303) — the image PREINSTALLS
`libunwind-14-dev`, LLVM 14's versioned unwinder dev package, which declares:

    Replaces: libunwind-dev
    Breaks:   libunwind-dev

so installing `libunwind-dev` requires REMOVING `libunwind-14-dev` — and the
`libc++-dev` / `libc++-14-dev` that depend on it:

    Remv libc++-dev [1:14.0-55~exp2]
    Remv libc++-14-dev [1:14.0.0-1ubuntu1.1]
    Remv libunwind-14-dev [1:14.0.0-1ubuntu1.1]
    Inst libunwind-dev (1.3.2-2build2.1)

apt will not remove an installed package as a side effect of resolving a
dependency inside a COMBINED transaction, so asking for `libgstreamer1.0-dev`
alone dead-ends. Naming `libunwind-dev` as an explicit target authorises the
replacement, and the chain then resolves.

Confirmed end-to-end in the same run: the swap happened, pkg-config reported
gstreamer-1.0 1.20.3 / gstreamer-app-1.0 1.20.1, and the helper built —
`Compiling osa-screen-capture-wayland v0.1.0`, `Finished release profile in
1m 12s`, a 4,182,136-byte ELF x86-64 executable.

This is a RUNNER-IMAGE fact, not a Docker one. A bare `ubuntu:22.04` container
does not ship `libunwind-14-dev`, so the combined install succeeds there — I
reproduced that on both arm64 and amd64 before finding the real cause. That is
why it could only ever be caught in CI.

Why it matters beyond the helper: `release.yml` runs the same install on the
same image, so this does not merely fail a helper build — it fails
`build-linux-x64`, after which the publish gate refuses to publish and the tag
ships with no assets (the v1.0.191 failure mode).

Adds `test/release_pipeline_contract_test.exs`, which reads the workflows as
DATA (following `toolchain_pin_test.exs`) and fails if:

  * either workflow installs the GStreamer dev packages without installing
    `libunwind-dev` first, or installs them in a different order;
  * the two workflows install different GStreamer package sets — they must
    agree, because linux-helper exists to fail BEFORE the release does;
  * the publish gate requires an asset that nothing in release.yml or the
    scripts it invokes produces;
  * the gate stops checking the `.sha256` sidecar for each asset;
  * `install.sh` / `install.ps1` stop deriving asset names from a platform
    variable, or the Windows enrollment bundle loses an input it copies.

Both new assertions were confirmed to FAIL against the regressed state:
removing the `libunwind-dev` line trips the ordering guard, and adding a bogus
asset to the gate trips the producer guard. The producer search deliberately
excludes the gate's own list — the first version of this test passed with a
bogus asset added, because the search matched the gate line and every name
"produced itself".
`release.yml` builds the Windows desktop helper, validates its PE
architecture, hashes it into `priv/helpers`, runs the enrollment suite and
assembles `osa-opencomputers-windows.zip` — and the publish gate now REQUIRES
that zip, so a failure anywhere in that chain blocks the ENTIRE release,
Linux and macOS included.

None of it ran anywhere except `release.yml` itself, i.e. only after a tag
existed. That is precisely the v1.0.191 failure mode this repository has
already paid for once: a lane whose first execution is the release it was
supposed to protect.

Coverage was asymmetric before this commit:

  | native target          | PR-exercised                       |
  |------------------------|------------------------------------|
  | Linux Wayland helper   | linux-wayland-helper.yml           |
  | macOS capture          | native-capture.yml                 |
  | Windows helper+bundle  | (nothing — tag only)               |

`windows-desktop-helper.yml` runs the same steps as the release job on the
same runner label (`windows-latest`), so a break fails on the pull request
instead of on the tag: .NET 8, `build.ps1 -Runtime win-x64 -BundleDirectory
priv/helpers` (which runs the protocol tests and validates the PE header
first), `Enrollment.Tests.ps1`, then `Build-EnrollmentBundle.ps1` with a
SYNTHETIC `v0.0.0-ci` tag so CI never presents itself as a release.

It also asserts the bundle's contents rather than just its existence: the
runtime installer reads `RELEASE_TAG` to pin what it downloads and delegates
to `install-runtime.ps1`, so a bundle missing either is unusable for
enrollment — which is the entire reason the release requires it.

Also strengthens the pipeline contract test with a sidecar-glob guard. The
`.sha256` sidecars are produced by a globbed loop:

    for f in *.tar.gz *.zip osagent-tui-*; do ... done

An asset whose extension matches no glob silently gets no sidecar, and the
gate then fails on a missing `.sha256` — for an asset that was built
correctly. The new assertion fails if any asset the gate requires matches no
glob. Confirmed by adding `osa-newformat.tar.zst` to the gate: both this
guard and the producer guard fire, with messages naming the fix.
@robertohluna
robertohluna marked this pull request as ready for review September 12, 2026 04:03
@github-actions github-actions Bot added the area/tui Rust TUI label Sep 12, 2026
@robertohluna
robertohluna merged commit 8ab00b2 into main Sep 12, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/ci CI and workflow config area/docs Documentation content area/lib Core library code area/priv priv resources area/tui Rust TUI documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant