Compare commits

..
Author SHA1 Message Date
a4d5ed6877
README, FUNDING.yml: Forgejo's funding file and the shared License / Funding / Copyright footer
All checks were successful
package / package (push) Successful in 1m23s
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.
2026-10-16 23:12:17 +02:00
9810811d3b Update README.md
All checks were successful
package / package (push) Successful in 1m19s
2026-10-14 14:11:13 +00:00
e7d8da0a2a Update README.md
All checks were successful
package / package (push) Successful in 1m23s
2026-10-14 14:03:32 +00:00
2ef1e6b96a
packaging, ci: build the apk with the Crafter.Build Flatpak
All checks were successful
package / package (push) Successful in 1m17s
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.
2026-10-14 02:41:15 +02:00
5 changed files with 100 additions and 94 deletions

View file

@ -7,9 +7,10 @@ name: package
# #
# Build: crafter-build cross-compiles against an Alpine aarch64 sysroot # Build: crafter-build cross-compiles against an Alpine aarch64 sysroot
# (packaging/build-package.sh — the README's "Cross-compiling" flow), the test # (packaging/build-package.sh — the README's "Cross-compiling" flow), the test
# suite runs natively, and packaging/APKBUILD wraps the result. This needs no # suite runs natively, and packaging/APKBUILD wraps the result. crafter-build
# privileged runner: it runs in an alpine:edge container on the ordinary # is the Crafter.Build Flatpak, and bubblewrap inside the job container needs
# arch-latest runner. # 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, # Release gating is the version: pkgver comes from interfaces/Fingerprintd.cppm,
# the registry answers 409 for an already-published version, and the publish # the registry answers 409 for an already-published version, and the publish
@ -27,7 +28,7 @@ on:
jobs: jobs:
package: package:
runs-on: arch-latest runs-on: flatpak
container: container:
image: alpine:edge image: alpine:edge
timeout-minutes: 90 timeout-minutes: 90

2
FUNDING.yml Normal file
View file

@ -0,0 +1,2 @@
# Read by Forgejo for the repository's Donate button.
custom: 'https://catcrafts.net/shop/donation'

107
README.md
View file

@ -8,24 +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 dev box with no phone, no TEE and no sensor. The daemon shell holds everything
that touches hardware. that touches hardware.
## Build
```sh
crafter-build # bin/fingerprintd-<target>-<march>/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-enroll -f right-index-finger user ten stages, enroll-completed
fprintd-list user - #0: right-index-finger fprintd-list user - #0: right-index-finger
@ -56,55 +38,62 @@ Two routes, both on by default:
announced as `FingerMatched` for the session agent. `--standby-unlock=off` announced as `FingerMatched` for the session agent. `--standby-unlock=off`
disables it. 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
# <finger> [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 ## Runtime dependencies
The `focal64` trustlet is proprietary and is not in this repo. It is 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 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. `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 ## 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
Copyright (C) 2026 Catcrafts® catcrafts.net Copyright (C) 2026 Catcrafts® [catcrafts.net](https://catcrafts.net/)

View file

@ -5,44 +5,28 @@
# (the README's "Cross-compiling" flow), run the test suite natively, and # (the README's "Cross-compiling" flow), run the test suite natively, and
# package the result via packaging/APKBUILD. Expects an x86_64 Alpine # package the result via packaging/APKBUILD. Expects an x86_64 Alpine
# environment with root — the workflow runs it in an alpine:edge job # environment with root — the workflow runs it in an alpine:edge job
# container on an ordinary runner. Root only installs packages and hands off # container on the privileged `flatpak` runner: crafter-build is the
# to a scratch user: the sysroot is built with apk.static --usermode (which # Crafter.Build Flatpak, and bubblewrap inside the container needs the
# refuses root) and abuild wants a user too. # 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; # Built packages land in /home/build/.local/share/abuild/*/aarch64/fingerprintd*.apk;
# the workflow's publish step uploads them to the Forgejo Alpine registry. # the workflow's publish step uploads them to the Forgejo Alpine registry.
set -eu set -eu
# The musl build of crafter-build (Crafter.Build CI's release-musl job): this # crafter-build is the Crafter.Build Flatpak: it carries its own clang, lld
# container is Alpine, and the glibc launcher cannot run on musl. v2 = SSE4.2 # and libc++, and its SDK has glib, so nothing the daemon's build compiles
# baseline: the CI box is an Intel N5105 (no AVX). Overridable for local # with is installed here. `stable` is the branch Crafter.Build publishes from
# rehearsals (file:// works). # its release tags (`beta` follows every push to its master), so the
CRAFTER_URL=${CRAFTER_URL:-https://forgejo.catcrafts.net/Catcrafts/Crafter.Build/releases/download/latest/crafter-build-linux-x86_64-musl-v2.tar.gz} # 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) 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 <description> <cmd...> retry() { # retry <description> <cmd...>
_desc=$1; shift _desc=$1; shift
for _i in 1 2 3; do for _i in 1 2 3; do
@ -54,18 +38,43 @@ retry() { # retry <description> <cmd...>
return 1 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 # interfaces/Fingerprintd.cppm is the version's single source of truth (same
# derivation as make-bin-tarball.sh) # derivation as make-bin-tarball.sh)
VER=$(sed -n 's/.*Version = "\(.*\)".*/\1/p' "$SRC/interfaces/Fingerprintd.cppm") VER=$(sed -n 's/.*Version = "\(.*\)".*/\1/p' "$SRC/interfaces/Fingerprintd.cppm")
[ -n "$VER" ] || { echo "cannot read Version from interfaces/Fingerprintd.cppm" >&2; exit 1; } [ -n "$VER" ] || { echo "cannot read Version from interfaces/Fingerprintd.cppm" >&2; exit 1; }
echo ">> packaging fingerprintd $VER" echo ">> packaging fingerprintd $VER"
# --- crafter-build: static launcher from the rolling release # --- crafter-build: the Flatpak, under the name the rest of this file uses.
mkdir -p "$HOME/crafter-build" # The build log names the build it ran with.
retry "fetch crafter-build" \ mkdir -p "$HOME/bin"
sh -c "curl -fsSL '$CRAFTER_URL' | tar -xz -C '$HOME/crafter-build'" printf '#!/bin/sh\nexec flatpak run %s "$@"\n' "$CRAFTER_REF" > "$HOME/bin/crafter-build"
PATH="$HOME/crafter-build/bin:$PATH" chmod +x "$HOME/bin/crafter-build"
export CRAFTER_BUILD_HOME="$HOME/crafter-build/share/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) # --- aarch64 Alpine sysroot (unprivileged: apk.static --usermode)
SYSROOT="$HOME/.cache/fingerprintd/sysroot-aarch64-alpine" SYSROOT="$HOME/.cache/fingerprintd/sysroot-aarch64-alpine"

View file

@ -23,9 +23,14 @@ MIRROR="${MIRROR:-https://dl-cdn.alpinelinux.org/alpine/edge}"
ARCH=aarch64 ARCH=aarch64
# gcc is in the list for its crtbeginS.o/libgcc: clang's driver for # 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 # *-alpine-linux-musl links against the GCC runtime it detects under
# <sysroot>/usr/lib/gcc/<triple>/. # <sysroot>/usr/lib/gcc/<triple>/. 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). # 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) work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT trap 'rm -rf "$work"' EXIT
@ -48,6 +53,6 @@ mkdir -p "$SYSROOT"
--arch "$ARCH" --root "$SYSROOT" --initdb --no-scripts --no-cache \ --arch "$ARCH" --root "$SYSROOT" --initdb --no-scripts --no-cache \
--usermode --keys-dir "$work/usr/share/apk/keys/$ARCH" \ --usermode --keys-dir "$work/usr/share/apk/keys/$ARCH" \
-X "$MIRROR/main" -X "$MIRROR/community" \ -X "$MIRROR/main" -X "$MIRROR/community" \
add $PKGS add --upgrade $PKGS
echo ">> done: $SYSROOT" echo ">> done: $SYSROOT"