6.7 KiB
Architecture Overview
Workspace Shape
upm is a Rust workspace with three main crates today and a fourth planned frontend:
crates/upm-core: the application layer forupm. It owns command orchestration, module contracts, module registry and composition, registry persistence, install policies, desktop integration, and the unified frontend-facing API that both the CLI and a future GUI will call.crates/upm: the CLI frontend overupm-core. It handles argument parsing, config loading, terminal UX, prompting, progress reporting, summary rendering, and config-driven module presentation.crates/upm-appimage: the AppImage package-manager module. It should own AppImage-specific acquisition backends, artifact selection, and install-resolution behavior.crates/upm-ui(planned): a GUI frontend overupm-core, not a second application layer.
The intended split is strict:
upm-coreis effectively the applicationupmis one frontend over that applicationupm-uiwill be another frontend over that application- package-manager modules own their own implementation detail and speak to
upm-corethrough normalized traits
That keeps frontend-agnostic logic in upm-core, makes a future GUI a first-class consumer instead of a later retrofit, and prevents frontend layers from accumulating package-manager-specific behavior.
Application Boundary
The architectural boundary is:
upmmay know which modules exist for configuration, enablement, disablement, priority, and displayupm-uishould operate under the same rule as the CLI: it talks toupm-core, not directly to modulesupmmust not talk directly to a package-manager module or implement module-specific logicupm-coreowns the unified application interface used by the CLI now and a GUI laterupm-coreowns module registration, composition, enablement checks, and request fan-outupm-corefans requests out to enabled modules and aggregates normalized results- each module owns its own internal backends, source quirks, artifact selection, and provider-specific rules
In practical terms, upm-core is where the product behavior lives. The CLI should remain replaceable.
Public API Shape
upm-core should expose one high-level application facade to frontend crates.
- the public boundary should be an application-facing type such as
UpmApp - the facade should present operations like search, add, show, update, remove, and config management in product terms
- frontends should not compose lower-level orchestration services themselves
That public facade should stay thin. The internal implementation in upm-core can and should be split into smaller services such as:
- module registry and module loading
- search orchestration
- add planning and execution
- show resolution
- update planning and execution
- configuration and state services
This gives both frontends one stable application boundary without turning the facade into a god object. The orchestration depth stays inside upm-core, where it belongs.
Module Tree
The intended tree is:
upm-core- public application facade
- internal orchestration services
- module registry and composition
- normalized contracts for package-manager modules
- frontend crates
upmfor CLI concerns onlyupm-uifor GUI concerns only
- module crates
upm-appimage- AppImageHub backend
- GitHub-backed AppImage acquisition
- GitLab-backed AppImage acquisition
- SourceForge-backed AppImage acquisition
- direct AppImage URL handling
- AppImage-specific artifact and metadata rules
The important constraint is that the top layer understands package-manager modules, not the inner mechanics of how each module finds or resolves artifacts.
Core Flow
The main execution path is:
- Parse CLI input and load runtime config in
upm. - Call the unified application facade in
upm-core. - Let
upm-coreroute the request into internal orchestration services. - Let those services select enabled modules and fan the request out through normalized module traits.
- Aggregate normalized results into an add, show, update, search, or remove flow.
- Download the selected AppImage into a staged path when the chosen module requires it.
- Verify integrity metadata when available.
- Commit the payload into the managed install location.
- Write desktop integration artifacts and refresh helper caches.
- Persist registry state atomically.
Source And Provider Model
Supported source classes currently include:
- GitHub repository and release forms
- GitLab repository forms
- AppImageHub item forms
- SourceForge release and download forms
- direct URLs
- local file imports
Core orchestration and normalized module contracts live in crates/upm-core. Package-manager-specific behavior belongs in module crates.
For the AppImage module, that means crates/upm-appimage is the package-manager boundary and should grow to own AppImage-specific backing sources internally. upm-core should coordinate the module through normalized traits, not absorb AppImageHub, GitHub-backed AppImage discovery, GitLab-backed AppImage discovery, SourceForge-backed AppImage discovery, or direct AppImage URL handling as first-class application concepts.
Runtime Interface
The rename to upm is a hard cutover:
- runtime overrides use
UPM_* - legacy
AIM_*runtime overrides are not read - default config, registry, payload, and desktop-entry paths use
upmnames - helper audit logging uses
UPM_DEBUG_EXTERNAL_HELPERS=1
Security Hardening State
The current workspace enforces the following download and install boundaries:
- user-supplied
http://inputs are rejected by default - runtime opt-in is available through
allow_http = true - that opt-in applies only to user-supplied sources, including update flows derived from stored direct HTTP origins
- AppImageHub provider-returned download URLs must remain HTTPS
- AppImageHub MD5 metadata is verified as weak integrity before payload commit
- desktop entry display names are sanitized to prevent newline and control-character field injection
- stable identifiers that normalize to empty or contain
..are rejected
The remaining deferred AppImageHub host-trust concern is tracked in security-issues.md.
Persistence And Integration
- Registry writes are atomic and live under the registry store implementation in
upm-core. - Managed payload, desktop entry, and icon paths are resolved from install policy and scope.
- Desktop integration refresh uses external helpers when available and now supports env-gated audit logging through
UPM_DEBUG_EXTERNAL_HELPERS=1.
Planning And Audit Artifacts
- implementation plans live under
.plans/ - audit reports live under
.audits/ - architecture state and tracked security issues live under
.architecture/