9 KiB
Per-Distro Installation Design
Goal
Implement actual AppImage installation for aim across a broad Linux distro set, while preserving a single reusable install engine in aim-core, keeping aim-cli thin, and respecting distro policy constraints instead of fighting them.
Agreed Product Shape
Supported distro families
- Debian
- Fedora
- Arch
- openSUSE
- Alpine
- Nix
- Immutable
- Generic Linux fallback
Detection model
- Detect distro family from
/etc/os-release - Detect policy constraints such as immutable or Nix-style environments
- Probe runtime capabilities such as directory writability and optional helper availability
- Let distro family define intended policy, then let capabilities refine the safe final action
Support posture
- Broad Linux coverage is the target
- Deep integration is preferred for mainstream distro families
- Immutable systems should get best-effort user installs with explicit warnings
- System installs should prefer native shared integration locations when policy allows, with fallback to
aim-managed locations where appropriate
Recommended Approach
Use a hybrid family-plus-capability model.
This was chosen over a family-only matrix because distro identity alone is not enough to determine what the current host will allow. It was also chosen over capability-only probing because distro family still matters for native path expectations and default policy.
The model is:
- distro family selects the intended install policy
- host capabilities determine whether that policy can be executed fully, degraded, or not at all
- one generic install engine executes the workflow using the resolved policy
Install Policy Model
Debian, Fedora, Arch, openSUSE
--userinstalls useaim-managed user paths--systemprefers native shared desktop integration locations and system icon locations- AppImage payloads remain in an
aim-managed system payload directory unless a later distro-specific need proves otherwise - Desktop caches are refreshed only when helper tools are present
Alpine
- Treat Alpine as a lightweight Linux with shallower integration assumptions
- Prefer
aim-managed paths for both user and system installs - Run optional helpers only when present
Nix
- Default to conservative behavior
- Allow
--userinstalls intoaim-managed user paths, with a warning that the install is outside declarative package management - Deny
--systeminstalls until a Nix-native strategy exists - Never attempt to write into the Nix store or emulate a declarative install
Immutable
- Treat
--useras the primary supported path - If
--systemis requested, first test whether the host genuinely permits the required writes - If policy blocks system writes, either downgrade to user scope with an explicit warning or fail clearly when downgrade is not allowed by policy
Generic fallback
- Use
aim-managed paths only - Apply standards-based XDG desktop integration only
- Skip distro-specific helper assumptions
Cross-cutting rule
Payload storage and integration targets are separate concerns.
For example, a system install may place the AppImage payload under /opt/aim/appimages, while writing the .desktop file to /usr/share/applications and icons to /usr/share/icons. This keeps artifact replacement and rollback simple while still integrating natively.
Execution Pipeline
Use one install engine with policy injected into it rather than separate installers per distro.
1. Resolve policy
- Detect distro family
- Detect capabilities and policy markers
- Resolve an
InstallPolicydescribing:- allowed scope
- payload root
- desktop entry root
- icon root
- integration mode
- helper refresh actions
- warnings and fallback notes
2. Stage artifact
- Download into a temporary staging path
- Validate that the payload is an AppImage
- Derive stable identity and final filenames before touching permanent locations
- Mark the staged artifact executable before final commit
3. Commit payload atomically
- Move the staged AppImage into the final managed payload location
- Use replacement semantics suitable for safe updates
- Keep the old installed payload until the new one is validated and committed
4. Commit integration
- Generate a normalized
.desktopfile - Extract and install icons when available
- Write integration artifacts into policy-selected locations
- Keep these artifacts generated by
aim, not manually managed
5. Refresh caches best-effort
- Run optional helpers only when present and relevant
- Cache refresh failures should produce warnings, not rollback a valid install
6. Persist registry last
- Only persist the final
AppRecordafter payload and required integration steps succeed - The registry should represent completed installs, not partial attempts
Reliability Model
Preflight failures
Stop early for:
- unsupported scope for the resolved host policy
- unwritable required target directories with no allowed fallback
- payloads that are not valid AppImages
- missing required normalized metadata for integration
Transaction boundary
Treat install as three phases:
- preflight and staging
- payload commit
- integration commit
Nothing is persisted in the registry until the required phases succeed.
Rollback rules
- If staging fails, clean temporary files only
- If payload commit succeeds but required integration fails, remove the new payload unless degraded payload-only success was explicitly allowed
- If integration partially succeeds, remove generated
.desktopand icon artifacts before returning failure - Cache refresh failures do not trigger rollback
Degraded success
Success with warnings is acceptable only when:
- optional cache refresh helpers are missing or fail
- desktop integration was optional and the host lacks the helper stack to finish niceties
- immutable or policy-heavy systems forced a user-scope fallback that policy explicitly allows
Update safety
- Updates stage side-by-side and replace atomically
- The previous working AppImage stays active until the new one is validated and committed
- Failed updates should leave the previous installed version intact
Verification Strategy
Unit tests
- distro detection
- immutable and Nix marker detection
- helper capability probing
- writable path probing
- install policy resolution per family and scope
- degraded and denied policy decisions
Integration tests
- successful user install
- successful system install using managed payload plus native integration paths
- denied Nix system install
- immutable system fallback to user install with warning
- payload commit failure cleanup
- integration failure rollback
- cache refresh warning-only behavior
Fixture-driven filesystem tests should cover most of this. First implementation should not depend on real distro images in CI.
Architecture Slice
Core types
DistroFamily
DebianFedoraArchOpenSuseAlpineNixImmutableGeneric
HostCapabilities
- parsed
os-releaseidentity - immutable and Nix policy markers
- writable status for candidate directories
- desktop-session presence
- helper availability such as
update-desktop-databaseandgtk-update-icon-cache
InstallPolicy
- resolved scope
- payload path root
- desktop entry root
- icon path root
- integration mode:
full,degraded,payload-only, ordenied - fallback notes and warnings for UI surfaces
InstallPlan
- selected artifact URL and expected identity
- staging paths
- final payload path
- desktop file path
- icon path
- helper refresh actions to attempt after commit
InstallOutcome
- installed scope
- resolved mode
- written paths
- warnings
- rollback status
Recommended module layout
Within crates/aim-core/src/:
platform/distro.rsplatform/capabilities.rsintegration/policy.rsintegration/install.rsintegration/desktop.rsintegration/refresh.rs
Responsibilities:
platform/distro.rsdetects distro families and policy markersplatform/capabilities.rsprobes writability and helper availabilityintegration/policy.rsconverts requested scope plus host facts intoInstallPolicyintegration/install.rsowns the transactional install pipelineintegration/desktop.rsowns desktop file generation and icon handlingintegration/refresh.rsowns best-effort helper execution
CLI behavior
aim <QUERY>should become a real install path rather than registry-only tracking- The CLI should print the resolved install mode before commit
- If the plan degrades, the CLI should explain that before confirmation
- Bare
aimcan later reuse the same policy summary when showing pending updates
Summary
The agreed design is a single install engine in aim-core driven by a resolved per-host InstallPolicy. Distro families provide intended policy, runtime capabilities decide what is safe, and transactional install semantics keep installs recoverable. This preserves the thin-CLI architecture while allowing broad Linux coverage without scattering distro-specific conditionals across the entire codebase.