DEVICE-002: ARM machine-ID fallback — device-tree model + /etc/machine-id combo, then /proc/cpuinfo Serial (all-zero rejected), before the weak hostname fallback. Hardware-bound IDs on DMI-less devices. DEVICE-001: agent detects device_type (server/desktop/phone/tablet) from /sys signals — system battery (scope=Device peripherals excluded, UPS excluded), DRM connector state, framebuffer min-dimension for phone/tablet split. Reports device_type/device_model/os_distro in registration and system-info paths. SERVER-001: migration 061 — device_type, device_type_manual (operator override, never agent-written), device_model, os_distro on agents. effective_device_type computed into every serialized agent. SERVER-002: PUT /admin/agents/:id/device-type — set/clear override, enum-validated, journaled. WEB-001: device-type icons + fleet filter, device model in list, detail header badge with reclassify dropdown, os_distro surfaced. INSTALL-003: arm64 install path unblocked — helper (required manifest component) now cross-built aarch64-unknown-linux-musl via rust-lld in the server image, signed at boot (helperArches += arm64), listed in the release manifest. Install template already handled uname -m and pacman. Plus in-flight: desktop tray wiring, enrollment page polish, CI workflow updates, RAF session-broker/pacman-scanner docs, native installer scaffold.
2.5 KiB
2.5 KiB
Pacman Scanner
Arch Linux package manager scanning via pacman-contrib's checkupdates.
Component Details
| Property | Value |
|---|---|
| Method | checkupdates --color=never |
| Platform | Linux (Arch, Manjaro, EndeavourOS) |
| Execution time | ~5-15 seconds per scan |
| Output format | pkgname oldver -> newver (one per line) |
| Failure modes | pacman-contrib not installed, network timeout, database lock |
| Root required | No (checkupdates uses a private db copy) |
Implementation
File: agent/internal/scanner/pacman.go
Detection
func (s *PacmanScanner) IsAvailable() bool {
// Requires both pacman and checkupdates (from pacman-contrib)
if _, err := exec.LookPath("checkupdates"); err != nil {
return false
}
_, err := exec.LookPath("pacman")
return err == nil
}
Scanning
Uses DiscoveryRunner (same as apt/dnf) for sandboxed execution:
func (s *PacmanScanner) Scan() ([]client.UpdateReportItem, error) {
runner, err := installer.NewDiscoveryRunner("pacman")
// checkupdates --color=never
result, err := runner.Run(context.Background(), "--color=never")
// Parse "pkgname oldver -> newver" lines
}
Output Parsing
checkupdates output format:
fakeroot 1:1.37.2-1 -> 1:1.37.2-2
linux 6.14.6.arch1-1 -> 6.15.1.arch1-1
The current (installed) version is embedded in the output — no secondary
query needed (unlike dnf which calls rpm -q).
Sudoers
The agent user needs checkupdates in sudoers. This is an unprivileged
discovery command (no mutation). Example:
redflag-agent ALL=(ALL) NOPASSWD: /usr/bin/checkupdates
Known Limitations
- Requires pacman-contrib —
pacmanalone is not sufficient. Thecheckupdatesbinary is in thepacman-contribpackage, which may not be installed by default. - AUR packages not scanned —
checkupdatesonly checks official repos. AUR helpers (yay, paru) have their own update checkers but are not integrated. - No dry-run for mutations — pacman's
--printflag exists but is not used for hash resolution. Artifact hashing goes through the capability gate path when it lands for pacman.
Cross-References
- core/02-architecture-decisions — DiscoveryRunner pattern
- security/05-supply-chain-gate — capability token path (future for pacman)
agent/internal/scanner/detect.go— scanner registration