587 lines
28 KiB
Markdown
587 lines
28 KiB
Markdown
# Trusted installation inputs implementation plan
|
|
|
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
|
|
**Goal:** Ensure every automatic executable input is publisher-signed or pinned by a reviewed SHA-256 before Panama executes or installs it.
|
|
|
|
**Architecture:** Add four small provenance helpers plus declarative reviewed pins. Keep vendor-specific decisions in `install-packages`, drive them through one hermetic public contract, and preserve existing installations when verification fails. Finish with a commit-pinned, digest-checked Panama bootstrap.
|
|
|
|
**Tech Stack:** Bash 5, GnuPG, rpmkeys with a temporary database, SHA-256, DNF5/Flatpak/Git command adapters, Panama contract runner.
|
|
|
|
**Spec:** `docs/superpowers/specs/2026-08-27-secure-bootstrap-privileged-installation-design.md`
|
|
|
|
## Global constraints
|
|
|
|
- Never execute fetched shell, install an unverified RPM, or accept a moving `latest` response during automatic setup.
|
|
- Download without sudo into a private checked temporary directory. Verify before any sudo, extraction, execution, or target replacement.
|
|
- Preserve a known-good installed version on every verification/download failure.
|
|
- Use exact complete fingerprints and reviewed per-architecture SHA-256 values from the approved spec.
|
|
- Use `curl --connect-timeout 10 --max-time 600`; enforce each configured maximum byte count before verification.
|
|
- Parse `setup/provenance/installers.conf` as data. Do not `source`, `eval`, or shell-expand it.
|
|
- Claude Desktop repository setup is optional and never automatic.
|
|
- No task may mutate the host package database, repository configuration, Flatpak remotes, or live installed tools.
|
|
- The Terra command gets at most one disposable Fedora 44 container smoke test with no host mounts, credentials, services, or production state.
|
|
- Package 3 owns authenticated `panama update`; Package 5 owns mutable source-app and Neovim inputs.
|
|
- Preserve `/home/gib/.local/share/Panama/config/bash/.bashrc` outside this worktree.
|
|
|
|
## File map
|
|
|
|
- `setup/lib/artifact-provenance`: fingerprint, digest-download, detached-signature, and temporary-RPM-keyring helpers.
|
|
- `setup/provenance/installers.conf`: strict reviewed versions, URLs, SHA-256 values, fingerprints, and maximum sizes.
|
|
- `setup/provenance/keys/*`: reviewed ASCII-armored public keys.
|
|
- `setup/provenance/README.md`: source, retrieval date, verification command, and rotation notes.
|
|
- `tests/setup/package-provenance-contract`: real cryptographic fixture plus stateful curl/sudo/DNF/Flatpak/rpm command adapters.
|
|
- `tests/setup/fixtures/provenance/*`: test-only GPG key, tiny signed manifest, good/tampered artifacts, and trusted/untrusted repo data.
|
|
- `setup/scripts/install-packages`: vendor-specific verified repository/artifact flows.
|
|
- `install`: include provenance and installer behavior in the package-stage hash.
|
|
- `setup/scripts/link-vicinae-scripts`: use `npm ci` against the tracked lock.
|
|
- `boot`, `README.md`, `tests/setup/boot-contract`, `tests/setup/readme-contract`: verified initial Panama revision and boot digest.
|
|
- `tests/contracts.manifest`: one new hermetic contract.
|
|
|
|
---
|
|
|
|
### Task 1: Build and prove the provenance helpers
|
|
|
|
**Files:**
|
|
- Create: `setup/lib/artifact-provenance`
|
|
- Create: `setup/provenance/installers.conf`
|
|
- Create: `setup/provenance/keys/terra44.asc`
|
|
- Create: `setup/provenance/keys/claude-code.asc`
|
|
- Create: `setup/provenance/keys/bun.asc`
|
|
- Create: `setup/provenance/keys/rpmfusion-free.asc`
|
|
- Create: `setup/provenance/keys/rpmfusion-nonfree.asc`
|
|
- Create: `setup/provenance/keys/hyprland-copr.asc`
|
|
- Create: `setup/provenance/keys/flathub.asc`
|
|
- Create: `setup/provenance/keys/claude-desktop.asc`
|
|
- Create: `setup/provenance/README.md`
|
|
- Create: `tests/setup/package-provenance-contract`
|
|
- Create: `tests/setup/fixtures/provenance/`
|
|
- Modify: `tests/contracts.manifest`
|
|
|
|
**Interfaces:**
|
|
- Consumes: `curl`, `gpg`, `sha256sum`, `rpmkeys`, `stat`, and strict `NAME=value` provenance data.
|
|
- Produces: `load_installer_provenance FILE`, `key_fingerprint_matches FILE EXPECTED`, `download_sha256 URL EXPECTED MAX_BYTES DEST`, `verify_detached_signature KEY SIGNATURE CONTENT`, and `rpm_signature_matches PACKAGE KEY EXPECTED`.
|
|
|
|
- [ ] **Step 1: Create cryptographic fixtures and write failing helper cases**
|
|
|
|
Generate a test-only key in a temporary `GNUPGHOME`, export its public key, sign a four-line `SHASUMS256.txt`, and commit only the public key, content, detached signature, a good tiny artifact, and a one-byte-tampered artifact. The private key must never enter the repository; the contract does not need it after fixture creation.
|
|
|
|
Write contract cases that source only `setup/lib/artifact-provenance` and assert:
|
|
|
|
```text
|
|
known fingerprint -> 0
|
|
wrong fingerprint -> nonzero
|
|
valid detached signature -> 0
|
|
wrong content or signature -> nonzero
|
|
matching digest/size -> atomic destination created
|
|
wrong digest, oversized body, interrupted curl -> destination absent or original bytes preserved
|
|
valid RPM signed by fixture key -> 0 through a temporary rpmdb
|
|
unsigned/wrong-key RPM -> nonzero and host rpmdb untouched
|
|
```
|
|
|
|
Add provenance-parser cases for unknown key, duplicate key, missing required key, whitespace around the name, shell expansion text, and unsupported architecture.
|
|
|
|
- [ ] **Step 2: Run the new contract and confirm the missing-helper failure**
|
|
|
|
```bash
|
|
bash -n tests/setup/package-provenance-contract
|
|
tests/setup/package-provenance-contract
|
|
```
|
|
|
|
Expected: nonzero because the helper/config do not exist.
|
|
|
|
- [ ] **Step 3: Implement exact helpers**
|
|
|
|
Use these signatures and behaviors:
|
|
|
|
```bash
|
|
key_fingerprint_matches() {
|
|
local file="$1" expected="$2" actual
|
|
actual="$(gpg --batch --with-colons --import-options show-only --import "$file" 2>/dev/null \
|
|
| awk -F: '$1 == "fpr" { print $10; exit }')"
|
|
[[ "$actual" == "$expected" ]]
|
|
}
|
|
|
|
verify_detached_signature() {
|
|
local key="$1" signature="$2" content="$3" home
|
|
home="$(mktemp -d)" || return 1
|
|
chmod 700 "$home"
|
|
GNUPGHOME="$home" gpg --batch --quiet --import "$key" >/dev/null 2>&1 \
|
|
&& GNUPGHOME="$home" gpg --batch --verify "$signature" "$content" >/dev/null 2>&1
|
|
local status=$?
|
|
rm -rf -- "$home"
|
|
return "$status"
|
|
}
|
|
```
|
|
|
|
`download_sha256` downloads to `DEST.part`, passes `--max-filesize MAX_BYTES`, verifies `stat -c %s <= MAX_BYTES`, compares a lowercase 64-hex digest, then `mv -f` atomically. Its EXIT/INT/TERM cleanup removes only the checked `.part` path.
|
|
|
|
The installer may define one private `download_bounded URL MAX_BYTES DEST` wrapper for publisher-signed RPMs whose trust assertion is the later RPM signature rather than a reviewed digest. It uses the same curl timeouts, `.part` cleanup, post-download size check, and atomic rename as `download_sha256`; it does not execute or install the result before `rpm_signature_matches` succeeds.
|
|
|
|
`rpm_signature_matches` creates a private temporary rpmdb, imports only `KEY`, verifies the complete expected primary fingerprint before import, and requires `rpmkeys --dbpath DB --checksig PACKAGE` success with an OpenPGP signature line. It never imports into the host keyring.
|
|
|
|
`load_installer_provenance` reads with `IFS='=' read -r name value`, accepts only an explicit name allowlist, rejects duplicate/empty values and any line without exactly one `=`, and exports nothing. Store values in one associative array named `INSTALLER_PROVENANCE`.
|
|
|
|
- [ ] **Step 4: Add reviewed keys and config**
|
|
|
|
Fetch each key from the exact source URL in the spec to a temporary directory, verify its full fingerprint, and add its exact ASCII-armored content with `apply_patch`. Fill `installers.conf` with these reviewed values and conservative byte caps:
|
|
|
|
```text
|
|
BUN_VERSION=1.4.0
|
|
BUN_X86_64_URL=https://github.com/oven-sh/bun/releases/download/bun-v1.4.0/bun-linux-x64.zip
|
|
BUN_X86_64_SHA256=2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452
|
|
BUN_X86_64_MAX_BYTES=67108864
|
|
BUN_AARCH64_URL=https://github.com/oven-sh/bun/releases/download/bun-v1.4.0/bun-linux-aarch64.zip
|
|
BUN_AARCH64_SHA256=4b1a332ee861983eb93bcfe6f770fff94e3e31b2c388bdaea3c8ed35e58eed0e
|
|
BUN_AARCH64_MAX_BYTES=67108864
|
|
NODE_VERSION=24.20.0
|
|
NODE_X86_64_URL=https://nodejs.org/dist/v24.20.0/node-v24.20.0-linux-x64.tar.xz
|
|
NODE_X86_64_SHA256=2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2
|
|
NODE_X86_64_MAX_BYTES=67108864
|
|
NODE_AARCH64_URL=https://nodejs.org/dist/v24.20.0/node-v24.20.0-linux-arm64.tar.xz
|
|
NODE_AARCH64_SHA256=5f4ddab610c1ab2016b3c227cebdbf6d9495161487e4739c7b90090595f465f7
|
|
NODE_AARCH64_MAX_BYTES=67108864
|
|
CODEX_VERSION=0.150.1
|
|
CODEX_X86_64_URL=https://github.com/openai/codex/releases/download/rust-v0.150.1/codex-package-x86_64-unknown-linux-musl.tar.gz
|
|
CODEX_X86_64_SHA256=00aba704f029f6dc0d948be407a756e0c97cc840132fd691353b2c6b0a505b17
|
|
CODEX_X86_64_MAX_BYTES=134217728
|
|
CODEX_AARCH64_URL=https://github.com/openai/codex/releases/download/rust-v0.150.1/codex-package-aarch64-unknown-linux-musl.tar.gz
|
|
CODEX_AARCH64_SHA256=1ecac3f87823efb98153233b076ea3d6e34a7a8cebe43c5285dc5f79e1514639
|
|
CODEX_AARCH64_MAX_BYTES=134217728
|
|
RUSTDESK_VERSION=1.4.9
|
|
RUSTDESK_X86_64_URL=https://github.com/rustdesk/rustdesk/releases/download/1.4.9/rustdesk-1.4.9-0.x86_64.rpm
|
|
RUSTDESK_X86_64_SHA256=eb1b053ac5b2f774f2271f7fbbfd2ea475899f7a55135c5e172bc54b9388f108
|
|
RUSTDESK_X86_64_MAX_BYTES=134217728
|
|
FEDORA_RELEASE=44
|
|
RPMFUSION_FREE_RELEASE_URL=https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-44.noarch.rpm
|
|
RPMFUSION_FREE_RELEASE_MAX_BYTES=4194304
|
|
RPMFUSION_NONFREE_RELEASE_URL=https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-44.noarch.rpm
|
|
RPMFUSION_NONFREE_RELEASE_MAX_BYTES=4194304
|
|
TERRA_BASEURL=https://repos.fyralabs.com/terra44
|
|
HYPRLAND_COPR_BASEURL=https://download.copr.fedorainfracloud.org/results/lionheartp/Hyprland/fedora-$releasever-$basearch/
|
|
FLATHUB_DESCRIPTOR_URL=https://flathub.org/repo/flathub.flatpakrepo
|
|
FLATHUB_DESCRIPTOR_MAX_BYTES=1048576
|
|
CLAUDE_CODE_BASEURL=https://downloads.claude.ai/claude-code/rpm/stable
|
|
CLAUDE_DESKTOP_BASEURL=https://patrickjaja.github.io/claude-desktop-extra/rpm/
|
|
TERRA_FINGERPRINT=AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
|
|
CLAUDE_CODE_FINGERPRINT=31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE
|
|
BUN_FINGERPRINT=F3DCC08A8572C0749B3E18888EAB4D40A7B22B59
|
|
RPMFUSION_FREE_FINGERPRINT=E9A491A3DE247814E7E067EAE06F8ECDD651FF2E
|
|
RPMFUSION_NONFREE_FINGERPRINT=79BDB88F9BBF73910FD4095B6A2AF96194843C65
|
|
HYPRLAND_COPR_FINGERPRINT=97E23476C89635135407C7D5E9BA41342C4B2995
|
|
FLATHUB_FINGERPRINT=6E5C05D979C76DAF93C081354184DD4D907A7CAE
|
|
CLAUDE_DESKTOP_FINGERPRINT=825A7D15D78BABE45646D5DF382409F597908867
|
|
```
|
|
|
|
Use the exact hashes and fingerprints from the spec; no value may be resolved through `latest`. The provenance README must list every source URL and the command used to verify it on 2026-08-27.
|
|
|
|
- [ ] **Step 5: Add the manifest entry and run the focused gate**
|
|
|
|
```text
|
|
# Provenance uses local signed fixtures and stubs every network/package operation.
|
|
hermetic tests/setup/package-provenance-contract
|
|
```
|
|
|
|
Run:
|
|
|
|
```bash
|
|
bash -n setup/lib/artifact-provenance tests/setup/package-provenance-contract
|
|
tests/setup/package-provenance-contract
|
|
tests/setup/contract-manifest-contract
|
|
./bin/panama test --safe package-provenance
|
|
git diff --check
|
|
```
|
|
|
|
Expected: all pass and no host GPG/RPM state changes.
|
|
|
|
- [ ] **Step 6: Commit the provenance foundation**
|
|
|
|
```bash
|
|
git add setup/lib/artifact-provenance setup/provenance tests/setup/fixtures/provenance \
|
|
tests/setup/package-provenance-contract tests/contracts.manifest
|
|
git commit -m "Test: Add installer provenance boundary"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 2: Verify third-party repository roots
|
|
|
|
**Files:**
|
|
- Modify: `setup/scripts/install-packages:218-276,339-370,396-456`
|
|
- Modify: `tests/setup/package-provenance-contract`
|
|
- Test: `tests/setup/desktop-first-contract`
|
|
- Test: `tests/setup/package-lists-contract`
|
|
|
|
**Interfaces:**
|
|
- Consumes: `INSTALLER_PROVENANCE`, the four helper functions, vendored keys, and command adapters.
|
|
- Produces: `install_rpmfusion_repositories`, `install_terra_repository`, `configure_hyprland_repository`, `ensure_flathub_remote`, `install_claude_code`, and `install_claude_desktop_if_trusted`.
|
|
|
|
- [ ] **Step 1: Add public installer cases for every repository**
|
|
|
|
Run a fixture copy of `install-packages` with temporary HOME/state and stubbed `sudo`, `dnf`, `rpm`, `rpmkeys`, `curl`, `flatpak`, and `gpg`. Assert exact command-log order and policy:
|
|
|
|
```text
|
|
RPM Fusion: exact Fedora 44 URL -> size cap -> RPM signature -> localpkg_gpgcheck=1 install
|
|
Terra: exact F44 key -> pkg_gpgcheck=1 -> repo_gpgcheck=1 -> local gpgkey -> terra-release
|
|
COPR: exact baseurl/local key, package gpgcheck=1, explicit metadata-signature exception; no `dnf copr enable`
|
|
Flathub: decoded embedded key fingerprint and GPG-enabled remote; mismatch preserves existing remote
|
|
Claude Code: exact Anthropic key/repo checks before DNF
|
|
Claude Desktop absent/untrusted: one manual message, no download, no DNF, overall success
|
|
Claude Desktop trusted existing repo: DNF install only
|
|
```
|
|
|
|
Inject wrong keys, wrong base URLs, GPG flags off, signature failure, and DNF failure. Assert nothing downstream in the dependent transaction runs after a trust-root failure. Require `rpm -E %fedora` to equal the reviewed `FEDORA_RELEASE`; any other release fails before a third-party download or repository mutation.
|
|
|
|
- [ ] **Step 2: Run the contract and confirm current unsafe paths fail**
|
|
|
|
```bash
|
|
tests/setup/package-provenance-contract
|
|
```
|
|
|
|
Expected: nonzero findings for `--nogpgcheck`, TOFU COPR, unvalidated Flathub, remote-script Claude Desktop, and unverified RPM Fusion URLs.
|
|
|
|
- [ ] **Step 3: Implement signed repository setup**
|
|
|
|
Source the helper and load the config from `PANAMA_PATH` at installer start. Download RPM Fusion release RPMs, verify signatures with the matching vendored key, then call:
|
|
|
|
```bash
|
|
sudo dnf install -y --setopt=localpkg_gpgcheck=1 "$free_rpm" "$nonfree_rpm"
|
|
```
|
|
|
|
Replace Terra with `--repofrompath terra,https://repos.fyralabs.com/terra44` plus:
|
|
|
|
```text
|
|
--setopt=terra.pkg_gpgcheck=1
|
|
--setopt=terra.repo_gpgcheck=1
|
|
--setopt=terra.gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama
|
|
```
|
|
|
|
Stage repo/key files completely before atomic sudo install. Write the COPR repo from reviewed local values rather than `dnf copr enable`, with:
|
|
|
|
```ini
|
|
[panama-hyprland]
|
|
name=Panama reviewed Hyprland COPR
|
|
baseurl=https://download.copr.fedorainfracloud.org/results/lionheartp/Hyprland/fedora-$releasever-$basearch/
|
|
enabled=1
|
|
gpgcheck=1
|
|
repo_gpgcheck=0
|
|
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-panama-hyprland
|
|
```
|
|
|
|
The `repo_gpgcheck=0` line is the single audited exception: the publisher returns no `repodata/repomd.xml.asc`, while its RPMs are signed by the pinned project key. The contract rejects this exception for every other repository and still requires package signatures.
|
|
|
|
Parse the Flathub descriptor as INI data, base64-decode `GPGKey`, verify its fingerprint, and reject `NoGPGVerify=true` or equivalent disabled state. Write Claude Code's stable repository with the vendored local key, `gpgcheck=1`, and `repo_gpgcheck=1`; its publisher provides signed metadata.
|
|
|
|
For Claude Desktop, inspect only an already configured repo. Trust it only when its base URL equals `https://patrickjaja.github.io/claude-desktop-extra/rpm/`, both GPG checks are `1`, and its `gpgkey` is an existing local file whose complete fingerprint matches the vendored `claude-desktop.asc`. Never curl or run the community setup script. The host's current remote-key configuration is therefore treated as untrusted until an operator configures a local reviewed key. Untrusted/absent configuration logs an optional manual instruction and returns success.
|
|
|
|
- [ ] **Step 4: Verify ordering and regression contracts**
|
|
|
|
```bash
|
|
bash -n setup/scripts/install-packages tests/setup/package-provenance-contract
|
|
tests/setup/package-provenance-contract
|
|
tests/setup/desktop-first-contract
|
|
tests/setup/package-lists-contract
|
|
./bin/panama test --safe package-provenance
|
|
git diff --check
|
|
```
|
|
|
|
- [ ] **Step 5: Commit repository trust roots**
|
|
|
|
```bash
|
|
git add setup/scripts/install-packages tests/setup/package-provenance-contract
|
|
git commit -m "Fix: Verify third-party package repositories"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 3: Pin language runtimes and agent tools
|
|
|
|
**Files:**
|
|
- Modify: `setup/scripts/install-packages:100-177,334-394`
|
|
- Modify: `tests/setup/package-provenance-contract`
|
|
- Modify: `tests/quickshell/declared-dependencies-contract`
|
|
|
|
**Interfaces:**
|
|
- Consumes: `download_sha256`, loaded reviewed pins, supported `uname -m` values `x86_64` and `aarch64`.
|
|
- Produces: `install_node`, `install_bun`, `install_claude_code`, `install_codex`, and `install_rustdesk` with verified staging and known-good preservation.
|
|
|
|
- [ ] **Step 1: Add failing per-architecture and preservation cases**
|
|
|
|
For each artifact, assert exact URL/digest selection for x86_64 and aarch64, unsupported-architecture refusal before curl, digest mismatch preserving a seeded old version, interrupted download cleanup, atomic replacement, and already-installed exact-version no-op.
|
|
|
|
Require source scans and public command logs to reject:
|
|
|
|
```text
|
|
curl ... | bash
|
|
nvm install --lts
|
|
npm install -g pnpm
|
|
npm install -g @openai/codex
|
|
releases/latest
|
|
api.github.com/.../releases/latest
|
|
```
|
|
|
|
RustDesk supports only the reviewed x86_64 RPM. aarch64 records a deliberate soft failure without downloading. pnpm must come from signed Fedora DNF; no network-script/npm fallback is allowed.
|
|
|
|
- [ ] **Step 2: Run the focused contract and confirm it fails on moving inputs**
|
|
|
|
```bash
|
|
tests/setup/package-provenance-contract
|
|
```
|
|
|
|
Expected: nonzero findings naming each current moving or piped installer.
|
|
|
|
- [ ] **Step 3: Implement verified atomic installs**
|
|
|
|
Map architecture once:
|
|
|
|
```bash
|
|
case "$(uname -m)" in
|
|
x86_64) artifact_arch=X86_64 ;;
|
|
aarch64) artifact_arch=AARCH64 ;;
|
|
*) log "Unsupported architecture: $(uname -m)"; return 1 ;;
|
|
esac
|
|
```
|
|
|
|
Install Node 24.20.0 into `$NVM_DIR/versions/node/v24.20.0` from a sibling staging directory, reject archive entries outside the expected single top-level directory, require staged `bin/node --version` to print `v24.20.0`, then rename and set nvm's default alias to `24.20.0` without `nvm install`.
|
|
|
|
For Bun, reject unexpected archive paths, stage the binary at `$HOME/.bun/versions/1.4.0/bin/bun`, require `--version` to print `1.4.0`, then atomically replace a temporary symlink at `$HOME/.bun/bin/bun`. For Codex, reject absolute/parent-traversal tar members, stage the release's `codex` binary at `$HOME/.local/lib/panama/codex/0.150.1/codex`, require `--version` to identify `0.150.1`, then atomically replace `$HOME/.local/bin/codex` through a temporary symlink. Existing version directories must match the reviewed binary/version or cause a soft failure; never delete and recreate an unverified collision.
|
|
|
|
Install Claude Code through the signed stable repository from Task 2. Download RustDesk's versioned RPM, verify SHA-256, then pass only that local path to DNF. Install pnpm through the signed Fedora package transaction and record a soft failure if unavailable. Every archive extracts into a checked private directory first; no archive writes directly into its final prefix.
|
|
|
|
Every helper failure appends the same component name to `softly_failed`; `report_soft_failures` keeps the package hash unstamped.
|
|
|
|
- [ ] **Step 4: Run focused and dependency checks**
|
|
|
|
```bash
|
|
bash -n setup/scripts/install-packages tests/setup/package-provenance-contract
|
|
tests/setup/package-provenance-contract
|
|
tests/quickshell/declared-dependencies-contract
|
|
tests/setup/desktop-first-contract
|
|
./bin/panama test --safe package-provenance
|
|
git diff --check
|
|
```
|
|
|
|
- [ ] **Step 5: Commit pinned user tools**
|
|
|
|
```bash
|
|
git add setup/scripts/install-packages tests/setup/package-provenance-contract \
|
|
tests/quickshell/declared-dependencies-contract
|
|
git commit -m "Fix: Pin runtime and agent artifacts"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 4: Invalidate stale installer state and lock npm installs
|
|
|
|
**Files:**
|
|
- Modify: `install:67-106`
|
|
- Modify: `setup/scripts/link-vicinae-scripts:79-97`
|
|
- Modify: `tests/setup/launcher-search-contract`
|
|
- Modify: `tests/setup/update-command-contract`
|
|
- Modify: `tests/setup/package-provenance-contract`
|
|
|
|
**Interfaces:**
|
|
- Consumes: tracked package lists, installer, provenance helper/config/keys, and extension lockfile.
|
|
- Produces: `hash_packages` covering every installer trust input and Vicinae `npm ci` behavior.
|
|
|
|
- [ ] **Step 1: Write failing hash and lockfile assertions**
|
|
|
|
Run `hash_packages` from a disposable installer copy and assert the digest changes independently when each of these changes:
|
|
|
|
```text
|
|
setup/packages/core-packages
|
|
setup/scripts/install-packages
|
|
setup/lib/artifact-provenance
|
|
setup/provenance/installers.conf
|
|
one setup/provenance/keys file
|
|
```
|
|
|
|
In the Vicinae fixture, stub npm and require argv `ci`, not `install`. Seed a lock mismatch and assert nonzero extension-build status with the lockfile byte-for-byte unchanged.
|
|
|
|
- [ ] **Step 2: Confirm current hash and npm behavior fail**
|
|
|
|
```bash
|
|
tests/setup/package-provenance-contract
|
|
tests/setup/launcher-search-contract
|
|
```
|
|
|
|
Expected: current hash ignores installer/provenance changes and extension setup invokes `npm install`.
|
|
|
|
- [ ] **Step 3: Hash exact inputs and switch to `npm ci`**
|
|
|
|
Replace the current `find ... -maxdepth 1` stream with a sorted NUL-safe list containing top-level package files, `setup/scripts/install-packages`, `setup/lib/artifact-provenance`, and every regular file under `setup/provenance`. Hash file paths plus contents so renames change the digest.
|
|
|
|
Change only the extension dependency command to `npm ci`; do not add lockfile repair or update behavior.
|
|
|
|
- [ ] **Step 4: Verify upgrade and extension behavior**
|
|
|
|
```bash
|
|
bash -n install setup/scripts/link-vicinae-scripts
|
|
tests/setup/package-provenance-contract
|
|
tests/setup/launcher-search-contract
|
|
tests/setup/update-command-contract
|
|
./bin/panama test --safe package-provenance
|
|
git diff --check
|
|
```
|
|
|
|
- [ ] **Step 5: Commit state invalidation and npm locking**
|
|
|
|
```bash
|
|
git add install setup/scripts/link-vicinae-scripts tests/setup/launcher-search-contract \
|
|
tests/setup/update-command-contract tests/setup/package-provenance-contract
|
|
git commit -m "Fix: Re-run verified installer inputs"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 5: Verify the initial Panama revision before handoff
|
|
|
|
**Files:**
|
|
- Modify: `boot:20-166`
|
|
- Modify: `tests/setup/boot-contract`
|
|
- Modify: `README.md:1-48`
|
|
- Modify: `tests/setup/readme-contract`
|
|
- Modify: `.claude/skills/panama/SKILL.md`
|
|
- Modify: `skills/panama-desktop/SKILL.md`
|
|
|
|
**Interfaces:**
|
|
- Consumes: `PANAMA_BOOT_REVISION` as a full lowercase 40-hex commit and `PANAMA_BOOT_SHA256` as a lowercase 64-hex digest.
|
|
- Produces: verified fresh clone at that revision, fast-forward-only clean existing checkout, and documentation pinned to the implementation commit immediately preceding its documentation commit.
|
|
|
|
- [ ] **Step 1: Replace old permissive boot-contract expectations with red trust cases**
|
|
|
|
The public fixture must assert:
|
|
|
|
```text
|
|
missing/malformed revision -> no git clone/fetch and no install
|
|
fresh clone -> fetch exact revision, resolve HEAD^{commit}, equality, handoff
|
|
HEAD mismatch -> nonzero, no install
|
|
existing clean ancestor -> fast-forward to exact revision, then install
|
|
existing dirty or divergent checkout -> nonzero, no reset, no install
|
|
fetch failure -> nonzero, no install
|
|
```
|
|
|
|
Delete the old assertion that a failed pull proceeds with the checkout as-is. Add README assertions rejecting `bash <(curl .../main/boot)` and requiring a commit URL, 40-hex revision, 64-hex SHA-256, `sha256sum -c`, 10-second connect timeout, 30-second total timeout, and 256 KiB maximum.
|
|
|
|
- [ ] **Step 2: Run boot and README contracts to prove they fail**
|
|
|
|
```bash
|
|
tests/setup/boot-contract
|
|
tests/setup/readme-contract
|
|
```
|
|
|
|
Expected: both fail on the mutable branch bootstrap and permissive pull fallback.
|
|
|
|
- [ ] **Step 3: Implement exact-revision clone/handoff and commit it**
|
|
|
|
Validate inputs before Git:
|
|
|
|
```bash
|
|
[[ "${PANAMA_BOOT_REVISION:-}" =~ ^[0-9a-f]{40}$ ]] || exit 1
|
|
[[ "${PANAMA_BOOT_SHA256:-}" =~ ^[0-9a-f]{64}$ ]] || exit 1
|
|
actual_boot_sha="$(sha256sum "${BASH_SOURCE[0]}" | cut -d' ' -f1)"
|
|
[[ "$actual_boot_sha" == "$PANAMA_BOOT_SHA256" ]] || exit 1
|
|
```
|
|
|
|
For a fresh destination, initialize/fetch the exact commit, verify `git rev-parse HEAD^{commit}` equality, create local `main` at that commit, and set `branch.main.remote=origin` plus `branch.main.merge=refs/heads/main`. For an existing checkout, require empty `git status --porcelain`, fetch the exact commit, require `git merge-base --is-ancestor HEAD REVISION`, and fast-forward only. Never use reset or execute after mismatch/failure.
|
|
|
|
Run focused tests, then commit only implementation and contract changes:
|
|
|
|
```bash
|
|
bash -n boot tests/setup/boot-contract
|
|
tests/setup/boot-contract
|
|
tests/setup/root-server-bootstrap-contract
|
|
git add boot tests/setup/boot-contract tests/setup/root-server-bootstrap-contract
|
|
git commit -m "Fix: Verify the initial Panama revision"
|
|
```
|
|
|
|
- [ ] **Step 4: Compute the committed boot pin and write the documented command**
|
|
|
|
Use the implementation commit just created:
|
|
|
|
```bash
|
|
bootstrap_commit="$(git rev-parse HEAD)"
|
|
bootstrap_sha="$(git show "$bootstrap_commit:boot" | sha256sum | cut -d' ' -f1)"
|
|
```
|
|
|
|
Write README commands that download
|
|
`https://git.gbrown.org/gib/Panama/raw/commit/$bootstrap_commit/boot` to a checked temporary file with `curl --connect-timeout 10 --max-time 30 --max-filesize 262144`, compare `$bootstrap_sha` through `sha256sum -c`, then invoke with both environment values. Use the same verified command for desktop and `--server`; never pipe the response to Bash.
|
|
|
|
The README contract must parse those literal values, run `git cat-file -e COMMIT^{commit}`, and require:
|
|
|
|
```bash
|
|
test "$(git show "$commit:boot" | sha256sum | cut -d' ' -f1)" = "$documented_sha"
|
|
```
|
|
|
|
- [ ] **Step 5: Update operator skills and run the complete installer plan gate**
|
|
|
|
```bash
|
|
bash -n boot install setup/scripts/install-packages setup/scripts/link-vicinae-scripts \
|
|
setup/lib/artifact-provenance tests/setup/package-provenance-contract \
|
|
tests/setup/boot-contract
|
|
tests/setup/package-provenance-contract
|
|
tests/setup/boot-contract
|
|
tests/setup/root-server-bootstrap-contract
|
|
tests/setup/readme-contract
|
|
tests/setup/package-lists-contract
|
|
tests/setup/desktop-first-contract
|
|
tests/setup/launcher-search-contract
|
|
tests/setup/update-command-contract
|
|
./bin/panama test --safe
|
|
git diff --check
|
|
```
|
|
|
|
Expected: 133 hermetic contracts pass after the SSH and provenance contracts exist; non-hermetic skip counts remain unchanged.
|
|
|
|
- [ ] **Step 6: Commit the pinned documentation**
|
|
|
|
```bash
|
|
git add README.md tests/setup/readme-contract .claude/skills/panama/SKILL.md \
|
|
skills/panama-desktop/SKILL.md setup/provenance/README.md
|
|
git commit -m "Docs: Pin the verified Panama bootstrap"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 6: Smoke-test signed Terra bootstrap in a disposable Fedora 44 container
|
|
|
|
**Files:**
|
|
- Modify: `setup/provenance/README.md`
|
|
- Modify: `tests/setup/package-provenance-contract` only if the smoke test exposes a fixture gap
|
|
|
|
**Interfaces:**
|
|
- Consumes: exact Terra key/repo command landed in Task 2.
|
|
- Produces: recorded disposable proof or a fail-closed Terra-unavailable implementation; never host installation.
|
|
|
|
- [ ] **Step 1: Preflight the disposable target**
|
|
|
|
Require rootless Podman, no bind mounts, a fresh `registry.fedoraproject.org/fedora:44` container, and no forwarded credentials or host sockets. The command may download repository metadata and the `terra-release` package only inside the disposable container.
|
|
|
|
- [ ] **Step 2: Run the exact key and DNF verification path**
|
|
|
|
Copy only the vendored Terra key into the container, verify its full fingerprint, then run the exact `--repofrompath` and three `terra.*` GPG settings from Task 2. Query the resulting repo file and package signature settings. Remove the container on exit.
|
|
|
|
Expected: DNF installs `terra-release` with both package and metadata verification enabled and without `--nogpgcheck`.
|
|
|
|
- [ ] **Step 3: Apply the fail-closed result**
|
|
|
|
If the exact command fails, do not weaken GPG settings. Change the installer to print Terra unavailable and exit before initial/desktop/Hyprland transactions; update the fixture expectation to that branch. If it succeeds, make no production change.
|
|
|
|
- [ ] **Step 4: Record proof and rerun the hermetic gate**
|
|
|
|
Document the container image, date, exact command, exit status, key fingerprint, and inspected repo settings in `setup/provenance/README.md`. Do not claim host installation.
|
|
|
|
```bash
|
|
tests/setup/package-provenance-contract
|
|
./bin/panama test --safe
|
|
git diff --check
|
|
```
|
|
|
|
- [ ] **Step 5: Commit the provenance proof**
|
|
|
|
```bash
|
|
git add setup/provenance/README.md setup/scripts/install-packages \
|
|
tests/setup/package-provenance-contract
|
|
git commit -m "Docs: Record signed Terra bootstrap proof"
|
|
```
|