-`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.
- Runtime package listings now derive LAN launch URLs from manifest-owned `interfaces.main` declarations or HTTP app ports before falling back to legacy compatibility aliases.
- 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.
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.
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.
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.
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.