The maintainer-side tool that turns a small hotfix SPEC into the two signed
artifacts the install verifies + applies (completes the hotfix product):
dist/<channel>/payloads/<id>.json(.minisig) the bounded declarative op list
dist/<channel>/index.json(.minisig) the catalog (entry upserted, serial++)
laid out exactly like get.libreportal.org serves it (local-serve testable).
- Reads a spec (envelope fields + an embedded ops array); inlines any
op `content_file` to content_b64 for convenience.
- Validates id charset + every op name against the applier's CLOSED vocabulary,
so a typo can't ship an artifact that fails-closed on every box.
- Builds the payload (sha256), the envelope (payload ref {kind,url,sha256,sig}),
and upserts it into index.json — bumping index_serial, refreshing valid_until
(LP_HOTFIX_VALID_DAYS, default 30), and recording the publisher in the
publishers map with role + the footprint public key.
- minisign-signs the payload + index when LP_MINISIGN_SECKEY is set (the offline
key, kept on the release machine, same as make_release.sh); unsigned otherwise
for local testing — `libreportal artifact apply` refuses to apply unsigned.
Verified end-to-end (unsigned mode): produces a valid index.json + payload.json
matching the §8.1 envelope that lpFetchIndex / artifactApply consume.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>