{"slug": "show-hn-vitrin-os-an-agent-first-display-server-with-per-app-isolation", "title": "Show HN: Vitrin OS – An agent-first display server with per-app isolation", "summary": "Vitrin OS is an open-source, agent-first display server that provides per-app isolation for human and AI agents to concurrently observe and operate GUIs under granular, revocable, capability-scoped authorization. The system uses a small trusted core with a new wire protocol, confining legacy Wayland/X11 applications to unprivileged nested shims, enabling precise control such as allowing an agent to fill one form in one Firefox window for five minutes while unable to see other windows, with instant human override via mouse or Escape key.", "body_md": "Vitrin OS is an open-source, **agent-first display server**: a small trusted\ncore (`vitrind`\n\n) speaking a new capability-native wire protocol, with every\nlegacy [Wayland](https://wayland.freedesktop.org/)/X11 application confined to its own per-app nested shim — so\nthat humans and AI agents can concurrently observe and operate GUIs under\ngranular, revocable, capability-scoped authorization. Every principal has\nidentity; every action carries a capability; every action is journaled; the\ntrusted core stays small and legacy complexity is exiled to disposable,\nunprivileged shims.\n\nThe full vision, object model, and technical architecture live in\n[docs/PRD.md](/vitrin-os/vitrin-os/blob/main/docs/PRD.md). The wire protocol is specified in\n[protocol/vitrin-v0.xml](/vitrin-os/vitrin-os/blob/main/protocol/vitrin-v0.xml) with prose in\n[docs/protocol/](/vitrin-os/vitrin-os/blob/main/docs/protocol/00-conventions.md).\n\nOne sentence, no jargon:\n\nAn agent is allowed to fill in one form, in one Firefox window, for the next five minutes. It cannot see the password manager open beside it. The moment you touch the mouse, you have control back. Hold Escape for a second and its authority is gone — mid-click, mid-keystroke, whatever it was doing.\n\nEvery clause there is a mechanism, not a policy setting:\n\n| The claim | What makes it true |\n|---|---|\nallowed to fill in one form |\nA grant: (principal × resource × verbs × constraints). Not a role, not a config flag — a row in the grant table the core checks on every single action. |\nfor the next five minutes |\nExpiry is a constraint on the grant, enforced at the chokepoint. There is no path that skips it. |\ncannot see the password manager |\nThe agent's Firefox is in a realm, talking to its own private nested shim. The other window is not \"hidden\" from it; it is not in its universe. Scoping is structural. |\nyou touch the mouse, you have control |\nPhysical input is origin-tagged at the core and preempts agent input by construction, not by a race between two clients. |\nhold Escape and its authority is gone |\nThe dead-man switch: the core revokes every live grant, and the agent's next call fails `revoked` . Proven against a real app in `test_real_deadman.py` . |\n\nThe prompt that asks you to approve any of this is drawn by the trusted core\nitself — the thing that owns the screen — so no client can paint a fake one\nover it. That property has its own gate (`test_real_consent.py`\n\n): the prompt\nis shown to occlude the app's real pixels while the agent's own capture path\nsees the app unchanged.\n\nAgents that drive desktops today work screenshot-by-screenshot: capture the\nscreen, pick pixel coordinates, click, capture again. The loop is slow,\ntoken-hungry, and race-prone — and it runs with all-or-nothing authority. The\nisolation unit is a whole VM or desktop session, so a single prompt-injected\nagent's blast radius is everything on screen. There is no structural way to\nsay: *this agent may operate this one form in this one app, may not read the\npassword-manager window beside it, and loses all input the instant a human\ntouches the keyboard.*\n\nThe underlying stack cannot express that sentence. [X11](https://www.x.org/wiki/) grants every client\nnear-total authority over the session — that is its protocol model, not a\nbug. Wayland achieved isolation by *removing* cross-client capabilities\nrather than *mediating* them, and its `wl_seat`\n\nsingleton has no notion of N\nconcurrent authenticated principals. [AT-SPI2](https://gitlab.gnome.org/GNOME/at-spi2-core), the accessibility tree agents\nuse to avoid pixels, is an unauthorized backdoor onto every application's\nwidgets. The managed-cloud answers ([AWS WorkSpaces](https://aws.amazon.com/workspaces/ai-agents/) for AI agents, [Windows 365](https://www.microsoft.com/en-us/windows-365/agents)\nfor Agents) have the right instinct — identity per agent, audit, oversight —\nbut at whole-VM granularity, locked to proprietary clouds.\n\nVitrin is designed from day zero around the missing primitives:\n\n**Principals.** Every connection authenticates an identity (human or agent workload) at handshake; agent credentials are[SPIFFE](https://spiffe.io/)/OIDC-shaped.**Grants.** No ambient authority. A grant is (principal × resource × verbs × constraints — expiry, rate ceilings, focus conditions), sender-constrained to the connection, attenuable, and revocable — immediately and transitively.**Consent.** Rendered by the trusted core itself, which owns the screen and input, so prompts cannot be spoofed by any client.**Realms and shims.** Apps launch into realms; each legacy app gets its own private nested shim server, so its universe contains only itself — scoping is structural, not policy.**Human override.** Physical input preempts agent input by construction.\n\nSee [docs/PRD.md](/vitrin-os/vitrin-os/blob/main/docs/PRD.md) §1 for the full problem statement and §6 for\nthe pillar-by-pillar requirements.\n\n**Phase 1 is complete**, and there is a\n[ 60-second recording](https://vitrin-os.github.io/vitrin-os/#demo) of it\nworking: the core-drawn consent card over a real Firefox, a human clicking\nAllow, the agent filling a record it was handed, and — in a second take — a\nphysically held Escape revoking every grant mid-task so the run exits non-zero.\nTwo takes rather than one, because a revoked run cannot also print\n\n`PASS`\n\n.All nine epics (E1–E9) have landed on `main`\n\n, and\nevery milestone's named acceptance gate is closed under the rule this\nproject holds itself to — decision **D12**: *a milestone closes only when a\nnamed integration test passes green against the shipped binaries with no\nmock on any seam it claims.* Component tests built on\n[ vitrin-mock-shim](/vitrin-os/vitrin-os/blob/main/crates/vitrin-mock-shim) are never that evidence, and\nare labelled as what they are.\n\n| Milestone | Gate | Proven by |\n|---|---|---|\nM1.2 — buffer path |\n|\n\n`tests/integration/test_real_app.py`\n\n— real core + real shim + real `weston-terminal`\n\n**M1.3**— observation[#107](https://github.com/vitrin-os/vitrin-os/issues/107)`test_real_capture_fidelity.py`\n\n— an agent observes a real app through the enforcement chokepoint**M1.4**— actuation, consent, dead-man[#108](https://github.com/vitrin-os/vitrin-os/issues/108)+[#109](https://github.com/vitrin-os/vitrin-os/issues/109)+[#138](https://github.com/vitrin-os/vitrin-os/issues/138)`test_real_actuation.py`\n\n, `test_real_deadman.py`\n\n, `test_real_consent.py`\n\n**M1.5**— demo[#110](https://github.com/vitrin-os/vitrin-os/issues/110)`test_demo.py`\n\n— the demo agent is handed a task record it did not author, fills it into a real app and submits it, and the gate demands the confirmation carry *that record's*36-bit checksum (a positive content check, not a pixel diff) plus the app's own byte-exact report. The app,`form-target`\n\n, is repo-authored — a real Wayland client, no mock, but less independent than the `weston-terminal`\n\nit replaced; disclosed in [and the D12 seam table](/vitrin-os/vitrin-os/blob/main/examples/agent-demo/README.md)`examples/agent-demo/README.md`\n\nWhat exists today, on `main`\n\n:\n\n**Protocol spec v0**—[protocol/vitrin-v0.xml](/vitrin-os/vitrin-os/blob/main/protocol/vitrin-v0.xml)(11 interfaces, wire format, error taxonomy; the source of truth), its RELAX NG schema[protocol/vitrin-v0.rng](/vitrin-os/vitrin-os/blob/main/protocol/vitrin-v0.rng), and a prose page per interface under[docs/protocol/](/vitrin-os/vitrin-os/blob/main/docs/protocol/00-conventions.md)kept in lockstep with every landing PR.**Codegen**—plus`vitrin-scanner`\n\n`cargo xtask codegen`\n\ngenerate theRust crate (message types + codec; pure data, no I/O) and the C header`vitrin-protocol`\n\n[shim/include/vitrin-protocol.h](/vitrin-os/vitrin-os/blob/main/shim/include/vitrin-protocol.h)from the IDL.(`vitrind`\n\n, the trusted core) — a real`crates/vitrin-core`\n\n[Smithay](https://github.com/Smithay/smithay)compositor with both output backends (`--nested`\n\n, a host Wayland client;`--headless --size WxH`\n\n, GPU-less pixman software rendering for CI); the capability kernel and in-memory grant table (request → pending → consent → resolved, sender-constrained, rate-limited, revocable); the realm/spawn manager (fork/exec the shim with a private runtime dir and a scrubbed, allow-listed environment); the core-rendered consent prompt with an exclusive input grab; the hold-Esc dead-man revocation switch; the[dmabuf](https://docs.kernel.org/driver-api/dma-buf.html)import path (— zero-copy shim→core frames on a real GPU, with shm as the universal fallback and an explicit`dmabuf.rs`\n\n`buffer_done(import_failed)`\n\nevent rather than a silent black frame, so CI stays GPU-free); and the flight-recorder log. See the[Architecture at a glance](#architecture-at-a-glance)section and[docs/ARCHITECTURE.md](/vitrin-os/vitrin-os/blob/main/docs/ARCHITECTURE.md)for the full crate↔PRD map.**The Wayland shim**() — a`shim/`\n\n[wlroots](https://gitlab.freedesktop.org/wlroots/wlroots)headless compositor, C +[Meson](https://mesonbuild.com/), outside the Cargo workspace by design. It forwards every composited frame to the core over the inherited socketpair, replays origin-tagged input into the app through its own`wl_seat`\n\n, and runs real apps:`weston-terminal`\n\n, a[GTK](https://www.gtk.org/)entry probe, and[Firefox ESR](https://www.mozilla.org/en-US/firefox/enterprise/)(pinned), climbed one rung at a time and proven in CI with no mock on the shim seam.**The agent SDK**() — a pure-Python (stdlib-only, D8) wire client: connect → handshake →`sdk/python/`\n\n`request_grant`\n\n→`observe()`\n\n→`actuate_pointer`\n\n/`actuate_text`\n\n, typed grant-error exceptions, and capture ergonomics (`.to_png()`\n\n).is the demo agent and doubles as the M1.5 integration test; run it with`examples/agent-demo/run_demo.py`\n\n`cargo xtask demo`\n\n(see[Quickstart](#quickstart)below).**CI**—[.github/workflows/ci.yml](/vitrin-os/vitrin-os/blob/main/.github/workflows/ci.yml): Rust fmt/clippy/tests (debug + release), IDL schema validation and generated-code drift checking, a Rust-free container build of the C shim, golden-frame pixel/SSIM checks, and a headless integration suite that drives the real core against real apps (`weston-terminal`\n\n, GTK, Firefox ESR) with a hard 10-minute budget.\n\nPhase 1 being complete is a statement about a defined slice, not about readiness for anything real. Read this list before the pitch above convinces you of more than it should.\n\n**No sandbox (decision D9, closes in Phase 2).** No namespaces, no[seccomp](https://man7.org/linux/man-pages/man2/seccomp.2.html), no[Landlock](https://landlock.io/). A realm's app runs as the core's own uid with the core's full view of the filesystem and network, and the session D-Bus remains reachable. Environment hygiene confines the well-behaved; it does not contain the hostile. This is the big one — see[Security notes](#security-notes--what-the-mvp-does-and-does-not-confine).**The 24-hour fuzz soak has not been run.**`fuzz/`\n\nships two cargo-fuzz targets (protocol decode,`vitrin-ipc`\n\nframing) with a checked-in corpus CI replays on every PR plus a short per-PR burst, but the 24-hour clean run the plan asks for is still a manual, documented procedure rather than a scheduled job —[fuzz/README.md](/vitrin-os/vitrin-os/blob/main/fuzz/README.md)says so in its own words. Nobody has run it end to end.**wlcs conformance is advisory and mostly red:**`total=180 passed=3 failed=145 skipped=32`\n\non the 2026-07-25 run. That number is expected and is not a bug count —[wlcs](https://github.com/canonical/wlcs)tests a general-purpose desktop compositor, and the shim deliberately serves a narrow surface (no touch, no full`xdg-shell`\n\npolicy, no decoration protocols), so most failures are \"no such global\" rather than misbehaviour. It is still the honest number, it has**not** been re-measured since, and`shim/wlcs/README.md`\n\ndistinguishes the structural absences from the genuine ones. Never built by default; never gates a PR.**One maintainer.** Governance is a documented BDFL ([plan §5](/vitrin-os/vitrin-os/blob/main/docs/plan/12-workstream-community.md)); bus factor is tracked as a first-class project risk, not waved away.**v0 protocol, and it will break.** The IDL is frozen for Phase 1, not forever — Phase 2 adds semantic trees and epoch/CAS action semantics, and v0 clients should expect to move.\n\nNothing above is discovered-by-a-reader; each is a decision with a recorded\nrationale in [docs/plan/20-decision-log.md](/vitrin-os/vitrin-os/blob/main/docs/plan/20-decision-log.md).\n\nFrom a clean clone to a running demo. Verified against the state of `main`\n\ndescribed above (headless venue; the nested venue additionally needs a\nWayland session and Firefox ESR — see the note at the end).\n\nThe demo runs the **real** wlroots shim in both venues, so unlike a\nRust-only build it needs the C shim built and a real Wayland client\ninstalled. `cargo xtask demo`\n\nfails with the exact `meson`\n\ncommand below if\nthe shim is missing, rather than silently substituting anything.\n\n```\ngit clone https://github.com/vitrin-os/vitrin-os.git\ncd vitrin-os\n\n# The toolchain is pinned (rust-toolchain.toml); rustup installs it on the\n# first cargo invocation. System deps the workspace links, plus the C\n# shim's build deps and a real Wayland client for it to run:\nsudo apt-get install -y libxkbcommon-dev libpixman-1-dev weston   # Debian/Ubuntu\nbash shim/ci/install-deps.sh                                      # Meson + wlroots deps\n\n# Build vitrind, xtask, and the fixtures the test suites use.\ncargo build --workspace\n\n# Build the real per-app wlroots shim (C + Meson, outside the Cargo\n# workspace). cargo xtask demo looks for it at shim/build/vitrin-shim, or\n# wherever VITRIN_C_SHIM_BIN points.\nmeson setup shim/build shim && meson compile -C shim/build\n\n# Run the Phase-1 demo agent headless: boots vitrind --headless, which\n# execs the real shim, which fork/execs a real Wayland client (form-target,\n# co-built with the shim) in its own confined Wayland socket, and drives\n# examples/agent-demo/run_demo.py over a real Unix socket. The agent is\n# handed a task record it did not author, locates each form field by its\n# marker colour in its own captured frame, clicks, types the value, clicks\n# the located submit button, and then decodes the confirmation's three\n# receipt bands -- a 36-bit checksum of the record the app received,\n# computed from the SUPPLIED task at runtime. Exits non-zero on any failure.\ncargo xtask demo --headless\n\n# ... or with a record the agent has never seen:\ncargo xtask demo --headless --task name=Grace --task email=grace@example.net\n```\n\nExpect output ending in `xtask demo: PASS`\n\nand a path to the run's flight\nrecorder (`flight.jsonl`\n\n) and captured frames. This exercises the real wire\nprotocol, the real capability kernel, the real consent auto-approve path,\nthe real wlroots shim, and a real app — `vitrin-mock-shim`\n\nappears in no\ndemo venue ([#110](https://github.com/vitrin-os/vitrin-os/issues/110) /\n[#127](https://github.com/vitrin-os/vitrin-os/pull/127)); it survives only\nas a unit-test fixture for the component tests in `tests/integration/`\n\n.\n\nThe same chain under the full integration suite, including the named milestone gates:\n\n```\nVITRIN_C_SHIM_BIN=\"$PWD/shim/build/vitrin-shim\" bash tests/integration/run.sh\n```\n\n**Nested mode** (`cargo xtask demo`\n\n, no `--headless`\n\n) draws a real window on\nyour own Wayland session and launches Firefox ESR in the realm — it needs a\nrunning compositor (GNOME, Hyprland, ...) and `firefox-esr`\n\n(or\n`VITRIN_DEMO_FIREFOX=/path/to/firefox`\n\n) on the machine you run it on; it is\nnever a CI dependency (nested mode has no headless equivalent by design —\nplan risk R1).\n\nOther useful commands, all covered in CI:\n\n```\nxmllint --noout --relaxng protocol/vitrin-v0.rng protocol/vitrin-v0.xml  # validate the IDL\ncargo test --workspace && cargo test --workspace --release              # unit + integration tests\ncargo xtask codegen --check                                              # generated-code drift check\nphp\nflowchart TD\n    HUMAN[\"human<br/>physical input\"] --> CORE\n    AGENT[\"agent principal<br/>SDK client\"] -->|\"capability-native wire protocol<br/>handshake · grants · observe · actuate\"| CORE\n\n    CORE[\"<b>vitrind — trusted core (Rust)</b><br/>capability kernel · grant store · compositor<br/>input router · consent surface · journal\"]\n\n    CORE <-->|\"frames up (dmabuf fd)<br/>input down (origin-tagged)\"| S1\n    CORE <--> S2\n\n    subgraph R1[\"realm\"]\n        S1[\"per-app Wayland shim\"] --> A1[\"unmodified legacy app\"]\n    end\n    subgraph R2[\"realm\"]\n        S2[\"per-app Wayland shim\"] --> A2[\"unmodified legacy app\"]\n    end\n```\n\nThe core is the entire trusted computing base: capability kernel and grant\nstore, scene composition, input routing, consent surface, journals. Legacy\napps never touch it directly — each is launched with `WAYLAND_DISPLAY`\n\npointing only at its own private shim, which is itself an unprivileged client\nof the core. Frame buffers move as dmabuf file descriptors over `SCM_RIGHTS`\n\n(zero-copy, one extra IPC hop — the [gamescope](https://github.com/ValveSoftware/gamescope)/[Qubes](https://www.qubes-os.org/) precedent). Window\nmanagement policy, decoration, and theming stay out of the core, permanently.\n\nVitrin's design claims are strong, and the Phase-1 MVP does not yet deliver\nall of them. The gaps below are **deliberate, settled decisions with a\nscheduled closure**, not oversights — and they are stated here, at the front\nof the security story, rather than buried in a module doc, because a\nhalf-believed confinement claim is worse than an honest gap.\n\n**Realm confinement today is environment-structural only.** When the core\nlaunches a realm's app it forks a per-app shim, hands it one end of a\nsocketpair as its identity (no credential, no handshake — holding the\ndescriptor *is* being that realm's shim), gives it a private `0700`\n\nruntime\ndirectory, and builds its environment from nothing: only names the operator\nallow-listed in `realm.toml`\n\n, plus a `WAYLAND_DISPLAY`\n\npointing at that\nrealm's own private socket. `DISPLAY`\n\n, the host `WAYLAND_DISPLAY`\n\n,\n`WAYLAND_SOCKET`\n\n, `XAUTHORITY`\n\nand the host `XDG_RUNTIME_DIR`\n\ncannot reach\nthe app at all. No unrelated descriptor of the core's — the agent listener,\nthe flight-recorder log, other realms' sockets, capture memfds — crosses the\nfork, and the child starts from a defined signal state rather than inheriting\nwhatever the operator's shell happened to be ignoring. Both are enforced by\nthe fork itself (a `close_range`\n\nsweep and a disposition reset between `fork`\n\nand `execve`\n\n), not by every other module remembering to be careful.\n\nThat is the complete list of what confines a realm right now.\n\n**No sandbox (decision D9, closes in Phase 2).** There are**no namespaces, no seccomp filter, and no Landlock policy**. The shim and its app run as the core's own uid with the core's full view of the filesystem and the network. An app that ignores`WAYLAND_DISPLAY`\n\nand connects directly to a path it already knows is not stopped by anything in the MVP. Real sandboxing arrives with the Phase-2 powerbox (E2.6/E2.7). Environment hygiene confines the well-behaved; it does not contain the hostile.**The session** The core advertises no[D-Bus](https://www.freedesktop.org/wiki/Software/dbus/)is reachable (known hole, closes with P13 in Phase 2).`DBUS_SESSION_BUS_ADDRESS`\n\nand points`XDG_RUNTIME_DIR`\n\nat the realm's private directory, so a well-behaved client finds no bus. But advertisement is not reachability:`/run/user/<uid>/bus`\n\nis still on the filesystem and still connectable by any process of that uid, and the abstract-socket namespace is still shared. In practice, running Firefox — the Phase-1 acceptance app — means allow-listing`DBUS_SESSION_BUS_ADDRESS`\n\nexplicitly, which turns the implicit hole into an audited one. This is a lateral-escape path of exactly the shape[PRD](/vitrin-os/vitrin-os/blob/main/docs/PRD.md)§15 catalogues (D-Bus activation of a privileged helper);**P13** closes it with a loopback-only network namespace plus an empty mount namespace, so that there is nothing to reach rather than nothing advertised.**Same-uid separation is not attempted.** The`0700`\n\nruntime directory bounds other*users*on the machine, not other processes of this user. Note what the realm's`XDG_RUNTIME_DIR`\n\ntherefore is and is not: its value,`$XDG_RUNTIME_DIR/vitrin-0/<realm>`\n\n, sits one level below the directory holding the core's own agent socket and this run's flight-recorder log, so it names the control plane as much as it hides it. Redirecting it means a well-behaved client finds its own realm's socket instead of the host session's — it does not mean the app cannot reach the rest, because under D9 it runs as the core's uid and can derive those paths with or without a variable pointing at them.\n\nThe spawn path and every decision above are documented in full in\n[ crates/vitrin-core/src/spawn.rs](/vitrin-os/vitrin-os/blob/main/crates/vitrin-core/src/spawn.rs);\n\n`realm.toml`\n\n's own security rules are in\n[.](/vitrin-os/vitrin-os/blob/main/examples/realm.toml)\n\n`examples/realm.toml`\n\n| Path | What it is |\n|---|---|\n`docs/PRD.md` |\n\n`docs/ARCHITECTURE.md`\n\n`protocol/vitrin-v0.xml`\n\n`protocol/vitrin-v0.rng`\n\n`docs/protocol/`\n\n`docs/plan/`\n\n`docs/demo/`\n\n`crates/vitrin-core/`\n\n`vitrind`\n\n— the trusted core (compositor, capability kernel, grant store, realms, consent)`crates/vitrin-protocol/`\n\n`crates/vitrin-scanner/`\n\n`crates/vitrin-ipc/`\n\n`SCM_RIGHTS`\n\n, `SO_PEERCRED`\n\n, backpressure policy`crates/vitrin-mock-shim/`\n\n`crates/vitrin-golden/`\n\n`crates/xtask/`\n\n`cargo xtask codegen [--check]`\n\n/ `bless`\n\n/ `demo [--headless]`\n\n`shim/`\n\n`sdk/python/`\n\n`vitrin_os`\n\npackage, D8)`examples/agent-demo/run_demo.py`\n\n`cargo xtask demo`\n\n`tests/integration/`\n\n`vitrind`\n\nbinary + real shim + real apps over a real socket`.github/workflows/ci.yml`\n\nThe toolchain is pinned to Rust **1.94.1** in\n[rust-toolchain.toml](/vitrin-os/vitrin-os/blob/main/rust-toolchain.toml); rustup installs it automatically\non the first `cargo`\n\ninvocation. The pin is exact rather than `stable`\n\nbecause `cargo xtask codegen --check`\n\ncompares generated output byte-for-byte\nand that output depends on rustfmt's exact formatting decisions (the codegen\nshells out to `rustfmt`\n\n; the pinned toolchain's default profile ships it).\nThe crates' MSRV is 1.87. CI builds with `RUSTFLAGS=\"-D warnings\"`\n\n.\n\n```\n# Validate the IDL against the RELAX NG schema (requires xmllint/libxml2)\nxmllint --noout --relaxng protocol/vitrin-v0.rng protocol/vitrin-v0.xml\n\n# Build and test the workspace — CI runs both debug and release\ncargo test --workspace\ncargo test --workspace --release\n\n# Check that the generated Rust crate and C header match the IDL\ncargo xtask codegen --check\n\n# Regenerate them after editing protocol/vitrin-v0.xml\ncargo xtask codegen\n\n# C header: standalone compile + golden-frame check against the Rust codec\ncc -std=c11 -Wall -Werror -I shim/include -c shim/tests/test_header_compiles.c -o /dev/null\ncc -std=c11 -Wall -Werror -I shim/include shim/tests/test_golden_frames.c -o /tmp/golden && /tmp/golden\n```\n\nThe wire format (little-endian frames, 8-byte header, at most one fd per\nmessage), object-id rules, ordering guarantee, error taxonomy, and versioning\nrules are defined in [00-conventions.md](/vitrin-os/vitrin-os/blob/main/docs/protocol/00-conventions.md).\nThere are two connection classes sharing one wire format: agent principals on\nthe core's listening socket, and per-app shims on a core-inherited\nsocketpair. A grant petition co-mints the grant, a consent observer, and the\nfacets (view, pointer, text) in one request; facets are born inert and confer\nnothing until the grant resolves. Where prose and IDL disagree, **the IDL's\n<description> text wins.**\n\n| Interface | Purpose |\n|---|---|\n`vitrin_handshake` |\n\n`vitrin_principal`\n\n`vitrin_realm`\n\n`realm-0`\n\n)`vitrin_grant`\n\n`vitrin_consent`\n\n`vitrin_view`\n\n`vitrin_actuator_pointer`\n\n`vitrin_actuator_text`\n\n`vitrin_shim_session`\n\n`vitrin_shim_surface`\n\n`vitrin_shim_seat`\n\nCondensed from [docs/PRD.md](/vitrin-os/vitrin-os/blob/main/docs/PRD.md) §8:\n\n**Phase 0 — Spec & manifesto.** Vision, object model, wire-protocol draft (the PRD in this repo).**Phase 1 — MVP slice***(complete)*. Trusted core (headless + nested), one wlroots Wayland shim, Firefox in a realm, and an agent that captures the realm and injects scoped input — gated by a single grant with consent rendered by the core.**Phase 2 — Semantic + epochs***(next)*.[AccessKit](https://accesskit.dev/)/AT-SPI2 bridge, versioned and diffable semantic trees, epoch/CAS action semantics, VLM fallback for treeless surfaces, native semantic demo app, filesystem powerbox v0, network authority v0 (per-realm loopback-only netns, egress as a grant).**Phase 3 — Network + X11 + fleet.**[QUIC](https://www.rfc-editor.org/rfc/rfc9000)network sessions, per-app X11 shim with embedded WM, multi-realm headless fleet mode, journal replay tooling, synthetic-path FUSE layer, credential wallet v0, mission-control shell v0.**Phase 4 — Horizon.** Session mode on bare DRM/KMS, Flutter/iced/egui semantic backends, capability-remoting protocol hardened for third-party clients, EUDI/OID4VC conformance — entered only when adoption justifies the support burden.\n\nWork is tracked as GitHub issues in\n[vitrin-os/vitrin-os](https://github.com/vitrin-os/vitrin-os/issues). Phase 1\nis split into nine epics, each carrying exactly one `track:*`\n\nlabel\n(`track:protocol`\n\n, `track:rust-core`\n\n, `track:c-shim`\n\n, `track:sdk`\n\n,\n`track:ci-docs`\n\n), sequenced by milestones `M1.1`\n\n–`M1.5`\n\n.\n\n**Branches**:`p<phase>.<epic>.<task>-slug`\n\n, e.g.`p1.1.1-protocol-idl`\n\n.**Commits**:[Conventional Commits](https://www.conventionalcommits.org/),`type(scope): summary`\n\n, where scope is the track (`protocol`\n\n,`rust-core`\n\n,`c-shim`\n\n,`sdk`\n\n,`ci-docs`\n\n) or`root`\n\n. Reference the tracking issue in the footer (`Closes #10`\n\n/`Refs #10`\n\n).**Protocol changes are paired edits, never one alone**: change`protocol/vitrin-v0.xml`\n\n(and`protocol/vitrin-v0.rng`\n\nonly if the dialect itself changes) together with the matching`docs/protocol/NN-*.md`\n\npage, then validate with`xmllint --noout --relaxng protocol/vitrin-v0.rng protocol/vitrin-v0.xml`\n\n. After IDL edits, run`cargo xtask codegen`\n\nand commit the regenerated code in the same change.**Language**: English only — code, docs, commits, issues, PRs.\n\nSplit per decisions D-005 and D-016 (`docs/plan/20-decision-log.md`\n\n).\n[ NOTICE](/vitrin-os/vitrin-os/blob/main/NOTICE) is the normative path→license map; the\n\n`SPDX-License-Identifier`\n\nheader on a file is authoritative for that file.**Apache-2.0**() — the protocol (`LICENSE`\n\n,`protocol/vitrin-v0.xml`\n\n), the bindings generated from it (`protocol/vitrin-v0.rng`\n\n`crates/vitrin-protocol`\n\n,`shim/include/vitrin-protocol.h`\n\n), the code generator that emits them and its driver (`crates/vitrin-scanner`\n\n,`crates/xtask`\n\n), the conformance and fuzz instruments, the**client SDKs**(), the integration harness (`sdk/python/`\n\n) and the shipped`tests/integration/`\n\n.`examples/`\n\n**MPL-2.0**(`LICENSE-MPL-2.0`\n\n) — the reference implementation:`crates/vitrin-core`\n\n(the trusted core),`crates/vitrin-ipc`\n\n, and the per-app Wayland shim under.`shim/`\n\n**CC-BY-4.0**() — spec prose (`LICENSE-CC-BY-4.0`\n\n,`docs/PRD.md`\n\n,`docs/protocol/`\n\n).`docs/plan/`\n\n**GPL-3.0-only**(`LICENSE-GPL-3.0-only`\n\n) — one carve-out,, the advisory`shim/wlcs/`\n\n[WLCS](https://github.com/canonical/wlcs)conformance module, which links GPL-3.0 headers from Canonical's wlcs. It is never built by default, never installed, and never linked into`vitrin-shim`\n\n.\n\n**What that means for you, plainly:** you never have to touch an MPL-2.0\nfile to write a client, build an alternate compositor or shim, or ship an\nintegration — the protocol, the generated bindings, the codegen and the\nSDKs are all Apache-2.0, patent grant included. The copyleft binds one\ngroup only: people who modify the trusted core itself, whose changes to a\ncapability kernel should come back. MPL's copyleft is per-file, so\napplications running under Vitrin are unaffected — running an app inside a\nshim does not make it derivative of anything here.\n\n**No CLA.** Contributions are taken under the\n[Developer Certificate of Origin](https://developercertificate.org/)\n(a `Signed-off-by:`\n\nline), not a Contributor License Agreement — decision\nD-012. Nobody is asked to assign copyright: contributors keep theirs, and\nthe project never acquires the unilateral power over *their* code that a\nCLA would hand it. That is deliberate — it is what stops the split above\nfrom being merely this year's mood.\n\n**No patents.** Vitrin OS files none and intends to file none (D-015). The\ndesign is protected by publishing it — a timestamped spec is itself the\nprior art — and by the Apache-2.0 §3 and MPL-2.0 §2.1(b) patent grants that\nship with the code. Both are in force today. A third leg, joining the [Open\nInvention Network](https://openinventionnetwork.com/)'s royalty-free cross-licence, is decided but **not yet\ndone**. None of this is a patent wall, and none of it is a\nfreedom-to-operate opinion.", "url": "https://wpnews.pro/news/show-hn-vitrin-os-an-agent-first-display-server-with-per-app-isolation", "canonical_source": "https://github.com/vitrin-os/vitrin-os", "published_at": "2026-07-26 14:41:24+00:00", "updated_at": "2026-07-26 14:52:33.299782+00:00", "lang": "en", "topics": ["ai-agents", "ai-safety", "ai-infrastructure", "ai-tools", "ai-research"], "entities": ["Vitrin OS", "Wayland", "X11", "Firefox", "AWS WorkSpaces", "Windows 365", "AT-SPI2"], "alternates": {"html": "https://wpnews.pro/news/show-hn-vitrin-os-an-agent-first-display-server-with-per-app-isolation", "markdown": "https://wpnews.pro/news/show-hn-vitrin-os-an-agent-first-display-server-with-per-app-isolation.md", "text": "https://wpnews.pro/news/show-hn-vitrin-os-an-agent-first-display-server-with-per-app-isolation.txt", "jsonld": "https://wpnews.pro/news/show-hn-vitrin-os-an-agent-first-display-server-with-per-app-isolation.jsonld"}}