Merge claude/2
This commit is contained in:
commit
9b5a4d276d
@ -85,6 +85,33 @@ builds. Same tooling, different `<channel>`. To promote an edge build to stable,
|
||||
rebuild with `make_release.sh stable` at that ref (or copy its artifacts into the
|
||||
`stable/` path and update `stable/latest.json`).
|
||||
|
||||
## Publish an app to the marketplace catalog
|
||||
|
||||
The registry/marketplace rides the same signed channel as releases and hotfixes
|
||||
(`docs/roadmap/updates-and-distribution.md` §8): one `index.json`, one signing
|
||||
key, one static tree. An app in the catalog is just the drop-in definition
|
||||
folder, packed and signed:
|
||||
|
||||
```bash
|
||||
scripts/release/make_app.sh <slug> [channel] [spec.json] # e.g. make_app.sh speedtest
|
||||
```
|
||||
|
||||
Produces, under `dist/<channel>/`: `payloads/app-<slug>.tar.gz(.minisig)`, a
|
||||
pinned catalog icon under `payloads/icons/`, and the upserted + re-signed
|
||||
`index.json`. Title/category/descriptions come from the app's own `.config` —
|
||||
no second source of truth; the optional `spec.json` overlays envelope fields
|
||||
(`id`, `version`, `why`, `applies_when.min_lp`, …). Re-publishing an unchanged
|
||||
app is byte-identical and keeps its version; changed bytes auto-bump it.
|
||||
|
||||
Boxes surface catalog apps in the App Center as **"Available — Add"** cards
|
||||
(`updater check` refreshes `registry_catalog.json`), and `libreportal app add
|
||||
<slug>` verifies + quarantine-validates the bundle and drops the definition
|
||||
into the install tree — from there it's a normal app with a normal Install
|
||||
button. Local definitions always win over the catalog; installed apps are
|
||||
never touched. Test the whole loop against a local server exactly like a
|
||||
release (`python3 -m http.server` + `LP_RELEASE_BASE_URL`), starting with
|
||||
`libreportal artifact index`.
|
||||
|
||||
## Test a release locally before publishing
|
||||
|
||||
No hosting needed — serve `dist/` and point an install at it:
|
||||
|
||||
@ -358,6 +358,36 @@ components is **purely additive**:
|
||||
|
||||
This is exactly the §3 "registry, not marketplace" shape, now expressed in the format.
|
||||
|
||||
### 8.4b App bundles — the registry payload (spec addendum, built 2026-07-03)
|
||||
|
||||
The first non-hotfix type through the pipe. **Envelope**: byte-for-byte §8.1 with
|
||||
`type:"app"`, `payload.kind:"bundle"` (a `.tar.gz`), the app slug in
|
||||
`applies_when.app` (fork 3's field doing double duty — for `type:"app"` the
|
||||
resolver skips the "must be installed" gate; presence is the collision policy's
|
||||
call), and one **optional additive object** for the browse UI:
|
||||
|
||||
```jsonc
|
||||
"meta": { "category": "media", "description": "…", "long_description": "…",
|
||||
"icon": "stable/payloads/icons/<slug>.svg", "icon_sha256": "…" }
|
||||
```
|
||||
|
||||
Old boxes ignore `meta` (parsers select known fields). Icons are mirrored locally
|
||||
by the scan **only when their bytes match the pin** — the browser is never pointed
|
||||
at a remote host.
|
||||
|
||||
**Tarball contract** (enforced fail-closed by the box-side quarantine validator
|
||||
*before* anything lands in the live-sourced definition tree — placing a definition
|
||||
is deferred code execution, so trust gates → quarantine → placement, in that
|
||||
order): gzip; exactly one top-level dir `== slug`; slug `^[a-z0-9][a-z0-9_]{0,31}$`
|
||||
(shell-identifier-safe; `template`/`libreportal`/`tools`/`scripts`/`resources`
|
||||
reserved); regular files + dirs only (no links/devices); no `..`/absolute paths;
|
||||
path charset `[A-Za-z0-9._/@:+-]`; caps 5 MB tarball / 20 MB declared extracted /
|
||||
400 entries; set-id bits stripped; must contain `<slug>.config` (TITLE+CATEGORY,
|
||||
parsed line-wise, never sourced) + `docker-compose.yml`; every `*.sh` must pass
|
||||
`bash -n`. **Collision policy:** an installed app refuses (updating live apps is
|
||||
the updater's job); a local non-registry definition always wins; a registry-owned
|
||||
definition updates reversibly (prior tree packed into the undo array).
|
||||
|
||||
### 8.5 Fork resolutions (was §6)
|
||||
|
||||
1. **Hotfix scope** → **config/compose ops + checksum-pinned file patches; NO code
|
||||
@ -449,8 +479,29 @@ This is exactly the §3 "registry, not marketplace" shape, now expressed in the
|
||||
- ✅ **Phase 5 — publisher tooling (BUILT 2026-05-31).** `make_hotfix.sh` turns a spec into
|
||||
the signed payload + index entry (serial bump, freshness, publishers map), minisign-signs
|
||||
with `LP_MINISIGN_SECKEY`. Verified end-to-end in unsigned/local mode.
|
||||
- ⬜ **Deferred (registry; additive, demand-gated — intentionally NOT built).**
|
||||
`payload.kind:"bundle"` applier (verify tarball → extract into the app tree → scan/regen) +
|
||||
`type:"app"|"theme"|"component"` + the `app_add` task + community trust-tier **host-script
|
||||
quarantine** (§3.2) + multi-source "tap" UX + the warrant-canary countersigning
|
||||
`index_serial`. The hotfix product (Phases 1–5) is complete; the registry waits for demand.
|
||||
- ✅ **Phase 6 — the registry slice: app bundles + Browse-&-Add (BUILT 2026-07-03).**
|
||||
- `make_app.sh <slug>` packs `containers/<slug>/` (the unchanged drop-in contract)
|
||||
into a deterministic signed tarball + a `type:"app"` / `payload.kind:"bundle"`
|
||||
envelope (metadata derived from the app's own `.config`; `auto:false` forced —
|
||||
apps never auto-apply). Shared index logic factored into `lib/release_index.sh`.
|
||||
- Box side: the two designed seams opened (`_artifactResolve` accepts `app`;
|
||||
`_artifactApplyBundleFlow` handles `bundle`) — fetch → sha256-pin → minisig →
|
||||
**quarantine validation** (§8.8) → place in the definition tree → regen →
|
||||
verify-in-apps.json → applied-record with precise undo → History. Collision
|
||||
policy: installed-live refused, local definitions win, registry-owned re-add =
|
||||
reversible definition update. Revert removes/restores the definition.
|
||||
- `libreportal app add <slug|id>` (the `app_add` task) + `webuiRegistryCatalogScan`
|
||||
(registry_catalog.json + pin-verified same-origin icons) + App Center
|
||||
"Available — Add" cards (indigo pill, official badge; local always wins).
|
||||
The Improvements stream is now explicitly hotfix-only (unknown types skip+log).
|
||||
- En route, two latent trust-gate bugs fixed: `LP_INDEX_SIGSTATE` (and the whole
|
||||
resolve-global set) was stranded in command-substitution subshells — on a
|
||||
signed box every apply would have refused as unsigned. `lpFetchIndexInto` +
|
||||
direct-call resolve now propagate them.
|
||||
- Harness-tested: 10/10 (read path), 46/46 (bundle applier), 14/14 (catalog scan);
|
||||
live task-loop + WebUI verified visually.
|
||||
- ⬜ **Deferred (additive, demand-gated — intentionally NOT built).** Community
|
||||
trust-tier **host-script quarantine** (§3.2) + multi-source "tap" UX +
|
||||
`type:"theme"|"component"` bundle handlers + the warrant-canary countersigning
|
||||
`index_serial`. The browsable marketplace *website* (shipped as a dev-mode-gated
|
||||
LibrePortal app) is designed in `marketplace-website.md`.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user