From 021d83ec27bb9322930199e8396b5fcd7e41213a Mon Sep 17 00:00:00 2001 From: csd113 Date: Tue, 6 Oct 2026 12:47:59 -0700 Subject: [PATCH 1/4] docs: refresh README with real PocketCHIP screenshots --- README.md | 176 +++++++++++++++++++---------- docs/images/README.md | 72 ++++++++++++ docs/images/app-center-480x272.png | Bin 0 -> 3436 bytes docs/images/files-480x272.png | Bin 0 -> 3103 bytes docs/images/launcher-480x272.png | Bin 0 -> 15961 bytes docs/images/notepad-480x272.png | Bin 0 -> 4340 bytes docs/images/settings-480x272.png | Bin 0 -> 4875 bytes docs/images/terminal-480x272.png | Bin 0 -> 2802 bytes docs/shell.md | 5 +- docs/validation.md | 9 +- 10 files changed, 200 insertions(+), 62 deletions(-) create mode 100644 docs/images/README.md create mode 100644 docs/images/app-center-480x272.png create mode 100644 docs/images/files-480x272.png create mode 100644 docs/images/launcher-480x272.png create mode 100644 docs/images/notepad-480x272.png create mode 100644 docs/images/settings-480x272.png create mode 100644 docs/images/terminal-480x272.png diff --git a/README.md b/README.md index 85443f8..16749c1 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,85 @@ # Vitrallis Shell -**Small screens. Big possibilities.** - -A compact Rust + SDL2 launcher for small Linux screens. Open a terminal, jot down a note, browse your files, and make a small screen feel useful again. Vitrallis runs inside your existing desktop session. +A compact Rust + SDL2 launcher for small Linux screens, built around PocketCHIP +and its 480×272 display. Open a terminal, take notes, browse files, and return to +your running apps with the Home key. Vitrallis runs inside the existing desktop +session, with controls for both keyboard and touch. [![Validate](https://github.com/csd113/Vitrallis-Shell/actions/workflows/validate.yml/badge.svg)](https://github.com/csd113/Vitrallis-Shell/actions/workflows/validate.yml) [![Releases](https://img.shields.io/github/v/release/csd113/Vitrallis-Shell?include_prereleases&label=release)](https://github.com/csd113/Vitrallis-Shell/releases) -![Vitrallis at 480×272: Terminal, Notepad, Files and App Center, with Terminal selected](docs/images/shell-480x272.png) +![Vitrallis launcher with Terminal selected, alongside Notepad, Files, System Settings and App Center](docs/images/launcher-480x272.png) + +*Launcher · Vitrallis 1.0.4 on a physical PocketCHIP, 480×272; live battery and Wi-Fi status.* + +For PocketCHIP owners and small-screen Linux tinkerers who want a readable, +keyboard-friendly starting point for everyday tools. **The project is still +pre-release:** check the platform and hardware-validation limits below before +installing. + +## What you can do + +- **Use three offline native apps:** Terminal runs a real PTY with ANSI colors and scrollback; Notepad opens, edits, finds and saves plain text; Files browses, copies, moves, renames and deletes with confirmations. +- **Keep your tools close:** return Home to background an app, then select its tile to resume it. Organize tiles in folders or add shortcuts for ordinary Linux programs and scripts. +- **Browse App Center:** search and filter GitHub catalogs, read package details and release notes, then install, update or remove apps. Source trust checks, checksums and transaction recovery protect managed installs and local edits. +- **Tune the session:** Settings includes display and sound, date and time, wireless and Tor, background-app policy, storage, device controls, software updates and About. Device controls depend on the supported platform and available utilities. +- **Update the complete build:** Software Updates checks for compatible releases and updates the shell and three native apps together with the shared Arti executable. A verified retained build can be restored from the same screen. + +## A look inside + +Each image is an unaltered **480×272** display capture from a physical PocketCHIP +running the current 1.0.4 build, with isolated sample files. This short screenshot +session is not a full release certification. [Capture details](docs/images/README.md) +record the build, method and limits. + +**Terminal** — run shell commands, with ANSI colors and bounded scrollback. + +![Terminal running a Bash PTY, listing sample folders and reading a to-do text file](docs/images/terminal-480x272.png) + +*Terminal · live Bash session, physical PocketCHIP.* + +**Notepad** — a small plain-text editor with save, find and unsaved-change protection. + +![Notepad editing a sample weekend checklist, with New, Open, Save, Save as, Find and Close controls](docs/images/notepad-480x272.png) + +*Notepad · sample notes, physical PocketCHIP.* + +**Files** — browse folders and open text in Notepad; file actions live in the menu. -*Current desktop build rendered at 480×272; hardware status is unavailable.* +![Files showing sample Notes and Projects folders plus notes.txt and todo.txt](docs/images/files-480x272.png) -- **Three native essentials:** Terminal with a real PTY, a text-editing Notepad, and Files for browsing and everyday file operations. All ship with the shell and work offline. -- **Room to explore:** App Center checks GitHub catalogs and installs selected manifest packages, with source trust, checksums, and local-edit protection. -- **Keys or touch:** visible selection and shared activation across native controls, with confirmations for destructive actions. -- **Optional system integration:** brightness, volume, status, Wi-Fi utility access, and a supervised session that restores the original Home binding on exit. -- **Whole-build updates:** System Settings updates the shell and all three native utilities together when a compatible newer release is available. -- **Compact Settings:** display and sound, clock format and time zone, wireless and Tor, background-app policy, storage, device controls, software updates and About. A verified retained build can be restored from Software Updates. +*Files · isolated sample Documents folder, physical PocketCHIP.* + +**Settings** — large category buttons with visible keyboard selection. + +![System Settings overview with Display and Sound, Date and Time, Wireless Network, Applications, Storage, Device, Software Updates and About](docs/images/settings-480x272.png) + +*Settings · physical PocketCHIP; category overview.* + +**App Center** — search, filter and manage packages from configured sources. + +![App Center before its first refresh, with search, filter, sources and package controls](docs/images/app-center-480x272.png) + +*App Center · before first refresh in an isolated profile, physical PocketCHIP; no third-party apps shown.* ## Installation -**[Vitrallis Shell 1.0.4 reliability hardening release](https://github.com/csd113/Vitrallis-Shell/releases/tag/v1.0.4)** -hardens App Center updates, process cleanup, file operations and input boundaries, -with permanent strict Rust 1.99 lint enforcement. Host and Linux simulator checks -pass; see the [changelog](docs/releases.md#104--reliability-hardening-release) -and [hardening report](docs/rust-lint-hardening.md). This release has no new physical -PocketCHIP certification. The earlier 1.0.3 installation, cold-start, keyboard, -touch and audible-audio results remain qualified to that candidate in the -[readiness report](docs/release-readiness-2026-10-02.md) and -[certification record](docs/release-certification-2026-10-02.md#final-public-candidate). -Install the complete bundle containing the shell, Terminal, Notepad, -Files and the shared Arti executable. -See [device installation and recovery](docs/devices/pocketchip.md) for supported -OS/runtime requirements, the single copy-and-paste setup command and hardware -validation limits. Setup prepares missing Debian packages and installs a verified -complete bundle with all five executables. Run it as your normal desktop user; -sudo is used for package and platform preparation. - -Run this as your normal user on a supported PocketCHIP (requires `curl` and -working HTTPS certificates): +The current published bundle is [Vitrallis Shell 1.0.4](https://github.com/csd113/Vitrallis-Shell/releases/tag/v1.0.4). +It contains the shell, Terminal, Notepad, Files and the shared Arti executable. +See the [changelog](docs/releases.md#104--reliability-hardening-release) for the +reliability hardening in this release. + +**PocketCHIP setup requires Debian 12 or 13 ARMv7 hard-float (`armhf`), the existing +Awesome 4/PocketHome desktop, a systemd user session and sudo access. Original +Jessie is unsupported.** Keep at least 128 MiB free on the system, home and +temporary filesystems; missing packages may need more. Save your work and exit +any running Vitrallis session before setup. + +Run the command below as your **normal desktop user**, in Terminal or SSH, +with `curl`, working HTTPS certificates and access to GitHub and Debian repositories. +It prepares missing Debian packages and platform integration with sudo, then +verifies and installs the complete bundle as your user. Read the +[device installation and recovery guide](docs/devices/pocketchip.md) for the +prerequisites, system changes and recovery procedure. ```sh @@ -45,12 +87,12 @@ working HTTPS certificates): ``` Setup replaces PocketHome's launch command with Vitrallis at login. PocketHome -does not run behind the Shell. Uninstall restores its original launch command; +does not run behind the shell. Uninstall restores its original launch command; SSH and serial login remain available for recovery. ## First launch and controls -Reboot after setup to start Vitrallis in place of PocketHome. Vitrallis starts +Reboot after setup to start Vitrallis in place of PocketHome. It starts automatically at subsequent desktop logins. For later launches within that session, use `~/.local/share/vitrallis/launch` or the Home key. @@ -60,43 +102,63 @@ session, use `~/.local/share/vitrallis/launch` or the Home key. | Open or resume it | Enter, click, or tap | | Change page | Page Up / Page Down or header arrows | | Return from an app | Home in a supervised session | -| Leave Vitrallis for Awesome | Select **Exit Vitrallis** in a supervised session | -| Update the native build | System Settings → Software Updates → Check for Updates | - -Returning Home backgrounds an app. Apps stay open by default; Settings → Applications can set an automatic background timeout or exempt individual apps. Save and close them before stopping or removing the session. Native utility menus and dialogs have visible keyboard focus; see [Terminal, Notepad and Files controls](docs/native-apps.md). App Center has [its own navigation and package guide](docs/app-center.md). - -Use **Manage [F10] → Add shortcut** (F2) to launch ordinary Linux programs or scripts without an -App Center package. See [desktop shortcuts](docs/desktop-shortcuts.md) for command -quoting, terminal mode, icons, editing, and removal rules. - -## Compatibility and beta limits - -Linux release packaging targets x86-64 and ARMv7, with glibc 2.36+ and SDL2 -2.26.5+. macOS is a development host; Linux artifacts cannot install there. -The shell uses SDL hardware acceleration when available, with automatic software -fallback and a GLES2 compatibility floor. See [renderer selection and diagnostics](docs/shell.md#sdl-renderer-selection) -for overrides and GPU validation limits. Hardware support requires a matching adapter and validation. A matching screen -size alone is not support. See the [device guide](docs/devices/pocketchip.md) for -recorded evidence and checks still requiring hardware. - -Apps run with your user's permissions: **Vitrallis is not an app sandbox**. Catalog availability and runtime dependencies belong to each publisher. Publisher-disabled packages appear as **Unavailable** with their compatibility note. Fonts do not provide full Unicode shaping; Bluetooth hardware qualification, a public Python SDK, and signed publisher packages are future work. See the [trust model](docs/security.md) and [design roadmap](docs/design.md). +| Manage shortcuts and folders | **Manage [F10]**; F2 adds a shortcut | +| Navigate native dialogs | Tab or Left/Right, then Enter; Escape cancels | +| Leave Vitrallis for Awesome | **Exit Vitrallis** in a supervised session | +| Update or restore the native build | System Settings → Software Updates | + +Returning Home backgrounds an app. Apps stay open by default; **Settings → +Applications** can set an automatic background timeout or exempt individual apps. +Save and close them before stopping or removing the session. Destructive dialogs +start on Cancel. Native text entry uses a physical keyboard; these apps do not +supply an on-screen typing keyboard. + +See [Terminal, Notepad and Files controls](docs/native-apps.md), +[App Center navigation](docs/app-center.md), and +[desktop shortcuts](docs/desktop-shortcuts.md) for app-specific keys, command +quoting, terminal mode, icons and editing rules. + +## Platforms and current limits + +- **Linux builds:** release bundles target x86-64 and ARMv7, with glibc 2.36+ and SDL2 2.26.5+. The one-line installer above is specifically for the supported PocketCHIP desktop, not arbitrary Linux machines. +- **Development hosts:** macOS can build and preview the shell and native apps; Linux artifacts cannot install there. Desktop/simulator captures establish software behavior, not device support. +- **Rendering:** SDL hardware acceleration is used when available, with automatic software fallback and a GLES2 compatibility floor. A matching screen size or GPU API alone does not establish hardware support. See [renderer selection](docs/shell.md#sdl-renderer-selection) and [hardware acceleration evidence](docs/hardware-acceleration.md). +- **Physical validation:** 1.0.4 has host and Linux simulator checks plus the limited physical screenshot session above, but no new full PocketCHIP certification. Earlier 1.0.3 installation, cold-start, keyboard, touch and audible-audio results apply to that candidate; see the [readiness report](docs/release-readiness-2026-10-02.md) and [certification record](docs/release-certification-2026-10-02.md#final-public-candidate). The [device guide](docs/devices/pocketchip.md) records OS/runtime boundaries and outstanding hardware checks. +- **App and editor limits:** catalog availability and runtime dependencies belong to each publisher; disabled packages show **Unavailable** with a compatibility note. Notepad handles UTF-8 text up to 1 MiB and has no undo/redo or syntax highlighting. Bitmap fonts do not provide full Unicode shaping. Bluetooth hardware qualification, a public Python SDK and signed publisher packages remain future work. + +**Vitrallis is not an app sandbox.** Apps run with your user's permissions; +checksums verify catalog content, not an independent publisher signature. Review +sources before installing. See the [trust model](docs/security.md), +[security reporting policy](SECURITY.md) and [design roadmap](docs/design.md). ## Documentation and development -Start with the [documentation index](docs/README.md), [device guide](docs/devices/pocketchip.md), or [app developer guide](docs/app-development.md). +Start with the [documentation index](docs/README.md), +[device guide](docs/devices/pocketchip.md), or +[app developer guide](docs/app-development.md). +Report bugs and suggestions through the [issue forms](https://github.com/csd113/Vitrallis-Shell/issues/new/choose); +use [SECURITY.md](SECURITY.md) for security concerns. -Host development requires the pinned Rust 1.99.0 toolchain, SDL2 development libraries, and pkg-config. CI also validates latest stable; release pins advance after the complete validation gates pass, including ARMv7 cross-builds. See [compiler policy](docs/dependencies.md). +Host development requires the pinned Rust 1.99.0 toolchain, SDL2 development +libraries and pkg-config. CI also validates latest stable. See +[contributor setup](CONTRIBUTING.md) and [compiler policy](docs/dependencies.md). +Build the workspace so the three native apps are available beside the shell: ```sh cargo build --workspace --locked -cargo run --locked +cargo run --locked -- --size 480x272 sh scripts/validate.sh cargo build --workspace --release --locked ``` -The workspace build includes all native utilities. The validation script runs formatting, strict Clippy, Rust/Python tests, release builds and SDL smoke checks; [validation guidance](docs/validation.md) covers visual checks and Linux simulators. [Contributor guidance](CONTRIBUTING.md) covers setup, focused changes, validation, and pull requests. Please use the [bug and feature forms](https://github.com/csd113/Vitrallis-Shell/issues/new/choose) for feedback and the [security reporting policy](SECURITY.md) for security concerns. +The validation script runs formatting, strict Clippy, Rust/Python tests, release +builds and SDL smoke checks. [Validation guidance](docs/validation.md) covers +visual checks and Linux simulators; system controls in a desktop preview may be +unavailable. -**License:** project-owned code and documentation use [MIT](LICENSE). Third-party terms and artwork provenance are listed in [third-party notices](THIRD_PARTY_NOTICES.md); MIT does not relicense those items. +**License:** project-owned code and documentation use [MIT](LICENSE). +[Third-party notices](THIRD_PARTY_NOTICES.md) record dependency terms and artwork +provenance; MIT does not relicense those items. ## Uninstall diff --git a/docs/images/README.md b/docs/images/README.md new file mode 100644 index 0000000..cd811c2 --- /dev/null +++ b/docs/images/README.md @@ -0,0 +1,72 @@ +# README screenshot provenance + +The six images linked from the [project README](../../README.md) were captured on +2026-10-06 from **Vitrallis 1.0.4 running on a physical PocketCHIP** over USB SSH. +They are real 480×272 display captures, not simulator images or mockups. + +## Build and environment + +- PocketCHIP, Debian 13 ARMv7 hard-float; X11/Awesome, native 480×272 display. +- Published `v1.0.4` ARMv7 complete bundle, downloaded from the + [release](https://github.com/csd113/Vitrallis-Shell/releases/tag/v1.0.4). + Bundle SHA-256: `eb2b9be59066112668f2802a91bfb5205ba3c7ded6f181dbda2b5d378b62212c`. + The sidecar checksum and all five embedded executable hashes were verified before + extraction and execution. +- Release source: `874526879bf0defb25d68fd493d066623c0f3fd8`. Current `main` at + capture time was `1fd29aecd26546866f002a176207d4b0ed7fca9e`; only README/release + documentation differs between those commits. Application code is identical. +- The release binaries ran from a private temporary directory. The existing + installed 1.0.3 generation, session, preferences and apps were preserved. +- Auto selected SDL `opengles2`; startup reported Mesa `Mali400`, OpenGL ES 2.0 + Mesa 25.0.7, 480×272 window/output/display and requested SDL VSync. + These diagnostics and still images do not measure frame pacing or endurance. + +## Actual states + +| Asset | State shown | +| --- | --- | +| [Launcher](launcher-480x272.png) | Terminal, Notepad, Files, App Center and System Settings. Battery/charging, Wi-Fi and clock readings come from the live device. | +| [Terminal](terminal-480x272.png) | Real Bash PTY: a colored greeting, `ls` and `cat todo.txt` against sample files. The command waits for input after displaying its output. Terminal's static `--screenshot` preview fixture was not used. | +| [Notepad](notepad-480x272.png) | Sample `notes.txt` containing a weekend checklist, opened without edits. | +| [Files](files-480x272.png) | Sample Documents directory with empty Notes/Projects folders, `notes.txt` and `todo.txt`. | +| [Settings](settings-480x272.png) | Category overview with live device status; no setting or power action was activated. | +| [App Center](app-center-480x272.png) | Before first refresh in an isolated profile. No catalog entries or third-party apps are shown; no package operation was performed. | + +A private temporary HOME and XDG configuration/data directories kept existing app +installs, catalogs, folders and personal data out of the captures. A temporary +PocketHome-format menu contained no third-party entries and retained the device's +existing Wi-Fi utility command for the System Settings tile. Native apps and App +Center were discovered normally, without `--demo`. Sample documents lived in the +same temporary workspace; their displayed paths contain no personal files. + +The running windows used `--size 480x272` and fullscreen (the shell selected +`--linux-handheld`). Each capture used Pillow's X11 `ImageGrab.grab()` against the +physical device's entire 480×272 display. The frames were saved as lossless PNG +with compression optimization and transferred unchanged. No image was cropped, +resized, composited, annotated, retouched or AI-generated. Temporary capture +processes were closed and the original session regained focus. + +## Repeating captures and evidence limits + +Use an isolated profile and reviewed matching binaries; preserve the installed +session and user data. Wait for the shell's `event=ready` and live status refresh +before capturing the launcher. Open Settings from its tile, then close it and open +App Center without refreshing its empty profile. For native utilities, open only +sample documents/directories and run harmless commands through Terminal's +`--command` option. Capture each active fullscreen window on the device, verifying +that the resulting image is 480×272. + +For desktop-only documentation work, the existing SDL `--screenshot NEW.bmp` +exports and [Linux simulator](../../tests/simulator/README.md) remain useful. +Label those environments explicitly; Terminal's static export is not evidence +of a live PTY. Lossless BMP-to-PNG conversion should preserve identical RGB pixels. + +This bounded hardware session establishes the displayed states on this device. +It is **not full 1.0.4 hardware certification** and does not retest public +installation, cold boot, physical keyboard/touch, audio, power-loss durability or +long-running behavior. Earlier release-qualified results remain in the +[device guide](../devices/pocketchip.md) and [validation records](../validation.md). + +The earlier [desktop launcher image](shell-480x272.png) is retained at its original +path for existing references. Historical physical-device screenshots under +`docs/devices/pocketchip/evidence/` are unchanged. diff --git a/docs/images/app-center-480x272.png b/docs/images/app-center-480x272.png new file mode 100644 index 0000000000000000000000000000000000000000..3f55a85454dc7e4b93b22d910d62070de950f355 GIT binary patch literal 3436 zcmcJSc{JPk7Qjld2^|JK9ldsnMb(jK$RzI9|N-Q~4l(eRvQcXII zUShyIIySvc1pFUghx_1o$UJ5<915#x68)CG4MYyM(ExpR3$*o2OpL<0bMh#12;wCs z7ay&YIB6yXvT8SaJ1b0Jn}$qmtWws|;^07@RT3pd>P1W*?yBMA9?I?bl@Gr)TZwl` zH_t5|R4wUoA+v8_oT9%H6kdRdB->BkO8)l8sT(u0!A=Y@iKCvtYt%AdLUzXdj%t{nzy@x(Vx@F97<-9f~+@k6IVVpX@Ed07ylq4+7Rj}g9n zbAhQCC0PKs$P*Q#r2+>|WI&C)VVx4g;$`tN-u8 zpXkb4tJ7wapCc4Hs~1(d(VMd&emx{r$jqiAB`%&vo&FNi&SL#UTwfRArd-IVBPfz! zZAgqEtJUgVYR<3|utrU6l3&qx`wGvnVZ$s}q^oEBoNz2VUUs?7>ybOg7 zLITcRh03K}IZs^%#*-(-Bk{N_T&r>)!rs-jSTH3Fz&5P2ramlJh5Ak$IKT`)7H+~v z7pPu$9Q+0*qdMegBU(GbAiBRhrCK2%ZDOC%Hn94bT;t(FbC&V3qjy?|7`D#WN9NP6 z&FS9M+x~EMfR$WvG^F-J*4=QD!X5th{8f*E0w4z+Xi9=V(zhGlwK@PW6}8Z*dfk_< zvPTh~iHB?9?`%V6W=8MLl_gsU;PZ@?i*s>pt=`+TV(SI3k@{WP*=2sDLFs;^D5nN6 zO0~b>{kV^JHTr1zaKDSD!YI9;-YyQVDu5V>x$9Fi@khUUv>1)mg7;A3Db^f1UNQk5 zT&Vy|8v!kc^YTQRwhBmHlLj|OGQ48m$}xOySvM)4zJ5;rVE&f+r(*L|JLWH86(^*n z(+x8z&JgESIPz&3D4{@^_t!4MFNBjeiUL$nlO{5;Sz`-q@Uhz9Nrm?6JKU z+WLl-NF`{i#5~wd1IcNEw(KOz4Tik>UQr}?TM2?mZUZo;>! ztqeg)a{+lz$=n*1PV4S1>0UbcGWR(2lAfv2ntw@thg(O_;??A9bk<^g0R13dEpoOG zP2hPi;GQQw>Zd@XCOqbfvNIFV^>uAR{(>Gbp-*8T@dfk>IDcvXx|pcerhoj+m^z6kW={pN@_czF^j9=NC(RmVjX6$Cel^dwgUG=0M`Ri@mg+--CSR6jSvptJe33fgbS ze33g)L2WeRlsWmV4rI_X2`fofgbkFITBe>PBc`}sJn6yt5-D}{a#} zWOYa!SP0yVf*BC;1kWI6(!!`6#`)*KNbX?h|EKiI>Xvbt zeeoOS_ztp9PGv^Gs)F+ghkw14jf9V7kWC_)D4RrEcK%@~{Xqn2RT5TTQrv(q6DOxx zarDi~p?KL&AzS&~_2W}vTpjV3?kz0rY28WcY` z9d4ToKamcT5RKW#7MDEYA~0r{_bc}YFi44Tt-{Wu-#<)2E*#~MNSz|6L2GoHnWKwc*SILJ8w=xx7!Ew8#Bk1^BhQy_2^Fvr5gM)p z$UaV4$c4Gfx_NrEeND57Pt#D z*vszmEfS;9<~VW9%qu^hu;su*1hv>O&G5IB17=aog45$FVe9vA5E}a1>59#e!WyQ6 zb!TXuL;G>v4yvuP;bJE58R1E{b{4QfkI?cK@MAbH23Kh$v%V=W82Dh>&iSbKR9mT) z{e;@?H;8x{3EtCs+qY~zBjZE1dSx7a6lHwK#J{HI6F|Q&>yKOQ&&Hqp{BHco&+o|n zlfH@_vTgrotN-_v{|Wr>!T#0v{*T@AUy}A$asNfqXnm_gs{YKVf-uK%Dia%Y7H^}D zKC7s3&wfwz#&VghiLb2v!~UVgo&{S6f^?q#sfC9!IG5iysLr(x(lJqs*_m};Taepx za)A=5P{fDuqpJqvvLD6U%#qrr3sLL&lDkrg`WtS+srkEd03ft!7)|0@)*ZqNb?ze@ zu8=w1&eUNm=!2OL1y+@9b%n9H!ePUl=L~2tr^~n90OC%bf5q!|Vi63>Wef6tZ++J5 zfzvxVcR~W^rl8=6yY|%1E>duLtxC(IQHdKY?a2+(y*)qbq_<_Q2Q{s71%A)P;j3$F zrMN4ix`zQ3y1JX$Dnhprzlx^JrQCYs{F}&%5Z4a>@@urqnVe4Cpy{GpxRO2GPOUH4 zq`oH63BKqad=>wVVE&Mr*C;u^V_eAxY(tFjD*H z<-K?y#U`nA09QNKqX(ciUh$gjRHQ%XR6KD=ST!>1!eHjacT>FF{1gK@l#L~k&rjlT zeEa=?$gG0Q*@0?pd{DR~Li4XCIviV)}5yjyZHQeE)6 zOZL%YCXZ&`R`msR*`v4oergdMSsstuzYEnE3zr?NuR5jCHRJ6$sK_o0^LUy@1}jOR zIy*c3BN_7AiCa3p4l3*^oDXqLkfxJ~i%N|r5+@gbqR;rU5qopeaix{YwJtT3#9D{T z15~p{RBP=4>u15d<#!v96lv84uFjn|0_Qp5j`Hipj`jGtFwae8*n5V(r<+r*nc;V1 z(Ivf+zjVkQ`bx9RyyR?Uuir8MBDB{%4)|LvP1XPSuKbqR!T|RoVO=mA`BG{(KuzGy zx?)8d3j3qCMd|PCb5O)1591qZLw8?fe&}%HG+S}sqF#$KEoXy`N4Cd5hBh$oq!(x5 ztlZ6rX)!?6sYPGAT(}{6b%7t#Tm=(NG__BGIRxd5OR#}n7I&bDiYu*yj5wl}1{tLb zBj5Y+a#}F^lFM9e&ETnye!Tm^?}FSRBmT^Lw%llf6o)t-S0`aTF7x}AZb5)J_w2Di zbrCL6)ZX}G`y3!f!gmQ9d(XmB+&xxJe;vAN&D73npMo({Q=pRmJDV0j8cq1SBR*NTY7%!4mwe^DUIi?$akzxh7)@0kLU1^@s6 literal 0 HcmV?d00001 diff --git a/docs/images/files-480x272.png b/docs/images/files-480x272.png new file mode 100644 index 0000000000000000000000000000000000000000..c5baf17b3531a6db882bbf9c1dc0ebd0ff379541 GIT binary patch literal 3103 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 literal 0 HcmV?d00001 diff --git a/docs/images/launcher-480x272.png b/docs/images/launcher-480x272.png new file mode 100644 index 0000000000000000000000000000000000000000..1123c625c15b36822460736786545a91dd47e8f4 GIT binary patch literal 15961 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 ha!hE+C9Au@PZ5q1svnB^Ke+@e*k^m*Q@{l literal 0 HcmV?d00001 diff --git a/docs/images/terminal-480x272.png b/docs/images/terminal-480x272.png new file mode 100644 index 0000000000000000000000000000000000000000..8794c28e0aa7cc8bbeb43e7fa53e467b649cd86c GIT binary patch literal 2802 zcmcguYfzI{8vZCYD3nV}3xYvPw~DBMiU_a8*iqNh@l*Vll%K#f6OHAm ziw23Ww7~ZJi2kKdv_R&XI-N;3w7k+!pw54O7o$f-|B&pKZsK2RXb;}Ce`|`q(Y@wr zC^Tlcv6J-j%-EPR#IKy8@o{5y$-=@y$4P?c>Pfo@R+#F41#Da=FGVy+>0om=r zsu2{y7U$^>i%b}o>58iK^u;Y~Msr2SQ2aDde)^^ zx4t?NVB_n&mBkL(a}z5b{IZc6>pOXt0uiyvAM3n*h%$7q327S@#-x{b-R?HNx-}vB zI9x_1j;@NtyUPkDa;dR(fODgsF4(M;B9(3{2kKmTsep@%`j@3@n+^gW8GnF!!zu`< zX`_0?I24bMN1LU{bOmEh#B*dU2G~h2Wj?~lW|6X17b|se5Xkw8z~7-Up95%ZT9s=^ zX3|A(P9yitsckNX*!h_@&}=zIP5%THZW~t0*~E9elS}%uXAj{)--d&n`Bqu)EHsCK zDphp^CwVu3(4M1rGV)rWX#w!y2sm{H=RtCy+=iPxBgt%<<@cmE7uww&x)6@0_BV}I z#f+Zj`xs?+=j_VHktR5dr|-e5qgTtH-%)RmHFS^lzWRL(FA_i4X=)!Qzg(N&8Wo); zYl+dF7EdIEHB~5ssTYI_17nBrXjlya-1{bK_ztxbOL@GVUCp=Z~`x*ccU2D3=w5}yjDo)g|&9ZT=|17<8A^;;5A%X{bHK1p&-Qr zb1E7Y6qDpJbr-(3i?Szgv%%X?*61@y%f5Tax~b-3Ik%~pG1RIEzbBsv+&XUkTsuE+ zFbcdw7ARuT*QniM^gYgjcVa(YCb!$uDQJIN!;1)k^>Hu(>ISd&H$9emnryY7mv_$R zmZcQd zob>?Gr{rG8{oHyq_+S#*W{!pbVR{e#P$5Wdj;T$*Efck~GHP+|?hq0$<=K7Ry3^Bk zou_di_UXMDye6?0)ore~@@eTFh~Q-WO3NT;{Tff)PV;%&T1?+?4mdEWFqY5jdWnLa zuJeTh@QEe@Yy3e=in@EDE7e+PYxRi7bKAIl`^3hs^$Sv;OT2xi;U|M`Sl{paG|-lj zJx@p6Bonib*S|5}u4Q+G_YtO2ky7SZnM)Y*7h87ciM1wgyhVbO3Bo5kpmGh*o_Ca- z+u_7zwL3lR@xm8`eGMR!RK-=;`ao&LaK|WC4Y6hJZ`czT!z$~q;5w2u61~*4T`WqK z8w77z8qX~86F!HaIG?Y9@_eRo=1A&lFpvcv1~y*;9&cJDvp|y}E>*7T;RoM4`-#x? z3&7?Qu<4-6UR86eR{f#R-$PZO|KSvTfU0F`+8++Y`-ndTe}I2IFaDKhO7B-T|JRbg zPOIPfzK6ew{VTqIq5IFlsz&(t3jRDN{}-F~pA@|3^ZSJUuM~Ja5%^LR)hB_;AQv=N zb&o8{`=lH!ZLNH;zeW1iw6>Q@wuH#I*G3q&*W-H>LJqs9|6IJ%A_cX!LF4!6_4L0O z(`B2N2q!3M62erT;~*z)rh3(|!ciZ}BzT$TFf(NY*6Nn+4ftrmQWV#o$~hD#w=6Oz zD;}=2O^zDn?TYq?w1q!U=zP_#5XWFS5fCG^K`LskSMu~+E3T6inuyOqlNQnVHU~U2 zPSBCzgIL<|ZGTzC^S+CVFug0*k}pNIu@&jy2K>tWQWVBVmtXwQ(A8V|m6s3=(l+iq{KmXYwOvdlK{8^M0_PFv~@W>%D)(j2` zz4A2q(B2#y5=+S0K0uEmefOPjmW#49itj|z?*H*Ht&mfQfCNm>D;sz;D?GDu!PUpn zs|F|MU;IkMiKC}68H&g!)sPC4!?mybhIZ6g1{C!VI{M;d4t^B|cl=eH!>3?HPdnod zljxY2sD^gZl9!CoT|bn;xX&qcv_d#}8_35BopkqPf8Q8Lo|4|+d53_CVVxCmEPAMH zF%Z($0bi)|=F106RmJG6Slh69Cxx`;s+$qv44Fz^^BHWnCeBFHt+*2uq=R2*xMc$@ z6N@BON1Mv!|4{Vt={h=@qA`&I8M8y*?%9_TKAaEB(Hf2GI| z8;{_kSH88*h=Z$Px~CTF>ABe23KPkIPb6yn73^CGy Date: Tue, 6 Oct 2026 16:38:23 -0700 Subject: [PATCH 2/4] test: prevent App Center fetch gates from hanging --- src/app_center/tests.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/app_center/tests.rs b/src/app_center/tests.rs index 531a83e..c88bf5f 100644 --- a/src/app_center/tests.rs +++ b/src/app_center/tests.rs @@ -2074,7 +2074,9 @@ impl GateFetch { }; drop(first_request); self.entered.send(()).map_err(|error| error.to_string())?; - receiver.recv().map_err(|error| error.to_string()) + receiver + .recv_timeout(std::time::Duration::from_secs(30)) + .map_err(|error| format!("download gate was not released: {error}")) } } impl network::Fetch for GateFetch { @@ -2221,7 +2223,9 @@ fn check_commit_fails_cleanly_if_lock_taken_mid_fetch() -> Result<(), String> { let (release, locked) = mpsc::channel(); let state = loc.state.clone(); let holder = std::thread::spawn(move || -> Result { - started.recv().map_err(|e| e.to_string())?; + started + .recv_timeout(std::time::Duration::from_secs(30)) + .map_err(|error| format!("catalog fetch did not start: {error}"))?; let lock = storage::Lock::take(&state)?; release.send(()).map_err(|e| e.to_string())?; Ok(lock) @@ -2232,6 +2236,7 @@ fn check_commit_fails_cleanly_if_lock_taken_mid_fetch() -> Result<(), String> { release: std::sync::Mutex::new(Some(locked)), }; let commit_messages = run_service(&loc, &next_fetch, vec![Command::Check]); + drop(next_fetch); match commit_messages.as_slice() { [Ok(_), Err(error)] => assert!( error.starts_with("Another Vitrallis storage operation is active"), @@ -2292,7 +2297,9 @@ fn concurrent_install_between_download_and_commit_is_detected() -> Result<(), St let install_package = p.clone(); let install_files = files.clone(); std::thread::spawn(move || -> Result<(), String> { - started.recv().map_err(|e| e.to_string())?; + started + .recv_timeout(std::time::Duration::from_secs(30)) + .map_err(|error| format!("download did not start: {error}"))?; let prepared = install::prepare(&install_locations, install_package.clone(), install_files)?; install::install(&install_locations, &prepared)?; @@ -2305,9 +2312,15 @@ fn concurrent_install_between_download_and_commit_is_detected() -> Result<(), St release: std::sync::Mutex::new(Some(unblock)), }; let next_messages = run_service(&loc, &fetch, vec![Command::Install(vec![p.key()])]); + // An early service error can skip fetching. Disconnect the start signal + // before joining so that error cannot leave the installer waiting forever. + drop(fetch); installer .join() - .map_err(|payload| format!("installer panicked: {payload:?}"))??; + .map_err(|payload| format!("installer panicked: {payload:?}"))? + .map_err(|error| { + format!("concurrent installer: {error}; service updates: {next_messages:?}") + })?; match next_messages.as_slice() { [Ok(_), Err(error)] => assert!(error.contains("No available update"), "{error}"), other => return Err(format!("unexpected updates: {other:?}")), From 617bcc4c3bf032cd4512a738f3d312cfaef766e8 Mon Sep 17 00:00:00 2001 From: csd113 Date: Tue, 6 Oct 2026 16:46:53 -0700 Subject: [PATCH 3/4] ci: isolate process inspection from runner processes --- .github/workflows/validate.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index c4e7510..9b5f708 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -19,4 +19,14 @@ jobs: - name: Install selected Rust run: rustup toolchain install "${{ matrix.rust }}" --profile minimal --component clippy --component rustfmt - name: Validate owned code - run: sh scripts/validate.sh + run: | + # App Center fails closed when a same-user process cannot be inspected. + # Exclude unrelated runner processes while keeping all fixture processes + # visible, and run the unchanged gates as the unprivileged runner user. + sudo unshare --pid --fork --mount-proc --kill-child \ + sudo --user="$(id -un)" -- \ + env HOME="$HOME" PATH="$PATH" \ + RUSTUP_HOME="${RUSTUP_HOME:-$HOME/.rustup}" \ + CARGO_HOME="${CARGO_HOME:-$HOME/.cargo}" \ + RUSTUP_TOOLCHAIN="$RUSTUP_TOOLCHAIN" \ + sh scripts/validate.sh From f4c89ac2e4dd5fa158302960ff4dcc7b0e9b02fe Mon Sep 17 00:00:00 2001 From: csd113 Date: Tue, 6 Oct 2026 16:49:01 -0700 Subject: [PATCH 4/4] ci: create validation fixtures with private permissions --- .github/workflows/validate.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 9b5f708..4cd30a3 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -22,11 +22,11 @@ jobs: run: | # App Center fails closed when a same-user process cannot be inspected. # Exclude unrelated runner processes while keeping all fixture processes - # visible, and run the unchanged gates as the unprivileged runner user. + # visible. Use the unprivileged runner user and private fixture modes. sudo unshare --pid --fork --mount-proc --kill-child \ sudo --user="$(id -un)" -- \ env HOME="$HOME" PATH="$PATH" \ RUSTUP_HOME="${RUSTUP_HOME:-$HOME/.rustup}" \ CARGO_HOME="${CARGO_HOME:-$HOME/.cargo}" \ RUSTUP_TOOLCHAIN="$RUSTUP_TOOLCHAIN" \ - sh scripts/validate.sh + sh -c 'umask 077; exec sh scripts/validate.sh'