Skip to content
Draft
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
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ artifacts-local/
/artifacts/
website/node_modules/
website/dist/
spikes/v4-tauri-studio/node_modules/
spikes/v4-tauri-studio/dist/
spikes/v4-tauri-studio/src-tauri/target/
spikes/v4-tauri-studio/results/local/
spikes/v4-tauri-studio/test-results/
spikes/v4-tauri-studio/playwright-report/
spikes/v4-tauri-studio/*.tsbuildinfo
spikes/v4-tauri-studio/src-tauri/gen/
aqtinstall.log
/.phase0-smoke/
/.phase0-smoke-*.log
89 changes: 89 additions & 0 deletions docs/adr/0001-tauri-2-studio-feasibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# ADR 0001: Keep the V4 Studio framework decision open after the Tauri 2 spike

- Status: Proposed; decision gate remains open
- Date: 2026-08-02
- Scope: V4-03 Studio framework evidence update

## Context

The V4 execution plan provisionally prefers C++/WinRT with WinUI 3 and requires
equivalent WinUI 3 and Qt 6 shells to present four shared D3D11 textures before
selecting a framework. This spike adds Tauri 2 + React/shadcn as a third
candidate at the request of the lead. It does not alter the central execution
ledger or replace the original comparison.

V4 requires a disposable `OpenStreamStudio.exe`: views and commands only,
versioned named-pipe control/state, and shared D3D11 handles from an independent
C++ engine. Studio must never own media lifetime or put network/disk work on the
UI thread.

## Decision

Do not select Tauri, WinUI 3, or Qt 6 from this spike. Retain the provisional
WinUI 3 direction and keep V4-03 open until the original equivalent-candidate
matrix and the native shared-texture presentation gate have measured results.

Tauri remains a viable control-shell candidate because this spike proves:

- the Rust backend client can connect to a typed fake pipe in behavioral tests,
and the standalone service creates a user-SID-scoped protected pipe;
- the pipe hello is versioned, length-bounded, nonce checked, request matched,
time bounded, and executed on Tokio rather than React's UI thread;
- React receives typed preview descriptors without owning sessions or seeing
native handle values;
- the compact shell has keyboard, semantic, light/dark, reduced-motion, and
browser scale-test infrastructure.

Tauri is not yet acceptable for product Studio because this spike does not
present keyed/shared D3D11 textures. A WebView placeholder says nothing about
the native compositor path, keyed-mutex scheduling, adapter identity, occlusion,
device removal, resize synchronization, or zero CPU frame copies. A native
overlay/child-window presenter may erase much of Tauri's simplicity and must be
prototyped before selection.

## Evidence matrix

| Gate | Tauri 2 | WinUI 3 | Qt 6 |
|---|---|---|---|
| Four typed simulated previews | PASS | NOT_RUN | NOT_RUN |
| Separate user-scoped pipe service | PASS standalone; GUI consumption FAIL 0/3 | NOT_RUN | NOT_RUN |
| UI-thread-independent pipe delay/timeout | PASS (async contract and timeout) | NOT_RUN | NOT_RUN |
| Real shared D3D11 texture presentation | NOT_RUN | NOT_RUN | NOT_RUN |
| Installed bytes / start / private bytes / idle CPU | See raw spike evidence | NOT_RUN | NOT_RUN |
| Process-attributed idle GPU | NOT_RUN | NOT_RUN | NOT_RUN |
| Resize measurement | See raw spike evidence | NOT_RUN | NOT_RUN |
| 100/150/200% browser rendering | PASS | NOT_RUN | NOT_RUN |
| Actual Windows 100/150/200% DPI | NOT_RUN | NOT_RUN | NOT_RUN |
| Keyboard and automated accessibility scan | PASS | NOT_RUN | NOT_RUN |
| Narrator human verification | NOT_RUN | NOT_RUN | NOT_RUN |
| Light/dark and reduced motion | PASS | NOT_RUN | NOT_RUN |

`NOT_RUN` is intentional. API availability, browser emulation, and simulated
descriptors are not substituted for native evidence.

## Deployment and IPC consequences

Tauri adds Rust and npm supply chains plus the system WebView2 runtime. The
measured bundle uses a current-user NSIS installer and embedded Evergreen
bootstrapper. Offline/fixed runtime choices materially change installer size
and servicing; Evergreen improves security servicing but requires forward-
compatibility testing and an application restart to take a newly installed
runtime. Product artifacts require signing of every executable and installer,
trusted timestamping, pinned dependencies, licenses, and SBOM/provenance.

Tauri commands serialize JSON through the core process. Capabilities limit
frontend API exposure but do not make incorrect Rust safe. Product IPC would
retain the engine's independent named-pipe protocol, strict maximum frames,
timeouts/cancellation, snapshot/reconnect, a bounded/coalescing event channel,
and user-only ACL. Same-user pipe squatting remains in the threat model; the
test nonce is not a production credential.

## Consequences and rollback

- Keep this prototype quarantined under `spikes/v4-tauri-studio`; it is not
referenced by root CMake or product builds.
- Do not move Rust, WebView2, npm, or Tauri dependencies into the C++ engine.
- Before reconsidering Tauri, build the smallest native D3D11 presenter and
collect the original WinUI/Qt/Tauri comparison on the same host and method.
- Rollback is deletion of the quarantined spike, raw evidence, and this ADR;
no legacy, engine, Android, OBS, or product Studio path changes.
110 changes: 110 additions & 0 deletions docs/evidence/v4-03-tauri-studio.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# V4-03 Tauri 2 Studio feasibility evidence

Date: 2026-08-02. Candidate: Tauri 2.11.5, React 19.2.8, shadcn-style
primitives, WebView2 150.0.4078.105. This updates evidence only; it does not
complete the WinUI 3 versus Qt 6 framework decision.

## Result

The candidate is useful but **not selected**. The typed Rust pipe client passes
its behavioral test against a versioned named-pipe snapshot and the standalone
test service creates a protected current-user-SID pipe. The release GUI launched
and resized reliably, but its three measurement launches did not consume the
standalone service snapshot (`pipeServiceExitCode: null` in every raw row).
Therefore an end-to-end GUI-to-service connection is a **failed cell**, not a
pass. Real D3D11 shared-texture presentation, OS DPI switching, Narrator, and
process-attributed GPU are also `NOT_RUN`.

## Environment

- HP Victus 15-fa1xxx, i5-13420H (12 logical processors), 16 GB RAM.
- NVIDIA RTX 4050 Laptop GPU, driver 32.0.15.9282; Intel UHD 32.0.101.5542.
- Windows 11 Home 10.0.26200; Node 25.6.1; npm 11.9.0; Rust 1.88.0.
- Tauri 2.11.5 / tauri-build 2.6.3 exact; WebView2 150.0.4078.105.
- Release Studio executable: 3,320,832 bytes. Studio plus test-service
executables: 3,641,856 bytes. No installer was installed, so true expanded
installed bytes are `NOT_RUN`.
- Bundle policy is current-user NSIS with embedded Evergreen bootstrapper.

## Raw measurements

Source: `spikes/v4-tauri-studio/results/2026-08-02-target/measurements.jsonl`.
Three launches used a three-second settle and a two-second CPU sample. The first
launch is only `cold-unprimed`, not a post-reboot cold launch.

| Measurement | Result |
|---|---:|
| Launch to responsive window, first | 1122.53 ms |
| Warm launch to responsive window, mean (2) | 753.55 ms |
| Process-tree private bytes, mean | 196,227,072 bytes |
| Process-tree working set, range | 444,575,744–452,501,504 bytes |
| Idle CPU, mean | 0.369% of total logical CPU |
| Three-step resize call sequence, mean | 23.38 ms |
| Process count | 8 |
| Standalone pipe service consumed by GUI | FAIL, 0/3 |

The resize value measures synchronous window resize calls, not rendered-frame
jank or latency. CPU and memory include the Studio/WebView2 descendant process
tree discovered from parent PIDs. GPU is not inferred from API availability.

## Automated gates

| Gate | Result |
|---|---|
| `npm run build:web` | PASS |
| `npm test` | PASS, 2 tests |
| `npm run test:a11y` | PASS, 6 tests |
| `npm audit --audit-level=high` | PASS, 0 vulnerabilities |
| `cargo fmt --check` | PASS |
| `cargo test --workspace --all-targets` | PASS, 2 tests |
| User-SID pipe/DACL standalone service smoke | PASS |
| GUI process launch/resize samples | PASS, 3 rows |

The rendered flow was tested through Playwright after the in-app Browser runtime
failed to initialize with `failed to write kernel assets`. At browser device
scale factors 1.0, 1.5, and 2.0 the four-preview shell rendered, keyboard focus
reached and activated Theme, axe reported no violations, light/dark changed,
and reduced-motion CSS reduced transition duration. Browser scale factors are
not claimed as Windows OS DPI tests.

Visual inspection of the 1180×760 screenshot checked: all four previews are
visible; no clipping or overlap; warning is text and colour; focus ring is
visible; status and descriptor text are legible; footer safety statements fit.

## Exact `NOT_RUN` and failed cells

- **Installed size:** `NOT_RUN`; no NSIS installation was performed under the
lead timebox. Executable bytes and WebView2 policy are reported separately.
- **Post-reboot cold start:** `NOT_RUN`; rebooting the host was outside the
isolated spike session. The first sample is labeled cold-unprimed only.
- **Idle GPU:** `NOT_RUN`; the harness did not obtain reliable per-process GPU
Engine counters for the complete WebView2 process tree.
- **Real 100/150/200% OS DPI:** `NOT_RUN`; Windows scale changes require an
interactive sign-out/reconfiguration. Browser device-scale automation is
retained only as layout evidence.
- **Narrator:** `NOT_RUN`; screen-reader quality requires an interactive human
judgment pass. Axe and semantic DOM evidence do not substitute for it.
- **Real shared D3D11 textures:** `NOT_RUN`; descriptors are simulated and no
native texture presenter exists.
- **GUI-to-standalone-service snapshot:** **FAIL** in the release measurement,
0/3 services exited after Studio launch. The narrower Rust typed-client test
passes and must not be broadened into an end-to-end claim.

## Reproduce

```powershell
cd spikes/v4-tauri-studio
npm ci
npm run build:web
npm test
npm run test:a11y
cargo fmt --manifest-path src-tauri/Cargo.toml -- --check
cargo test --manifest-path src-tauri/Cargo.toml --workspace --all-targets
npm run build
powershell -File scripts/measure-release.ps1 -Launches 3 -IdleSeconds 3 -OutputDirectory results/2026-08-02-target
powershell -File scripts/summarize-results.ps1 -InputPath results/2026-08-02-target/measurements.jsonl
```

The raw service stdout files preserve each protected pipe name. Generated media
does not exist; this spike performs no codec, recording, timestamp, or frame-copy
work.
78 changes: 78 additions & 0 deletions spikes/v4-tauri-studio/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Tauri 2 Studio feasibility spike

This quarantined V4-03 candidate evaluates a Tauri 2 host with React and
shadcn-style primitives. It is not product Studio code and is not part of the
root CMake build.

The Rust Tauri host is a disposable control client. A second executable,
`openstream-tauri-pipe-service`, is only a fake engine-style service. It owns
the user-SID-scoped named-pipe server and remains a separate process. Neither
binary contains media, codec, OBS, recording, or C++ engine code.

The flow under test is: launch the independent test service -> launch Studio ->
the async Tauri command performs a nonce-authenticated, versioned pipe hello ->
React receives four typed `PreviewDescriptor` values and renders placeholders.
No D3D11 handle is exposed to JavaScript. These descriptors do not prove shared
texture presentation or a zero-copy path.

## Pinned toolchain

- Rust 1.88.0 (`rust-toolchain.toml`)
- Node from the environment; the measured version is captured in JSONL
- Exact npm dependency versions in `package-lock.json`
- Exact Cargo resolution in `src-tauri/Cargo.lock`

On Windows, install the Rust MSVC toolchain and Visual Studio 2022 Build Tools
with MSVC v143 and a Windows 11 SDK. Then run:

```powershell
npm ci
npm run build:web
npm test
npm run test:a11y
cargo test --manifest-path src-tauri/Cargo.toml --all-targets
npm run build
powershell -File scripts/measure-release.ps1 -Launches 5 -IdleSeconds 10
```

The Browser plugin was attempted first for rendered QA. Its local runtime was
unavailable in this worktree (`failed to write kernel assets`), so the checked-in
Playwright suite is the recorded fallback.

## Security and bounds

- Pipe name includes the current user SID; the server applies a protected DACL
granting that SID access and uses first-instance creation.
- A 32--128 character per-run nonce and protocol/request IDs reject accidental
or stale same-user connections. This is test hardening, not production
authentication.
- Frames are length-prefixed and capped at 64 KiB. The request has a two-second
timeout. There is no event queue in this single-snapshot spike.
- Pipe work is awaited by Tokio inside an async Tauri command, never by React
or the WebView UI thread.
- The capability contains only Tauri core defaults for the `main` window; the
app loads bundled local content under a restrictive CSP and grants no remote
URL access.

## Windows distribution implications

The spike fixes NSIS to current-user install and uses Tauri's embedded
WebView2 bootstrapper mode. That adds roughly 1.8 MB to the installer and still
requires network access if Evergreen WebView2 is missing. Offline and fixed
runtime modes add roughly 127 MB and 180 MB respectively, so installed-size
claims must always state the mode. Windows 11 normally carries Evergreen
WebView2, but the installer must still handle missing/managed runtimes.

Product release would sign the Studio executable, independent engine/test
sidecar, and NSIS installer using SHA-256 plus trusted timestamping (or Tauri's
custom `signCommand` backed by organizational key custody). This spike has no
certificate and does not produce signed release evidence.

Primary references:

- <https://v2.tauri.app/concept/inter-process-communication/>
- <https://v2.tauri.app/security/capabilities/>
- <https://v2.tauri.app/distribute/windows-installer/>
- <https://v2.tauri.app/distribute/sign/windows/>
- <https://learn.microsoft.com/microsoft-edge/webview2/concepts/distribution>
- <https://learn.microsoft.com/windows/apps/design/accessibility/accessibility-testing>
8 changes: 8 additions & 0 deletions spikes/v4-tauri-studio/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": { "config": "tailwind.config.ts", "css": "src/styles.css", "baseColor": "slate", "cssVariables": true },
"aliases": { "components": "@/components", "utils": "@/lib/utils", "ui": "@/components/ui" }
}
5 changes: 5 additions & 0 deletions spikes/v4-tauri-studio/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!doctype html>
<html lang="en">
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>OpenStream Studio feasibility spike</title></head>
<body><div id="root"></div><script type="module" src="/src/main.tsx"></script></body>
</html>
Loading
Loading