140 lines
No EOL
6.7 KiB
Markdown
140 lines
No EOL
6.7 KiB
Markdown
# 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 for `upm`. 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 over `upm-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 over `upm-core`, not a second application layer.
|
|
|
|
The intended split is strict:
|
|
|
|
- `upm-core` is effectively the application
|
|
- `upm` is one frontend over that application
|
|
- `upm-ui` will be another frontend over that application
|
|
- package-manager modules own their own implementation detail and speak to `upm-core` through 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:
|
|
|
|
- `upm` may know which modules exist for configuration, enablement, disablement, priority, and display
|
|
- `upm-ui` should operate under the same rule as the CLI: it talks to `upm-core`, not directly to modules
|
|
- `upm` must not talk directly to a package-manager module or implement module-specific logic
|
|
- `upm-core` owns the unified application interface used by the CLI now and a GUI later
|
|
- `upm-core` owns module registration, composition, enablement checks, and request fan-out
|
|
- `upm-core` fans 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
|
|
- `upm` for CLI concerns only
|
|
- `upm-ui` for 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:
|
|
|
|
1. Parse CLI input and load runtime config in `upm`.
|
|
2. Call the unified application facade in `upm-core`.
|
|
3. Let `upm-core` route the request into internal orchestration services.
|
|
4. Let those services select enabled modules and fan the request out through normalized module traits.
|
|
5. Aggregate normalized results into an add, show, update, search, or remove flow.
|
|
6. Download the selected AppImage into a staged path when the chosen module requires it.
|
|
7. Verify integrity metadata when available.
|
|
8. Commit the payload into the managed install location.
|
|
9. Write desktop integration artifacts and refresh helper caches.
|
|
10. 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 `upm` names
|
|
- 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/` |