Package the Frame Control web UI as an installable Electron Mac app and bring in the tooling built alongside it. - app/: Electron wrapper that starts ui/server.py on a free loopback port, hardened window (sandbox, no navigation, runAsNode fuse off), login-shell PATH so Homebrew tools work from Finder, first-run offer to run connect.sh, ad-hoc signed DMG/zip via electron-builder. - ui/: headset view (OpenVR screenshots), device status, library, Steam "Get games" (owned games, install, store search), Android apps as persistent Lepton instances with a rated F-Droid catalogue and a private compatibility database, Android display controls over ADB, file and clipboard transfer, Flatpaks, remote and power actions. - apk-catalog/, compat-db/, frame/: catalogue build pipeline, Lakebed capsule for compatibility reports, Frame-side launchers. - tests/ and CI: server guard and validation tests plus Steam helper tests, run on Python 3.9 with script and app syntax checks. - Docs: README leads with the Mac app; new Android, panels, Steam games and field-notes docs; security notes on LAN-exposed ADB ports. Screenshot values for the headset's IP and Wi-Fi name are placeholders. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
compat-db: Frame Control's compatibility database
A private Lakebed capsule holding compatibility reports for Android apps on the Steam Frame. Only Frame Control can read or write it.
- Live:
https://frame-compat.lakebed.app(deploydep_dDmcsosVSiFirpW6, owned bysaphid, doesn't expire). The browser page only says it's private. - Access:
GET /v1/reports?since=<createdAt>andPOST /v1/reportswith{"reports": [...]}. Both need thex-frame-control-keyheader. There are no Lakebed queries or mutations, so nothing else can reach the rows. - Key:
FRAME_CONTROL_KEYin.env.lakebed.server(git-ignored, synced on deploy) and in the Mac's login Keychain (serviceframe-control-compat-db, accountapp-key), whereui/frame_compat_db.pyreads it. - Duplicates: each report carries a
clientId, and a report already stored is skipped, so retries and restores are safe to repeat. - Free-plan limits: 1 MiB of data and 16,384 rows per deploy, 1,000 writes a day. A report is about 300 bytes, so roughly 3,000 reports fit.
Backups
scripts/compat-db-backup.sh exports every report through the app key and
keeps dated copies in
~/Library/Application Support/Frame Control/compat-db/backups (newest 60).
When the data has changed, it also uploads them to Google Drive
(the backup folder, folder
<drive-folder-id>) with gog. The LaunchAgent
frame-compat-backup runs it daily at 03:40; the log is
~/Library/Logs/frame-compat-backup.log. If an export has fewer reports than
the last good backup (backups/.last-good), it's kept as refused-*.json,
nothing is uploaded, and every later run refuses too until you rerun with
--accept-shrink.
Reports that can't be sent (unreadable outbox lines, or ones the server
rejects, which it lists by clientId) are never dropped: they move to
~/Library/Application Support/Frame Control/compat-db/compat-outbox.jsonl.rejected,
with the reason.
Restore (to this deploy or a new one):
python3 ui/frame_compat_db.py import BACKUP.json # duplicates are skipped
python3 ui/frame_compat_db.py count
npx lakebed db export dep_dDmcsosVSiFirpW6 --out full.json is a second,
owner-only export path through the Lakebed CLI.
Change and deploy
cd compat-db
npx lakebed dev --port 3917 # local; data resets on restart
npx lakebed deploy # updates frame-compat.lakebed.app
To rotate the key: generate a new one, update the Keychain item and
.env.lakebed.server, then deploy.