16 KiB
AppImage Manager Design
Goal
Build AppImage Manager as a Rust workspace where aim-core contains the business logic and reusable APIs, and aim-cli is a thin terminal client. The first shipped application is the CLI, but the architecture must leave a clean path for a later GUI client to consume the same core install, update, registry, and adapter logic.
Agreed Product Shape
Command surface
aim {QUERY}: search and add from a query sourceaim: review-first update flow; aliasesaim updateaim update: explicit update flowaim remove {QUERY}: remove by registered app nameaim list: list installed AppImages
Supported source types
- GitHub Releases
- Direct URL / generic website downloads
- GitLab Releases
- zsync / embedded AppImage update info
- SourceForge
- Custom JSON feed adapters
Install behavior
- Default installation mode is auto-detected by effective privileges
--systemand--useroverride the auto-detected scope- The tool supports both user and system installations
- The tool performs full desktop-style integration for installed apps
Identity and update behavior
- The system should infer app identity and version when possible
- If confidence is low, the client should prompt interactively for confirmation or edits
- If identity still cannot be stabilized, the registry should fall back to the raw URL as the last-resort key
- Running
aimwith no query should discover updates, present a review list, and then apply only selected updates - Architecture handling should remain generic:
aim-coremanages whatever AppImage artifact is resolved, while validating obvious mismatches at install time
Recommended Architecture
Use typed source adapters behind a common update engine, packaged in aim-core and consumed by thin frontend clients.
This architecture fits the source diversity without forcing a plugin runtime into v1. Each upstream source gets an explicit Rust adapter that implements a shared contract for identity resolution, release discovery, artifact selection, and update metadata extraction. The shared update engine operates on normalized internal types rather than source-specific details.
This approach was selected over:
- a registry-centric-first design, which risks smearing source-specific logic across storage and service layers
- a plugin-first design, which adds packaging, security, and testing complexity too early
Workspace Architecture
The project should be a Cargo workspace with frontend clients over a shared core crate.
Workspace crates
crates/aim-core: all business logic and reusable APIscrates/aim-cli: thin terminal frontend for the initial shipped applicationcrates/aim-gui: deferred future GUI client, planned but not implemented in v1
The critical rule is that aim-cli must not become the home for install, update, registry, or source logic. If behavior should be reusable by a future GUI, it belongs in aim-core.
Architecture Layers
The system should be organized into four layers, with the bottom three living in aim-core.
1. Client layer
- Implemented first in
aim-cli - Parses commands, flags, and defaults
- Owns presentation only: prompts, colors, spinners, progress bars, and terminal summaries
- Uses:
clapfor CLI parsingdialoguerfor interactive prompts and multi-select review flowsconsolefor styled output and readable summariesindicatiffor progress bars and spinners
This layer should translate user intent into calls into aim-core and render responses. It should not contain source-specific business logic, registry mutation logic, or install/update decision logic.
2. Application/service layer
- Lives in
aim-core - Coordinates workflows like add, remove, list, and update
- Applies product rules such as scope selection, update review behavior, and low-confidence identity confirmation
- Suggested services:
AddServiceUpdateServiceRegistryServiceIntegrationService
3. Domain model layer
- Lives in
aim-core - Holds the canonical source-agnostic types used across the system
Suggested domain types:
AppRecordInstallScopeSourceRefSourceKindResolvedReleaseInstalledArtifactUpdatePlanDesktopIntegrationInteractionRequestInteractionResponse
4. Infrastructure layer
- Lives in
aim-core - Source adapters
- Filesystem and install location management
- Registry persistence
- Desktop integration helpers
- Download and HTTP client behavior
- Optional subprocess wrappers for system integration tasks
Suggested Module Layout
Suggested workspace layout:
Cargo.tomlcrates/aim-core/Cargo.tomlcrates/aim-core/src/lib.rscrates/aim-core/src/app/crates/aim-core/src/domain/crates/aim-core/src/adapters/crates/aim-core/src/integration/crates/aim-core/src/registry/crates/aim-core/src/platform/crates/aim-cli/Cargo.tomlcrates/aim-cli/src/lib.rscrates/aim-cli/src/main.rscrates/aim-cli/src/cli/crates/aim-cli/src/ui/
Future-facing placeholder:
crates/aim-gui/
This keeps terminal UX separate from the install/update engine and ensures the later GUI can reuse the same core APIs.
Core Components
Query resolver
Lives in aim-core and turns user input into a normalized SourceRef.
Accepted input forms:
- URL
user_or_org/project- file URI
- bare
aimwith no query
Behavior:
- Resolve GitHub URLs and
owner/repoforms to GitHub when unambiguous - Resolve GitLab URLs and explicit
gitlab:references to GitLab - Resolve direct URLs and generic web pages to the direct URL / web adapter
- Resolve
file://inputs into local import flow
The query resolver should not perform install logic.
Source adapter layer
Lives in aim-core, with one typed adapter per source:
- GitHub Releases adapter
- GitLab Releases adapter
- Direct URL / generic web adapter
- zsync / embedded update info adapter
- SourceForge adapter
- Custom JSON feed adapter
Each adapter should expose a shared capability shape:
- identify app
- enumerate candidate releases
- choose preferred artifact
- expose update metadata
- download or resolve the artifact for download
Not every source needs to support true search. Some only support exact resolution. The contract should represent those differences honestly.
Registry
Lives in aim-core and stores normalized installed app records across user and system scopes.
It should track:
- canonical app identity
- display name
- install scope
- source type
- source locator and source-specific update hints
- installed version
- installed artifact path
- artifact fingerprint or hash
- release metadata
- integration artifact paths
- timestamps
The registry is the bridge between one-time install and repeatable updates, so it must be migration-friendly.
Installer and integrator
Live in aim-core.
Installer responsibilities:
- staging downloads
- validating artifacts
- moving binaries into managed locations
- setting permissions
- replacing installed artifacts atomically where possible
Integrator responsibilities:
.desktopentry generation- icon extraction or acquisition
- symlink creation
- MIME and related registration where feasible
- correct handling of user vs system targets
Installer and integration concerns should remain separate so updates can replace binaries without always rebuilding every integration artifact.
Update planner and executor
Live in aim-core.
Planner responsibilities:
- load registry entries
- ask adapters for update candidates
- compare installed state to available state
- build a reviewable
UpdatePlan
Executor responsibilities:
- apply selected updates
- download and validate updated artifacts
- replace existing artifacts safely
- refresh integration artifacts only when needed
- update registry state
- surface typed results and events for clients
Client interaction boundary
Terminal-specific UI belongs in aim-cli, not aim-core.
aim-core should expose operation APIs and typed interaction or progress models that clients can render however they want. aim-cli should wrap all usage of dialoguer, console, and indicatif.
This keeps business logic testable without terminal coupling and makes a GUI frontend viable later.
Custom JSON feed support
Custom JSON feeds should be declarative in v1, not arbitrary executable plugins.
The adapter should support field mapping and release selection rules against a constrained schema family, rather than loading arbitrary code. This delivers flexibility without turning the CLI into a plugin host.
End-to-End Data Flow
aim {QUERY} add flow
aim-cliparses CLI input and scope override flagsaim-clicallsaim-corewith a normalized requestaim-coreresolves the query into aSourceRefaim-coreselects the appropriate adapteraim-coreidentifies the app and candidate releasesaim-corereturns an interaction state if confidence is lowaim-cliprompts, then sends the decision back toaim-coreaim-corefalls back to raw URL identity if neededaim-coredownloads to stagingaim-corevalidates the artifact as an AppImage and inspects update metadataaim-coreinstalls into the correct managed locationaim-coregenerates integration artifacts and persists a normalized registry entry
aim and aim update flow
aim-cliinvokes update discovery inaim-coreaim-coreloads relevant registry entriesaim-coreasks each adapter for update candidatesaim-corebuilds anUpdatePlanaim-clirenders the review list and collects selectionaim-coreapplies selected updatesaim-corerefreshes registry state and integration artifacts as neededaim-cliprints a final success/failure summary
aim list flow
aim-clirequests installed app state fromaim-coreand renders the result grouped by scope, source, and version
aim remove {QUERY} flow
aim-cliforwards the query toaim-coreaim-coreresolves the query against registered app namesaim-coreemits an interaction request if ambiguity must be resolvedaim-cliprompts if needed and returns the selectionaim-coreremoves artifacts and integration files in the correct orderaim-coreremoves registry state while preserving uncertain shared resources conservatively
Registry Data Shape
Each registry record should contain enough source-specific state to make updates reliable without re-deriving identity from filenames.
Recommended fields:
- stable app id
- display name
- install scope
- source kind
- source locator
- installed version
- installed file path
- file hash or fingerprint
- release metadata
- updater metadata
- integration artifact paths
- created and updated timestamps
Examples of source-specific metadata:
- GitHub/GitLab: owner, repo, release/tag, asset selection hints
- Direct URL: original URL, resolved URL, etag, last-modified when available
- zsync: zsync URL or embedded update info extracted from the AppImage
- SourceForge: project and file path hints
- Custom JSON feed: feed URL plus mapping profile
Error Handling Model
Error handling should be structured internally and concise externally.
aim-core should own structured error types and machine-readable outcomes. aim-cli should map those into concise terminal messages. A future GUI should be able to present the same failures without reparsing CLI text.
Suggested error categories:
- query resolution error
- source adapter error
- network/download error
- artifact validation error
- install permission or scope error
- desktop integration error
- registry persistence error
- update planning error
Behavioral expectations:
- prompt on low-confidence identity rather than silently guessing
- fail clearly on insufficient privileges for system install unless explicit elevation behavior is designed later
- continue update processing across apps when one app fails
- fail-fast within a single app transaction unless a step is intentionally non-fatal
- either roll back on integration failure or explicitly record the app as installed-but-needing-repair
For v1, prefer:
- best-effort continuation across apps during update runs
- fail-fast inside a single app update or install transaction
- atomic replacement where possible
- future room for an
aim repaircommand, even if not implemented in v1
Testing Strategy
Testing should map directly to the architecture layers.
aim-core unit tests
- query parsing and source resolution
- identity normalization and fallback logic
- version comparison and update selection logic
- install scope resolution
- registry serialization and migrations
- adapter-specific parsing helpers
Shared adapter contract tests
Every adapter should pass a common behavior suite where applicable:
- can identify app
- can resolve latest candidate
- reports unsupported capabilities honestly
- produces normalized release metadata
This is the primary protection against drift across heterogeneous source implementations.
aim-core integration tests
- add flow per source type using fixtures or mocked HTTP
- update planning across mixed registry entries
- remove flow cleaning registry and integration artifacts
- user vs system path resolution
- registry migration compatibility
Filesystem tests
Use temp directories to simulate:
- user install roots
- system install roots
- desktop entry locations
- icon and symlink generation
aim-cli client behavior tests
- snapshot or golden tests for key terminal flows
- update review list interaction
- low-confidence identity prompt
- success and failure summaries
Most behavioral coverage should target aim-core, with only thin client verification in aim-cli.
Avoid relying on live network tests in the main suite. Keep those as optional smoke coverage.
Main risks the test plan must cover
- Incorrect identity causing duplicate or non-updatable entries
- Source-specific regressions hidden behind a shared API surface
- Incomplete rollback leaving broken installs
- Scope confusion causing files to land in the wrong locations
- Business logic leaking into
aim-cliand diverging from future GUI needs
Recommended Persisted Formats And Key Decisions
Persisted formats
- Use a structured registry file or registry store that is easy to migrate and inspect
- Keep source-specific update metadata embedded in each app record rather than scattered across auxiliary files
- Store integration artifact paths explicitly so removal and repair remain deterministic
Key design decisions
- Use a Cargo workspace with
aim-coreandaim-cli - Put all business logic in
aim-core - Keep
aim-clias a thin terminal adapter overaim-core - Design
aim-coreto be reusable by a futureaim-gui - Use typed Rust adapters behind a common update engine
- Normalize identity early and once
- Separate update planning from update execution
- Treat custom JSON feeds as declarative adapters, not executable plugins
- Auto-detect scope by effective privileges, with
--systemand--useroverrides - Make bare
aima review-first update path
Explicit v1 Boundaries
Included in v1:
- Cargo workspace with
aim-coreandaim-cli - multi-source AppImage add flow
- user and system scope support
- update planning and selected update execution
- desktop-style integration
- typed adapters for the agreed source list
- declarative custom JSON feed support
Deferred from v1:
aim-guiimplementation- general plugin runtime
- arbitrary executable custom adapters
- broad distro-specific deep integration beyond the agreed desktop registration model
- live network-dependent test suite as the main verification strategy
- repair and doctor commands, though the design should leave room for them
Open Implementation Notes
- Because the current workspace is not a git repository, the design document can be saved but not committed yet
- The next step should be an implementation plan that breaks this design into small TDD-oriented tasks