* Host Decky plugin panels in Frame Control VR * Record Framey VR verification limits * Lock Frame before worn checks and clean private captures * Browse and search the full Decky store in VR * Show Decky plugin job progress in VR --------- Co-authored-by: SAPHID <saphid@users.noreply.github.com>
6.9 KiB
Framey loader on Steam Frame
Framey is an experimental bridge for Decky store plugins inside Frame Control's
SteamVR dashboard panel. It is opt in. The panel and Decky backend are ordinary
user services under ~/.local/share/frame-control; neither needs root or a
read-write SteamOS root filesystem. Decky's standard Quick Access menu still
does not appear in the separate SteamVR CEF target.
Use in the headset
Install Frame Control in VR and Decky Loader on Frame from Frame Control on your computer. Open the Frame Control tab in SteamVR, then choose Decky plugins → Open official store. Search the catalogue, confirm an install, and choose Open panel from the installed plugin list. The panel contains the plugin's own controls and settings. Update and Remove affect only plugin folders that Frame Control's store manager installed. Removing a plugin keeps its Decky settings. Removing the Frame Control headset panel or Decky backend keeps plugin folders until each is removed separately.
Frame Control's existing official-store manager downloads a published ZIP from
Decky's content-addressed CDN, verifies its store SHA-256 hash and manifest,
rejects unsafe ZIP members and root plugins, and swaps one managed plugin
folder with rollback on failure. It rejects non-ARM64 ELF and Windows binaries.
The Decky user service restarts after a file change. Framey's panel host accepts
only API v2 bundles with dist/index.js; other installed plugins remain
manageable but have no Framey panel. The store manager does not currently
screen a plugin's runtime network downloads.
What the compatibility host supplies
The plugin bundle runs in an opaque-origin iframe in the Frame Control SteamVR
panel. It receives React 18.3.1, a small DFL panel-control shim, and the
Decky API v2 call/callable interface. Backend methods are sent by the
authenticated parent to Steam's existing SharedJSContext CDP client, then
through Decky's existing websocket connection. Framey never opens a second
Decky websocket, because Decky allows only one frontend connection. The iframe
does not receive Frame Control's UI key. React and ReactDOM are bundled for
offline use under their MIT licenses in ui/vendor.
The host currently supplies sections, rows, buttons, toggles, dropdowns, scroll containers, a modal, locale information, and backend calls for the plugin that owns the panel. Steam library/store page patches, global Steam UI components, Decky route hooks, file pickers, and privileged root methods have no VR target. A plugin that depends on them can render only part of its UI or show an incompatibility message. A Decky plugin's Python backend still runs as the headset user when Decky loads it, so review third-party code before opting in. The panel iframe can make HTTPS requests subject to ordinary browser CORS.
ProtonDB Badges' current API v2 bundle rendered its settings in a local Chromium browser with a stubbed backend reply on 25 October 2026. Its controls accepted pointer input. This does not verify SteamVR visibility, controller input, live Decky settings persistence, or badges on Steam game pages. The last item has no supported injection target in the Frame Control panel. The browser and fake Frame evidence records exactly what was exercised.
The in-headset Open official store view loads the full official catalogue. Search matches names, authors, and descriptions. Filters show non-root plugins or installed plugins, and Previous/Next buttons browse 20 results per page. The root label is a compatibility warning; other APIs are checked only when a plugin is opened. The store still needs the managed Decky user service running before its Install or Update buttons are enabled.
Install, Update, and Remove show the current job stage in the headset panel. The store verifies each downloaded archive and the headset applies it with a rollback if Decky cannot start. The panel remembers the current job ID for this browser session, so Check result can resume polling after a navigation or temporary connection error. Refresh the installed list before starting another operation after an error.
For plugin authors
Publish a normal Decky store API v2 release
with plugin.json, a single dist/index.js ESM frontend entry point, and a
Python backend if needed. Give the frontend a content React element returned
by definePlugin, and build settings as panel controls from @decky/ui.
Backend calls through @decky/api reach only that plugin's own backend.
For best VR compatibility, keep the panel self-contained and use PanelSection,
PanelSectionRow, ButtonItem, ToggleField, and DropdownItem. Handle a
missing Steam UI route, store overlay, or SteamClient object gracefully.
Avoid root, remote binary downloads, Steam library patches, and assumptions
about Steam Deck hardware. Declare settings defaults so a fresh user service
can render without pre-existing files. Test your ZIP with the store validator
and the fake Frame flow before asking users to install it.
The Frame Control extension API is an alternative for a
Frame-specific panel. It needs only a local manifest, HTML, and the read-only
status bridge; it does not need Decky.
Removal
Use Remove on each installed Framey plugin, then Remove managed Decky
and Remove under Frame Control in VR. These operations remove only
Frame Control-owned folders and services. Settings, the Chromium profile, and
other users' plugins are preserved. To remove one managed plugin manually,
first inspect its .frame-control-store.json receipt in
~/.local/share/frame-control/decky/plugins/<folder>, then delete only that
folder and run systemctl --user try-restart frame-control-decky.service.
The Decky and headset service manual removal commands are in
Decky on Frame and
Frame Control in the headset.
Fast worn-headset check
With an already installed Framey plugin and someone wearing the headset, run
python3 scripts/framey-worn-check.py from a paired computer. The script
atomically acquires /tmp/frame-test.lock before reading battery or SteamVR
activity. If another job owns the lock, it makes no sensor reads. It releases
its own lock without starting the interaction when the headset is in standby.
Within 35 seconds, use a controller
to open a managed plugin panel and touch one setting. The script records a
fresh trusted pointer event, the panel-ready event, SteamVR overlay visibility,
and a stereo screenshot with varied pixels. It deletes its own short-lived
challenge and releases only its own lock. It records the screenshot hash and
color count, then removes the private temporary image from both devices.
Pixel variety alone cannot prove the UI text was legible; a person must still
confirm the view in the headset. The script does not install or remove a plugin,
alter Steam settings, or wake the headset.