The mutating side of the unified distribution primitive (spec §8.3). Hotfixes can now be applied and reverted, first-party, through the task system. New scripts/cli/commands/artifact/cli_artifact_apply.sh: - artifactApply <id>: resolve+gate (applies_when / min_lp / max_lp / max_footprint / publishers-map role) → fetch+verify payload (sha256 pinned by the signed index + minisig) → dry-precheck ALL ops (all-or-nothing) → best- effort snapshot → apply each op recording a precise inverse → bring app up → auto-rollback (replay undo LIFO, snapshot fallback) → applied-record + History. - artifactRevert <id>: replay the applied-record's undo log (LIFO). - Bounded, CLOSED op vocabulary (no run-script/exec, ever): set-config-key, set-compose-image, patch-file-if-checksum-matches, set-data-file. An unsupported op rejects the whole artifact at precheck (fail-closed). - Write-target firewall: scope:app → containers/<app>/ only; scope:system → configs/ only; the install tree (our code) is off-limits to hotfixes (fork 1). Drift guards (expect_current / checksum) skip cleanly rather than clobber. - Two-tier trust: index minisig-verified vs the footprint key (lpFetchIndex) covers the envelope; payload sha256-pinned + minisig-verified; publishers-map role gate (a non-official publisher can't claim official). Community per- artifact-key sigs are gated off until that tier is enabled. cli_artifact_commands.sh: apply/revert via the task system (artifact_apply / artifact_revert types — no allowlist needed), + read-only `applied` list. cli_updater_commands.sh: - FIX verified safety bug: updaterApplyApp/RollbackApp called `libreportal backup app "$app"` and `... restore latest`, which parse the app name as the ACTION, hit the dispatcher's `*)` default (exits 0) — so updates ran with NO snapshot and rollback was a silent no-op. Call backupAppStart / restoreAppStart directly. - FIX updaterRecordHistory jq-silent-skip: was `command -v jq || return 0` (silently dropped the audit entry). Now fail-closed with a brace-agnostic bash-native prepend fallback; extended with artifact_id/serial/undo_id. fetch.sh: add _lpJsonEsc (shared JSON-escape for the jq-free fallbacks). Regenerated source arrays + lazy-load manifest for the new file/functions. Unit-tested 31/31: every op apply+precheck+undo round-trip, the path-allowlist firewall (incl. .. traversal + install-tree + cross-app rejection), all-or- nothing abort, unsupported-op rejection, and the History bash-native fallback (records + preserves prior entries without jq). A full signed-apply e2e needs minisign + the signing key (Phase 5 make_hotfix.sh). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: librelad <librelad@digitalangels.vip>
25 lines
1.2 KiB
Bash
25 lines
1.2 KiB
Bash
#!/bin/bash
|
|
|
|
# Artifact (distribution primitive) Commands Header
|
|
# Shows available `libreportal artifact` subcommands.
|
|
|
|
cliShowArtifactHelp()
|
|
{
|
|
echo ""
|
|
echo "Available Artifact Commands:"
|
|
echo ""
|
|
echo " libreportal artifact index - Fetch + verify the signed index; list what's available"
|
|
echo " libreportal artifact applied - List the hotfixes currently applied to this install"
|
|
echo " libreportal artifact apply <id> - Apply a hotfix (snapshot + reversible declarative ops)"
|
|
echo " libreportal artifact revert <id> - Revert a previously-applied hotfix (replays its undo log)"
|
|
echo ""
|
|
echo "An 'artifact' is anything LibrePortal pulls from the outside and applies"
|
|
echo "reversibly — a hotfix today; apps / themes / components later. They share"
|
|
echo "one team-signed catalog (index.json) on the same channel as the version"
|
|
echo "check, verified against the root-owned signing key. apply/revert run through"
|
|
echo "the task system (never a mutating API): each apply dry-prechecks a bounded,"
|
|
echo "declarative op list (no scripts), snapshots, applies, records a precise undo,"
|
|
echo "and auto-rolls-back on failure. See docs/roadmap/updates-and-distribution.md."
|
|
echo ""
|
|
}
|