No description
  • Python 56.4%
  • TypeScript 41.1%
  • Shell 2.3%
  • JavaScript 0.2%
Find a file
Jonathan Cremin e12b36b62e
All checks were successful
Build plugin bundle / bundle (push) Successful in 20s
Clean up small details
2026-07-02 21:59:41 +01:00
.forgejo/workflows Clean up small details 2026-07-02 21:59:41 +01:00
bin Working fingerprint enroll+verify on ROG Xbox Ally X (1c7a:0588) 2026-07-02 16:47:24 +01:00
defaults Working fingerprint enroll+verify on ROG Xbox Ally X (1c7a:0588) 2026-07-02 16:47:24 +01:00
dist Unlock via onSuccess() so guarded actions (e.g. switch to Desktop) proceed 2026-07-02 19:29:47 +01:00
lib Working fingerprint enroll+verify on ROG Xbox Ally X (1c7a:0588) 2026-07-02 16:47:24 +01:00
scripts Phase 3 hint: patch SteamUI LockScreen component to render the hint 2026-07-02 18:28:31 +01:00
src Unlock via onSuccess() so guarded actions (e.g. switch to Desktop) proceed 2026-07-02 19:29:47 +01:00
typelib Working fingerprint enroll+verify on ROG Xbox Ally X (1c7a:0588) 2026-07-02 16:47:24 +01:00
.gitignore Working fingerprint enroll+verify on ROG Xbox Ally X (1c7a:0588) 2026-07-02 16:47:24 +01:00
.npmrc Add Forgejo Actions build 2026-07-02 20:34:12 +01:00
LICENSE Clean up small details 2026-07-02 21:59:41 +01:00
main.py Fix daemon crash-spin + native lock styling (prompt icon + feedback) 2026-07-02 19:15:41 +01:00
package.json Working fingerprint enroll+verify on ROG Xbox Ally X (1c7a:0588) 2026-07-02 16:47:24 +01:00
plugin.json Clean up small details 2026-07-02 21:59:41 +01:00
pnpm-lock.yaml Working fingerprint enroll+verify on ROG Xbox Ally X (1c7a:0588) 2026-07-02 16:47:24 +01:00
README.md Clean up small details 2026-07-02 21:59:41 +01:00
rollup.config.js Working fingerprint enroll+verify on ROG Xbox Ally X (1c7a:0588) 2026-07-02 16:47:24 +01:00
tools_clear_storage.py Working fingerprint enroll+verify on ROG Xbox Ally X (1c7a:0588) 2026-07-02 16:47:24 +01:00
tools_list_prints.py Working fingerprint enroll+verify on ROG Xbox Ally X (1c7a:0588) 2026-07-02 16:47:24 +01:00
tsconfig.json Working fingerprint enroll+verify on ROG Xbox Ally X (1c7a:0588) 2026-07-02 16:47:24 +01:00

Deck Fingerprint

Fingerprint enrollment and (eventually) Game Mode unlock for the ROG Xbox Ally X and other SteamOS devices with an EgisTec Match-on-Chip sensor.

Packaged as a Decky Loader plugin so it persists across SteamOS updates (lives in ~/homebrew, no immutable-rootfs changes) and gets a Game Mode UI for free.

Why this approach

SteamOS ships no fprintd/libfprint, and its rootfs is immutable/atomic, so you can't just pacman -S fprintd. Instead we vendor a minimal stack that was validated to run natively on the host:

  • bin/fprintd daemon + fprintd-{enroll,verify,list,delete} clients
  • lib/libfprint-2.so.2 (carries the egismoc driver) and libgusb.so.2

Everything else in the dependency closure is already present on SteamOS. glibc is forward-compatible here (host 2.41 runs these fine; verified — no missing symbols). The egismoc driver binds the sensor (1c7a:0588), reports a press-type Match-on-Chip device with 21 enroll stages.

Runtime model

The backend runs as root ("flags": ["root"]). It does not register fprintd on the host system bus. Instead it spawns a private dbus-daemon with a permissive policy and points fprintd + the client tools at it via DBUS_SYSTEM_BUS_ADDRESS. Nothing touches /etc or /usr; teardown on unload is clean. Enrolled templates live on the sensor; only metadata sits in /var/lib/fprint (persistent /var).

The SDCP requirement

Stock upstream egismoc drives this sensor's probe/enroll motions but the 1c7a:0588 on the ROG Xbox Ally X is an SDCP (Secure Device Connection Protocol) part: without the secure channel, enroll "completes" but the sensor stores nothing (device template count stays 0), so verify never matches.

The working stack uses libfprint from the SDCP fork (TenSeventy7/libfprint-egismoc-sdcp, based on libfprint 1.94.9) with two local changes, built egismoc-only and vendored as lib/libfprint-2.so.2:

  1. Add 0588 to the egismoc id table (TYPE1 | MAX_ENROLL_STAGES_20, matching its 0586/0587 siblings).
  2. OpenSSL 3.6 host-key fix in fpi_sdcp_set_host_keys: the fork exported the generated EC public key via EVP_PKEY_get_octet_string_param(PUB_KEY), which on OpenSSL ≥ 3.x ignores the uncompressed point-format param on immutable provider keys and returns the wrong length (assert public_key_len == 65 fails → crash). Fixed by deriving the 65-byte uncompressed key with the low-level EC_POINT_point2oct(..., POINT_CONVERSION_UNCOMPRESSED, ...), matching what sdcp_get_pkey() already does. Also requires linking openssl_dep into the core deps in libfprint/meson.build (the fork only wired it in per-driver).

With that, SDCP Connect → device ConnectResponse verifies ("CN=Secure BIO Sensor"), enroll commits a real on-chip template (count=1), and verify matches.

MoC gotcha: re-enrolling the same finger deletes the existing template first; if the re-enroll doesn't complete you lose the enrollment. The UI must guard this. Orphaned templates (e.g. factory Windows Hello) are cleared via libfprint clear_storage (see tools_clear_storage.py) since fprintd's per-user delete can't reach them.

TODO

  • Lock watcher polls every 1s when unlocked; could use a MobX reaction instead.

Build

Requires Node + pnpm (frontend) — the vendored binaries are prebuilt.

pnpm install
pnpm build          # -> dist/index.js
# then zip {plugin.json, dist/, main.py, bin/, lib/, defaults/} into ~/homebrew/plugins/