Files
dotfiles/docs/superpowers/plans/2026-05-25-fingerprint-reader.md
funman300 ede1399853 docs: implementation plan for fingerprint reader
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 10:30:08 -07:00

9.0 KiB

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
grep -c "^fprintd$" packages.txt

Expected: 1.

  • Step 3: Commit
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:

#!/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
chmod +x scripts/enable-fingerprint.sh
ls -la scripts/enable-fingerprint.sh

Expected: -rwxr-xr-x mode.

  • Step 3: Syntax-check
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 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:

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:

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:

rm /tmp/fake-pam
  • Step 6: Commit
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:


### 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
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
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 install.sh
# OR direct: sudo pacman -S --needed fprintd
which fprintd-enroll fprintd-verify fprintd-list
  • Enroll a finger (interactive, ~10 s):
fprintd-enroll
# Touch the reader as prompted (~5 swipes)
fprintd-list "$USER"
# Expected: lists enrolled fingers
  • Run the bootstrap script
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:

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.