docs: implementation plan for fingerprint reader
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,302 @@
|
||||
# Framework 13 Fingerprint Reader Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Enable the Framework 13's Goodix fingerprint reader for `sudo` and `gtklock` via `fprintd` + a one-time idempotent PAM-edit script.
|
||||
|
||||
**Architecture:** Three small repo changes plus one user-side runtime activity. Repo: add `fprintd` to `packages.txt`, ship a `scripts/enable-fingerprint.sh` bootstrap that adds `auth sufficient pam_fprintd.so` to two PAM files only if not already present, document the two manual user steps (enroll + run script) in README. Runtime: user runs `fprintd-enroll` once + `sudo bash scripts/enable-fingerprint.sh` once.
|
||||
|
||||
**Tech Stack:** bash, `fprintd`/`libfprint`, PAM, Arch Linux.
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-05-25-fingerprint-reader-design.md`
|
||||
|
||||
**Verification model:** Bash syntax checks for the new script + idempotency dry-run (run script's grep-guards against a copy and confirm they short-circuit). End-to-end functional check (touch reader → unlock) is interactive and live-only — controller verifies after install + manual enroll.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
| File | Role |
|
||||
|---|---|
|
||||
| `packages.txt` | Add `fprintd` line (libfprint pulled as dep). |
|
||||
| `scripts/enable-fingerprint.sh` | New bash bootstrap. For each of `/etc/pam.d/sudo` and `/etc/pam.d/gtklock`: backup + insert `auth sufficient pam_fprintd.so` before first existing `auth ` line if not already present. Requires sudo. |
|
||||
| `README.md` | Append `### One-time fingerprint setup` subsection under `## Setup`. |
|
||||
|
||||
No new niri config, no install.sh changes (the bootstrap is user-triggered like `enable-hibernation.sh`).
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Add `fprintd` to `packages.txt`
|
||||
|
||||
After this task, fresh installs get `fprintd` (and `libfprint` as transitive dependency). The reader is unusable until enrollment + PAM wiring (later tasks).
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages.txt` (add one line)
|
||||
|
||||
- [ ] **Step 1: Add the package**
|
||||
|
||||
The list isn't strictly alphabetical. Insert `fprintd` between two logical neighbours — after `polkit-gnome` (line 16) is a sensible "auth/security" cluster slot:
|
||||
|
||||
Current lines 16-17:
|
||||
```
|
||||
polkit-gnome
|
||||
networkmanager
|
||||
```
|
||||
|
||||
Becomes:
|
||||
```
|
||||
polkit-gnome
|
||||
fprintd
|
||||
networkmanager
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify the entry is present exactly once**
|
||||
|
||||
```bash
|
||||
grep -c "^fprintd$" packages.txt
|
||||
```
|
||||
|
||||
Expected: `1`.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add packages.txt
|
||||
git commit -m "packages: add fprintd for fingerprint reader support"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Write the bootstrap script
|
||||
|
||||
After this task, `scripts/enable-fingerprint.sh` exists. Running it as root adds `auth sufficient pam_fprintd.so` to the two target PAM files. Re-runs are no-ops.
|
||||
|
||||
**Files:**
|
||||
- Create: `scripts/enable-fingerprint.sh`
|
||||
|
||||
- [ ] **Step 1: Create the script**
|
||||
|
||||
Write `scripts/enable-fingerprint.sh` with this exact content:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# enable-fingerprint.sh — wire pam_fprintd.so into sudo + gtklock auth stacks.
|
||||
# Idempotent: re-runs are no-ops once the line is present. Run with sudo.
|
||||
# Prerequisite: fprintd installed and at least one finger enrolled
|
||||
# (run `fprintd-enroll` as your user first).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Run as root: sudo bash $0" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v fprintd-enroll >/dev/null 2>&1; then
|
||||
echo "ERROR: fprintd is not installed. Run install.sh first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PAM_LINE="auth sufficient pam_fprintd.so"
|
||||
TARGETS=(/etc/pam.d/sudo /etc/pam.d/gtklock)
|
||||
|
||||
for f in "${TARGETS[@]}"; do
|
||||
if [ ! -f "$f" ]; then
|
||||
echo "ERROR: $f does not exist. Is the corresponding package installed?" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if grep -q "pam_fprintd" "$f"; then
|
||||
echo "$f: pam_fprintd already present — skipping"
|
||||
continue
|
||||
fi
|
||||
|
||||
backup="$f.bak.$(date +%s)"
|
||||
cp -a "$f" "$backup"
|
||||
echo "$f: backed up to $backup"
|
||||
|
||||
# Insert PAM_LINE on a new line directly before the first `auth ` directive.
|
||||
awk -v line="$PAM_LINE" '
|
||||
!inserted && /^auth[[:space:]]/ { print line; inserted = 1 }
|
||||
{ print }
|
||||
' "$f" > "$f.new"
|
||||
|
||||
if ! grep -q "pam_fprintd" "$f.new"; then
|
||||
echo "ERROR: failed to insert PAM line into $f (no auth directive found?). Restoring backup." >&2
|
||||
rm -f "$f.new"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mv "$f.new" "$f"
|
||||
chmod --reference="$backup" "$f"
|
||||
echo "$f: inserted '$PAM_LINE'"
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Done. Test with: sudo true (should prompt for fingerprint)"
|
||||
echo "Or lock the screen (Mod+Shift+E) and touch the reader."
|
||||
echo
|
||||
echo "To revert: restore the .bak.<ts> files in /etc/pam.d/, or:"
|
||||
echo " sudo sed -i '/pam_fprintd/d' ${TARGETS[*]}"
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Make it executable**
|
||||
|
||||
```bash
|
||||
chmod +x scripts/enable-fingerprint.sh
|
||||
ls -la scripts/enable-fingerprint.sh
|
||||
```
|
||||
|
||||
Expected: `-rwxr-xr-x` mode.
|
||||
|
||||
- [ ] **Step 3: Syntax-check**
|
||||
|
||||
```bash
|
||||
bash -n scripts/enable-fingerprint.sh && echo "syntax ok"
|
||||
```
|
||||
|
||||
Expected: `syntax ok`, exit 0.
|
||||
|
||||
- [ ] **Step 4: Dry-run the EUID guard (no sudo, no side effects)**
|
||||
|
||||
```bash
|
||||
bash scripts/enable-fingerprint.sh 2>&1
|
||||
echo "exit=$?"
|
||||
```
|
||||
|
||||
Expected:
|
||||
```
|
||||
Run as root: sudo bash scripts/enable-fingerprint.sh
|
||||
exit=1
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Dry-run the idempotency / awk logic against a fixture (no sudo)**
|
||||
|
||||
Create a fake PAM file in /tmp with an `auth` line, run the insertion logic by hand, and verify the line lands in the right place:
|
||||
|
||||
```bash
|
||||
cat > /tmp/fake-pam <<'EOF'
|
||||
#%PAM-1.0
|
||||
auth include system-auth
|
||||
account include system-auth
|
||||
password include system-auth
|
||||
session include system-auth
|
||||
EOF
|
||||
|
||||
# Pull the awk insertion block out and run it
|
||||
awk -v line="auth sufficient pam_fprintd.so" '
|
||||
!inserted && /^auth[[:space:]]/ { print line; inserted = 1 }
|
||||
{ print }
|
||||
' /tmp/fake-pam
|
||||
|
||||
# Expected output: the new auth line appears on line 2, original 4 lines below it.
|
||||
```
|
||||
|
||||
Then verify the idempotency grep works — running on a fixture that already has `pam_fprintd` should not duplicate:
|
||||
|
||||
```bash
|
||||
echo "auth sufficient pam_fprintd.so" >> /tmp/fake-pam
|
||||
grep -q "pam_fprintd" /tmp/fake-pam && echo "idempotency guard triggers correctly"
|
||||
```
|
||||
|
||||
Expected: `idempotency guard triggers correctly`.
|
||||
|
||||
Cleanup:
|
||||
```bash
|
||||
rm /tmp/fake-pam
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add scripts/enable-fingerprint.sh
|
||||
git commit -m "scripts: add enable-fingerprint.sh (one-time PAM bootstrap)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 3: README — `### One-time fingerprint setup` subsection
|
||||
|
||||
After this task, the README documents all three user-side steps (install run is implicit via packages.txt; enrollment + bootstrap script are explicit).
|
||||
|
||||
**Files:**
|
||||
- Modify: `README.md` (append a subsection after the existing "One-time NetworkManager activation")
|
||||
|
||||
- [ ] **Step 1: Append the new subsection**
|
||||
|
||||
Open `README.md`. The last subsection is `### One-time NetworkManager activation`. After its closing line, append:
|
||||
|
||||
````markdown
|
||||
|
||||
### One-time fingerprint setup
|
||||
|
||||
After `install.sh` installs `fprintd`, enroll a finger and wire it into the sudo + lockscreen auth stacks:
|
||||
|
||||
```bash
|
||||
fprintd-enroll # interactive; touch the reader as prompted
|
||||
sudo bash scripts/enable-fingerprint.sh # idempotent — safe to re-run
|
||||
```
|
||||
|
||||
Test with `sudo true` (should prompt for fingerprint) or `Mod+Shift+E` to lock + touch the reader.
|
||||
|
||||
To enroll additional fingers: `fprintd-enroll -f left-thumb` (or `right-thumb`, `left-index`, etc.).
|
||||
````
|
||||
|
||||
(The outer fence above is four backticks because the block contains a triple-backtick code block — paste the inner content verbatim, no enclosing 4-backtick fence.)
|
||||
|
||||
- [ ] **Step 2: Verify the section was added**
|
||||
|
||||
```bash
|
||||
grep -A 8 "One-time fingerprint setup" README.md
|
||||
```
|
||||
|
||||
Expected: the new subsection prints, including both the `fprintd-enroll` and `sudo bash scripts/enable-fingerprint.sh` lines.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add README.md
|
||||
git commit -m "docs: document one-time fingerprint setup"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Final verification (controller, post-merge)
|
||||
|
||||
After all three task commits land:
|
||||
|
||||
- [ ] **Install fprintd**
|
||||
|
||||
```bash
|
||||
bash install.sh
|
||||
# OR direct: sudo pacman -S --needed fprintd
|
||||
which fprintd-enroll fprintd-verify fprintd-list
|
||||
```
|
||||
|
||||
- [ ] **Enroll a finger** (interactive, ~10 s):
|
||||
```bash
|
||||
fprintd-enroll
|
||||
# Touch the reader as prompted (~5 swipes)
|
||||
fprintd-list "$USER"
|
||||
# Expected: lists enrolled fingers
|
||||
```
|
||||
|
||||
- [ ] **Run the bootstrap script**
|
||||
|
||||
```bash
|
||||
sudo bash scripts/enable-fingerprint.sh
|
||||
```
|
||||
|
||||
Expected output mentions backing up `/etc/pam.d/sudo` and `/etc/pam.d/gtklock` and inserting the line. Re-running it should print "pam_fprintd already present — skipping" for both.
|
||||
|
||||
Confirm:
|
||||
```bash
|
||||
grep "pam_fprintd" /etc/pam.d/sudo /etc/pam.d/gtklock
|
||||
```
|
||||
Expected: both files show the line near the top.
|
||||
|
||||
- [ ] **Functional tests:**
|
||||
|
||||
1. `sudo true` — should prompt for fingerprint; touch reader → no password requested. (Password fallback still works if you ignore the prompt or press Enter.)
|
||||
2. `Mod+Shift+E` → lock screen → touch reader → unlocks without typing password.
|
||||
3. `fprintd-verify` — touch reader → "verify-match" exits 0.
|
||||
Reference in New Issue
Block a user