mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-27 08:01:40 +01:00
Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c60985c9f | ||
|
|
4da7220660 | ||
|
|
728a4dbe9b | ||
|
|
91cf774b21 | ||
|
|
560f3de563 | ||
|
|
022d407ff9 | ||
|
|
014f554012 | ||
|
|
c847b7a3ca | ||
|
|
ee13edabb8 | ||
|
|
fc030c6191 | ||
|
|
8990ddfb91 | ||
|
|
af8fc63f3b | ||
|
|
9c39300185 | ||
|
|
592988c212 | ||
|
|
89f5dc164e | ||
|
|
13c689926d | ||
|
|
1b143094d1 |
No files matched your search
@@ -20,6 +20,12 @@ const REQUIRED = [
|
||||
"ui/vendor/deckyfileserver-1.2.0/GO-LICENSE.txt",
|
||||
"ui/vendor/deckyfileserver-1.2.0/README.md",
|
||||
"scripts/build-framey-fileserver.py",
|
||||
"ui/vendor/localsend-0.4.5/aarch64.gz",
|
||||
"ui/vendor/localsend-0.4.5/source.tar.gz",
|
||||
"ui/vendor/localsend-0.4.5/LICENSE.txt",
|
||||
"ui/vendor/localsend-0.4.5/GO-LICENSE.txt",
|
||||
"ui/vendor/localsend-0.4.5/README.md",
|
||||
"scripts/build-framey-localsend.py",
|
||||
"ui/vendor/psutil-7.2.2/aarch64.whl",
|
||||
"ui/vendor/psutil-7.2.2/x86_64.whl",
|
||||
"ui/vendor/psutil-7.2.2/LICENSE.txt",
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "frame-control",
|
||||
"version": "0.4.50",
|
||||
"version": "0.4.51",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "frame-control",
|
||||
"version": "0.4.50",
|
||||
"version": "0.4.51",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"electron": "^44.4.5",
|
||||
|
||||
+8
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "frame-control",
|
||||
"productName": "Frame Control",
|
||||
"version": "0.4.50",
|
||||
"version": "0.4.51",
|
||||
"description": "Desktop app for managing a Valve Steam Frame over SSH",
|
||||
"private": true,
|
||||
"main": "main.js",
|
||||
@@ -65,6 +65,11 @@
|
||||
"to": "ui/vendor/deckyfileserver-1.2.0",
|
||||
"filter": ["*.gz", "*.txt", "README.md"]
|
||||
},
|
||||
{
|
||||
"from": "../ui/vendor/localsend-0.4.5",
|
||||
"to": "ui/vendor/localsend-0.4.5",
|
||||
"filter": ["*.gz", "*.txt", "README.md"]
|
||||
},
|
||||
{
|
||||
"from": "../ui/vendor/psutil-7.2.2",
|
||||
"to": "ui/vendor/psutil-7.2.2",
|
||||
@@ -86,7 +91,8 @@
|
||||
"obs-headset-source.py",
|
||||
"frame_power_sample.py",
|
||||
"frame_power_report.py",
|
||||
"build-framey-fileserver.py"
|
||||
"build-framey-fileserver.py",
|
||||
"build-framey-localsend.py"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"helperSha256": "e20363237e606ee65eae857792176d21a0bb8ba863489952728eaf2b13277dae",
|
||||
"lockAcquired": true,
|
||||
"action": "private_network_marker_only",
|
||||
"before": {
|
||||
"activity": 3,
|
||||
"proximity": [
|
||||
4.6875
|
||||
],
|
||||
"battery": [
|
||||
72
|
||||
],
|
||||
"services": {
|
||||
"frame-control-decky.service": "inactive",
|
||||
"frame-control-headset.service": "inactive"
|
||||
}
|
||||
},
|
||||
"transfer": {
|
||||
"result": "private_arm64_transfer_passed",
|
||||
"protocolVersion": "2.0",
|
||||
"markerBytes": 38,
|
||||
"markerSha256": "3e724efac20a4a600a93f7bdddd469b8287a6aaa2b7b433d838d8ef23e1a9c2c",
|
||||
"network": "private-veth-no-default-route",
|
||||
"helperSha256": "e20363237e606ee65eae857792176d21a0bb8ba863489952728eaf2b13277dae"
|
||||
},
|
||||
"after": {
|
||||
"activity": 3,
|
||||
"proximity": [
|
||||
4.6875
|
||||
],
|
||||
"battery": [
|
||||
72
|
||||
],
|
||||
"services": {
|
||||
"frame-control-decky.service": "inactive",
|
||||
"frame-control-headset.service": "inactive"
|
||||
}
|
||||
},
|
||||
"result": "private_arm64_transfer_passed",
|
||||
"ownedDirectoryAbsent": true,
|
||||
"ownLockReleased": true
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
# Decky Localsend managed ARM64 candidate, 27 October 2026
|
||||
|
||||
Official Decky Localsend **0.4.5**, store ID **125**, archive SHA-256
|
||||
`e271ccb55eb23f918b99a69f79aec905df4c9b2ef7b23bbf4ec1c2d1a6f0c6b7`,
|
||||
contains an x86_64 helper. Frame Control's proposed managed adaptation accepts
|
||||
only that exact archive. The original x86 helper and Python backend hashes are
|
||||
checked before a pinned static ARM64 helper replaces the binary. The headset
|
||||
transaction checks the ARM64 SHA-256 and ELF machine again. It leaves the
|
||||
official Python, frontend, manifest and BSD license unchanged, retains
|
||||
managed update/remove receipts, and rejects changed archive digests.
|
||||
|
||||
[Source correspondence and local upstream transfer](framey-localsend-source-match-2026-10-27.md)
|
||||
records the exact upstream plugin tag and Go commit. The native asset and
|
||||
build recipe are [bundled](../../ui/vendor/localsend-0.4.5/README.md).
|
||||
Go dependencies are checksum-pinned but not vendored. The Frame installer
|
||||
does not download modules or run a compiler.
|
||||
|
||||
## Evidence so far
|
||||
|
||||
- The host transaction tests installed and removed the exact archive,
|
||||
preserved its Python/LICENSE bytes, checked the managed receipt and
|
||||
rejected a changed archive. The ARM64 branch independently wrote and
|
||||
hashed the AArch64 ELF on x86 without executing it.
|
||||
- The fake Frame managed install/remove flow passed with the exact official
|
||||
archive and checked the installed helper digest. Its project containers,
|
||||
volumes and network were absent after the run.
|
||||
- The full local Python suite passed **1,669 tests with 16 skipped** after
|
||||
rerunning with loopback socket access. The first attempt ran inside a
|
||||
workspace sandbox that denied local test sockets across unrelated server
|
||||
tests; it did not identify a LocalSend regression.
|
||||
- A fresh local Chromium opaque iframe initialized the exact official frontend
|
||||
with backend and network effects denied. Its visible text included Backend is
|
||||
stopped, Device Name, Available Devices, File Upload, Receive History and
|
||||
Configuration. The initializer reported ready and called only
|
||||
`get_receive_history`, `get_backend_status` and `get_backend_config`.
|
||||
This proves a visible initial panel with safe fallbacks, not working send,
|
||||
receive or settings controls.
|
||||
- The original x86 backend and helper completed one private no-route marker
|
||||
transfer outside Decky. This does not prove the managed ARM64 binary or
|
||||
actual Decky service.
|
||||
- The published Decky 3.3.0 AMD64 and ARM64 runtime gate discovered the
|
||||
adapted plugin and returned its stopped status and safe configuration through
|
||||
public RPCs. Its helper remained stopped during that gate.
|
||||
- A locked real Frame probe ran the rebuilt ARM64 helper with SHA-256
|
||||
`e20363237e606ee65eae857792176d21a0bb8ba863489952728eaf2b13277dae`
|
||||
in a private network namespace with no default route. Its LocalSend 2.0
|
||||
endpoint accepted and saved one 38-byte marker, and the saved bytes matched
|
||||
the sent marker. The headset was in standby (SteamVR activity 3, proximity
|
||||
raw 4.6875) with 72% battery before and after. Both normal Frame Control
|
||||
services stayed inactive. The owned helper/process/files and shared lock
|
||||
were removed. [Machine-readable result](framey-localsend-arm64-transfer-2026-10-27.json).
|
||||
|
||||
A useful frontend workflow still needs verification. The ARM64 marker test
|
||||
exercised the helper in isolation, not the actual Decky plugin service, local
|
||||
discovery, user-facing transfer, or installation on the headset. No worn
|
||||
visibility or physical-controller claim follows from this candidate.
|
||||
|
||||
The proposed catalog status is **partial** for exact managed archive
|
||||
installation and settings/status. Steam routes, local discovery, user-facing
|
||||
transfer, clipboard/screenshot sharing and worn VR remain unverified. The
|
||||
historical raw-archive audit fields still describe the earlier unadapted x86
|
||||
ZIP; `managedNative` records the current digest-scoped path separately.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Decky Localsend 0.4.5 source match and ARM64 build, 27 October 2026
|
||||
|
||||
The exact official store archive (ID 125, SHA-256
|
||||
`e271ccb55eb23f918b99a69f79aec905df4c9b2ef7b23bbf4ec1c2d1a6f0c6b7`)
|
||||
remains **blocked** on Frame: it carries an x86_64 `bin/localsend-core`.
|
||||
No managed plugin install, transfer, or status promotion was performed.
|
||||
|
||||
The archive's `main.py` has SHA-256
|
||||
`db03f9201fe50b75dea99a7a05b54ec3f352d1bdfe174115b0e569154198d8ad`,
|
||||
identical byte for byte to `main.py` at the upstream Decky plugin tag `v0.45`
|
||||
(`7c166bdc33e2a667527ffd511eb5b90d3ce0bd67`). That tag pins its Go
|
||||
submodule to `ad28b8e9440e17e44b4735a6c4aa0512009cf333`. All 42 dependency
|
||||
versions embedded in the official helper match that source's `go.mod`, which
|
||||
declares Go 1.25.7.
|
||||
|
||||
Building that pinned Go commit with `CGO_ENABLED=0`, `GOOS=linux`,
|
||||
`GOARCH=amd64`, and the flags `-trimpath -buildvcs=false -ldflags='-s -w'`
|
||||
produced **byte-identical** official helper bytes: 15,241,400 bytes,
|
||||
SHA-256 `d448d03861d578685e6c70ea3ace25eb34fca99cb9d4f98bf51ba23aa5cae843`.
|
||||
This resolves the source correspondence gap left by the earlier build from
|
||||
the later `66edc509` commit. The same pinned source and flags with
|
||||
`GOARCH=arm64` produced a static AArch64 ELF of 11,731,128 bytes, SHA-256
|
||||
`e20363237e606ee65eae857792176d21a0bb8ba863489952728eaf2b13277dae`.
|
||||
Two ARM64 builds matched byte for byte. The Go core is MIT licensed; the
|
||||
separate Decky Python plugin is BSD-3-Clause.
|
||||
|
||||
A unique-owner atomic `/tmp/frame-test.lock` preflight on the real Frame read
|
||||
SteamVR activity **1**, proximity raw **88.0625**, and battery **35%**.
|
||||
Because the headset was active or wearer status was unknown, the probe did
|
||||
not upload or execute the ARM64 helper, start a LocalSend service, or send
|
||||
input. The task-owned directory was absent and only the owned lock was
|
||||
released. This is **not** a worn visual or controller result.
|
||||
|
||||
The unmodified plugin backend's `_main` starts a fixed-path Unix notification
|
||||
socket but does not start the Go network service. Its explicit `start_backend`
|
||||
method starts the helper with a default all-interface setting, HTTPS and
|
||||
auto-save disabled. A local follow-up ran that exact Python backend with the
|
||||
official x86_64 helper in a disposable user/network namespace. A veth pair
|
||||
provided only documentation-range private addresses and there was no default
|
||||
route. A private Decky module stub supplied task-owned settings, runtime,
|
||||
plugin and log directories; its notification socket was moved into that
|
||||
directory. The backend's `start_backend`, `proxy_get` info request and
|
||||
`proxy_post` prepare/upload methods passed. One 42-byte public marker
|
||||
arrived in the private upload directory with independent SHA-256
|
||||
`346373ca56ec52c3f47b8d41de2a6a8479d8ad10d2891ff3b0dc7ea9c3892a39`.
|
||||
The backend stopped, its process and private directory were absent, and the
|
||||
global notification socket was untouched. This verifies the upstream x86
|
||||
backend/proxy/transfer contract locally, **not** Decky 3.3.0 discovery, ARM64
|
||||
transfer, an installed Frame plugin or real-network transfer.
|
||||
|
||||
Before a managed ARM64 adaptation, verify actual Decky backend discovery/RPC,
|
||||
the frontend and a private transfer with the exact-source ARM64 helper;
|
||||
package the source, license and exact helper digest. The official x86_64
|
||||
archive remains blocked until that work passes. Worn VR and physical
|
||||
controller use remain unverified.
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"tag": "v0.4.51",
|
||||
"releaseId": 425049939,
|
||||
"tagNameAtAudit": "v0.4.51",
|
||||
"draftAtAudit": true,
|
||||
"sha256sumsSha256": "9d28eae8610356cba56310c892b6dd16dfc55c0507c9089600ab86e9697a04f8",
|
||||
"assets": [
|
||||
{
|
||||
"name": "Frame-Control-mac-arm64.dmg",
|
||||
"size": 177621496,
|
||||
"sha256": "8c84ef490402c202ad694f9d363d792c5dbc9b046e40d8d834bcb6d7752ab6e8",
|
||||
"udifTrailer": "ok"
|
||||
},
|
||||
{
|
||||
"name": "Frame-Control-mac-arm64.zip",
|
||||
"size": 177449210,
|
||||
"sha256": "3541b67985e3931b9469af9249df3f71133589d726ffbd7b0c2b9c1c3dd3f67c",
|
||||
"zipCrc": "ok",
|
||||
"members": 1673,
|
||||
"bundleVersion": "0.4.51"
|
||||
},
|
||||
{
|
||||
"name": "Frame-Control-mac-x64.dmg",
|
||||
"size": 184691853,
|
||||
"sha256": "04fecaf012e6ab4ca139823d2220bf61b788568cdffbfdad22b5d0b2c2b63763",
|
||||
"udifTrailer": "ok"
|
||||
},
|
||||
{
|
||||
"name": "Frame-Control-mac-x64.zip",
|
||||
"size": 184535648,
|
||||
"sha256": "7e9836d62f882fbaac3d9b35831410c5e6138d9b68fb93815cbd2bb9200bd46b",
|
||||
"zipCrc": "ok",
|
||||
"members": 1669,
|
||||
"bundleVersion": "0.4.51"
|
||||
},
|
||||
{
|
||||
"name": "Frame-Control-Setup-x64.exe",
|
||||
"size": 151882885,
|
||||
"sha256": "a45996f621901c2df8fc41d9eca2ae3fcd6b9ff40de66ed8acfca9415a3a81b0",
|
||||
"peMachine": 332
|
||||
},
|
||||
{
|
||||
"name": "Frame-Control-win-x64.zip",
|
||||
"size": 198837858,
|
||||
"sha256": "41791b6ca2c40164eb44cdc29e1281886556c257b88bcd5f86941daddd15d66a",
|
||||
"zipCrc": "ok",
|
||||
"members": 1517
|
||||
},
|
||||
{
|
||||
"name": "Frame-Control-linux-x86_64.AppImage",
|
||||
"size": 179993078,
|
||||
"sha256": "5ebbb931276cd3126cb3ecab88c6cde05f490cbd0e0205b418c32d00f765ea42",
|
||||
"elfMachine": 62
|
||||
},
|
||||
{
|
||||
"name": "Frame-Control-linux-arm64.AppImage",
|
||||
"size": 171579329,
|
||||
"sha256": "4205422b02b9207bf2cf59ce3eb847b601306cef326df9fb005a7df540d82bb5",
|
||||
"elfMachine": 183
|
||||
},
|
||||
{
|
||||
"name": "Frame-Control-linux-amd64.deb",
|
||||
"size": 146747460,
|
||||
"sha256": "7c3a4eae7fb2098c759cbd9a8194c326b70f2fa078030062c047e4c8418b4bef",
|
||||
"debVersion": "0.4.51",
|
||||
"architecture": "amd64"
|
||||
},
|
||||
{
|
||||
"name": "Frame-Control-linux-arm64.deb",
|
||||
"size": 133829576,
|
||||
"sha256": "793433d21e457d5422ea982678f50d5f3ac0c1c46d40c5dcebf42d16651d4995",
|
||||
"debVersion": "0.4.51",
|
||||
"architecture": "arm64"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
# v0.4.51 candidate gate, 27 October 2026
|
||||
|
||||
The source candidate `91cf774` follows green main merge commit
|
||||
`560f3de5631ce2dca641c68a5cba7b2dd1d2660f`. The main
|
||||
[checks](https://github.com/saphid/frame-control/actions/runs/40450100545)
|
||||
and [native Decky runtime](https://github.com/saphid/frame-control/actions/runs/40450100462)
|
||||
passed on that exact merge commit. This candidate bumps the app and lockfile
|
||||
to 0.4.51 and requires standby-range proximity before the release smoke can
|
||||
change test titles.
|
||||
|
||||
Since v0.4.50, [#362](https://github.com/saphid/frame-control/pull/362)
|
||||
recorded the pinned official LocalSend source match, and
|
||||
[#363](https://github.com/saphid/frame-control/pull/363) added an exact-digest
|
||||
managed ARM64 helper, desktop/headset packaging, and a scoped **partial**
|
||||
compatibility entry. Its PR head passed e2e, native Decky AMD64/ARM64, and all
|
||||
four installer previews. The catalog is **6 works / 52 partial / 47 broken /
|
||||
11 N/A**. A private no-route ARM64 helper marker transfer passed on the real
|
||||
Frame, but the managed Decky service on Frame, user-facing transfer, local
|
||||
discovery, Steam routes, worn VR and physical controllers remain unverified.
|
||||
|
||||
## Locked real Frame smoke
|
||||
|
||||
At 04:33:21 UTC, `scripts/frame-smoke.sh` ran from source candidate `91cf774`
|
||||
with a unique-owner atomic `/tmp/frame-test.lock`. Its read-only preflight
|
||||
found Frame OS 0.5.6 build `20261022.6153909`, SteamVR activity **3
|
||||
(standby)**, proximity raw **4.6875**, and battery **88%**. It passed **12
|
||||
steps**, skipped one optional x86_64 launch, and failed **zero**. ARM64 and
|
||||
Windows/Proton test titles installed, launched and were removed. The x86_64
|
||||
title installed and was removed; Steam Linux Runtime 4 was absent, so no
|
||||
x86_64 launch is claimed. The runner's owned cleanup passed and it released
|
||||
only its lock. The private result is
|
||||
`/tmp/frame-control-v0451-smoke-results/2026-10-27T043321Z-20261022.6153909.json`
|
||||
(SHA-256 `c2b9611e072fd08109c31f4aa05a4ba6b93de82f532a7d87a4a1c6dcd52024a3`).
|
||||
|
||||
A separate read-only audit acquired a fresh unique lock after the smoke. It
|
||||
found activity **3**, proximity raw **4.6875**, battery **91%** while charging,
|
||||
no exact reserved smoke title files or metadata, and no processes owned by
|
||||
those title paths. It released its own lock. The private audit result is
|
||||
`/tmp/frame-control-v0451-cleanup-audit.json` (SHA-256
|
||||
`3953072bb5457f4fef2cb766d83f65f844896fc7893d2479a166cb9325f72b48`).
|
||||
No power setting or security state was changed. No wearer was confirmed and no
|
||||
worn panel or controller interaction was tested; the hourly worn check remains
|
||||
enabled.
|
||||
|
||||
## Remaining release gates
|
||||
|
||||
The candidate needs green exact-head PR CI, e2e, native Decky runtime and all
|
||||
four installer previews. After the candidate merges, its exact main commit
|
||||
needs green CI and e2e. The tagged release then needs every installer build,
|
||||
ten assets, matching checksums, release verification, independent package
|
||||
checks and publisher dry run. A failed, cancelled, skipped, pending or
|
||||
unverified release gate holds publication. The current published release
|
||||
remains v0.4.50.
|
||||
@@ -0,0 +1,75 @@
|
||||
# v0.4.51 tagged release verification, 27 October 2026
|
||||
|
||||
The annotated `v0.4.51` tag dereferences to merged main commit
|
||||
`4da7220660ba345bc46cdb525b6c512bc6a9d8bd`. Its tree
|
||||
`16aa888207e8661280aa659c96a08fee05f17689` exactly matches reviewed
|
||||
[candidate PR #364](https://github.com/saphid/frame-control/pull/364) head
|
||||
`728a4dbe9b049e5f5cfcd6de02d9ca858d3e9aa4`. The candidate source
|
||||
passed the locked smoke described in
|
||||
[its evidence](v0451-candidate-2026-10-27.md).
|
||||
|
||||
## Exact commit and tagged build gates
|
||||
|
||||
- [Main checks](https://github.com/saphid/frame-control/actions/runs/40451752952)
|
||||
passed on the tag commit: all seven jobs on the first attempt, including
|
||||
e2e, iOS, Windows, macOS, Linux and the aggregate check.
|
||||
- [Native Decky runtime](https://github.com/saphid/frame-control/actions/runs/40451752953)
|
||||
passed on the tag commit: both published Decky 3.3.0 AMD64 and ARM64 jobs.
|
||||
- An exact-main [installer preview](https://github.com/saphid/frame-control/actions/runs/40451826490)
|
||||
built all four platform installers successfully before tagging. Its draft
|
||||
and checksum jobs were inapplicable to manual preview runs.
|
||||
- The [exact-tag release workflow](https://github.com/saphid/frame-control/actions/runs/40452627862)
|
||||
passed all six jobs on its first attempt: draft, Linux, Windows, Intel Mac,
|
||||
Apple Silicon Mac and checksums. No tagged job failed, was cancelled,
|
||||
skipped or overridden.
|
||||
|
||||
The unpublished draft held exactly ten installers and `SHA256SUMS` before
|
||||
publication. Every installer was independently downloaded by GitHub asset ID,
|
||||
one at a time. Each downloaded size and SHA-256 matched GitHub's asset
|
||||
metadata; the downloaded `SHA256SUMS` matched all ten entries byte for byte.
|
||||
Its SHA-256 was
|
||||
`9d28eae8610356cba56310c892b6dd16dfc55c0507c9089600ab86e9697a04f8`.
|
||||
The [sanitized asset audit](v0451-assets-2026-10-27.json) records all ten
|
||||
names, sizes, hashes and package checks. It checked both Mac ZIP CRCs and
|
||||
bundle version `0.4.51`, both DMG UDIF trailers, Windows ZIP CRC and PE
|
||||
installer header, both AppImage ELF architectures, and both DEB version and
|
||||
architecture fields.
|
||||
|
||||
The independently downloaded Linux x86_64 AppImage had SHA-256
|
||||
`5ebbb931276cd3126cb3ecab88c6cde05f490cbd0e0205b418c32d00f765ea42`.
|
||||
It launched under isolated Xvfb with a temporary profile, telemetry and
|
||||
update checks disabled. Its bundled server returned HTTP 200 with
|
||||
`FrameControl/1` and stopped after app exit. No physical Mac or Windows
|
||||
launch of these exact installers was performed.
|
||||
|
||||
## Real Frame and publication
|
||||
|
||||
The [candidate evidence](v0451-candidate-2026-10-27.md) records a unique-lock
|
||||
real Frame smoke on the version-bumped source: **12 passed, one optional
|
||||
x86_64 launch skipped, zero failed**. Steam Linux Runtime 4 was absent, so no
|
||||
x86_64 launch is claimed. A separate fresh-lock audit found no reserved smoke
|
||||
title files, metadata or owned processes; both locks were released. SteamVR
|
||||
activity was 3 (standby), proximity raw 4.6875, battery 88% before and 91%
|
||||
while charging at audit. There was no wearer confirmation, wake command or
|
||||
worn interaction test. The private smoke result's SHA-256 is
|
||||
`c2b9611e072fd08109c31f4aa05a4ba6b93de82f532a7d87a4a1c6dcd52024a3`.
|
||||
|
||||
`scripts/publish-release.sh --dry-run v0.4.51` passed exact-main, native
|
||||
runtime and tagged release jobs, all ten installer hashes, regenerated
|
||||
`SHA256SUMS`, draft notes and update-manifest checks. The real publisher
|
||||
repeated those gates, attached `update.json`, and published
|
||||
[v0.4.51](https://github.com/saphid/frame-control/releases/tag/v0.4.51) at
|
||||
**05:10:05 UTC**. GitHub's latest-release API returned `draft=false`,
|
||||
`prerelease=false`, ten installers, `SHA256SUMS` and `update.json`. A fresh
|
||||
public latest-download of `update.json` reported version `0.4.51`, the correct
|
||||
release page, ten hashes matching the independent downloads, and the worn
|
||||
limit in its notes. The public manifest's SHA-256 was
|
||||
`27bc0e45acf6b9a5677639e66ab501f2acc3eb7f5c0f4f867ced8a5d11825c52`.
|
||||
|
||||
The exact-version Decky catalog is **6 works / 52 partial / 47 broken /
|
||||
11 N/A**. Managed Decky Localsend is **partial**: real Frame testing proved
|
||||
only its rebuilt helper's private marker transfer, while the published
|
||||
Decky 3.3.0 runtime gate proved discovery and stopped status/configuration
|
||||
RPCs. Real managed-service operation, user-facing transfer, network
|
||||
discovery, Steam routes, worn visibility and physical-controller input remain
|
||||
unverified. The hourly shared-lock worn check remains enabled.
|
||||
@@ -4,7 +4,7 @@ Snapshot: 2026-10-26T06:10:58.537029+00:00; 116 current visible official plugins
|
||||
|
||||
This table and both store views use [`ui/framey-compatibility.json`](../ui/framey-compatibility.json). Results apply only to the exact store ID, version and SHA-256. A changed digest/version is **unverified**. Regenerate this document with `python3 scripts/framey-compatibility-doc.py`; use `--check` in review.
|
||||
|
||||
**Works: 6 · Partial: 51 · Broken: 48 · N/A: 11.**
|
||||
**Works: 6 · Partial: 52 · Broken: 47 · N/A: 11.**
|
||||
|
||||
- **Works** requires verified useful plugin behavior with its real backend. Read the reason for the verified scope and remaining limits; it does not imply worn-VR verification.
|
||||
- **Partial** means only a subset or a local fixture scope was verified, often just frontend/settings initialization. The remaining backend/device functionality is unverified; read the reason for the exact scope.
|
||||
@@ -132,7 +132,7 @@ The API column shows manifest / actual bundled frontend API. Many manifests say
|
||||
| 121: DeckPass 1.1.1 | 1 / 2 | partial | Initializer succeeds; KeePass Flatpak discovery and secrets integration were not exercised. | static archive, local browser, fake-Frame |
|
||||
| 123: Steam Status 1.2.3 | 2 / 2 | partial | Settings/public status rendered locally; cache errors fixed. Read-only get_settings RPC returned actual Decky 3.3.0 defaults on ARM64 Frame. Persistence, live backend diagnostics and VR controls unverified. | static archive, local browser, fake-Frame, real Frame SSH |
|
||||
| 124: AlarMe 1.6.3 | 1 / 2 | partial | Silent labeled timer create/ticks/pause/cancel, preset save/cancel-confirm/delete and stopwatch start/lap/pause/reset/reopen pass in local Chromium with the official Decky 3.3.0 ARM64 backend. Audio, alarms/reminders, suspend inhibition, Steam overlays and worn VR remain unverified; notifications require the panel to stay open. | static archive, local browser, fake-Frame, real Frame SSH |
|
||||
| 125: Decky Localsend 0.4.5 | 1 / 2 | broken | Plugin contains a non-ARM64 Linux binary: decky-localsend/bin/localsend-core. | static archive, local browser, fake-Frame |
|
||||
| 125: Decky Localsend 0.4.5 | 1 / 2 | partial | Exact managed 0.4.5 archive replaces its x86 helper with a pinned ARM64 build. Published Decky 3.3.0 ARM64 stopped status/configuration RPCs and a private real-Frame ARM64 helper marker transfer passed. The managed service, user-facing discovery/transfer, Steam routes and worn VR remain unverified. | exact archive and source hashes, local Chromium initial panel, host managed transaction, fake Frame managed install/remove, private x86 backend transfer, published Decky 3.3.0 AMD64/ARM64 stopped status/configuration RPCs, private real Frame ARM64 helper marker transfer |
|
||||
| 126: Launch Options 1.9.2 | 1 / 2 | broken | Plugin contains a non-ARM64 Linux binary: decky-launch-options/bin/hello. Frontend audit failed: Cannot convert undefined or null to object. Shared Toggle value rendering is repaired; other listed blockers remain. | static archive, local browser, fake-Frame |
|
||||
| 127: Decky Translator 0.9.0 | 1 / 2 | partial | Initializer succeeds; x86_64 download and hidraw/controller assumptions need an ARM64 audit. OCR/translation not exercised. | static archive, local browser, fake-Frame |
|
||||
| 128: Quick User Switcher 1.0.0 | 1 / 2 | broken | This plugin needs root and is not supported by the Frame user service. | static archive, local browser, fake-Frame |
|
||||
@@ -279,11 +279,11 @@ DFL includes exports imported through decky-frontend-lib / @decky/ui. Steam stor
|
||||
|
||||
## Native payload portability
|
||||
|
||||
All published Linux native payloads in this snapshot are x86_64; no published aarch64 payload was found. The three Windows DLLs belong to Decky-Framegen. Except for the exact managed DeckyFileServer adaptation below, the installer still rejects these archives. Apart from LocalSend and DeckyFileServer, ARM64 builds were not attempted: the constraints below remain unresolved.
|
||||
All published Linux native payloads in this snapshot are x86_64; no published aarch64 payload was found. The three Windows DLLs belong to Decky-Framegen. Except for the exact managed DeckyFileServer and Decky Localsend adaptations below, the installer still rejects these archives. Apart from LocalSend and DeckyFileServer, ARM64 builds were not attempted: the constraints below remain unresolved.
|
||||
|
||||
A [DeckyFileServer ARM64 follow-up](evidence/framey-fileserver-arm64-2026-10-27.md) ships an offline, digest-checked GPL helper build, corresponding vendored source and a build recipe. Only its exact managed archive can replace the x86 helper and remove its unnecessary root flag. Actual Decky 3.3.0 discovery/RPC and real Frame HTTPS private-fixture listing/download pass; uploads stay disabled. Hostname-DNS failures and empty optional arguments are repaired. Decky 3.3.0 still exposes `decky.HOME`; that call is unchanged. The plugin moves from broken to partial: its frontend advertises HTTP/steamdeck URLs and needs a Steam folder picker. Use HTTPS and a preconfigured directory. Uploads, generated thumbnails and worn VR remain unverified.
|
||||
|
||||
[LocalSend ARM64 build/runtime evidence](evidence/framey-localsend-arm64-2026-10-26.md) covers a reproducible MIT helper build and its isolated real-Frame protocol 2.0 info response. The official x86 archive remains blocked; full plugin integration and file transfers are unverified.
|
||||
[LocalSend ARM64 build/runtime evidence](evidence/framey-localsend-arm64-2026-10-26.md) covers an isolated real-Frame protocol 2.0 info response from an earlier helper build. [Exact official source matching](evidence/framey-localsend-source-match-2026-10-27.md) now identifies the official helper source and a reproducible ARM64 build from that revision. The exact official x86 backend passed one local private-namespace transfer, and the rebuilt ARM64 helper passed one locked real-Frame private marker transfer. Published Decky 3.3.0 AMD64/ARM64 runtime checks discovered the adapted plugin and read its stopped status and configuration. Only the exact managed 0.4.5 archive can replace its x86 helper on Frame. The actual managed Decky service on Frame, user-facing transfer, local discovery and worn VR remain unverified ([managed adaptation evidence](evidence/framey-localsend-managed-2026-10-27.md)).
|
||||
|
||||
| Plugin | Native payloads | ARM64 result / remaining blocker |
|
||||
| --- | --- | --- |
|
||||
@@ -308,7 +308,7 @@ A [DeckyFileServer ARM64 follow-up](evidence/framey-fileserver-arm64-2026-10-27.
|
||||
| HueSync | bin/hello, py_modules/portio.so | portio and handheld LED backends depend on x86 I/O/unsupported hardware; ARM64 compilation alone is insufficient. |
|
||||
| Decky Clipboard | bin/xclip | xclip needs ARM64 X11 libraries/server; root and gamescope assumptions also block this archive. |
|
||||
| Reroll | bin/memscan.so | memscan.so needs ARM64 process-memory support and ABI testing; root requirement remains. Frontend initializes but no memory access was tested. |
|
||||
| Decky Localsend | bin/localsend-core | MIT LocalSend-Go 66edc509a28b9f877e7374504892dcbc653a4da4 reproducibly cross-built with Go 1.25.7/CGO_ENABLED=0 for ARM64; SHA-256 e79238f6051ce34f172f765ae918d3d902b819e27377989a55e5b1eba10e46f9. Help/official backend CLI flags and protocol 2.0 info endpoint passed on real Frame in an isolated network namespace. No file transfer or full plugin/CEF integration tested; exact source match to shipped helper unproven. Official ZIP stays x86_64 and blocked. |
|
||||
| Decky Localsend | bin/localsend-core | Exact official x86 helper is reproducible from pinned MIT source; the managed installer replaces only the exact 0.4.5 digest with SHA-256 e20363237e606ee65eae857792176d21a0bb8ba863489952728eaf2b13277dae on ARM64. The Frame rechecks original Python/helper bytes, replacement digest and ELF machine; x86 keeps the original helper. Local official x86 backend/proxy and real Frame rebuilt ARM64 helper each passed one private marker transfer. Published Decky 3.3.0 AMD64/ARM64 discovery and stopped status/configuration RPCs passed. The actual managed Decky service on Frame, network discovery, user-facing transfer, Steam routes and worn VR remain unverified (docs/evidence/framey-localsend-managed-2026-10-27.md). |
|
||||
| Launch Options | bin/hello | Remove the template x86 bin/hello or publish an ARM64 rebuild; missing Steam module mapping also prevents frontend initialization. |
|
||||
| Decky-Clipper | bin/gstreamer-1.0/libgstproxy.so, bin/gstreamer-1.0/libgstmidi.so, bin/gstreamer-1.0/libgstmpegtsmux.so, bin/gstreamer-1.0/gst-plugin-scanner, bin/gstreamer-1.0/libgstivfparse.so, … 115 total; full list in JSON | Rebuild all bundled GStreamer libraries/plugins for ARM64 and verify capture/codec ABI against Frame SteamVR; gamescope capture differs. |
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild the pinned LocalSend-Go helper (Go 1.25.7 and module access required)."""
|
||||
import argparse
|
||||
import gzip
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tarfile
|
||||
import tempfile
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
ASSETS = ROOT / 'ui/vendor/localsend-0.4.5'
|
||||
SOURCE_SHA256 = 'f1f86292624b989d540689dbfad503bf15f3937d156978157b8dd418d54415d1'
|
||||
EXPECTED = {
|
||||
'amd64': 'd448d03861d578685e6c70ea3ace25eb34fca99cb9d4f98bf51ba23aa5cae843',
|
||||
'arm64': 'e20363237e606ee65eae857792176d21a0bb8ba863489952728eaf2b13277dae',
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--go', default='go', help='Path to Go 1.25.7')
|
||||
parser.add_argument('--output', type=Path, default=ASSETS)
|
||||
args = parser.parse_args()
|
||||
assert subprocess.check_output([args.go, 'version'], text=True).split()[2] == 'go1.25.7', 'Use Go 1.25.7'
|
||||
source_data = (ASSETS/'source.tar.gz').read_bytes()
|
||||
assert hashlib.sha256(source_data).hexdigest() == SOURCE_SHA256, 'Pinned source archive changed'
|
||||
args.output.mkdir(parents=True, exist_ok=True)
|
||||
with tempfile.TemporaryDirectory(prefix='framey-localsend-build-') as temporary:
|
||||
base = Path(temporary)
|
||||
with tarfile.open(fileobj=io.BytesIO(source_data), mode='r:gz') as archive:
|
||||
members = archive.getmembers()
|
||||
assert all((m.isfile() or m.isdir()) and
|
||||
(m.name == 'source' or m.name.startswith('source/'))
|
||||
and '..' not in Path(m.name).parts for m in members), 'Unsafe source archive'
|
||||
archive.extractall(base, filter='data')
|
||||
env = dict(os.environ, CGO_ENABLED='0', GOOS='linux', GOTOOLCHAIN='local',
|
||||
GOCACHE=str(base/'cache'), GOMODCACHE=str(base/'modules'))
|
||||
for arch in ('amd64', 'arm64'):
|
||||
output = base/'localsend-core'
|
||||
subprocess.run([args.go, 'build', '-mod=readonly', '-trimpath', '-buildvcs=false',
|
||||
'-ldflags=-s -w', '-o', str(output), '.'], cwd=base/'source',
|
||||
env=dict(env, GOARCH=arch), check=True)
|
||||
data = output.read_bytes()
|
||||
digest = hashlib.sha256(data).hexdigest()
|
||||
assert digest == EXPECTED[arch], f'{arch} helper does not match pinned bytes'
|
||||
if arch == 'arm64':
|
||||
packed = gzip.compress(data, mtime=0)
|
||||
(args.output/'aarch64.gz').write_bytes(packed)
|
||||
packed_digest = hashlib.sha256(packed).hexdigest()
|
||||
else:
|
||||
packed_digest = None
|
||||
print(json.dumps({'arch': arch, 'sha256': digest, 'gzipSha256': packed_digest,
|
||||
'bytes': len(data)}), flush=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -123,15 +123,21 @@ def render(data):
|
||||
p['backend']+('; '+', '.join(native) if native else ''),p['hardware']]))+' |')
|
||||
lines += ['', '## Native payload portability', '',
|
||||
'All published Linux native payloads in this snapshot are x86_64; no published aarch64 payload was found. '
|
||||
'The three Windows DLLs belong to Decky-Framegen. Except for the exact managed DeckyFileServer adaptation below, the installer still rejects these archives. '
|
||||
'The three Windows DLLs belong to Decky-Framegen. Except for the exact managed DeckyFileServer and Decky Localsend adaptations below, the installer still rejects these archives. '
|
||||
'Apart from LocalSend and DeckyFileServer, ARM64 builds were not attempted: the constraints below remain unresolved.', '',
|
||||
'A [DeckyFileServer ARM64 follow-up](evidence/framey-fileserver-arm64-2026-10-27.md) ships an offline, digest-checked GPL helper build, corresponding vendored source and a build recipe. '
|
||||
'Only its exact managed archive can replace the x86 helper and remove its unnecessary root flag. Actual Decky 3.3.0 discovery/RPC and real Frame HTTPS private-fixture listing/download pass; uploads stay disabled. '
|
||||
'Hostname-DNS failures and empty optional arguments are repaired. Decky 3.3.0 still exposes `decky.HOME`; that call is unchanged. '
|
||||
'The plugin moves from broken to partial: its frontend advertises HTTP/steamdeck URLs and needs a Steam folder picker. Use HTTPS and a preconfigured directory. Uploads, generated thumbnails and worn VR remain unverified.', '',
|
||||
'[LocalSend ARM64 build/runtime evidence](evidence/framey-localsend-arm64-2026-10-26.md) '
|
||||
'covers a reproducible MIT helper build and its isolated real-Frame protocol 2.0 info response. '
|
||||
'The official x86 archive remains blocked; full plugin integration and file transfers are unverified.', '',
|
||||
'covers an isolated real-Frame protocol 2.0 info response from an earlier helper build. '
|
||||
'[Exact official source matching](evidence/framey-localsend-source-match-2026-10-27.md) '
|
||||
'now identifies the official helper source and a reproducible ARM64 build from that revision. '
|
||||
'The exact official x86 backend passed one local private-namespace transfer, and the rebuilt ARM64 helper passed one locked real-Frame private marker transfer. '
|
||||
'Published Decky 3.3.0 AMD64/ARM64 runtime checks discovered the adapted plugin and read its stopped status and configuration. '
|
||||
'Only the exact managed 0.4.5 archive can replace its x86 helper on Frame. '
|
||||
'The actual managed Decky service on Frame, user-facing transfer, local discovery and worn VR remain unverified '
|
||||
'([managed adaptation evidence](evidence/framey-localsend-managed-2026-10-27.md)).', '',
|
||||
'| Plugin | Native payloads | ARM64 result / remaining blocker |', '| --- | --- | --- |']
|
||||
for p in data['plugins']:
|
||||
if p['native']:
|
||||
|
||||
@@ -33,5 +33,10 @@
|
||||
"id": 60,
|
||||
"shim": "deckyfileserver-user-native",
|
||||
"probe": "fileserver"
|
||||
},
|
||||
{
|
||||
"id": 125,
|
||||
"shim": "localsend-digest-native",
|
||||
"probe": "localsend"
|
||||
}
|
||||
]
|
||||
@@ -152,7 +152,22 @@ def main():
|
||||
report['calls'].append({'method': method, 'result': response})
|
||||
return response
|
||||
|
||||
if case['probe'] == 'fileserver':
|
||||
if case['probe'] == 'localsend':
|
||||
helper = plugin/'bin/localsend-core'
|
||||
expected = ('e20363237e606ee65eae857792176d21a0bb8ba863489952728eaf2b13277dae'
|
||||
if __import__('platform').machine() == 'aarch64'
|
||||
else 'd448d03861d578685e6c70ea3ace25eb34fca99cb9d4f98bf51ba23aa5cae843')
|
||||
assert hashlib.sha256(helper.read_bytes()).hexdigest() == expected
|
||||
status = current('get_backend_status')
|
||||
assert status == {'running': False, 'url': 'https://127.0.0.1:53317'}, status
|
||||
settings = current('get_backend_config')
|
||||
assert settings['use_https'] and not settings['auto_save'], settings
|
||||
assert settings['network_interface'] == '*', settings
|
||||
assert settings['download_folder'].startswith(str(base)), settings
|
||||
stopped = current('proxy_get', '/api/localsend/v2/info')
|
||||
assert stopped['status'] == 503 and stopped['data']['error'] == 'Backend not running', stopped
|
||||
report['result'] = 'published Decky discovery and public read-only settings/status RPC passed; network helper remained stopped'
|
||||
elif case['probe'] == 'fileserver':
|
||||
assert until_ready(lambda: current('get_port') == 8000, 'default port')
|
||||
assert current('get_directory') == str(home)
|
||||
assert current('get_server_running') is False
|
||||
|
||||
@@ -210,6 +210,13 @@ u=h/'.config/systemd/user/frame-control-decky.service'; u.parent.mkdir(parents=T
|
||||
arch = ssh('uname -m').strip()
|
||||
self.assertEqual(ssh(f'sha256sum {installed}/bin/backend').split()[0],
|
||||
frame_decky_store.FILESERVER_BINARIES[arch])
|
||||
if plugin['id'] == 125:
|
||||
receipt = json.loads(ssh(f'cat {installed}/.frame-control-store.json'))
|
||||
self.assertEqual(receipt['compatShims'], ['localsend-digest-native'])
|
||||
arch = ssh('uname -m').strip()
|
||||
self.assertEqual(ssh(f'sha256sum {installed}/bin/localsend-core').split()[0],
|
||||
frame_decky_store.LOCALSEND_AARCH64 if arch == 'aarch64'
|
||||
else frame_decky_store.LOCALSEND_ORIGINAL)
|
||||
if plugin['id'] == 53:
|
||||
self.assertEqual(ssh(f'test -f {installed}/py_modules/click.py && echo yes').strip(), 'yes')
|
||||
receipt = json.loads(ssh(f'cat {installed}/.frame-control-store.json'))
|
||||
|
||||
BIN
Binary file not shown.
+7
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
These are unmodified ZIPs downloaded from the official Decky store CDN on
|
||||
2026-10-25, except `96.zip` and `111.zip` downloaded on 2026-10-26 and `60.zip` on
|
||||
2026-10-27; `13.zip` was added from the digest-verified audit cache on 2026-10-27.
|
||||
2026-10-27; `13.zip` and `125.zip` were added from the digest-verified audit cache on 2026-10-27.
|
||||
Each ZIP includes its upstream `LICENSE`. They are test data, not plugins shipped or installed by
|
||||
Frame Control. The fake Frame tests serve them
|
||||
from a local fixture so CI does not depend on the store being online.
|
||||
@@ -11,6 +11,7 @@ from a local fixture so CI does not depend on the store being online.
|
||||
| --- | --- | --- | --- |
|
||||
| `13.zip` | Pause Games | 1.0.3 | `4aefdfb0ef25dea9c6d9fd037731c8b99ee8a1be966c2f7d5b0602112ef29f62` |
|
||||
| `60.zip` | DeckyFileServer | 1.2.0 | `7dc2ecdcd05aa415d1ac90afaff28ecb17f77809a2bc7a77aee8e92505b976f0` |
|
||||
| `125.zip` | Decky Localsend | 0.4.5 | `e271ccb55eb23f918b99a69f79aec905df4c9b2ef7b23bbf4ec1c2d1a6f0c6b7` |
|
||||
| `96.zip` | Simple Timer | 1.0.0 | `973d927e9d9a1feec04679c564af7e389a729b6735d2a988ffc2cb46ce4eb312` |
|
||||
| `7.zip` | CSS Loader | 2.1.3 | `99beaf1374af82408e6d056e0b0147e5fca544e1035fa7ab88419231697bc231` |
|
||||
| `12.zip` | MusicControl | 1.1.6 | `246b89fd653c60a735b1cc401d1b0937d5cb969eea9024561661ff741f081d62` |
|
||||
@@ -61,3 +62,8 @@ native helper replacement. HTTPS listing/download use only public markers in a
|
||||
private directory; uploads remain disabled. The exact source build, GPL licenses
|
||||
and reproducible recipe are in [the native assets](../../../ui/vendor/deckyfileserver-1.2.0/README.md).
|
||||
The original empty optional-argument bug is reproduced by a negative runtime case.
|
||||
Decky Localsend's exact original x86 backend/proxy passed an isolated private
|
||||
marker transfer outside Decky. The native Decky gate checks discovery and
|
||||
read-only backend status/configuration with its network helper stopped. The
|
||||
managed ARM64 helper and transfer remain separately gated; the fixture does
|
||||
not prove worn VR or a transfer on Frame.
|
||||
@@ -8,6 +8,8 @@ PLUGINS = (
|
||||
'folder': 'SDH-PauseGames', 'sha256': '4aefdfb0ef25dea9c6d9fd037731c8b99ee8a1be966c2f7d5b0602112ef29f62'},
|
||||
{'id': 60, 'name': 'DeckyFileServer', 'version': '1.2.0', 'api_version': 1,
|
||||
'folder': 'DeckyFileServer', 'sha256': '7dc2ecdcd05aa415d1ac90afaff28ecb17f77809a2bc7a77aee8e92505b976f0'},
|
||||
{'id': 125, 'name': 'Decky Localsend', 'version': '0.4.5', 'api_version': 1,
|
||||
'folder': 'decky-localsend', 'sha256': 'e271ccb55eb23f918b99a69f79aec905df4c9b2ef7b23bbf4ec1c2d1a6f0c6b7'},
|
||||
{'id': 89, 'name': 'DeckySpy', 'version': '0.6.7', 'api_version': 'legacy',
|
||||
'folder': 'decky-spy', 'sha256': '2d431a89826ed5cf3fd37258373eb3b46c841c39795aa8b25f83e59f4eee87c7'},
|
||||
{'id': 96, 'name': 'Simple Timer', 'version': '1.0.0', 'api_version': 1,
|
||||
|
||||
@@ -6,8 +6,9 @@ BUILD_ID in tests/smoke/results/<time>-<BUILD_ID>.json (git-ignored):
|
||||
|
||||
Before any status read or title change, it atomically owns /tmp/frame-test.lock
|
||||
on the Frame and checks SteamVR standby, proximity and battery read-only. A
|
||||
busy lock, awake headset or battery below 20% stops the run. Only this run's
|
||||
lock is released, including after test failures.
|
||||
busy lock, awake headset, missing or active-range proximity reading, or battery
|
||||
below 20% stops the run. Only this run's lock is released, including after
|
||||
test failures.
|
||||
|
||||
- properties.json from Valve's devkit service names the login user;
|
||||
- status (ui/frame_status.py) reads the build, battery and storage;
|
||||
@@ -107,6 +108,11 @@ def preflight():
|
||||
state = json.loads(result.stdout)
|
||||
if state.get('activity') != 3:
|
||||
raise RuntimeError('Frame is awake or SteamVR activity is unknown; active session untouched')
|
||||
proximity = state.get('proximity')
|
||||
if (not isinstance(proximity, list) or not proximity or
|
||||
any(not isinstance(value, (int, float)) or isinstance(value, bool) or
|
||||
value < 0 or value > 10 for value in proximity)):
|
||||
raise RuntimeError('Frame proximity is active or unknown; active session untouched')
|
||||
battery = state.get('battery')
|
||||
if not battery or min(battery) < 20:
|
||||
raise RuntimeError('Frame battery is unavailable or below the smoke threshold')
|
||||
|
||||
@@ -212,6 +212,62 @@ class StoreTransaction(unittest.TestCase):
|
||||
self.assertEqual(helper.stat().st_mode & 0o777, 0o700)
|
||||
compile((plugin/'main.py').read_text(), str(plugin/'main.py'), 'exec')
|
||||
self.assertIn('if arg]', (plugin/'main.py').read_text())
|
||||
|
||||
@unittest.skipUnless(__import__('platform').machine() in ('x86_64', 'aarch64'), 'native Linux helper architecture')
|
||||
def test_exact_localsend_gets_verified_native_helper_and_preserves_source(self):
|
||||
from framey_official import PLUGINS, archive as official_archive
|
||||
import platform
|
||||
plugin = next(p for p in PLUGINS if p['id'] == 125)
|
||||
data = official_archive(plugin)
|
||||
self.assertEqual(frame_decky_store.archive_info(data, plugin['sha256'], plugin['name']), plugin['folder'])
|
||||
result = self.apply('install', data, plugin_id=125, folder=plugin['folder'], version=plugin['version'])
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
folder = self.folder.parent/plugin['folder']
|
||||
receipt = json.loads((folder/'.frame-control-store.json').read_text())
|
||||
self.assertEqual(receipt['compatShims'], ['localsend-digest-native'])
|
||||
self.assertEqual(json.loads((folder/'plugin.json').read_text())['flags'], [])
|
||||
expected = (frame_decky_store.LOCALSEND_AARCH64 if platform.machine() == 'aarch64'
|
||||
else frame_decky_store.LOCALSEND_ORIGINAL)
|
||||
self.assertEqual(hashlib.sha256((folder/'bin/localsend-core').read_bytes()).hexdigest(), expected)
|
||||
self.assertEqual((folder/'bin/localsend-core').stat().st_mode & 0o777, 0o700)
|
||||
with zipfile.ZipFile(io.BytesIO(data)) as source:
|
||||
for name in ('main.py', 'LICENSE'):
|
||||
self.assertEqual((folder/name).read_bytes(), source.read(plugin['folder']+'/'+name))
|
||||
result = self.apply('remove', plugin_id=125, folder=plugin['folder'])
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertFalse(folder.exists())
|
||||
|
||||
def test_localsend_exception_does_not_accept_another_archive_or_corrupt_helper(self):
|
||||
from framey_official import PLUGINS, archive as official_archive
|
||||
plugin = next(p for p in PLUGINS if p['id'] == 125)
|
||||
with zipfile.ZipFile(io.BytesIO(official_archive(plugin))) as source:
|
||||
stream = io.BytesIO()
|
||||
with zipfile.ZipFile(stream, 'w', zipfile.ZIP_DEFLATED) as target:
|
||||
for entry in source.infolist():
|
||||
target.writestr(entry, source.read(entry))
|
||||
target.writestr(plugin['folder']+'/changed.txt', b'new version')
|
||||
changed = stream.getvalue()
|
||||
with self.assertRaisesRegex(ValueError, 'non-ARM64'):
|
||||
frame_decky_store.archive_info(changed, hashlib.sha256(changed).hexdigest(), plugin['name'])
|
||||
self.assertEqual(frame_decky_store.apply_script(125, '0'*64), frame_decky_store.APPLY)
|
||||
with patch.object(frame_decky_store, 'LOCALSEND_AARCH64', '0'*64):
|
||||
with self.assertRaisesRegex(ValueError, 'SHA-256 mismatch'):
|
||||
frame_decky_store.apply_script(125, frame_decky_store.LOCALSEND_DIGEST)
|
||||
|
||||
def test_localsend_arm64_transaction_checks_written_helper(self):
|
||||
from framey_official import PLUGINS, archive as official_archive
|
||||
plugin = next(p for p in PLUGINS if p['id'] == 125)
|
||||
# The local x86 host only writes the ELF; native ARM execution is gated
|
||||
# separately by the published Decky runtime runner.
|
||||
with patch.object(frame_decky_store, 'APPLY',
|
||||
"import platform\nplatform.machine=lambda:'aarch64'\n"+frame_decky_store.APPLY):
|
||||
result = self.apply('install', official_archive(plugin), plugin_id=125,
|
||||
folder=plugin['folder'], version=plugin['version'])
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
helper = self.folder.parent/plugin['folder']/'bin/localsend-core'
|
||||
self.assertEqual(hashlib.sha256(helper.read_bytes()).hexdigest(),
|
||||
frame_decky_store.LOCALSEND_AARCH64)
|
||||
self.assertEqual(int.from_bytes(helper.read_bytes()[18:20], 'little'), 183)
|
||||
@unittest.skipUnless(__import__('platform').machine() in ('x86_64', 'aarch64'), 'native Linux wheel architecture')
|
||||
def test_exact_deckyspy_gets_native_dependency_and_license_only_in_its_folder(self):
|
||||
from framey_official import PLUGINS, archive as official_archive
|
||||
|
||||
@@ -28,6 +28,8 @@ class HeadsetBundle(unittest.TestCase):
|
||||
self.assertIn('ui/server.py', names)
|
||||
for filename in ('aarch64.gz', 'x86_64.gz', 'source.tar.gz', 'LICENSE.txt', 'GO-LICENSE.txt', 'README.md'):
|
||||
self.assertIn('ui/vendor/deckyfileserver-1.2.0/'+filename, names)
|
||||
for filename in ('aarch64.gz', 'source.tar.gz', 'LICENSE.txt', 'GO-LICENSE.txt', 'README.md'):
|
||||
self.assertIn('ui/vendor/localsend-0.4.5/'+filename, names)
|
||||
self.assertIn('scripts/build-framey-fileserver.py', names)
|
||||
for name in ('aarch64.whl', 'x86_64.whl', 'LICENSE.txt'):
|
||||
self.assertIn('ui/vendor/psutil-7.2.2/'+name, names)
|
||||
|
||||
@@ -20,7 +20,7 @@ class RuntimeCoverage(unittest.TestCase):
|
||||
for case in cases:
|
||||
self.assertIn(case['id'], official)
|
||||
self.assertIn(case['probe'], ('mpris', 'screenshots', 'system-monitor', 'timer-lifecycle',
|
||||
'toggle-settings', 'fileserver'))
|
||||
'toggle-settings', 'fileserver', 'localsend'))
|
||||
|
||||
def test_new_shim_without_runtime_case_fails_coverage(self):
|
||||
import frame_decky_store
|
||||
|
||||
@@ -309,6 +309,12 @@ class Renderer(unittest.TestCase):
|
||||
resources = {r['from']: r.get('filter', ['**/*']) for r in config['build']['extraResources']}
|
||||
self.assertIn('*.js', resources['../ui'])
|
||||
self.assertIn('*.js', resources['../frame/android'])
|
||||
self.assertIn('*.gz', resources['../ui/vendor/localsend-0.4.5'])
|
||||
self.assertIn('*.txt', resources['../ui/vendor/localsend-0.4.5'])
|
||||
self.assertIn('build-framey-localsend.py', resources['../scripts'])
|
||||
checker = (ROOT / 'app/build/check-resources.js').read_text()
|
||||
for name in ('aarch64.gz', 'source.tar.gz', 'LICENSE.txt', 'GO-LICENSE.txt', 'README.md'):
|
||||
self.assertIn(f'ui/vendor/localsend-0.4.5/{name}', checker)
|
||||
|
||||
|
||||
class SteamGridDB(unittest.TestCase):
|
||||
|
||||
@@ -95,6 +95,19 @@ class RealProcesses(unittest.TestCase):
|
||||
|
||||
|
||||
class Safety(unittest.TestCase):
|
||||
def test_locked_preflight_rejects_active_or_unknown_proximity_before_titles(self):
|
||||
for proximity in ([], [88.0625], [4.6875, 88.0625], [None]):
|
||||
with self.subTest(proximity=proximity), mock.patch.object(
|
||||
frame_smoke, 'remote', return_value=mock.Mock(
|
||||
returncode=0, stdout=json.dumps({
|
||||
'activity': 3, 'proximity': proximity, 'battery': [72]}))):
|
||||
with self.assertRaisesRegex(RuntimeError, 'proximity is active or unknown'):
|
||||
frame_smoke.preflight()
|
||||
with mock.patch.object(frame_smoke, 'remote', return_value=mock.Mock(
|
||||
returncode=0, stdout=json.dumps({
|
||||
'activity': 3, 'proximity': [4.6875], 'battery': [72]}))):
|
||||
self.assertEqual(frame_smoke.preflight()['proximity'], [4.6875])
|
||||
|
||||
def test_missing_x86_runtime_skips_launch_but_still_removes_title(self):
|
||||
smoke = frame_smoke.Smoke()
|
||||
gid = 'fc_smoke_x86_64'
|
||||
|
||||
+45
-4
@@ -33,6 +33,10 @@ FILESERVER_BINARIES = {
|
||||
'aarch64': '86b80021ebd62ed619eba065757c1122defa5a74b2c045aa44a0c6b4403a508a',
|
||||
}
|
||||
FILESERVER_ORIGINAL = 'd9eab8559c87c01736188bc676f3b4b44dee47cf952b6e571c770d1ea61733e9'
|
||||
LOCALSEND_DIGEST = 'e271ccb55eb23f918b99a69f79aec905df4c9b2ef7b23bbf4ec1c2d1a6f0c6b7'
|
||||
LOCALSEND_ORIGINAL = 'd448d03861d578685e6c70ea3ace25eb34fca99cb9d4f98bf51ba23aa5cae843'
|
||||
LOCALSEND_MAIN = 'db03f9201fe50b75dea99a7a05b54ec3f352d1bdfe174115b0e569154198d8ad'
|
||||
LOCALSEND_AARCH64 = 'e20363237e606ee65eae857792176d21a0bb8ba863489952728eaf2b13277dae'
|
||||
|
||||
VERSION = re.compile(r'[A-Za-z0-9][A-Za-z0-9._+\-]{0,39}\Z')
|
||||
|
||||
@@ -101,6 +105,8 @@ def archive_info(data, expected_hash, expected_name):
|
||||
raise ValueError('Invalid plugin folder')
|
||||
fileserver = (expected_hash == FILESERVER_DIGEST and expected_name == 'DeckyFileServer'
|
||||
and folder == 'DeckyFileServer')
|
||||
localsend = (expected_hash == LOCALSEND_DIGEST and expected_name == 'Decky Localsend'
|
||||
and folder == 'decky-localsend')
|
||||
seen = {}
|
||||
for entry in files:
|
||||
name = entry.filename
|
||||
@@ -130,8 +136,10 @@ def archive_info(data, expected_hash, expected_name):
|
||||
with archive.open(entry) as source:
|
||||
header = source.read(20)
|
||||
if header[:4] == b'\x7fELF' and int.from_bytes(header[18:20], 'little') != 183:
|
||||
if not (fileserver and name == 'DeckyFileServer/bin/backend'
|
||||
and hashlib.sha256(archive.read(entry)).hexdigest() == FILESERVER_ORIGINAL):
|
||||
if not ((fileserver and name == 'DeckyFileServer/bin/backend'
|
||||
and hashlib.sha256(archive.read(entry)).hexdigest() == FILESERVER_ORIGINAL)
|
||||
or (localsend and name == 'decky-localsend/bin/localsend-core'
|
||||
and hashlib.sha256(archive.read(entry)).hexdigest() == LOCALSEND_ORIGINAL)):
|
||||
raise ValueError('Plugin contains a non-ARM64 Linux binary: ' + name)
|
||||
if header[:2] == b'MZ':
|
||||
raise ValueError('Plugin contains a Windows binary: ' + name)
|
||||
@@ -145,6 +153,8 @@ def archive_info(data, expected_hash, expected_name):
|
||||
raise ValueError('Invalid plugin flags')
|
||||
if ('root' in flags or '_root' in flags) and not fileserver:
|
||||
raise ValueError('This plugin needs root and is not supported by the Frame user service')
|
||||
if localsend and hashlib.sha256(archive.read(folder+'/main.py')).hexdigest() != LOCALSEND_MAIN:
|
||||
raise ValueError('LocalSend Python source differs from the pinned archive')
|
||||
return folder
|
||||
|
||||
|
||||
@@ -157,6 +167,12 @@ PSUTIL_DIGESTS = {
|
||||
|
||||
def apply_script(plugin_id, digest):
|
||||
"""Deliver verified offline dependencies only to exact managed archives."""
|
||||
if plugin_id == 125 and digest == LOCALSEND_DIGEST:
|
||||
packed = (Path(__file__).parent/'vendor/localsend-0.4.5/aarch64.gz').read_bytes()
|
||||
data = gzip.decompress(packed)
|
||||
if hashlib.sha256(data).hexdigest() != LOCALSEND_AARCH64:
|
||||
raise ValueError('Bundled LocalSend ARM64 helper SHA-256 mismatch')
|
||||
return ('FRAMEY_LOCALSEND_AARCH64 = '+repr(base64.b64encode(packed).decode('ascii'))+'\n'+APPLY)
|
||||
if plugin_id == 60 and digest == FILESERVER_DIGEST:
|
||||
assets = Path(__file__).parent/'vendor/deckyfileserver-1.2.0'
|
||||
payloads = {}
|
||||
@@ -227,6 +243,7 @@ try:
|
||||
if not files or len(files)>3000 or sum(f.file_size for f in files)>150*1024*1024:
|
||||
raise RuntimeError('Plugin archive too large')
|
||||
fileserver=(int(plugin_id)==60 and digest=='7dc2ecdcd05aa415d1ac90afaff28ecb17f77809a2bc7a77aee8e92505b976f0' and folder=='DeckyFileServer')
|
||||
localsend=(int(plugin_id)==125 and digest=='e271ccb55eb23f918b99a69f79aec905df4c9b2ef7b23bbf4ec1c2d1a6f0c6b7' and folder=='decky-localsend')
|
||||
manifest=json.loads(z.read(folder+'/plugin.json'))
|
||||
if not isinstance(manifest,dict): raise RuntimeError('Invalid plugin manifest')
|
||||
flags=manifest.get('flags',[])
|
||||
@@ -257,8 +274,10 @@ try:
|
||||
if not f.is_dir():
|
||||
with z.open(f) as source: header=source.read(20)
|
||||
if header[:4]==b'\x7fELF' and int.from_bytes(header[18:20],'little')!=183:
|
||||
if not (fileserver and f.filename==folder+'/bin/backend' and
|
||||
hashlib.sha256(z.read(f)).hexdigest()=='d9eab8559c87c01736188bc676f3b4b44dee47cf952b6e571c770d1ea61733e9'):
|
||||
if not ((fileserver and f.filename==folder+'/bin/backend' and
|
||||
hashlib.sha256(z.read(f)).hexdigest()=='d9eab8559c87c01736188bc676f3b4b44dee47cf952b6e571c770d1ea61733e9')
|
||||
or (localsend and f.filename==folder+'/bin/localsend-core' and
|
||||
hashlib.sha256(z.read(f)).hexdigest()=='d448d03861d578685e6c70ea3ace25eb34fca99cb9d4f98bf51ba23aa5cae843')):
|
||||
raise RuntimeError('Plugin contains a non-ARM64 Linux binary')
|
||||
if header[:2]==b'MZ': raise RuntimeError('Plugin contains a Windows binary')
|
||||
stage.mkdir(mode=0o700)
|
||||
@@ -276,6 +295,28 @@ try:
|
||||
unpacked=stage/folder
|
||||
if not unpacked.is_dir(): raise RuntimeError('Missing plugin folder')
|
||||
compat_shims=[]
|
||||
if localsend:
|
||||
helper=unpacked/'bin/localsend-core'
|
||||
source=unpacked/'main.py'
|
||||
if hashlib.sha256(helper.read_bytes()).hexdigest()!='d448d03861d578685e6c70ea3ace25eb34fca99cb9d4f98bf51ba23aa5cae843':
|
||||
raise RuntimeError('LocalSend original helper mismatch')
|
||||
if hashlib.sha256(source.read_bytes()).hexdigest()!='db03f9201fe50b75dea99a7a05b54ec3f352d1bdfe174115b0e569154198d8ad':
|
||||
raise RuntimeError('LocalSend Python source mismatch')
|
||||
if manifest.get('name')!='Decky Localsend' or manifest.get('api_version')!=1 or manifest.get('flags')!=[]:
|
||||
raise RuntimeError('LocalSend manifest mismatch')
|
||||
arch=platform.machine()
|
||||
if arch=='aarch64':
|
||||
packed=globals().get('FRAMEY_LOCALSEND_AARCH64')
|
||||
if not isinstance(packed,str): raise RuntimeError('Missing LocalSend ARM64 helper')
|
||||
native=gzip.decompress(base64.b64decode(packed,validate=True))
|
||||
if (hashlib.sha256(native).hexdigest()!='e20363237e606ee65eae857792176d21a0bb8ba863489952728eaf2b13277dae'
|
||||
or native[:4]!=b'\x7fELF' or int.from_bytes(native[18:20],'little')!=183):
|
||||
raise RuntimeError('LocalSend ARM64 helper mismatch')
|
||||
helper.write_bytes(native)
|
||||
elif arch!='x86_64':
|
||||
raise RuntimeError('Unsupported LocalSend helper architecture')
|
||||
helper.chmod(0o700)
|
||||
compat_shims.append('localsend-digest-native')
|
||||
# GPL source and reproducible build recipe ship with Frame Control.
|
||||
# The exact official helper is x86_64; this plugin needs no root.
|
||||
if int(plugin_id)==60 and digest=='7dc2ecdcd05aa415d1ac90afaff28ecb17f77809a2bc7a77aee8e92505b976f0' and folder=='DeckyFileServer':
|
||||
|
||||
@@ -11142,13 +11142,17 @@
|
||||
"https://github.com/moyoez/decky-localsend",
|
||||
"https://github.com/moyoez/decky-localsend.git"
|
||||
],
|
||||
"status": "broken",
|
||||
"reason": "Plugin contains a non-ARM64 Linux binary: decky-localsend/bin/localsend-core.",
|
||||
"blockInstall": true,
|
||||
"status": "partial",
|
||||
"reason": "Exact managed 0.4.5 archive replaces its x86 helper with a pinned ARM64 build. Published Decky 3.3.0 ARM64 stopped status/configuration RPCs and a private real-Frame ARM64 helper marker transfer passed. The managed service, user-facing discovery/transfer, Steam routes and worn VR remain unverified.",
|
||||
"blockInstall": false,
|
||||
"verification": [
|
||||
"static archive",
|
||||
"local browser",
|
||||
"fake-Frame"
|
||||
"exact archive and source hashes",
|
||||
"local Chromium initial panel",
|
||||
"host managed transaction",
|
||||
"fake Frame managed install/remove",
|
||||
"private x86 backend transfer",
|
||||
"published Decky 3.3.0 AMD64/ARM64 stopped status/configuration RPCs",
|
||||
"private real Frame ARM64 helper marker transfer"
|
||||
],
|
||||
"browser": {
|
||||
"id": 125,
|
||||
@@ -11174,7 +11178,7 @@
|
||||
},
|
||||
"backend": "Python + native payload",
|
||||
"hardware": "No specific hardware requirement detected statically; runtime requirements unverified.",
|
||||
"nativePort": "MIT LocalSend-Go 66edc509a28b9f877e7374504892dcbc653a4da4 reproducibly cross-built with Go 1.25.7/CGO_ENABLED=0 for ARM64; SHA-256 e79238f6051ce34f172f765ae918d3d902b819e27377989a55e5b1eba10e46f9. Help/official backend CLI flags and protocol 2.0 info endpoint passed on real Frame in an isolated network namespace. No file transfer or full plugin/CEF integration tested; exact source match to shipped helper unproven. Official ZIP stays x86_64 and blocked.",
|
||||
"nativePort": "Exact official x86 helper is reproducible from pinned MIT source; the managed installer replaces only the exact 0.4.5 digest with SHA-256 e20363237e606ee65eae857792176d21a0bb8ba863489952728eaf2b13277dae on ARM64. The Frame rechecks original Python/helper bytes, replacement digest and ELF machine; x86 keeps the original helper. Local official x86 backend/proxy and real Frame rebuilt ARM64 helper each passed one private marker transfer. Published Decky 3.3.0 AMD64/ARM64 discovery and stopped status/configuration RPCs passed. The actual managed Decky service on Frame, network discovery, user-facing transfer, Steam routes and worn VR remain unverified (docs/evidence/framey-localsend-managed-2026-10-27.md).",
|
||||
"uiSurfaces": [
|
||||
"local/Steam routes",
|
||||
"toasts",
|
||||
@@ -11186,6 +11190,15 @@
|
||||
"archiveValidation": "Plugin contains a non-ARM64 Linux binary: decky-localsend/bin/localsend-core",
|
||||
"result": "blocked",
|
||||
"reason": "ValueError: Plugin contains a non-ARM64 Linux binary: decky-localsend/bin/localsend-core."
|
||||
},
|
||||
"requiredShims": [
|
||||
"localsend-digest-native"
|
||||
],
|
||||
"managedNative": {
|
||||
"shim": "localsend-digest-native",
|
||||
"helperSha256": "e20363237e606ee65eae857792176d21a0bb8ba863489952728eaf2b13277dae",
|
||||
"sourceCommit": "ad28b8e9440e17e44b4735a6c4aa0512009cf333",
|
||||
"verification": "Host transaction and fake Frame managed install/remove; local original x86 backend private marker transfer. Published Decky ARM64 runtime and real Frame transfer pending (docs/evidence/framey-localsend-managed-2026-10-27.md)."
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 MoeMagicMango
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
# Decky Localsend 0.4.5 native helper
|
||||
|
||||
The official Decky store ZIP SHA-256 is
|
||||
`e271ccb55eb23f918b99a69f79aec905df4c9b2ef7b23bbf4ec1c2d1a6f0c6b7`.
|
||||
Its Python backend matches upstream plugin tag `v0.45` byte for byte. That
|
||||
tag pins LocalSend-Go commit `ad28b8e9440e17e44b4735a6c4aa0512009cf333`.
|
||||
Building that source with Go 1.25.7, CGO disabled, `-trimpath`,
|
||||
`-buildvcs=false` and `-ldflags='-s -w'` reproduces the published x86_64
|
||||
helper **byte for byte**. The same source produced the ARM64 helper twice
|
||||
with identical hashes.
|
||||
|
||||
| Artifact | SHA-256 |
|
||||
| --- | --- |
|
||||
| Official x86_64 helper | `d448d03861d578685e6c70ea3ace25eb34fca99cb9d4f98bf51ba23aa5cae843` |
|
||||
| Rebuilt ARM64 helper | `e20363237e606ee65eae857792176d21a0bb8ba863489952728eaf2b13277dae` |
|
||||
| `aarch64.gz` | `2c4e24dd00d67c6d4f2125b42f1eedc0fe8bcedcbf804be0c7ae91e7b391d5f9` |
|
||||
| `source.tar.gz` | `f1f86292624b989d540689dbfad503bf15f3937d156978157b8dd418d54415d1` |
|
||||
|
||||
`source.tar.gz` is a Git archive of the exact LocalSend-Go commit, including
|
||||
`go.mod` and `go.sum`. Go modules are **not vendored**: reproducing the
|
||||
build requires access to checksum-verified dependencies or a populated module
|
||||
cache. Run the following to check both expected helper hashes:
|
||||
|
||||
```sh
|
||||
python3 scripts/build-framey-localsend.py \
|
||||
--go /path/to/go1.25.7/bin/go \
|
||||
--output /tmp/framey-localsend-build
|
||||
```
|
||||
The helper shipped to Frame needs no compiler, modules, root service or
|
||||
runtime download. `LICENSE.txt` is the upstream MIT license; `GO-LICENSE.txt`
|
||||
is the Go runtime BSD license. The official plugin ZIP retains its own
|
||||
BSD-3-Clause license unchanged.
|
||||
|
||||
Only the exact official ZIP can use this managed adaptation. The Frame
|
||||
transaction checks its original x86 helper and Python backend hashes,
|
||||
checks the replacement ARM64 SHA-256 and ELF machine locally and again on the
|
||||
headset, then records a managed receipt. Other native archives remain blocked.
|
||||
The official x86 backend and the rebuilt ARM64 helper each passed a single
|
||||
private no-route marker transfer. Published Decky 3.3.0 AMD64/ARM64 runtime
|
||||
checks discovered the plugin and read its stopped status and configuration.
|
||||
The actual managed Decky service on Frame, user-facing transfer, headset UI
|
||||
and worn use remain unverified. See
|
||||
`docs/evidence/framey-localsend-managed-2026-10-27.md` and its linked result.
|
||||
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in new issue
Block a user