From 2ef1e6b96a3363b7aacab8b114923bd9c3dada71 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Wed, 14 Oct 2026 02:32:30 +0200 Subject: [PATCH 1/4] packaging, ci: build the apk with the Crafter.Build Flatpak Crafter.Build's releases carry a Flatpak and nothing else since 2026-10-02 (imsd a625248): the rolling `latest` release's crafter-build-linux-x86_64-musl-v2.tar.gz that build-package.sh fetched is gone, so the push of 0.2.6 failed at `fetch crafter-build` (run 10, 2026-10-14, three 404s) and published nothing; the registry still serves 0.2.5. The move imsd made on 2026-10-04: install the Flatpak (net.catcrafts.CrafterBuild//stable from flatpak.catcrafts.net) in the job container and run it as crafter-build. bubblewrap inside the container needs a privileged runner, so the job runs on the `flatpak` label. The host keeps clang and llvm-ar for libqcomtee, which make-libqcomtee.sh compiles against the sysroot; the daemon's compiler, lld and libc++ are the Flatpak's, so the host packages that served the native test-suite run go. The sysroot gains llvm-runtimes, the package with the std module source of its own libc++ (libc++-dev ships none), added with --upgrade so a sysroot from before this change comes out with a matching libc++ and source. The README's Build section carries the install lines. Tested: build-package.sh run end to end on this commit in a privileged alpine:edge container (podman) on the workstation: crafter-build 2.1.0 (clang 23.1.2) from `stable`, the sysroot at libc++ and llvm-runtimes 23.1.3, daemon and agent cross-built, 8 suites passed natively, the three apks built, the daemon an aarch64 PIE against the sysroot's musl and libc++ (65 s). The rehearsed copy differs from this commit by the wrapping of one comment line. --- .forgejo/workflows/package.yml | 9 ++-- README.md | 7 +++ packaging/build-package.sh | 79 +++++++++++++++++++--------------- packaging/make-sysroot.sh | 11 +++-- 4 files changed, 64 insertions(+), 42 deletions(-) diff --git a/.forgejo/workflows/package.yml b/.forgejo/workflows/package.yml index 6c18221..cd569f7 100644 --- a/.forgejo/workflows/package.yml +++ b/.forgejo/workflows/package.yml @@ -7,9 +7,10 @@ name: package # # Build: crafter-build cross-compiles against an Alpine aarch64 sysroot # (packaging/build-package.sh — the README's "Cross-compiling" flow), the test -# suite runs natively, and packaging/APKBUILD wraps the result. This needs no -# privileged runner: it runs in an alpine:edge container on the ordinary -# arch-latest runner. +# suite runs natively, and packaging/APKBUILD wraps the result. crafter-build +# is the Crafter.Build Flatpak, and bubblewrap inside the job container needs +# a privileged runner: the job runs on the `flatpak` label (the Catcrafts +# organisation's privileged runner), in an alpine:edge container. # # Release gating is the version: pkgver comes from interfaces/Fingerprintd.cppm, # the registry answers 409 for an already-published version, and the publish @@ -27,7 +28,7 @@ on: jobs: package: - runs-on: arch-latest + runs-on: flatpak container: image: alpine:edge timeout-minutes: 90 diff --git a/README.md b/README.md index d15276d..073f4dc 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,14 @@ that touches hardware. ## Build +With the [Crafter Build](https://forgejo.catcrafts.net/Catcrafts/Crafter.Build) +Flatpak, which carries the compiler: + ```sh +flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo +flatpak remote-add --if-not-exists catcrafts https://flatpak.catcrafts.net/catcrafts.flatpakrepo +flatpak install catcrafts net.catcrafts.CrafterBuild//stable +alias crafter-build='flatpak run net.catcrafts.CrafterBuild//stable' crafter-build # bin/fingerprintd--/fingerprintd crafter-build test # the unit suites ``` diff --git a/packaging/build-package.sh b/packaging/build-package.sh index 41e6d06..fe03746 100755 --- a/packaging/build-package.sh +++ b/packaging/build-package.sh @@ -5,44 +5,28 @@ # (the README's "Cross-compiling" flow), run the test suite natively, and # package the result via packaging/APKBUILD. Expects an x86_64 Alpine # environment with root — the workflow runs it in an alpine:edge job -# container on an ordinary runner. Root only installs packages and hands off -# to a scratch user: the sysroot is built with apk.static --usermode (which -# refuses root) and abuild wants a user too. +# container on the privileged `flatpak` runner: crafter-build is the +# Crafter.Build Flatpak, and bubblewrap inside the container needs the +# privilege. Root only installs packages and hands off to a scratch user: +# the sysroot is built with apk.static --usermode (which refuses root) and +# abuild wants a user too. # # Built packages land in /home/build/.local/share/abuild/*/aarch64/fingerprintd*.apk; # the workflow's publish step uploads them to the Forgejo Alpine registry. set -eu -# The musl build of crafter-build (Crafter.Build CI's release-musl job): this -# container is Alpine, and the glibc launcher cannot run on musl. v2 = SSE4.2 -# baseline: the CI box is an Intel N5105 (no AVX). Overridable for local -# rehearsals (file:// works). -CRAFTER_URL=${CRAFTER_URL:-https://forgejo.catcrafts.net/Catcrafts/Crafter.Build/releases/download/latest/crafter-build-linux-x86_64-musl-v2.tar.gz} +# crafter-build is the Crafter.Build Flatpak: it carries its own clang, lld +# and libc++, and its SDK has glib, so nothing the daemon's build compiles +# with is installed here. `stable` is the branch Crafter.Build publishes from +# its release tags (`beta` follows every push to its master), so the +# toolchain of a release moves only with a release of the toolchain. The +# branch is overridable for a rehearsal against another one. +CRAFTER_REMOTE=${CRAFTER_REMOTE:-https://flatpak.catcrafts.net/catcrafts.flatpakrepo} +CRAFTER_REF=${CRAFTER_REF:-net.catcrafts.CrafterBuild//stable} SRC=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd) -# clang cross-targets aarch64 natively and the target's libc++/glib come from -# the sysroot; llvm-runtimes/libc++-dev/glib-dev here serve the NATIVE -# test-suite run. build-base = Alpine's standard build environment (the one -# abuild implies): binutils' ld/ar for clang's default link driver, gcc's -# libgcc_s/crt objects the musl clang driver links against. -if [ "$(id -u)" = 0 ]; then - apk add -q git curl tar clang lld llvm llvm-runtimes libc++-dev llvm-libunwind-dev glib-dev \ - build-base abuild sudo - id build >/dev/null 2>&1 || adduser -D build - addgroup build abuild 2>/dev/null || true - echo 'build ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/build - # abuild in cross mode strips with $CHOST-strip; llvm-strip handles any - # ELF arch, so give it that name - ln -sf "$(command -v llvm-strip)" /usr/local/bin/aarch64-alpine-linux-musl-strip - # the CI checkout arrives root-owned; crafter-build writes bin/ into it - chown -R build "$SRC" - # -l: a login shell, so HOME really is /home/build (abuild keys + output); - # it scrubs the environment, so carry the one knob that matters across - exec su -l build -c "CRAFTER_URL='${CRAFTER_URL:-}' sh -eu '$SRC/packaging/build-package.sh'" -fi - retry() { # retry _desc=$1; shift for _i in 1 2 3; do @@ -54,18 +38,43 @@ retry() { # retry return 1 } +# clang and llvm-ar here are for libqcomtee: make-libqcomtee.sh compiles it +# with the host clang against the sysroot (C, objects and an archive, no +# link). llvm also has llvm-strip, which abuild in cross mode strips with. +if [ "$(id -u)" = 0 ]; then + apk add -q git curl tar flatpak clang llvm abuild sudo + # the Flatpak as a system installation the build user runs; its SDK + # (org.freedesktop.Sdk) comes from Flathub + flatpak remote-add --if-not-exists catcrafts "$CRAFTER_REMOTE" + flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo + retry "install crafter-build" flatpak install -y --noninteractive catcrafts "$CRAFTER_REF" + id build >/dev/null 2>&1 || adduser -D build + addgroup build abuild 2>/dev/null || true + echo 'build ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/build + # abuild in cross mode strips with $CHOST-strip; llvm-strip handles any + # ELF arch, so give it that name + ln -sf "$(command -v llvm-strip)" /usr/local/bin/aarch64-alpine-linux-musl-strip + # the CI checkout arrives root-owned; crafter-build writes bin/ into it + chown -R build "$SRC" + # -l: a login shell, so HOME really is /home/build (abuild keys + output); + # it scrubs the environment, so carry the one knob that matters across + exec su -l build -c "CRAFTER_REF='$CRAFTER_REF' sh -eu '$SRC/packaging/build-package.sh'" +fi + # interfaces/Fingerprintd.cppm is the version's single source of truth (same # derivation as make-bin-tarball.sh) VER=$(sed -n 's/.*Version = "\(.*\)".*/\1/p' "$SRC/interfaces/Fingerprintd.cppm") [ -n "$VER" ] || { echo "cannot read Version from interfaces/Fingerprintd.cppm" >&2; exit 1; } echo ">> packaging fingerprintd $VER" -# --- crafter-build: static launcher from the rolling release -mkdir -p "$HOME/crafter-build" -retry "fetch crafter-build" \ - sh -c "curl -fsSL '$CRAFTER_URL' | tar -xz -C '$HOME/crafter-build'" -PATH="$HOME/crafter-build/bin:$PATH" -export CRAFTER_BUILD_HOME="$HOME/crafter-build/share/crafter-build" +# --- crafter-build: the Flatpak, under the name the rest of this file uses. +# The build log names the build it ran with. +mkdir -p "$HOME/bin" +printf '#!/bin/sh\nexec flatpak run %s "$@"\n' "$CRAFTER_REF" > "$HOME/bin/crafter-build" +chmod +x "$HOME/bin/crafter-build" +PATH="$HOME/bin:$PATH" +flatpak info "$CRAFTER_REF" | sed -n 's/^ *\(Version\|Commit\|Date\): */crafter-build \1: /p' +crafter-build --version # --- aarch64 Alpine sysroot (unprivileged: apk.static --usermode) SYSROOT="$HOME/.cache/fingerprintd/sysroot-aarch64-alpine" diff --git a/packaging/make-sysroot.sh b/packaging/make-sysroot.sh index 3b52593..c08cbef 100755 --- a/packaging/make-sysroot.sh +++ b/packaging/make-sysroot.sh @@ -23,9 +23,14 @@ MIRROR="${MIRROR:-https://dl-cdn.alpinelinux.org/alpine/edge}" ARCH=aarch64 # gcc is in the list for its crtbeginS.o/libgcc: clang's driver for # *-alpine-linux-musl links against the GCC runtime it detects under -# /usr/lib/gcc//. +# /usr/lib/gcc//. llvm-runtimes is in it for the std module +# source of this very libc++ (usr/share/libc++/v1/std.cppm; libc++-dev ships +# none): crafter-build compiles the sysroot's own copy first. --upgrade below +# is for a run over an existing sysroot: added alone beside an older libc++, +# a newer llvm-runtimes' source does not compile against its headers +# (imsd, 2026-10-04: 23.1.2 against 22.1.8); upgraded together, it does. # glib-dev is for the GDBus bus layer (net.reactivated.Fprint). -PKGS="musl-dev libc++ libc++-dev glib-dev compiler-rt llvm-libunwind-dev gcc" +PKGS="musl-dev libc++ libc++-dev llvm-runtimes glib-dev compiler-rt llvm-libunwind-dev gcc" work=$(mktemp -d) trap 'rm -rf "$work"' EXIT @@ -48,6 +53,6 @@ mkdir -p "$SYSROOT" --arch "$ARCH" --root "$SYSROOT" --initdb --no-scripts --no-cache \ --usermode --keys-dir "$work/usr/share/apk/keys/$ARCH" \ -X "$MIRROR/main" -X "$MIRROR/community" \ - add $PKGS + add --upgrade $PKGS echo ">> done: $SYSROOT" From e7d8da0a2a35be66e5148857ad078b4d96650b83 Mon Sep 17 00:00:00 2001 From: jorijnvdgraaf Date: Wed, 14 Oct 2026 14:03:32 +0000 Subject: [PATCH 2/4] Update README.md --- README.md | 63 ------------------------------------------------------- 1 file changed, 63 deletions(-) diff --git a/README.md b/README.md index 073f4dc..46048d7 100644 --- a/README.md +++ b/README.md @@ -8,31 +8,6 @@ recovered by reverse-engineering, so all of it is pinned by tests that run on a dev box with no phone, no TEE and no sensor. The daemon shell holds everything that touches hardware. -## Build - -With the [Crafter Build](https://forgejo.catcrafts.net/Catcrafts/Crafter.Build) -Flatpak, which carries the compiler: - -```sh -flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo -flatpak remote-add --if-not-exists catcrafts https://flatpak.catcrafts.net/catcrafts.flatpakrepo -flatpak install catcrafts net.catcrafts.CrafterBuild//stable -alias crafter-build='flatpak run net.catcrafts.CrafterBuild//stable' -crafter-build # bin/fingerprintd--/fingerprintd -crafter-build test # the unit suites -``` - -Cross-compiling for the phone: - -```sh -packaging/make-sysroot.sh -crafter-build -- --target=aarch64-alpine-linux-musl --sysroot=~/.cache/fingerprintd/sysroot-aarch64-alpine --march=armv8.6-a+fp16fml+aes+sha3+sm4 --mtune=cortex-a720 -crafter-build test --target=aarch64-alpine-linux-musl --sysroot=~/.cache/fingerprintd/sysroot-aarch64-alpine --march=armv8.6-a+fp16fml+aes+sha3+sm4 --mtune=cortex-a720 -``` - -Verified on the device: all five suites pass cross-built and run on the phone -itself, not only under emulation. - ``` fprintd-enroll -f right-index-finger user ten stages, enroll-completed fprintd-list user - #0: right-index-finger @@ -69,44 +44,6 @@ The `focal64` trustlet is proprietary and is not in this repo. It is extracted from the device's own stock Android partition on first boot by the `fp6-vendor-blobs` mechanism, the same way other firmware in fp6-img is. -## Releases - -Every push to `main` is built and tested by CI, which then publishes the -apks to the Forgejo Alpine registry that installed phones already point at. -CI treats the registry's "already published" answer as nothing to do, so a -push publishes only a package version new to the registry. The package -version is `pkgver-rpkgrel`: `pkgver` is the `Version` constant in -`interfaces/Fingerprintd.cppm`, `pkgrel` is the APKBUILD's and stays at 0. -An installed phone picks a published version up with plain `apk upgrade`. - -From 0.2.6 on, a release is a range of commits ending in a bump commit -titled `fingerprintd X.Y.Z` that changes only the version: the constant and -the APKBUILD's `pkgver`. Each release is tagged `vX.Y.Z` on its bump commit -(signed, annotated) with the release notes as the tag message, and -published as a Forgejo release of the same name and text. No version -before 0.2.6 has a tag or release notes. The notes are written for the -people whose phones will upgrade: - -``` -fingerprintd X.Y.Z - -## New - -- **What a user can now do.** One bullet per change, the most important - first, with anything the user has to do inside the bullet. - -## Fixes - -- **What went wrong, in the user's terms.** What they saw, what changes, - what to do. -``` - -A section with nothing in it is left out, and so is behaviour the release -does not change. Versioning, from 0.2.6 on, is `MAJOR.MINOR.PATCH`: the -patch number counts releases, fixes and features alike; the minor number -moves when a release changes what a user can rely on the daemon for; the -major number moves when a release changes what an installed phone does -without being asked. ## License From 9810811d3b6e858f6701960dd389a83c673f67da Mon Sep 17 00:00:00 2001 From: jorijnvdgraaf Date: Wed, 14 Oct 2026 14:11:13 +0000 Subject: [PATCH 3/4] Update README.md --- README.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/README.md b/README.md index 46048d7..ed3d5b7 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,48 @@ Two routes, both on by default: announced as `FingerMatched` for the session agent. `--standby-unlock=off` disables it. +## Per-finger macros + +The trustlet reports *which* finger matched, so a finger can mean more than +"it was you". Two files, in two trust domains: + +- **`~/.config/fingerprintd/fingers.conf`** runs a command as you, in your + session. This is how a finger opens an application. +- **`/etc/fingerprintd/actions.conf`** runs a command as root, and can mark a + finger as one that never unlocks. + +### In your session: `fingers.conf` + +``` +# ~/.config/fingerprintd/fingers.conf +right-ring-finger kde-open camera +left-index-finger plasma-settings +left-thumb sh -c 'notify-send "hello from a finger"' +``` + +- using these requires the fingerprintd-agent service to be running. + +### As root: `actions.conf` + +``` +# /etc/fingerprintd/actions.conf +# [no-unlock] [absolute command...] +right-ring-finger /usr/local/bin/toggle-something +left-little-finger no-unlock /etc/fingerprintd/panic.sh +left-thumb no-unlock +``` + +- `no-unlock`: the finger matches and the client is told it did not. PAM + sees `verify-no-match`, the standby unlock stays locked. The command, if + there is one, runs regardless. +- the command: an absolute path, run by root through `/bin/sh -c` with a + fixed `PATH` and `FINGERPRINTD_FINGER` set to the finger's name, detached + from the daemon with its stdio on `/dev/null`, so it may block or reboot + the phone without stalling the sensor. +- a line needs at least one of the two, and a finger gets at most one line. +- editing this file requires a restart of the fingerprintd service before changes take effect. + + ## Runtime dependencies The `focal64` trustlet is proprietary and is not in this repo. It is From a4d5ed687749776fb0ef9cebe0de015bc2f8653b Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Fri, 16 Oct 2026 23:01:47 +0200 Subject: [PATCH 4/4] README, FUNDING.yml: Forgejo's funding file and the shared License / Funding / Copyright footer FUNDING.yml is the file Forgejo reads for a repository's Donate button (Forgejo 17.0 and later: .forgejo/, .github/, then the root of the default branch); it points at https://catcrafts.net/shop/donation. The README closes with the footer shared by imsd, fingerprintd and fp6-img: License, Funding, Copyright. --- FUNDING.yml | 2 ++ README.md | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 FUNDING.yml diff --git a/FUNDING.yml b/FUNDING.yml new file mode 100644 index 0000000..7a62ac3 --- /dev/null +++ b/FUNDING.yml @@ -0,0 +1,2 @@ +# Read by Forgejo for the repository's Donate button. +custom: 'https://catcrafts.net/shop/donation' diff --git a/README.md b/README.md index ed3d5b7..b64ff30 100644 --- a/README.md +++ b/README.md @@ -86,11 +86,14 @@ The `focal64` trustlet is proprietary and is not in this repo. It is extracted from the device's own stock Android partition on first boot by the `fp6-vendor-blobs` mechanism, the same way other firmware in fp6-img is. - ## License -GPL-3.0-only, see LICENSE. +GPL-3.0-only - see [LICENSE](LICENSE). + +## Funding + +https://catcrafts.net/shop/donation - see [FUNDING](FUNDING.yml) ## Copyright -Copyright (C) 2026 Catcrafts® catcrafts.net \ No newline at end of file +Copyright (C) 2026 Catcrafts® [catcrafts.net](https://catcrafts.net/)