docs: capture 1.8 app migration release plan
This commit is contained in:
@@ -0,0 +1,441 @@
|
||||
# App Packaging Migration Plan
|
||||
|
||||
## Goal
|
||||
|
||||
Turn Archipelago into a serious app platform while preserving the fundamentals that drove the original architecture:
|
||||
|
||||
- Rootless Podman and security-first execution.
|
||||
- Managed node-OS behavior: health, repair, backups, updates, secrets, and routing.
|
||||
- Bitcoin/LND/Tor/Web5/mesh integration where the platform genuinely needs deep awareness.
|
||||
- A developer-friendly app packaging model that avoids app-specific Rust installers as the normal path.
|
||||
|
||||
## Current Contract
|
||||
|
||||
The runtime contract is manifest-first. App packages live at `apps/<app-id>/manifest.yml` and are validated by the shared container manifest parser.
|
||||
|
||||
The current canonical manifest fields are:
|
||||
|
||||
- `app`: identity and app-level metadata.
|
||||
- `container`: image or build source, pull policy, network, entrypoint, custom args, derived env, secret env, and data UID.
|
||||
- `dependencies`: storage and app dependencies.
|
||||
- `resources`: CPU, memory, disk.
|
||||
- `security`: capabilities, read-only root, no-new-privileges, network policy, optional AppArmor profile.
|
||||
- `ports`, `volumes`, `files`, `environment`, `health_check`, and `devices`.
|
||||
- `metadata`: current catalog-facing presentation data such as category, tier, icon, repo/source, author, and features.
|
||||
- extension keys may exist temporarily, but they are transitional and should not become a second contract.
|
||||
|
||||
The historical `archy-app.yml` name should be treated as superseded. The active local package filename is `manifest.yml`.
|
||||
|
||||
## Current Progress
|
||||
|
||||
As of the current `1.8-alpha` workstream:
|
||||
|
||||
- `apps/*/manifest.yml` is the source of truth for runtime app definitions.
|
||||
- The Rust manifest parser validates app identity, image-vs-build source selection, safe environment/secrets, safe ports, safe bind/named/tmpfs volumes, generated files under declared bind mounts, devices, and security/network policy values.
|
||||
- Manifest-owned generated files exist through `app.files` and have been used for app config material such as Meshtastic config regeneration.
|
||||
- Local image builds are represented with `container.build`; pulled images are represented with `container.image`.
|
||||
- Data ownership repair is represented with `container.data_uid`.
|
||||
- Derived host facts and secret-file-backed environment variables are represented with `container.derived_env` and `container.secret_env`.
|
||||
- Catalog metadata generation is implemented by `scripts/generate-app-catalog.py`.
|
||||
- App-session launch ports/titles and new-tab launch behavior now have a generated TypeScript metadata path from manifests, with manual overrides preserved for companion UIs and aliases that do not have manifest-owned metadata yet.
|
||||
- Release drift checking is implemented by `scripts/check-app-catalog-drift.py --release --strict`.
|
||||
- The canonical catalog and the UI public catalog are expected to remain byte-for-byte synced after generation.
|
||||
- Runtime validation has already moved many simple and moderate apps into the manifest/orchestrator path, including Filebrowser, Vaultwarden, Portainer, Uptime Kuma, Grafana, Gitea, Nextcloud, SearXNG, Nostr Relay, PhotoPrism, Jellyfin, Meshtastic, and several Bitcoin-adjacent apps.
|
||||
|
||||
The remaining migration work is mostly orchestration quality: post-reboot adoption, progress reporting, stale scanner-state handling, update policy, multi-container stack ownership, proxy route generation, and cleanup of obsolete legacy installers/fallbacks.
|
||||
|
||||
## Target Architecture
|
||||
|
||||
Use a StartOS-inspired package model with Umbrel-like app folders.
|
||||
|
||||
```text
|
||||
apps/saleor/
|
||||
manifest.yml
|
||||
Dockerfile
|
||||
icon.svg
|
||||
screenshots/
|
||||
instructions.md
|
||||
hooks/
|
||||
post-install.sh
|
||||
pre-start.sh
|
||||
repair.sh
|
||||
health.sh
|
||||
backup.sh
|
||||
restore.sh
|
||||
proxy/
|
||||
routes.yml
|
||||
```
|
||||
|
||||
Archipelago becomes the secure compiler/runtime for these packages. The manifest declares what it needs; Archipelago validates it, injects secrets, creates rootless Podman containers, generates nginx/Tor/public routes, registers health checks, displays credentials, and manages lifecycle.
|
||||
|
||||
## Core Principles
|
||||
|
||||
- App packages are declarative by default.
|
||||
- Hooks are allowed only as controlled, reviewed escape hatches.
|
||||
- Rootless Podman stays.
|
||||
- Arbitrary privileged Compose execution is not allowed.
|
||||
- Each app has one source of truth.
|
||||
- Catalog, launch URLs, mobile behavior, credentials, backup paths, and public routes come from the app package or its generated catalog entry.
|
||||
- Rust backend owns orchestration, not app-specific business logic.
|
||||
- Core infrastructure can remain special-case where justified.
|
||||
|
||||
## What Stays
|
||||
|
||||
- Rootless Podman.
|
||||
- Archipelago orchestrator.
|
||||
- Health/reconcile/repair loops.
|
||||
- Host nginx.
|
||||
- Nginx Proxy Manager integration.
|
||||
- Tor/public routing goals.
|
||||
- Bitcoin/LND/mesh/Web5/FIPS/security direction.
|
||||
- OTA update system.
|
||||
- App-session/mobile shell.
|
||||
- Managed secrets and credentials display.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Complex app stacks stop living in Rust.
|
||||
- `app-catalog/catalog.json` becomes generated.
|
||||
- Frontend fallback marketplace data is removed or generated.
|
||||
- App-session port maps and new-tab launch behavior become generated.
|
||||
- Public proxy routes become app-declared.
|
||||
- Install/start/restart/backup/restore become package-driven.
|
||||
- App updates become app package changes where possible, not full backend code changes.
|
||||
|
||||
## Package Schema Direction
|
||||
|
||||
Example `manifest.yml`:
|
||||
|
||||
```yaml
|
||||
app:
|
||||
id: saleor
|
||||
name: Saleor
|
||||
version: 3.23.0
|
||||
description: Composable commerce platform
|
||||
container:
|
||||
image: docker.io/myorg/saleor:3.23.0
|
||||
pull_policy: if-not-present
|
||||
network: archy-net
|
||||
entrypoint: ["sh", "-lc"]
|
||||
custom_args:
|
||||
- /app/start.sh
|
||||
derived_env:
|
||||
- key: PUBLIC_URL
|
||||
template: https://{{HOST_MDNS}}:9010
|
||||
secret_env:
|
||||
- key: SALEOR_SECRET_KEY
|
||||
secret_file: saleor-secret-key
|
||||
dependencies:
|
||||
- storage: 20Gi
|
||||
resources:
|
||||
cpu_limit: 4
|
||||
memory_limit: 2Gi
|
||||
security:
|
||||
capabilities: []
|
||||
readonly_root: true
|
||||
no_new_privileges: true
|
||||
network_policy: isolated
|
||||
ports:
|
||||
- host: 9010
|
||||
container: 9000
|
||||
protocol: tcp
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /var/lib/archipelago/saleor
|
||||
target: /data
|
||||
options: [rw]
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
health_check:
|
||||
type: http
|
||||
endpoint: http://localhost:9000
|
||||
path: /health
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
Optional generated files, hooks, icons, and screenshots can sit beside the manifest, but the manifest stays the source of truth. Compose-style definitions are not executed directly.
|
||||
|
||||
## Security Model
|
||||
|
||||
Do not run arbitrary Compose directly. Archipelago validates:
|
||||
|
||||
- No privileged containers unless explicitly approved.
|
||||
- No host filesystem mounts outside approved paths.
|
||||
- No Docker socket mounts.
|
||||
- No host network unless explicitly approved.
|
||||
- No dangerous capabilities by default.
|
||||
- No arbitrary device access without declaration.
|
||||
- No rootful execution.
|
||||
- Pinned images preferred.
|
||||
- Resource limits required.
|
||||
- Backup paths declared where the app stores durable data.
|
||||
- Public routes explicit.
|
||||
- Secrets referenced by name, not hardcoded.
|
||||
|
||||
When the runtime needs app-specific facts that do not belong in the manifest, prefer adding a reusable platform primitive rather than introducing another ad hoc installer path.
|
||||
|
||||
This preserves the reason for avoiding raw Umbrel-style Compose while still giving developers a sane package format.
|
||||
|
||||
## Lifecycle Model
|
||||
|
||||
Every app package should support:
|
||||
|
||||
- install
|
||||
- configure
|
||||
- start
|
||||
- stop
|
||||
- restart
|
||||
- update
|
||||
- repair
|
||||
- health
|
||||
- backup
|
||||
- restore
|
||||
- uninstall
|
||||
- migrate
|
||||
|
||||
Archipelago owns the state machine.
|
||||
|
||||
Optional hooks:
|
||||
|
||||
- `post-install.sh` for migrations/admin creation.
|
||||
- `pre-start.sh` for ownership repair.
|
||||
- `repair.sh` for app-specific remediation.
|
||||
- `health.sh` for custom health checks.
|
||||
- `backup.sh` and `restore.sh` only when simple path backups are insufficient.
|
||||
|
||||
Hooks run with a controlled environment and restricted permissions.
|
||||
|
||||
## Hard Work
|
||||
|
||||
The hard work is not writing YAML. The hard work is safely translating app packages into reliable rootless runtime behavior:
|
||||
|
||||
- Build a robust package validator.
|
||||
- Map a safe Compose subset to rootless Podman.
|
||||
- Handle multi-container networks without hardcoded IPs.
|
||||
- Handle rootless volume ownership correctly.
|
||||
- Generate host nginx routes from app metadata.
|
||||
- Handle public-domain apps without leaking private `192.168.x.x` or `100.x.x.x` URLs.
|
||||
- Inject secrets without exposing values in logs or frontend bundles.
|
||||
- Make backup/restore consistent across databases and files.
|
||||
- Migrate existing hand-built containers to package-owned containers.
|
||||
- Keep old alpha nodes working while introducing the new system.
|
||||
- Avoid keeping two permanent systems that drift forever.
|
||||
|
||||
## Alpha Node Impact
|
||||
|
||||
Existing alpha nodes must not be broken.
|
||||
|
||||
Phase 1 behavior:
|
||||
|
||||
- Current Rust installers keep working.
|
||||
- Current app manifests keep working.
|
||||
- New app package loader exists beside the old system.
|
||||
- No existing app is automatically migrated.
|
||||
- Alpha nodes receive compatibility code only.
|
||||
|
||||
Phase 2 behavior:
|
||||
|
||||
- New installs of selected apps use package mode.
|
||||
- Existing installs can be detected and adopted.
|
||||
- App state is preserved.
|
||||
- Migration is opt-in or happens only for low-risk apps.
|
||||
|
||||
Phase 3 behavior:
|
||||
|
||||
- Stable migrated apps switch to package mode by default.
|
||||
- Existing containers are adopted if names/volumes match.
|
||||
- Data directories are preserved.
|
||||
- Old Rust installers remain as fallback for at least one release cycle.
|
||||
|
||||
Phase 4 behavior:
|
||||
|
||||
- Remove old installers only after live alpha validation.
|
||||
- Keep migration repair code for already-deployed nodes.
|
||||
|
||||
## Migration Rules
|
||||
|
||||
For every migrated app:
|
||||
|
||||
- Preserve `/var/lib/archipelago/<app>` data.
|
||||
- Preserve generated secrets.
|
||||
- Preserve credentials shown to users.
|
||||
- Preserve public ports where possible.
|
||||
- Preserve container names where needed for adoption.
|
||||
- Never delete volumes during migration.
|
||||
- Stop/recreate containers only when necessary.
|
||||
- Record migration version in app state.
|
||||
- Provide rollback path to old installer for alpha builds.
|
||||
|
||||
## Notes For The Release
|
||||
|
||||
- Catalog entries should be generated from manifests so the UI and runtime agree on launch metadata.
|
||||
- The developer docs should describe the manifest/runtime contract that exists today, not the older publish-model draft.
|
||||
- If a new capability is needed, add one reusable manifest field or orchestrator primitive and document it here before wiring a one-off app branch.
|
||||
|
||||
## First Apps To Migrate
|
||||
|
||||
Start with low-risk apps:
|
||||
|
||||
- Filebrowser
|
||||
- Vaultwarden
|
||||
- Uptime Kuma
|
||||
- Grafana
|
||||
|
||||
Then moderate apps:
|
||||
|
||||
- Gitea
|
||||
- Nextcloud
|
||||
- SearXNG
|
||||
- Nginx Proxy Manager metadata integration
|
||||
|
||||
Then complex apps:
|
||||
|
||||
- Saleor
|
||||
- Mempool
|
||||
- BTCPay Server
|
||||
- NetBird only if safe
|
||||
|
||||
Leave for later:
|
||||
|
||||
- Bitcoin
|
||||
- LND
|
||||
- Electrs/ElectrumX
|
||||
- Tor
|
||||
- System update
|
||||
- Mesh/Web5/FIPS core services
|
||||
|
||||
## Saleor Reference Goal
|
||||
|
||||
Saleor should become the showcase package. It should prove:
|
||||
|
||||
- Multi-container stack support.
|
||||
- Generated secrets.
|
||||
- Post-install migration/admin user hooks.
|
||||
- Dashboard/API/storefront routes.
|
||||
- Same-origin public GraphQL routing.
|
||||
- Credentials display.
|
||||
- Backup paths.
|
||||
- Health checks.
|
||||
- Public domain support.
|
||||
- Alpha-node adoption.
|
||||
|
||||
Once Saleor is clean, the app system is credible.
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1: Package Contract
|
||||
|
||||
- Use `apps/<app-id>/manifest.yml` as the package contract.
|
||||
- Keep the Rust parser/validator as the canonical schema implementation.
|
||||
- Keep generated catalog output from manifest-owned metadata.
|
||||
- Finish generated app-session launch metadata so launch behavior cannot drift from manifests.
|
||||
- Add/keep tests for unsafe package rejection.
|
||||
|
||||
### Phase 2: Single-Container Runtime
|
||||
|
||||
- Continue hardening package install for one-container apps.
|
||||
- Compile manifests to rootless Podman/Quadlet runtime behavior.
|
||||
- Support ports, env, generated files, devices, volumes, resources, health checks, data UID repair, image pull/build availability checks, and launch metadata.
|
||||
- Keep Filebrowser, Vaultwarden, Portainer, Uptime Kuma, Grafana, SearXNG, Jellyfin, PhotoPrism, Meshtastic, and similar apps as regression proofs.
|
||||
|
||||
### Phase 3: Multi-Container Runtime
|
||||
|
||||
- Decide whether multi-container stacks use a safe `compose.yml` subset or a manifest-native `services` section.
|
||||
- Support app-local networks.
|
||||
- Support service dependencies and readiness gates.
|
||||
- Support internal service names.
|
||||
- Support generated env/secrets across services.
|
||||
- Support controlled hooks only where declarative primitives are insufficient.
|
||||
- Adopt existing multi-container apps without deleting data.
|
||||
|
||||
### Phase 4: Routing
|
||||
|
||||
- Add `proxy/routes.yml`.
|
||||
- Generate host nginx routes.
|
||||
- Generate Tor/public routes.
|
||||
- Fix same-origin API routing class of bugs permanently.
|
||||
- Integrate with Nginx Proxy Manager sync.
|
||||
|
||||
### Phase 5: Migration
|
||||
|
||||
- Add adoption logic for existing containers.
|
||||
- Add migration metadata.
|
||||
- Migrate simple apps.
|
||||
- Migrate Saleor.
|
||||
- Keep rollback.
|
||||
- Prove reboot recovery with repeated clean post-reboot lifecycle passes.
|
||||
- Preserve Nostr signer bridges, Bitcoin dependency wait states, and public launch ports during adoption.
|
||||
|
||||
### Phase 6: Cleanup
|
||||
|
||||
- Remove duplicated catalog/frontend data.
|
||||
- Remove migrated Rust stack installers.
|
||||
- Document package format.
|
||||
- Add developer tooling: validate, test, package, install locally.
|
||||
- Remove stale fallback metadata, app-specific lifecycle branches, and compatibility shims only after live validation.
|
||||
|
||||
## Developer Tooling
|
||||
|
||||
Add commands like:
|
||||
|
||||
```bash
|
||||
archy app validate apps/saleor
|
||||
archy app render apps/saleor
|
||||
archy app install apps/saleor
|
||||
archy app test apps/saleor
|
||||
```
|
||||
|
||||
Developers should be able to package an app without understanding Archipelago internals.
|
||||
|
||||
## Open Source Story
|
||||
|
||||
Public explanation:
|
||||
|
||||
> Archipelago uses rootless Podman and a validated app package format. App authors define services declaratively, while the OS enforces security, secrets, routing, backups, health, and lifecycle repair. This gives us Umbrel-like app packaging with StartOS-like managed service discipline.
|
||||
|
||||
## Rework Estimate
|
||||
|
||||
- Package schema and validator: 1-2 weeks.
|
||||
- Single-container package runtime: 1-2 weeks.
|
||||
- Generated catalog/frontend metadata: 1 week.
|
||||
- Multi-container support: 2-4 weeks.
|
||||
- Routing/public proxy integration: 1-2 weeks.
|
||||
- Hooks/secrets/backups: 2-3 weeks.
|
||||
- First migrations: 2-4 weeks.
|
||||
- Saleor reference migration: 1-2 weeks.
|
||||
- Cleanup/docs/tooling: 2-3 weeks.
|
||||
|
||||
Total estimate: 8-14 weeks of serious work for an excellent system.
|
||||
|
||||
Minimum viable version: 3-5 weeks.
|
||||
|
||||
## Biggest Risks
|
||||
|
||||
- Rootless Podman edge cases continue to bite.
|
||||
- Compose compatibility scope creeps too wide.
|
||||
- Hooks become an unsafe escape hatch.
|
||||
- Migration accidentally disrupts alpha nodes.
|
||||
- Generated metadata drifts from old manual data during transition.
|
||||
- Old and new systems remain permanently duplicated.
|
||||
|
||||
## Risk Controls
|
||||
|
||||
- Support a strict Compose subset, not all Compose.
|
||||
- Validate everything.
|
||||
- Keep hooks minimal and logged.
|
||||
- Migrate one app at a time.
|
||||
- Add live alpha-node checks before each release.
|
||||
- Generate catalog/app-session data early.
|
||||
- Set a deadline for deleting migrated legacy installers.
|
||||
|
||||
## Immediate Next Steps
|
||||
|
||||
1. Expand generated app-session metadata beyond ports/titles/new-tab behavior to cover proxy paths and companion UI aliases where those can be declared safely in manifests.
|
||||
2. Define the app update policy and wire it into manifest/catalog metadata.
|
||||
3. Finish post-reboot adoption and stale scanner-state handling for migrated apps.
|
||||
4. Convert remaining multi-container legacy stacks to a manifest-owned model without deleting data.
|
||||
5. Add developer tooling around the current `manifest.yml` contract: validate, render, local install, lifecycle test.
|
||||
6. Migrate Saleor or another serious multi-container app as the proof package once the stack model is stable.
|
||||
7. Leave Bitcoin/LND/core services as managed infrastructure until the package system is proven for normal apps.
|
||||
Reference in New Issue
Block a user