# 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 source - `aim`: review-first update flow; aliases `aim update` - `aim update`: explicit update flow - `aim remove {QUERY}`: remove by registered app name - `aim list`: list installed AppImages ### Supported source types 1. GitHub Releases 2. Direct URL / generic website downloads 3. GitLab Releases 4. zsync / embedded AppImage update info 5. SourceForge 6. Custom JSON feed adapters ### Install behavior - Default installation mode is auto-detected by effective privileges - `--system` and `--user` override 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 `aim` with no query should discover updates, present a review list, and then apply only selected updates - Architecture handling should remain generic: `aim-core` manages 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 APIs - `crates/aim-cli`: thin terminal frontend for the initial shipped application - `crates/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: - `clap` for CLI parsing - `dialoguer` for interactive prompts and multi-select review flows - `console` for styled output and readable summaries - `indicatif` for 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: - `AddService` - `UpdateService` - `RegistryService` - `IntegrationService` ### 3. Domain model layer - Lives in `aim-core` - Holds the canonical source-agnostic types used across the system Suggested domain types: - `AppRecord` - `InstallScope` - `SourceRef` - `SourceKind` - `ResolvedRelease` - `InstalledArtifact` - `UpdatePlan` - `DesktopIntegration` - `InteractionRequest` - `InteractionResponse` ### 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.toml` - `crates/aim-core/Cargo.toml` - `crates/aim-core/src/lib.rs` - `crates/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.toml` - `crates/aim-cli/src/lib.rs` - `crates/aim-cli/src/main.rs` - `crates/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 `aim` with no query Behavior: - Resolve GitHub URLs and `owner/repo` forms 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: - `.desktop` entry 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 1. `aim-cli` parses CLI input and scope override flags 2. `aim-cli` calls `aim-core` with a normalized request 3. `aim-core` resolves the query into a `SourceRef` 4. `aim-core` selects the appropriate adapter 5. `aim-core` identifies the app and candidate releases 6. `aim-core` returns an interaction state if confidence is low 7. `aim-cli` prompts, then sends the decision back to `aim-core` 8. `aim-core` falls back to raw URL identity if needed 9. `aim-core` downloads to staging 10. `aim-core` validates the artifact as an AppImage and inspects update metadata 11. `aim-core` installs into the correct managed location 12. `aim-core` generates integration artifacts and persists a normalized registry entry ### `aim` and `aim update` flow 1. `aim-cli` invokes update discovery in `aim-core` 2. `aim-core` loads relevant registry entries 3. `aim-core` asks each adapter for update candidates 4. `aim-core` builds an `UpdatePlan` 5. `aim-cli` renders the review list and collects selection 6. `aim-core` applies selected updates 7. `aim-core` refreshes registry state and integration artifacts as needed 8. `aim-cli` prints a final success/failure summary ### `aim list` flow - `aim-cli` requests installed app state from `aim-core` and renders the result grouped by scope, source, and version ### `aim remove {QUERY}` flow 1. `aim-cli` forwards the query to `aim-core` 2. `aim-core` resolves the query against registered app names 3. `aim-core` emits an interaction request if ambiguity must be resolved 4. `aim-cli` prompts if needed and returns the selection 5. `aim-core` removes artifacts and integration files in the correct order 6. `aim-core` removes 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 repair` command, 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 1. Incorrect identity causing duplicate or non-updatable entries 2. Source-specific regressions hidden behind a shared API surface 3. Incomplete rollback leaving broken installs 4. Scope confusion causing files to land in the wrong locations 5. Business logic leaking into `aim-cli` and 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-core` and `aim-cli` - Put all business logic in `aim-core` - Keep `aim-cli` as a thin terminal adapter over `aim-core` - Design `aim-core` to be reusable by a future `aim-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 `--system` and `--user` overrides - Make bare `aim` a review-first update path ## Explicit v1 Boundaries Included in v1: - Cargo workspace with `aim-core` and `aim-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-gui` implementation - 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