Build the read side of the unified distribution primitive from docs/roadmap/updates-and-distribution.md: one team-signed catalog (index.json) on the same channel as latest.json, listing type-tagged artifact envelopes. A hotfix is the first artifact type; apps/themes/ components are future envelope rows through the SAME pipe — the marketplace seam is just the `type` + `payload.kind` fields. Phase 1 is fetch + verify + parse only (NO mutation; the snapshot → ops → rollback → History apply verb is Phase 2): - Factor `lpVerifyMinisig` out of `lpFetchRelease` (scripts/source/ fetch.sh) — one trust anchor (the root-owned footprint key) now shared by releases and the index; refactor `lpFetchRelease` to use it (behaviour-preserving, still fail-closed). - scripts/source/artifacts.sh: `lpFetchIndex` — download → verify-before-parse → `valid_until` freshness (anti-withholding) → `index_serial` monotonic high-water (anti-rollback, TUF-lite) → emit verified JSON. Trust core is jq-free; parsing accessors prefer jq with a grep fallback. - `libreportal artifact index` (scripts/cli/commands/artifact/) — read-only front door that fetches, verifies and lists. Runs directly like `updater check` (no task; no mutation). - Regenerate the source arrays + lazy-load function manifest for the new files. Doc: promote the format from vision to spec (§8) — 3 layers (INDEX/ENVELOPE/PIPELINE), the bounded declarative op vocabulary (no run-script, ever), the apply pipeline mapped onto existing functions, the marketplace seam, and resolutions for all five open forks. Self-tested 12/12: trust core fails closed (real key + no minisign → refuse), happy path, stale-refused, rollback-refused, signature-refused, jq + grep parsing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: librelad <librelad@digitalangels.vip>
78 lines
2.8 KiB
Bash
78 lines
2.8 KiB
Bash
#!/bin/bash
|
|
|
|
# Artifact command handler — `libreportal artifact <sub>`
|
|
# ---------------------------------------------------------------------------
|
|
# Dispatched automatically by cli_initialize.sh (category -> cliHandleArtifactCommands).
|
|
#
|
|
# This is PHASE 1 of the unified distribution primitive: the READ side. It fetches
|
|
# and verifies the team-signed artifact index (hotfixes today; apps/themes/
|
|
# components later — all the same envelope) and lists what's available. It makes
|
|
# NO changes to the system, so — like `updater check` — it runs directly rather
|
|
# than through the task system. The state-changing `apply`/`rollback` verbs (which
|
|
# DO route through tasks → snapshot → declarative ops → rollback → History) arrive
|
|
# in Phase 2. See docs/roadmap/updates-and-distribution.md.
|
|
|
|
cliHandleArtifactCommands()
|
|
{
|
|
local sub="$initial_command2"
|
|
|
|
# Lazy-loader gap: ensure the read primitives are defined. These are new
|
|
# files; the array/manifest regen self-heals them on deploy, but this covers
|
|
# the window before that (mirrors cli_updater_commands.sh sourcing its
|
|
# generator). artifacts.sh leans on fetch.sh helpers, so load both.
|
|
if ! declare -F lpFetchIndex >/dev/null 2>&1; then
|
|
source "$install_scripts_dir/source/fetch.sh" 2>/dev/null
|
|
source "$install_scripts_dir/source/artifacts.sh" 2>/dev/null
|
|
fi
|
|
|
|
case "$sub" in
|
|
""|"index"|"list")
|
|
artifactListIndex
|
|
;;
|
|
*)
|
|
cliShowArtifactHelp
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Fetch + verify the signed index and print a human summary. Read-only.
|
|
artifactListIndex()
|
|
{
|
|
isHeader "Artifact index ($(lpReleaseChannel))"
|
|
|
|
local json
|
|
if ! json="$(lpFetchIndex)"; then
|
|
isError "Could not fetch or verify the artifact index from $(lpArtifactIndexUrl)."
|
|
isNotice "Nothing is published yet, or the channel is unreachable. (This is expected before the first index ships.)"
|
|
return 1
|
|
fi
|
|
|
|
local serial generated_at
|
|
serial="$(_lpJsonNum "$json" index_serial)"
|
|
generated_at="$(lpIndexTop generated_at "$json")"
|
|
isNotice "Signed + verified. serial=${serial:-?} generated=${generated_at:-?}"
|
|
|
|
local ids; ids="$(lpIndexArtifactIds "$json")"
|
|
if [[ -z "$ids" ]]; then
|
|
isSuccessful "0 artifacts available — the index is empty (nothing to apply)."
|
|
return 0
|
|
fi
|
|
|
|
local n=0 id obj title type sev
|
|
while IFS= read -r id; do
|
|
[[ -z "$id" ]] && continue
|
|
n=$((n + 1))
|
|
obj="$(lpArtifactById "$json" "$id")"
|
|
if [[ -n "$obj" ]]; then
|
|
title="$(_lpJsonStr "$obj" title)"
|
|
type="$(_lpJsonStr "$obj" type)"
|
|
sev="$(_lpJsonStr "$obj" severity)"
|
|
echo " • [${type:-?}/${sev:-info}] $id — ${title:-}"
|
|
else
|
|
echo " • $id"
|
|
fi
|
|
done <<< "$ids"
|
|
|
|
isSuccessful "$n artifact(s) available."
|
|
}
|