Compare commits

..
5 changed files with 94 additions and 100 deletions

View file

@ -7,10 +7,9 @@ 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. 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.
# 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.
#
# Release gating is the version: pkgver comes from interfaces/Fingerprintd.cppm,
# the registry answers 409 for an already-published version, and the publish
@ -28,7 +27,7 @@ on:
jobs:
package:
runs-on: flatpak
runs-on: arch-latest
container:
image: alpine:edge
timeout-minutes: 90

View file

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

107
README.md
View file

@ -8,6 +8,24 @@ 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
```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-list user - #0: right-index-finger
@ -38,62 +56,55 @@ 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
# <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
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
GPL-3.0-only - see [LICENSE](LICENSE).
## Funding
https://catcrafts.net/shop/donation - see [FUNDING](FUNDING.yml)
GPL-3.0-only, see LICENSE.
## Copyright
Copyright (C) 2026 Catcrafts® [catcrafts.net](https://catcrafts.net/)
Copyright (C) 2026 Catcrafts® catcrafts.net

View file

@ -5,28 +5,44 @@
# (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 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.
# 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.
#
# 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
# 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}
# 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}
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...>
_desc=$1; shift
for _i in 1 2 3; do
@ -38,43 +54,18 @@ retry() { # retry <description> <cmd...>
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: 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
# --- 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"
# --- aarch64 Alpine sysroot (unprivileged: apk.static --usermode)
SYSROOT="$HOME/.cache/fingerprintd/sysroot-aarch64-alpine"

View file

@ -23,14 +23,9 @@ 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
# <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.
# <sysroot>/usr/lib/gcc/<triple>/.
# glib-dev is for the GDBus bus layer (net.reactivated.Fprint).
PKGS="musl-dev libc++ libc++-dev llvm-runtimes glib-dev compiler-rt llvm-libunwind-dev gcc"
PKGS="musl-dev libc++ libc++-dev glib-dev compiler-rt llvm-libunwind-dev gcc"
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
@ -53,6 +48,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 --upgrade $PKGS
add $PKGS
echo ">> done: $SYSROOT"