docs(app-platform): sync the platform docs with the shipped code
- app-manifest-spec.md: full rewrite from the real schema in core/container/src/manifest.rs — it was 5 months stale and missing the entire modern feature set (build, network/network_aliases, derived_env, secret_env, generated_secrets/certs, data_uid, files, interfaces, hooks, extensions flatten) and documented wrong network_policy values. - app-developer-guide.md: add generated_secrets/generated_certs/ network_aliases/hooks to the field table. - APP-PACKAGING-MIGRATION-PLAN.md: phase status stamped (1-3 done, 5 mostly, 4+6 open); deleted meshtastic app removed from regression-proof lists. - registry-manifest-design.md: status design → implemented (phases 1-3), stale manifest_dir:Option line fixed. - marketplace-protocol.md: reframed proposal → as-built (marketplace.rs + RPCs + UI shipped; create-invoice noted; schema disambiguated). - manifest-hooks-design.md: phase 3 (indeedhub) done, phase 4 resolved via orchestrator+generated_secrets instead of hooks. - README/architecture: restore the NIP-07 signer-bridge claim — it is real (neode-ui/public/nostr-provider.js), the earlier audit only grepped Rust. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
9897af1236
commit
6e2d128c51
@ -43,7 +43,7 @@ Storage (FileBrowser, Immich, Nextcloud), Productivity (Vaultwarden), Media (Jel
|
||||
- Ed25519 node identity with DID Documents (did:key)
|
||||
- Multi-identity management (Personal/Business/Anonymous)
|
||||
- W3C Verifiable Credentials issuance and verification
|
||||
- Nostr integration: NIP-33 node discovery, NIP-44/NIP-04 encryption, relay hosting
|
||||
- Nostr integration: NIP-33 node discovery, NIP-44/NIP-04 encryption, NIP-07 signer bridge for iframe apps, relay hosting
|
||||
- Decentralized Web Node (DWN) record sync between federated nodes over Tor
|
||||
|
||||
### Multi-Node Federation
|
||||
|
||||
@ -32,7 +32,7 @@ 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.
|
||||
- Manifest-owned generated files exist through `app.files` and have been used for app config material (e.g. strfry, netbird 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`.
|
||||
@ -41,7 +41,7 @@ As of the current `1.8-alpha` workstream:
|
||||
- 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.
|
||||
- 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, 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.
|
||||
|
||||
@ -327,6 +327,14 @@ Once a complex stack is clean, the app system is credible.
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
**Status (2026-07-08):** Phases 1–3 ✅ DONE (per-member manifests won over a
|
||||
compose subset; all five real multi-container stacks — btcpay, mempool,
|
||||
immich, netbird, indeedhub — install via `install_stack_via_orchestrator`).
|
||||
Phase 5 mostly done (orchestrator-first with legacy fallback + per-app
|
||||
adoption/repair). Phase 4 (routing via `proxy/routes.yml`) NOT started —
|
||||
routing is still host-nginx driven. Phase 6 (cleanup + developer CLI) NOT
|
||||
started; the CLI gates external app publishing.
|
||||
|
||||
### Phase 1: Package Contract
|
||||
|
||||
- Use `apps/<app-id>/manifest.yml` as the package contract.
|
||||
@ -340,7 +348,7 @@ Once a complex stack is clean, the app system is credible.
|
||||
- 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.
|
||||
- Keep Filebrowser, Vaultwarden, Portainer, Uptime Kuma, Grafana, SearXNG, Jellyfin, PhotoPrism, and similar apps as regression proofs.
|
||||
|
||||
### Phase 3: Multi-Container Runtime
|
||||
|
||||
|
||||
@ -110,8 +110,12 @@ app:
|
||||
| `app.container.network` | Podman network setting such as `archy-net` or `pasta`; dangerous namespace-sharing modes are rejected |
|
||||
| `app.container.entrypoint` / `custom_args` | Entrypoint and command override |
|
||||
| `app.container.derived_env` | Environment values rendered from allowed host facts such as `HOST_IP`, `HOST_MDNS`, and `DISK_GB` |
|
||||
| `app.container.secret_env` | Environment values read from `/var/lib/archipelago/secrets/<secret_file>` |
|
||||
| `app.container.secret_env` | Environment values read from `/var/lib/archipelago/secrets/<secret_file>`, injected as podman secrets (never visible in `podman inspect` or unit files) |
|
||||
| `app.container.generated_secrets` | Secrets the orchestrator creates on first use (`hex16`/`hex32`/`base64`/`bcrypt`) — self-healing, 0600, no host provisioning |
|
||||
| `app.container.generated_certs` | Self-signed TLS certs materialised before create; CN/SANs rendered from host facts |
|
||||
| `app.container.network_aliases` | Extra DNS names on the app network so stack members answer to short baked-in hostnames (`api`, `minio`, `relay`) |
|
||||
| `app.container.data_uid` | UID:GID ownership repair for app data directories |
|
||||
| `app.hooks` | Allow-listed lifecycle hooks (`post_install`: `exec` inside the app's own container, `copy_from_host` from allow-listed roots) — see `manifest-hooks-design.md` |
|
||||
| `app.dependencies` | Storage requirements and app dependencies |
|
||||
| `app.resources` | CPU, memory, and disk limits |
|
||||
| `app.security` | Capabilities, read-only root, no-new-privileges, network policy, optional AppArmor profile |
|
||||
|
||||
@ -1,126 +1,155 @@
|
||||
# App Manifest Specification
|
||||
|
||||
## Overview
|
||||
_Accurate as of 2026-07-08. The canonical schema is the Rust parser in
|
||||
`core/container/src/manifest.rs` (`AppManifest` → `AppDefinition`); if this
|
||||
document and the code disagree, the code wins. See
|
||||
[`app-developer-guide.md`](app-developer-guide.md) for the authoring workflow._
|
||||
|
||||
App manifests define containerized applications in Archipelago. They use YAML format and specify container configuration, dependencies, resources, security policies, and integration metadata.
|
||||
Every app is a directory `apps/<id>/` containing a `manifest.yml` with a single
|
||||
top-level `app:` block. Apps are purely declarative — the orchestrator owns the
|
||||
entire lifecycle; there is no per-app installer code.
|
||||
|
||||
## File Location
|
||||
## Top-level fields (`app:`)
|
||||
|
||||
App manifests are stored in `apps/{app-id}/manifest.yml`
|
||||
| Field | Type | Required | Notes |
|
||||
|-------|------|----------|-------|
|
||||
| `id` | string | ✅ | Lowercase alphanumeric + `-`/`_`. Must match the directory name. |
|
||||
| `name` | string | ✅ | Display name. |
|
||||
| `version` | string | ✅ | App version shown in the UI. |
|
||||
| `description` | string | — | One-line description. |
|
||||
| `container` | ContainerConfig | — | Image/build source + runtime shape (below). |
|
||||
| `dependencies` | list | — | `- storage: "10GB"`, `- { app_id: bitcoin, version: … }`, or a bare string. |
|
||||
| `resources` | ResourceLimits | — | `cpu_limit` (int), `memory_limit` (e.g. `"512m"`), `disk_limit`. |
|
||||
| `security` | SecurityPolicy | — | See [Security](#security). |
|
||||
| `ports` | list of PortMapping | — | `- { host: 8080, container: 80, protocol: tcp }`. |
|
||||
| `volumes` | list of Volume | — | See [Volumes](#volumes). |
|
||||
| `files` | list of GeneratedFile | — | Config files written before create: `{ path, content, overwrite }`. `path` must sit under a declared bind mount. |
|
||||
| `environment` | list of string | — | `- KEY=value` pairs (static). |
|
||||
| `health_check` | HealthCheck | — | `{ type, endpoint/path, interval, timeout, retries }`. `type` is free-form today; `http` is what the monitor exercises. |
|
||||
| `devices` | list of string | — | Host device paths; must start with `/dev/`. |
|
||||
| `interfaces` | map | — | Launch surfaces, keyed by name (`main`): `{ name, description, type, port, protocol, path }`. |
|
||||
| `hooks` | LifecycleHooks | — | Allow-listed lifecycle hooks. See [Hooks](#hooks). |
|
||||
| _anything else_ | — | — | Unknown keys are absorbed into an `extensions` map (serde flatten) and treated as transitional metadata — e.g. `container_name`, `metadata`, `category`, `bitcoin_integration`, `lightning_integration`. These are **not** typed schema; do not rely on them being validated. |
|
||||
|
||||
## Schema
|
||||
## `container:` (ContainerConfig)
|
||||
|
||||
### Required Fields
|
||||
Exactly **one** of `image` or `build` must be present (image XOR build).
|
||||
|
||||
| Field | Type | Notes |
|
||||
|-------|------|-------|
|
||||
| `image` | string | Registry reference. Pull source. |
|
||||
| `image_signature` | string | Optional signature reference for image verification. |
|
||||
| `pull_policy` | string | Default `if-not-present`. |
|
||||
| `build` | BuildConfig | Local build: `{ context, dockerfile (default "Dockerfile"), tag, build_args }`. |
|
||||
| `network` | string | Literal podman `--network` value (`archy-net`, `host`, a stack network, …). Omitted = rootless default isolated network. |
|
||||
| `network_aliases` | list of string | Extra DNS names on `network` (podman `--network-alias`) — lets stack members answer to short baked-in hostnames (`api`, `minio`, `relay`). |
|
||||
| `entrypoint` | list of string | Entrypoint override. |
|
||||
| `custom_args` | list of string | Extra positional args appended after the image. |
|
||||
| `derived_env` | list | `- { key, template }` — template rendered against host facts at apply time. Allowed placeholders: `{{HOST_IP}}`, `{{HOST_MDNS}}`, `{{DISK_GB}}` (plus dependency-resolved facts such as the active bitcoin host). Never hard-code host specifics. |
|
||||
| `secret_env` | list | `- { key, secret_file }` — value read from `/var/lib/archipelago/secrets/<secret_file>` and injected as a **podman secret**, so it never appears in `podman inspect` or unit files. `secret_file` must be a bare filename (no `/`, no `..`). |
|
||||
| `generated_secrets` | list | `- { name, kind }` — orchestrator materialises the secret on first use (0600, rootless service user, idempotent + self-healing). `kind ∈ hex16 | hex32 | base64 | bcrypt` (bcrypt writes `<name>` = hash and `<name>.pw` = plaintext). |
|
||||
| `generated_certs` | list | `- { crt, key, common_name?, sans? }` — self-signed TLS materialised before create; CN/SANs rendered against host facts. |
|
||||
| `data_uid` | string | `"UID:GID"` applied to the app's bind-mounted data dir before create (rootless subuid mapping, e.g. Postgres). |
|
||||
|
||||
## Security
|
||||
|
||||
```yaml
|
||||
app:
|
||||
id: string # Unique app identifier (lowercase, kebab-case)
|
||||
name: string # Human-readable name
|
||||
version: string # Semantic version (e.g., "1.0.0")
|
||||
container:
|
||||
image: string # Container image (e.g., "bitcoin/bitcoin:26.0")
|
||||
security:
|
||||
readonly_root: true # default true
|
||||
no_new_privileges: true # default true
|
||||
capabilities: [CHOWN] # default [] (cap-drop ALL, add back only these)
|
||||
network_policy: isolated # isolated | bridge | host (default isolated)
|
||||
apparmor_profile: null # optional profile name
|
||||
```
|
||||
|
||||
### Optional Fields
|
||||
Validation (enforced at `AppManifest::validate()`):
|
||||
|
||||
- Capabilities must come from the reviewed allow-list (CHOWN, DAC_OVERRIDE,
|
||||
FOWNER, NET_ADMIN, NET_BIND_SERVICE, NET_RAW, SETGID, SETUID, SYS_ADMIN).
|
||||
- `network_policy` must be exactly `isolated`, `bridge`, or `host`.
|
||||
- No `container:`/`ns:` network modes; devices must be `/dev/*`.
|
||||
- Bind-mount sources are confined to `/var/lib/archipelago` (reviewed
|
||||
exceptions: the rootless podman socket and dbus).
|
||||
- `derived_env` templates may only use the placeholder allow-list;
|
||||
`secret_env`/`generated_secrets` names must be bare filenames.
|
||||
- Hook steps are validated against the hook allow-list (below).
|
||||
|
||||
## Volumes
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- type: bind # bind | volume | tmpfs
|
||||
source: /var/lib/archipelago/myapp/data
|
||||
target: /data
|
||||
options: [rw] # allow-list: rw, ro, z, Z, shared, …
|
||||
- type: tmpfs
|
||||
target: /tmp
|
||||
tmpfs_options: "rw,noexec,nosuid,size=256m"
|
||||
```
|
||||
|
||||
## Hooks
|
||||
|
||||
Declarative, allow-listed operations that run against the app's **own
|
||||
container** — never the host (design: `manifest-hooks-design.md`).
|
||||
|
||||
```yaml
|
||||
hooks:
|
||||
post_install: # runs once after install, container running
|
||||
- copy_from_host: # src relative to an allow-listed root (data dir / web-ui);
|
||||
src: web-ui/nostr-provider.js # no absolute paths, no '..'
|
||||
dest: /usr/share/nginx/html/nostr-provider.js
|
||||
- exec: ["sh", "-c", "nginx -s reload"] # podman exec inside the container
|
||||
pre_start: [] # reserved in the schema; executor not yet wired
|
||||
```
|
||||
|
||||
## Installation semantics
|
||||
|
||||
The orchestrator compiles the manifest into a rootless Podman **Quadlet unit
|
||||
under `user.slice`** — the container survives backend restarts and reboots, and
|
||||
a level-triggered reconciler converges drift every 30 seconds. Multi-container
|
||||
apps are sets of per-member manifests installed together via the stack
|
||||
orchestrator (`api/rpc/package/stacks.rs`) on an app-local network.
|
||||
|
||||
## Distribution
|
||||
|
||||
Manifests ship two ways:
|
||||
|
||||
1. **Signed catalog** (primary): `releases/app-catalog.json` embeds the full
|
||||
manifest per app and carries an Ed25519 detached signature verified against
|
||||
the pinned release-root anchor. Nodes overlay catalog manifests over disk
|
||||
files — **catalog wins** for image-only apps; `apps/<id>/manifest.yml` on
|
||||
disk remains the fallback and is still required for build-source apps.
|
||||
2. **Decentralized marketplace**: Nostr NIP-78 discovery with DID-signed
|
||||
manifests ([`marketplace-protocol.md`](marketplace-protocol.md)). Note the
|
||||
marketplace uses its own flatter manifest schema, not this one.
|
||||
|
||||
## Minimal example
|
||||
|
||||
```yaml
|
||||
app:
|
||||
description: string # App description
|
||||
|
||||
id: myapp
|
||||
name: My App
|
||||
version: 1.0.0
|
||||
description: Does something useful
|
||||
container:
|
||||
image_signature: string # Cosign signature URL (e.g., "cosign://...")
|
||||
pull_policy: string # "if-not-present" | "always" | "never"
|
||||
|
||||
dependencies:
|
||||
- storage: string # Minimum disk space (e.g., "500Gi")
|
||||
- app_id: string # Required app dependency
|
||||
version: string # Version constraint (e.g., ">=26.0")
|
||||
- string # Simple app dependency
|
||||
|
||||
resources:
|
||||
cpu_limit: number # CPU cores (e.g., 2)
|
||||
memory_limit: string # Memory limit (e.g., "2Gi", "512Mi")
|
||||
disk_limit: string # Disk limit (e.g., "500Gi")
|
||||
|
||||
security:
|
||||
capabilities: [string] # Linux capabilities (e.g., ["NET_BIND_SERVICE"])
|
||||
readonly_root: boolean # Read-only root filesystem (default: true)
|
||||
network_policy: string # "isolated" | "host" | network name
|
||||
apparmor_profile: string # AppArmor profile name
|
||||
|
||||
image: docker.io/vendor/myapp:1.0.0
|
||||
generated_secrets:
|
||||
- { name: myapp-admin-password, kind: hex16 }
|
||||
secret_env:
|
||||
- { key: ADMIN_PASSWORD, secret_file: myapp-admin-password }
|
||||
ports:
|
||||
- host: number # Host port
|
||||
container: number # Container port
|
||||
protocol: string # "tcp" | "udp" (default: "tcp")
|
||||
|
||||
- { host: 8090, container: 8080 }
|
||||
volumes:
|
||||
- type: string # "bind" | "tmpfs" | "volume"
|
||||
source: string # Host path
|
||||
target: string # Container path
|
||||
options: [string] # Mount options (e.g., ["rw", "noexec"])
|
||||
|
||||
environment:
|
||||
- string # Environment variable (e.g., "NETWORK=mainnet")
|
||||
|
||||
devices:
|
||||
- string # Device path (e.g., "/dev/ttyUSB0")
|
||||
|
||||
- { type: bind, source: /var/lib/archipelago/myapp, target: /data, options: [rw] }
|
||||
health_check:
|
||||
type: string # "http" | "exec"
|
||||
endpoint: string # HTTP URL or command
|
||||
path: string # HTTP path (for http type)
|
||||
interval: string # Check interval (e.g., "30s")
|
||||
timeout: string # Timeout (e.g., "5s")
|
||||
retries: number # Failure retries (default: 3)
|
||||
|
||||
# Integration-specific metadata
|
||||
bitcoin_integration:
|
||||
rpc_access: string # "admin" | "read-only"
|
||||
sync_required: boolean # Requires synced node
|
||||
testnet_support: boolean
|
||||
pruning_support: boolean
|
||||
|
||||
lightning_integration:
|
||||
channel_management: boolean
|
||||
payment_routing: boolean
|
||||
|
||||
nostr_integration:
|
||||
relay_type: string # "public" | "private"
|
||||
monetization_enabled: boolean
|
||||
event_storage: string # "sqlite" | "postgres"
|
||||
|
||||
web5_integration:
|
||||
did_support: boolean
|
||||
dwn_protocol: boolean
|
||||
sync_enabled: boolean
|
||||
|
||||
networking:
|
||||
mesh_enabled: boolean
|
||||
local_network_access: boolean
|
||||
device_discovery: boolean
|
||||
routing_protocols: [string] # e.g., ["olsr", "babel"]
|
||||
type: http
|
||||
path: /health
|
||||
interfaces:
|
||||
main:
|
||||
type: web
|
||||
port: 8090
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
See `apps/` directory for complete examples:
|
||||
- `apps/bitcoin-core/manifest.yml`
|
||||
- `apps/lnd/manifest.yml`
|
||||
- `apps/nostr-rs-relay/manifest.yml`
|
||||
- `apps/meshtastic/manifest.yml`
|
||||
|
||||
## Validation
|
||||
|
||||
Manifests are validated on installation:
|
||||
- Required fields present
|
||||
- Version format valid
|
||||
- Resource limits reasonable
|
||||
- Port conflicts detected
|
||||
- Dependency cycles prevented
|
||||
|
||||
## Versioning
|
||||
|
||||
- Use semantic versioning (MAJOR.MINOR.PATCH)
|
||||
- Breaking changes increment MAJOR
|
||||
- New features increment MINOR
|
||||
- Bug fixes increment PATCH
|
||||
Validate with `scripts/validate-app-manifest.sh` and regenerate the catalog
|
||||
with `scripts/generate-app-catalog.py` (drift-checked in CI by
|
||||
`scripts/check-app-catalog-drift.py`).
|
||||
|
||||
@ -204,7 +204,7 @@ mesh, steganography, store-and-forward outbox.
|
||||
- Bitcoin Core **and** Knots with per-app version pinning and safe switching; LND + Core Lightning
|
||||
- Multi-node federation with invite-based joining and trust levels
|
||||
- W3C DID identity (did:key, DID Documents, Verifiable Credentials)
|
||||
- Nostr: NIP-33 node discovery, NIP-44/NIP-04 encryption, relay hosting
|
||||
- Nostr: NIP-33 node discovery, NIP-44/NIP-04 encryption, NIP-07 signer bridge for iframe apps, relay hosting
|
||||
- Decentralized marketplace (NIP-78 discovery, trust scoring, Lightning purchases)
|
||||
- File sharing with access controls (free/peers-only/paid via LN, on-chain, ecash)
|
||||
- Encrypted backups (Argon2 + ChaCha20-Poly1305)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Manifest Lifecycle Hooks — Design
|
||||
|
||||
**Status:** design (2026-06-21) · Task #20 · Prereq for migrating complex stacks
|
||||
**Status:** implemented through Phase 4 (see §6; updated 2026-07-08) — only declarative `pre_start` remains · originally Task #20
|
||||
(indeedhub, netbird) off legacy Rust installers.
|
||||
|
||||
See `docs/PRODUCTION-MASTER-PLAN.md`, `docs/APP-PACKAGING-MIGRATION-PLAN.md`
|
||||
@ -101,7 +101,14 @@ the `post_install` hook above. `install_indeedhub_stack` becomes orchestrator-fi
|
||||
(`exec` + `copy_from_host`, canonicalise + symlink-escape prefix check, best-effort);
|
||||
called from `install_fresh` after the container is up, fresh-container-only.
|
||||
(commit `955c54b7`)
|
||||
3. ⏳ **indeedhub**: author member manifests + frontend `post_install` hook; wire
|
||||
`install_indeedhub_stack` orchestrator-first; live-migrate + verify on .228.
|
||||
4. ⏳ **netbird**: assess its setup steps; migrate with hooks.
|
||||
5. ⏳ `pre_start` hooks (repair/ownership) — type exists; executor not yet wired.
|
||||
3. ✅ **indeedhub**: member manifests + frontend `post_install` hooks shipped
|
||||
(`apps/indeedhub/manifest.yml` declares the nostr-provider copy + nginx
|
||||
reload; `install_indeedhub_stack` is orchestrator-first via
|
||||
`install_stack_via_orchestrator`).
|
||||
4. ✅ **netbird** (resolved differently): installs via the stack orchestrator,
|
||||
but its setup is handled by `generated_secrets`/`generated_certs` + the
|
||||
per-app Rust `run_pre_start_hooks` path rather than manifest hooks — no
|
||||
`hooks:` block in its manifest.
|
||||
5. ⏳ `pre_start` hooks (repair/ownership) — type exists; executor not yet
|
||||
wired. Note: `prod_orchestrator.rs::run_pre_start_hooks` is a hardcoded
|
||||
per-app Rust match today, NOT this declarative path.
|
||||
|
||||
@ -1,5 +1,15 @@
|
||||
# Decentralized App Marketplace Protocol
|
||||
|
||||
**Status:** implemented (updated 2026-07-08). This started as a protocol
|
||||
proposal; the described subsystem is now shipped end-to-end —
|
||||
`core/archipelago/src/marketplace.rs` (discover/publish/trust scoring),
|
||||
the `marketplace.*` RPC namespace, and `Marketplace.vue`. Beyond this doc,
|
||||
the code also adds `marketplace.create-invoice` (Lightning BOLT11 app
|
||||
purchases). What remains is maturation: publishing tooling and trust UX
|
||||
(see `ROADMAP.md`). Note: the manifest schema below is the marketplace's
|
||||
own flatter format, **not** the runtime `apps/*/manifest.yml` schema
|
||||
(`app-manifest-spec.md`).
|
||||
|
||||
## Overview
|
||||
|
||||
Archipelago's community marketplace enables developers to publish app manifests to Nostr relays, where nodes discover and install them without a central app store. Trust is established through DID-signed manifests and community reputation.
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
# Registry-Distributed App Manifests — Design
|
||||
|
||||
**Status:** design (2026-06-21)
|
||||
**Status:** implemented — Phases 1–3 shipped (schema + catalog-wins overlay,
|
||||
signed publisher generator with embedded manifests for all apps, immich
|
||||
end-to-end via `install_stack_via_orchestrator`); Phases 4–5 (build-context
|
||||
apps content-addressed, drop `apps/` from OTA) remain open. Updated 2026-07-08.
|
||||
**Goal (north-star):** every app installs from a manifest distributed via the
|
||||
signed app-catalog on the registry — **no OS-level code reliance, no
|
||||
OTA-shipped disk manifest required**. Rootless, signed, robust, reboot-survivable.
|
||||
@ -120,17 +123,19 @@ Add a generator (extend `create-release.sh` / a small `scripts/gen-app-catalog`)
|
||||
|
||||
## 7. Phases
|
||||
|
||||
1. **Schema + load merge** (this design): `manifest` field, `load_manifests`
|
||||
catalog-wins merge, `manifest_dir: Option`, unit tests (catalog overrides disk;
|
||||
bad catalog manifest → disk fallback; absent → disk). Image-only apps.
|
||||
2. **Publisher generator + signing**: emit embedded+signed catalog; CI/release wiring.
|
||||
3. **First real app end-to-end**: immich as 3 registry manifests
|
||||
(`immich-postgres`/`immich-redis`/`immich-server`) installed via
|
||||
`install_stack_via_orchestrator` (delete legacy `install_immich_stack`).
|
||||
Uses `generated_secrets: [immich-db-password]` (already built).
|
||||
4. **Build-context apps**: content-addressed build contexts in the catalog (DHT
|
||||
1. ✅ **Schema + load merge** (this design): `manifest` field, `load_manifests`
|
||||
catalog-wins merge, unit tests (catalog overrides disk; bad catalog
|
||||
manifest → disk fallback; absent → disk); `manifest_dir` stayed a plain
|
||||
`PathBuf` (see §4). Image-only apps.
|
||||
2. ✅ **Publisher generator + signing**: `releases/app-catalog.json` embeds a
|
||||
full `manifest` block per app (all disk manifests covered) and is verified
|
||||
via the release-root detached signature.
|
||||
3. ✅ **First real app end-to-end**: immich installs via
|
||||
`install_stack_via_orchestrator` with `generated_secrets`; the
|
||||
`install_immich_stack` name survives only as an orchestrator-first wrapper.
|
||||
4. ⏳ **Build-context apps**: content-addressed build contexts in the catalog (DHT
|
||||
swarm fetch) so companions stop needing disk too.
|
||||
5. **Drop `apps/` from OTA** once coverage + live verification complete.
|
||||
5. ⏳ **Drop `apps/` from OTA** once coverage + live verification complete.
|
||||
|
||||
## 8. Open questions
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user