Closes the dns2 evidence finding (2026-04-30) where the source-install
path reached StateDegraded with three named assertion failures:
systemd_execstart_paths_ok (8 missing destinations)
systemd_payload_inventory_ok (6 unknown nftban-owned references)
panel_survival_ok (LoadPanelConfig directadmin not found)
Per the operator-locked PR26.5 scope (`project_pr26_5_locked.md`):
- source-install payload completeness ONLY
- no takeover/CSF binary handling (PR26.6)
- no cPanel/Plesk adapters (PR26.7+)
internal/installer/payload/payload.go (buildEntries):
- new shell-payload entries:
cli/lib/nftban/exporters/*.sh -> /usr/lib/nftban/exporters/
cli/lib/nftban/cron/*.sh -> /usr/lib/nftban/cron/
scripts/*.sh -> /usr/lib/nftban/scripts/
install/helpers/*.sh -> /usr/lib/nftban/helpers/ (joins
the existing cli/lib/nftban/helpers/ source — both flatten into
the same destination)
- new `panels` category staging the canonical panel conf.d files:
etc/nftban/conf.d/panels/<name>/main.conf -> /etc/nftban/conf.d/panels/<name>/main.conf
for all 8 first-class panels (directadmin, cpanel, plesk,
cyberpanel, cwp, interworx, vesta, generic).
policyConfigNoReplace: operator edits preserved on upgrade.
install/systemd/ (retired unit files removed):
- nftban-api.service, nftban-ui.service, nftban-ui-auth.service,
nftban-ui-auth.socket — all reference Go binaries the project
no longer builds. Operator-confirmed retired in v1.100.1b.A
(GOTH PR-D4 stage 1) and already removed by packaging on RPM/DEB
upgrade. Source-install path now matches that contract: not
shipped, not in source tree, not staged.
internal/installer/validate/assertions.go (defaultInventoryPaths):
- extended with shell-payload destinations referenced by remaining
units, so systemd_payload_inventory_ok no longer false-flags
legitimate staged shell payload as "unknown".
internal/installer/payload/payload_test.go (3 new tests):
- TestStageAll_AllUnitNftbanOwnedExecStartPathsStaged_PR26_5:
walks every install/systemd/*.service, parses every
ExecStart/Pre/Post path, asserts every nftban-owned
destination is in mock.WrittenFiles after StageAll.
- TestStageAll_AllPanelConfDStaged_PR26_5:
asserts every shipped panel's main.conf is staged at
/etc/nftban/conf.d/panels/<name>/main.conf.
- TestStageAll_PR26_5_NewShellCategoriesStaged:
regression guard pinning the four specific destinations
that failed on dns2 (exporter, cron, scripts, helpers).
Lab proof (lab2, Ubuntu 24.04, go1.22.2):
go vet payload/... validate/... cmd/nftban-installer/... clean
go test -v ./internal/installer/payload/... 3 new tests PASS, full file PASS
go test ./... 66 packages, 0 FAIL
Hard exclusions preserved: no takeover-side cleanup, no CSF binary
handling, no cPanel/Plesk adapters, no shell decommission, no
parser rewrite, no restore changes, no firewall mutation, no
destructive dns2 retry from this branch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Closes the dns2 install-evidence finding (2026-04-30) where
--mode=install --sourcereachedStateDegradedwith three named assertion failures:systemd_execstart_paths_oksystemd_payload_inventory_okdefaultInventoryPathspanel_survival_okLoadPanelConfig("directadmin")couldn't find conf.dpanelsstaging categoryPR26.5 makes the source-install path complete: every shipped systemd unit's ExecStart resolves to a staged file, and every panel's canonical conf.d is staged for
panel_loader.LoadPanelConfig.Scope (operator-locked)
What landed
internal/installer/payload/payload.go::buildEntriescli/lib/nftban/exporters/*.sh→/usr/lib/nftban/exporters/cli/lib/nftban/cron/*.sh→/usr/lib/nftban/cron/scripts/*.sh→/usr/lib/nftban/scripts/install/helpers/*.sh→/usr/lib/nftban/helpers/(joins existingcli/lib/nftban/helpers/)panelscategory — 8 single-file entries (directadmin, cpanel, plesk, cyberpanel, cwp, interworx, vesta, generic) →/etc/nftban/conf.d/panels/<name>/main.conf(policyConfigNoReplace).install/systemd/(retired unit files removed)nftban-api.service,nftban-ui.service,nftban-ui-auth.service,nftban-ui-auth.socket— all reference binaries the project no longer builds (retired v1.100.1b.A GOTH PR-D4 stage 1, already removed by packaging on RPM/DEB upgrade). Source-install path now matches.internal/installer/validate/assertions.go::defaultInventoryPathssystemd_payload_inventory_okdoesn't false-flag legitimate staged shell payload as "unknown".Tests (3 new in
payload_test.go)TestStageAll_AllUnitNftbanOwnedExecStartPathsStaged_PR26_5— walks every install/systemd/*.service, parses ExecStart, asserts every nftban-owned destination is inmock.WrittenFiles. The dns2 reproducer.TestStageAll_AllPanelConfDStaged_PR26_5— asserts every shipped panel's main.conf reaches/etc/nftban/conf.d/panels/<name>/main.conf.TestStageAll_PR26_5_NewShellCategoriesStaged— regression guard pinning the four destinations that failed on dns2 (exporter, cron, scripts, helpers).Lab proof (lab2, Ubuntu 24.04, go1.22.2)
Branch base:
bfe6eac9(origin/main, post-PR26.4).Staging output during the integration tests:
Reproducer
The TestStageAll_AllUnitNftbanOwnedExecStartPathsStaged_PR26_5 test would have caught the dns2 finding. Pre-PR26.5, that test fails with:
Post-PR26.5, all green.
Test plan
go vetclean on lab2go test ./internal/installer/payload/...green on lab2 (3 new PR26.5 tests + full file)go test ./...green on lab2 (66 packages, 0 fail)Followups (NOT in this PR)
TAKEOVER-PRESERVES-NON-NFTBAN-AUTHORITY-001) — covers nft safety tables, external firewall binaries, config trees, recovery pathsservices.EnablePanellegacy "non-fatal" warning alignment (PANEL-ENABLE-LEGACY-WARNING-001)🤖 Generated with Claude Code