30 KiB
Phase 13: Debian Packaging Finalization — Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use
superpowers:executing-plansto implement this plan task-by-task.
Goal: Turn the Phase 0 packaging skeleton into apt-installable, lintian-clean, piuparts-passing debs that survive install → reinstall → remove → purge cycles cleanly. Wire pam-auth-update --package registration into the install/remove hooks and seed an initial policy from a debconf prompt at first install.
Architecture:
- PAM profile lifecycle.
authforge-pam.postinstrunspam-auth-update --packageonconfigure;authforge-pam.prermrunspam-auth-update --package --remove authforgeonremove/deconfigure. The deb ships a baseline pam-configs profile (/usr/share/pam-configs/authforge) that only enforces the pending-flag backstop —Default: no, no required methods. The daemon overwrites this file at runtime viapolicy_apply::applyonce an admin sets a real policy. This guarantees a freshly-installed system never locks anyone out, andpam-auth-update --removereverses cleanly. - Purge purges data.
authforge-daemon.postrmremoves/etc/authforge/and/var/lib/authforge/only onpurge.remove(without purge) leaves them, matching standard Debian policy expectations. - Debconf-seeded policy. The metapackage ships
debian/authforge.config(debconf prompt: None / Optional-everywhere / Required-for-sudo) anddebian/authforge.templates(the prompt definitions). Theauthforge-daemon.postinstreads the debconf answer on first install only ([ -z "$2" ]) and writes/etc/authforge/policy.d/00-debconf.confaccordingly. Subsequent upgrades leave the policy alone — admin-edited config wins. - Test matrix. Targeting Ubuntu 24.04 (libadwaita 1.5+ requirement rules out 22.04). Manual smoke recipe documented in
debian/PIUPARTS.md. CI gets adpkg-buildpackage -us -uc -bjob and alintian -ijob; piuparts stays manual until Phase 14 PPA build.
Tech Stack:
debhelper-compat = 13(already pinned indebian/control).pam-auth-updatefromlibpam-runtime(already a Depends of authforge-pam).dh_installdebconffrom debhelper for debconf wiring.lintian,piupartsfor QA — installed locally, not in CI sandbox.dpkg-buildpackage -us -uc -b(no GPG signing, binary-only build).
Reference:
- Master plan: 2026-04-26-authforge-implementation.md § Phase 13 (line 1390).
- Existing scaffolding: debian/control, debian/rules, debian/authforge-daemon.postinst.
- PAM profile ground-truth:
/usr/share/pam-configs/<example>on any Debian/Ubuntu host withlibpam-modulesinstalled (e.g./usr/share/pam-configs/unix,/usr/share/pam-configs/cap). - Debconf manual:
man 7 debconf-devel(in packagedebconf-doc). - debhelper(7), dh_installpam(1), dh_installdebconf(1).
Conventions
- One logical change per commit. Conventional prefixes scoped to
pkg. - Always commit with
--no-gpg-sign. - TDD does not apply directly to shell hooks. Each task instead verifies via
dpkg-buildpackage+lintian -i+ (where possible) script linting (shellcheck). - The phase produces no Rust code —
cargo test --workspacecontinues to pass on every commit (it's a no-op for these changes), and CI's existing test stage is unaffected. - After every commit: rebuild with
dpkg-buildpackage -us -uc -bfrom a clean tree (ordh clean && dpkg-buildpackage -us -uc -b) to confirm the package still builds.
Task 1: Baseline pam-configs profile
Files:
- Create:
debian/authforge.pam-config - Modify:
debian/rules— add an install line for the new file.
Background: pam-auth-update --package consults /usr/share/pam-configs/<name> for the profile to enable. We need to ship a baseline file so the install hook in Task 2 has something to enable. The daemon's policy_apply::render_profile will overwrite this file at runtime once an admin sets a real policy; the baseline only enforces the pending-flag backstop and otherwise does nothing.
Step 1: Author the baseline profile
# debian/authforge.pam-config
Name: AuthForge first-login backstop
Default: no
Priority: 256
Auth-Type: Primary
Auth:
[success=ok default=die] pam_authforge_pending.so
Auth-Initial:
[success=ok default=die] pam_authforge_pending.so
Account-Type: Primary
Account:
sufficient pam_unix.so
Account-Initial:
sufficient pam_unix.so
The Priority: 256 puts AuthForge before stock primary auth (pam_unix is 192) so the pending-flag backstop runs first. Default: no means the profile is shipped but disabled by default — admins opt in via the debconf prompt or a later policy-set.
(Auth-Initial is required for the unconfigured-pam-runtime path; Account-Type: Primary + Account: sufficient pam_unix.so is a no-op pass-through that satisfies pam-auth-update's requirement that every primary profile have an Account block.)
Step 2: Install via debian/rules
In debian/rules override_dh_auto_install, after the # PAM comment block, add:
# Baseline pam-configs profile for pam-auth-update; daemon overwrites
# /usr/share/pam-configs/authforge at runtime when a real policy lands.
install -D -m 0644 debian/authforge.pam-config \
debian/authforge-pam/usr/share/pam-configs/authforge
Step 3: Build to verify
Run: dh clean && dpkg-buildpackage -us -uc -b 2>&1 | tail -20
Expected: no errors. The new file lands in debian/authforge-pam/usr/share/pam-configs/authforge (visible via find debian/authforge-pam/).
Step 4: Commit
git add debian/authforge.pam-config debian/rules
git commit --no-gpg-sign -m "feat(pkg): ship baseline pam-configs profile (Default: no, pending-flag only)"
Task 2: authforge-pam.postinst — register via pam-auth-update
Files:
- Create:
debian/authforge-pam.postinst
Step 1: Write the postinst
#!/bin/sh
# debian/authforge-pam.postinst
# Registers /usr/share/pam-configs/authforge via pam-auth-update on first
# install and reconfigure events. The profile ships Default: no, so this
# is a no-op for fresh installs — the metapackage's debconf flow flips it
# on if the admin opted in. Idempotent: safe to call repeatedly.
set -e
case "$1" in
configure)
if [ -x /usr/sbin/pam-auth-update ]; then
pam-auth-update --package
fi
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
#DEBHELPER#
exit 0
Step 2: Make executable + lint
chmod +x debian/authforge-pam.postinst
shellcheck debian/authforge-pam.postinst
Expected: shellcheck clean (or skip the run with a one-line note in the commit message if shellcheck isn't installed in the dev sandbox).
Step 3: Build to verify
Run: dh clean && dpkg-buildpackage -us -uc -b 2>&1 | tail -10
Expected: clean.
Step 4: Commit
git add debian/authforge-pam.postinst
git commit --no-gpg-sign -m "feat(pkg): authforge-pam postinst calls pam-auth-update --package"
Task 3: authforge-pam.prerm — deregister cleanly
Files:
- Create:
debian/authforge-pam.prerm
Background: Removing the package without pam-auth-update --remove would leave /etc/pam.d/common-* referencing a profile whose backing files are about to disappear, which would break authentication for everyone on the box. The --remove authforge invocation tells pam-auth-update to take our profile out of the rendered stack first.
Step 1: Write the prerm
#!/bin/sh
# debian/authforge-pam.prerm
# Deregisters the authforge profile from /etc/pam.d/common-* before
# the .so disappears. Ordering matters: prerm runs before any files are
# removed, so /usr/share/pam-configs/authforge is still present here for
# pam-auth-update to inspect.
set -e
case "$1" in
remove|deconfigure)
if [ -x /usr/sbin/pam-auth-update ]; then
pam-auth-update --package --remove authforge
fi
;;
upgrade|failed-upgrade)
;;
*)
echo "prerm called with unknown argument \`$1'" >&2
exit 1
;;
esac
#DEBHELPER#
exit 0
Step 2: Make executable + lint + build
chmod +x debian/authforge-pam.prerm
shellcheck debian/authforge-pam.prerm
dh clean && dpkg-buildpackage -us -uc -b 2>&1 | tail -10
Expected: clean.
Step 3: Commit
git add debian/authforge-pam.prerm
git commit --no-gpg-sign -m "feat(pkg): authforge-pam prerm calls pam-auth-update --remove on uninstall"
Task 4: authforge-daemon.postrm — purge cleanup
Files:
- Create:
debian/authforge-daemon.postrm
Background: On purge (not plain remove) we delete /etc/authforge/ (policy.d directory; admin-edited config) and /var/lib/authforge/ (pending flags, recovery codes, TOTP secrets, userdb). Plain remove leaves these so an upgrade-via-remove-then-install round-trip preserves admin state.
Step 1: Write the postrm
#!/bin/sh
# debian/authforge-daemon.postrm
# Cleans up state directories on purge. /etc/authforge/ holds policy
# config; /var/lib/authforge/ holds runtime state (pending flags,
# recovery hashes, TOTP secrets, userdb). Both are intentionally
# preserved on plain remove — admin state survives a remove/install
# round-trip, which matches Debian Policy §6.6 "Removing the package".
set -e
case "$1" in
purge)
rm -rf /etc/authforge
rm -rf /var/lib/authforge
;;
remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
;;
*)
echo "postrm called with unknown argument \`$1'" >&2
exit 1
;;
esac
#DEBHELPER#
exit 0
Step 2: Make executable + lint + build
chmod +x debian/authforge-daemon.postrm
shellcheck debian/authforge-daemon.postrm
dh clean && dpkg-buildpackage -us -uc -b 2>&1 | tail -10
Expected: clean.
Step 3: Commit
git add debian/authforge-daemon.postrm
git commit --no-gpg-sign -m "feat(pkg): authforge-daemon postrm purges /etc/authforge and /var/lib/authforge"
Task 5: Debconf templates + config script
Files:
- Create:
debian/authforge.templates - Create:
debian/authforge.config - Modify:
debian/control— adddebconf | debconf-2.0to the metapackage'sPre-Depends.
Background: Debconf prompts run before postinst, so the answer is available when the postinst writes the seed policy in Task 6. The prompt is on the metapackage (authforge), not authforge-daemon, because the metapackage is what users apt install — installing authforge-daemon directly is an advanced path that skips the prompt and gets the safe Default-no profile.
Step 1: Author the templates
# debian/authforge.templates
Template: authforge/initial-policy
Type: select
Default: None
Choices: None, Optional everywhere, Required for sudo
Description: Initial AuthForge policy:
AuthForge can write a starter policy at install time. Choose:
.
None — install only, no MFA enforcement (admins configure later).
Optional everywhere — every PAM stack offers FIDO2/TOTP but doesn't require it.
Required for sudo — sudo prompts for FIDO2 or recovery code in addition to the password.
.
You can change this any time later via the AuthForge GUI or `authforgectl policy set`.
Step 2: Author the config script
#!/bin/sh
# debian/authforge.config
# Asks the admin for an initial policy. Postinst (Task 6) reads the
# answer and writes /etc/authforge/policy.d/00-debconf.conf on first
# install only. Re-runs (e.g. `dpkg-reconfigure authforge`) re-prompt
# but do not overwrite an existing seed file unless the admin chose to.
set -e
. /usr/share/debconf/confmodule
db_input medium authforge/initial-policy || true
db_go || true
exit 0
Step 3: Make config executable + add Pre-Depends
chmod +x debian/authforge.config
In debian/control, the Package: authforge paragraph, add a Pre-Depends: line:
Pre-Depends: debconf (>= 0.5) | debconf-2.0
(Place it between Architecture: any and the existing Depends: line.)
Step 4: Lint + build
shellcheck debian/authforge.config
dh clean && dpkg-buildpackage -us -uc -b 2>&1 | tail -10
Expected: clean. The build output now mentions dh_installdebconf consuming the templates.
Step 5: Commit
git add debian/authforge.templates debian/authforge.config debian/control
git commit --no-gpg-sign -m "feat(pkg): debconf prompt for initial AuthForge policy on metapackage install"
Task 6: Postinst seeds policy from debconf answer
Files:
- Create:
debian/authforge.postinst
Background: The metapackage authforge has no binaries of its own, but it gets a postinst because that's the only place we can read the debconf answer with the metapackage's own confmodule context. The postinst writes /etc/authforge/policy.d/00-debconf.conf only on first install ([ -z "$2" ] — no previously-configured version) and only if the admin picked something other than "None". Subsequent upgrades and reconfigures leave any existing file alone — admin edits win.
Step 1: Author the postinst
#!/bin/sh
# debian/authforge.postinst
# Reads the debconf answer and writes /etc/authforge/policy.d/00-debconf.conf
# exactly once, on first install. Subsequent upgrades/reconfigures don't
# touch the file — admin edits via authforgectl/GUI win.
set -e
. /usr/share/debconf/confmodule
POLICY_DIR=/etc/authforge/policy.d
SEED_FILE=$POLICY_DIR/00-debconf.conf
case "$1" in
configure)
# Only seed on first install: $2 is empty when no previous version
# was configured. Skip if the seed file already exists (admin may
# have written one manually before apt configured the metapackage).
if [ -z "$2" ] && [ ! -e "$SEED_FILE" ]; then
db_get authforge/initial-policy || true
mkdir -p "$POLICY_DIR"
case "$RET" in
"None")
: # no seed — leave dir empty
;;
"Optional everywhere")
cat > "$SEED_FILE" <<'EOF'
# /etc/authforge/policy.d/00-debconf.conf
# Seeded by debconf on first install. Edit via `authforgectl policy set`
# or the AuthForge GUI; admin edits override on next daemon reload.
[stacks.gdm-password]
mode = "optional"
methods = ["fido2"]
[stacks.sudo]
mode = "optional"
methods = ["fido2", "totp"]
[stacks.sshd]
mode = "optional"
methods = ["fido2"]
EOF
;;
"Required for sudo")
cat > "$SEED_FILE" <<'EOF'
# /etc/authforge/policy.d/00-debconf.conf
# Seeded by debconf on first install. Edit via `authforgectl policy set`
# or the AuthForge GUI; admin edits override on next daemon reload.
[stacks.sudo]
mode = "required"
methods = ["fido2", "totp"]
EOF
;;
esac
fi
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
#DEBHELPER#
exit 0
Step 2: Make executable + lint + build
chmod +x debian/authforge.postinst
shellcheck debian/authforge.postinst
dh clean && dpkg-buildpackage -us -uc -b 2>&1 | tail -10
Expected: clean.
Step 3: Verify the seed file format matches daemon/src/storage/policy.rs
The policy parser is in daemon/src/storage/policy.rs. Skim it (look for the TOML deserialize struct) to confirm the seed file's shape — [stacks.<name>] tables with mode (string) + methods (array). If the parser shape diverges (e.g. expects [<name>] directly, or a different field for methods), update the seed snippet before committing.
Step 4: Commit
git add debian/authforge.postinst
git commit --no-gpg-sign -m "feat(pkg): metapackage postinst seeds /etc/authforge/policy.d/00-debconf.conf from debconf answer"
Task 7: lintian sweep + overrides
Files:
- Possibly:
debian/source/lintian-overrides - Possibly:
debian/<pkg>.lintian-overrides(per-binary-package overrides)
Background: lintian flags packaging deviations. Some are real bugs we'll fix; others are intentional (e.g., embedded-library for vendored Rust crates) and need explicit overrides with rationale comments.
Step 1: Build + run lintian
dh clean && dpkg-buildpackage -us -uc -b
lintian -i ../authforge_*.deb ../authforge-*.deb 2>&1 | tee /tmp/lintian.txt
-i includes verbose explanations. Capture the output to /tmp/lintian.txt for review.
Step 2: Categorize + fix or override
For each W: (warning) or E: (error) lintian reports:
- Real bug → fix at the source (e.g. missing manpage → write one; postinst-uses-tmp → use
mktemp). - Intentional deviation → add to a
lintian-overridesfile with a comment explaining why. Common cases for this project:embedded-libraryon the daemon binary — Rust statically links its deps; this is normal.extended-description-is-probably-too-shortif any package's Description field is one-line — fine for now, possibly extend later.binary-without-manpage— defer manpages to Phase 18 (user docs); add an override withmanpages-deferred-to-phase-18.executable-not-elf-or-scripton the GTK desktop file installer scripts (n/a here).
Override file format example:
# debian/source/lintian-overrides
# Rust binaries statically link the cargo dependency tree; this is
# the standard Cargo-built Debian package shape, not an embedded
# library bug. Same exception used by upstream rustc, cargo, ripgrep,
# fd-find, etc.
authforge-daemon: embedded-library
authforge-cli: embedded-library
authforge-gui: embedded-library
# Manpages are deferred to Phase 18 (user docs) — we want to ship 0.1.0
# without them rather than block on docs. Listed in master plan.
authforge-cli: binary-without-manpage usr/bin/authforgectl
authforge-gui: binary-without-manpage usr/bin/authforge
authforge-daemon: binary-without-manpage usr/sbin/authforged
(The exact set of overrides depends on what lintian actually reports — start with the categorized list from Step 1 and add only what's needed.)
Step 3: Re-run lintian to confirm zero W/E remain
dh clean && dpkg-buildpackage -us -uc -b
lintian ../authforge_*.deb ../authforge-*.deb
Expected: no output (silent = clean) or only I: info-level notes.
Step 4: Commit
git add debian/source/lintian-overrides debian/*.lintian-overrides
# plus any source-level fixes you made
git commit --no-gpg-sign -m "chore(pkg): lintian overrides for embedded-library + deferred-manpage; fix any real warnings"
(If you only added overrides and made no real fixes, the commit message can drop the "fix any real warnings" suffix.)
Task 8: piuparts test recipe
Files:
- Create:
debian/PIUPARTS.md
Background: piuparts (Package Installation, Upgrading, and Removal Testing Suite) runs install → upgrade → remove → purge → reinstall in a clean chroot. It's heavyweight (downloads a full Ubuntu base) and requires root, so it stays manual until the Phase 14 PPA build picks it up automatically.
Step 1: Author the recipe
# Testing the AuthForge debs with piuparts
piuparts simulates a clean Debian/Ubuntu install in a chroot, runs the
install/upgrade/remove/purge cycle, and verifies no files leak.
This is the strongest manual gate before the Phase 14 PPA build picks
up automated VM-based testing.
## Target
**Ubuntu 24.04 only** for v1. The GUI (`authforge-gui`) hard-requires
libadwaita 1.5+, which Ubuntu 22.04 does not ship. Older targets would
need a 1.5 backport PPA — not worth the matrix complexity for v1.
## Prerequisites
```bash
sudo apt install piuparts debootstrap
piuparts pulls a fresh noble (24.04) chroot on every run; first run
takes ~5 minutes to download base, subsequent runs use the cache at
/var/cache/piuparts/.
Run
From the project root, after a successful dpkg-buildpackage -us -uc -b:
sudo piuparts \
--distribution=noble \
--mirror "http://archive.ubuntu.com/ubuntu noble main universe" \
--arch amd64 \
--warn-on-leftovers-after-purge \
../authforge_*.deb \
../authforge-*.deb
The --mirror line includes universe because libpam-google-authenticator
(Recommends of authforge-daemon) lives there. Without it piuparts can't
resolve the recommends and the test fails before installing AuthForge.
What piuparts exercises
- Install all debs together (metapackage pulls in all binary packages).
- Verify the daemon's systemd unit enables + starts under systemd-in-chroot.
- Verify
pam-auth-update --packageregistered the AuthForge profile (look forauthforgein/etc/pam.d/common-auth). - Reinstall (0.1.0 over 0.1.0) — postinst runs again, must be idempotent.
- Remove —
pam-auth-update --remove authforgeruns in prerm; the binaries disappear;/etc/authforge/and/var/lib/authforge/survive. - Purge — postrm wipes
/etc/authforge/and/var/lib/authforge/; piuparts diffs the chroot against its initial snapshot and reports leftovers.
Pass criteria
- piuparts exit code 0.
- No
FAILlines in the output. - Any
WARN: leftoverlines refer only to systemd/journald state (logs, runtime files in/run/) — not files in/etc/authforge/,/var/lib/authforge/, or/usr/share/pam-configs/. pam-auth-update --list(run inside the chroot mid-test, if you pause it) showsauthforgeafter install and absent after remove — piuparts surfaces this implicitly via its/etc/pam.d/diff.
Known surface gaps
- piuparts can't exercise the GUI or the FIDO2 enrollment flow — those
need a real desktop session, deferred to Phase 14 VM smoke
(
gui/TESTING.md,gui/FIRSTRUN-TESTING.md). - The debconf prompt is non-interactive in piuparts (uses defaults).
To test the "Required for sudo" branch end-to-end, run a manual
install with
DEBCONF_PRIORITY=criticaland check that/etc/authforge/policy.d/00-debconf.confwas written.
**Step 2: Commit**
```bash
git add debian/PIUPARTS.md
git commit --no-gpg-sign -m "docs(pkg): piuparts test recipe — Ubuntu 24.04, install/remove/purge cycle"
Task 9: Master-plan closeout note
Files:
- Modify:
docs/plans/2026-04-26-authforge-implementation.md
Step 1: Flip Phase 13 to ✅ Code complete
The roadmap row at line 43 currently reads:
| 13 | Debian packaging finalization (postinst, debconf, purge) | 4 days | **Spec'd** — sequential tail; needs all binaries built |
Change to:
| 13 | Debian packaging finalization (postinst, debconf, purge) | 4 days | ✅ **Code complete** (yyyy-mm-dd) — `piuparts` smoke deferred to Phase 14 |
(Replace yyyy-mm-dd with the date you commit Task 9.)
Step 2: Append a closeout note
After the existing Phase 11 closeout (currently around line 316):
### Phase 13 closeout notes (yyyy-mm-dd)
Landed via [2026-04-27-phase-13-deb-packaging.md](2026-04-27-phase-13-deb-packaging.md). 9 commits on `main` (no worktree — all changes are in `debian/` and don't touch the workspace's Rust crates).
**Done:**
- `debian/authforge.pam-config` — baseline `pam-auth-update` profile, `Default: no`, pending-flag-only. Daemon's `policy_apply::render_profile` overwrites this file at runtime once a real policy is set.
- `debian/authforge-pam.{postinst,prerm}` — registers via `pam-auth-update --package` on `configure`; deregisters via `--remove authforge` on `remove`/`deconfigure`. Idempotent.
- `debian/authforge-daemon.postrm` — purges `/etc/authforge/` + `/var/lib/authforge/` on `purge`; preserves them on plain `remove` (Debian Policy §6.6).
- `debian/authforge.{templates,config}` — debconf prompt: `None` / `Optional everywhere` / `Required for sudo`. Default `None`. `Pre-Depends: debconf` added to the metapackage.
- `debian/authforge.postinst` — reads the debconf answer; writes `/etc/authforge/policy.d/00-debconf.conf` exactly once on first install. Subsequent upgrades and admin edits win.
- `debian/source/lintian-overrides` + per-binary `lintian-overrides` files — zero W/E from `lintian -i` on the built debs. _(fill in: any real warnings fixed at the source)_
- `debian/PIUPARTS.md` — manual smoke recipe for Ubuntu 24.04. Install → reinstall → remove → purge cycle documented; piuparts run is the Phase 14 gate.
**Plan deviations:** _(fill in)_.
**Test count delta:** 0 (Phase 13 ships no Rust code). Workspace test count remains 112.
**Deferred until Phase 14 PPA + VM smoke:**
- `piuparts` actually run on a clean Ubuntu 24.04 chroot.
- `pam-auth-update --package` actually mutating `/etc/pam.d/common-*` on a real system.
- The "Required for sudo" debconf branch tested end-to-end (sudo prompts for FIDO2 + password).
- `dpkg-buildpackage -us -uc -b` producing five debs that install on a fresh VM.
Step 3: Commit
git add docs/plans/2026-04-26-authforge-implementation.md
git commit --no-gpg-sign -m "docs: phase 13 closeout notes"
Phase 13 Acceptance Gate
Verify before declaring the phase done:
dh clean && dpkg-buildpackage -us -uc -bexits 0; produces 5 binary.debfiles in..(parent dir of project):authforge_*.deb,authforge-cli_*.deb,authforge-daemon_*.deb,authforge-gui_*.deb,authforge-pam_*.deb(plus the optionalauthforge-gnome-integration_*.deb).lintian -i ../authforge_*.deb ../authforge-*.debreports zeroW:/E:(onlyI:informational notes allowed).cargo test --workspacestill passes (112 tests; this phase doesn't touch Rust).cargo fmt --all -- --checkclean.shellcheckon every script indebian/*.{config,postinst,prerm,postrm}(or document why it was skipped).- All four hook scripts (
authforge.config,authforge.postinst,authforge-pam.postinst,authforge-pam.prerm,authforge-daemon.postrm) are mode 0755 / executable. debian/changelogupdated to bump from(0.1.0-1) UNRELEASEDto(0.1.0-1) noble; urgency=medium(Phase 14 will dput this).- Manual
piupartsrun indebian/PIUPARTS.mdis documented but not required for this phase — Phase 14 owns the actual run.
Risks / known unknowns
| Risk | Mitigation |
|---|---|
The baseline debian/authforge.pam-config profile syntax is rejected by pam-auth-update --package in the chroot. |
Cross-check against /usr/share/pam-configs/unix and /usr/share/pam-configs/cap on a 24.04 host before committing — those are the canonical reference profiles. If Auth-Initial: isn't matching the pattern, pam-auth-update --debug shows the parse error. |
Pre-Depends: debconf makes the metapackage unbootstrappable in some Launchpad sbuild configs. |
The exact line `debconf (>= 0.5) |
cat > "$SEED_FILE" <<'EOF' in the metapackage postinst writes a TOML structure that daemon/src/storage/policy.rs rejects. |
Task 6 Step 3 explicitly calls for sanity-checking the parser shape before committing. If the daemon's TOML schema differs, the parser will fail to load the seed and the daemon will boot with an empty policy — recoverable, but ugly. The cheap prevention is a cargo run -p authforge-daemon against a real seed file in a tempdir before final commit. |
lintian raises maintainer-script-empty on hooks that consist of only a case block + #DEBHELPER#. |
Confirmed false-positive — debhelper expands #DEBHELPER# to several lines at build time. If lintian flags it, override with non-empty-after-debhelper-expansion. |
piuparts can't run inside a sandbox (needs root + chroot). |
Documented as Phase 14 gate. CI gets dpkg-buildpackage + lintian only. |
dh clean doesn't fully clean the cargo target dir, slowing rebuilds. |
debian/rules' override_dh_auto_clean already calls cargo clean. If Phase 13 reveals stale target/ artifacts, add an explicit rm -rf target/cargo-home target/release/build before each test build. |
GUI hard-requires libadwaita 1.5; piuparts on a 24.04 chroot has it but headless install of authforge-gui may fail at dh_strip time without a display. |
dh_strip is a static analysis pass — no display server needed. apt install authforge-gui in a headless chroot will install fine; running the binary needs a display, which the piuparts test doesn't do. |
The Auth-Type: Primary baseline profile causes pam-auth-update to elevate AuthForge above pam_unix even with Default: no, breaking login on systems where someone re-runs pam-auth-update interactively. |
Default: no keeps the profile out of the rendered stack regardless of priority. The interactive run would offer the profile but leave it unchecked unless the admin actively selects it. Phase 14 smoke verifies this on a real desktop. |
Execution Handoff
Plan complete and saved to docs/plans/2026-04-27-phase-13-deb-packaging.md. Single-lane, single-agent — no parallelism wins (every task touches debian/, and Tasks 6 + 7 + 8 + 9 each depend on prior tasks for verification).
Two execution options:
-
Subagent-Driven (this session) — Dispatch a fresh subagent per task with code review between tasks. Fast iteration, but the main session carries the package-build context.
-
Parallel Session (separate) — Open a new session; that session uses
superpowers:executing-plansto walk the plan top-to-bottom with checkpoint commits. Frees this session for review work.
Either way, do not start until the dev environment has lintian and (preferably) shellcheck installed:
sudo apt install lintian shellcheck
piuparts is not required for this phase — it's the Phase 14 gate.