511 lines
24 KiB
Markdown
511 lines
24 KiB
Markdown
# Secure bootstrap and privileged installation design
|
|
|
|
**Status:** Approved in chat on 2026-08-27
|
|
|
|
**Parent program:** `docs/superpowers/specs/2026-08-26-repository-audit-remediation-design.md`
|
|
|
|
## Purpose
|
|
|
|
This document refines Package 2 after repository-wide reconnaissance found that the
|
|
same trust boundary extends beyond the three examples named in the parent design.
|
|
Panama must not turn mutable network content into root-capable code. That includes
|
|
the initial root bootstrap, package-manager repository bootstraps, downloaded
|
|
installers and artifacts, and user-level processes that run while the installer
|
|
keeps a live sudo credential.
|
|
|
|
The package still has three independently testable parts:
|
|
|
|
1. transactional SSH hardening in `boot --server`;
|
|
2. verified bootstrap and installer inputs;
|
|
3. transactional server firewall policy.
|
|
|
|
All three are fixture-tested. This branch does not reload a real SSH daemon, mutate a
|
|
real firewall, install a real package, start a service, or apply changes to a server.
|
|
|
|
## Decisions
|
|
|
|
- Keep automatic installation when an official signature or a reviewed immutable
|
|
digest is available. Otherwise make the component explicit and optional.
|
|
- Keep the public `boot --server` interface, but remove the mutable
|
|
`bash <(curl .../main/boot)` installation path from the documentation.
|
|
- A missing or unsafe target SSH key makes hardening unavailable but does not stop
|
|
bootstrap. Validation or reload failure rolls back and stops bootstrap.
|
|
- Safe target SSH state means a non-root account, an absolute home, a real `.ssh`
|
|
directory owned by the target UID with mode `0700`, and a nonempty regular
|
|
`authorized_keys` file owned by the target UID with mode `0600`. Symlinks are
|
|
refused. Panama may create and normalize files it copied from root, but it does
|
|
not take ownership of an unsafe pre-existing target path.
|
|
- Failed SSH reload rollback includes restored-config validation and a reload of the
|
|
restored configuration, because a command can apply state and still return
|
|
nonzero.
|
|
- Firewalld is required. Setup attempts to enable and start it, then verifies both
|
|
states before any rule mutation.
|
|
- Ports 80 and 443 are public only through Cloudflare source ipsets. Port 81 is open
|
|
only in a unique WireGuard-only zone. With no WireGuard zone it stays closed and
|
|
setup reports that fact; ambiguity or an unsafe mixed-interface zone fails before
|
|
mutation.
|
|
- Panama removes only its own rules and the exact legacy direct `80/tcp`, `443/tcp`,
|
|
and `81/tcp` rules created by the old server installer. Other broad rules are
|
|
reported as conflicts, not silently deleted.
|
|
- There is no privileged firewall timer. Server installation and upgrades are the
|
|
refresh entry points.
|
|
- Use narrow verification helpers, not a generalized installer framework.
|
|
|
|
## Shared trust model
|
|
|
|
`install` obtains one sudo credential and refreshes it until the run ends. Therefore
|
|
code executed as the target user during that window is root-capable in practice.
|
|
Panama treats fetched shell, binaries, RPM scriptlets, npm lifecycle code, container
|
|
entrypoints, and sourced repository build definitions as executable inputs.
|
|
|
|
Fetched JSON, repository descriptors, keys, checksums, HTML, icons, and CIDR lists are
|
|
data only while Panama parses them without executing them. They still require strict
|
|
shape, size, signer, and destination checks before they can authorize an executable
|
|
transaction.
|
|
|
|
Every download follows this order:
|
|
|
|
1. create a checked private temporary directory;
|
|
2. download without sudo with redirects, a 10-second connection timeout, a
|
|
600-second total timeout, and the reviewed per-artifact byte limit;
|
|
3. verify the expected fingerprint, signature, or reviewed SHA-256;
|
|
4. stage the complete result on the destination filesystem;
|
|
5. use sudo only for the narrow final package/repository operation that needs it;
|
|
6. atomically replace user-owned installed artifacts;
|
|
7. remove temporary material on success, failure, INT, and TERM.
|
|
|
|
Verification failure never falls back to the fetched artifact and never removes a
|
|
known-good installed version.
|
|
|
|
## SSH hardening transaction
|
|
|
|
### Public flow
|
|
|
|
The root `boot --server` branch keeps account creation, password setup, key copy,
|
|
clone, and target-user handoff in the standalone `boot` file. It adds one private
|
|
function, `harden_server_ssh USER HOME`, because no repository helper exists before
|
|
the clone.
|
|
|
|
Before offering hardening, Panama verifies:
|
|
|
|
- the target account exists, has a numeric UID other than 0, and its `getent` home is
|
|
absolute and nonempty;
|
|
- neither the home-relative `.ssh` path nor `authorized_keys` is a symlink;
|
|
- `.ssh` and `authorized_keys` have the exact ownership and modes in Decisions;
|
|
- `authorized_keys` contains at least one nonblank, non-comment line;
|
|
- exactly one installed SSH unit is detected, preferring `sshd.service` and falling
|
|
back to `ssh.service` only when the first unit is absent.
|
|
|
|
If the target has no key and root has a safe regular key file, Panama copies only that
|
|
file, creates `.ssh`, applies `0700/0600`, and changes ownership only on those two
|
|
paths. It does not recursively take ownership of an existing directory tree.
|
|
|
|
If the preconditions fail, Panama prints why hardening is unavailable, keeps
|
|
root/password authentication unchanged, and continues the clone/install handoff.
|
|
|
|
### Transaction
|
|
|
|
The desired drop-in is exactly:
|
|
|
|
```text
|
|
PermitRootLogin no
|
|
PasswordAuthentication no
|
|
```
|
|
|
|
Panama creates the candidate with `umask 077` and `mktemp` in
|
|
`/etc/ssh/sshd_config.d`. Its temporary name does not end in `.conf`, so the normal
|
|
include glob cannot activate it early. It preserves an existing
|
|
`90-panama.conf` in the same directory, arms EXIT/INT/TERM rollback, and atomically
|
|
renames the candidate over the final path.
|
|
|
|
It then runs `sshd -t` against the complete active configuration and reloads only the
|
|
detected unit. Success disarms rollback and removes the backup.
|
|
|
|
On validation failure, Panama restores or removes the new drop-in, validates the
|
|
restored configuration, and returns nonzero without reloading the rejected candidate.
|
|
On reload failure, Panama restores the previous drop-in, validates it, reloads the
|
|
restored unit, and returns nonzero. A rollback validation/reload failure preserves the
|
|
backup and prints its path plus exact recovery commands.
|
|
|
|
Existing drop-ins go through the same desired-content, validation, and reload path;
|
|
mere existence is not treated as proof of hardening.
|
|
|
|
### Hermetic adapter
|
|
|
|
`PANAMA_BOOT_FIXTURE_ROOT` exists only for the contract. `boot` accepts it only when
|
|
the real effective UID is nonzero while the stubbed `id -u` reports root; an actual
|
|
root process that sets it exits before mutation. Absolute system paths are resolved
|
|
beneath that fixture root. Command adapters remain ordinary PATH stubs.
|
|
|
|
The public test seam is the real `boot --server` command under a PTY. The fixture
|
|
stubs `id`, `passwd`, `getent`, `useradd`, `usermod`, `runuser`, `git`, `dnf`, `sshd`,
|
|
and `systemctl`, and provides temporary account and filesystem state.
|
|
|
|
Required cases are:
|
|
|
|
- missing, empty, comment-only, symlinked, wrong-owner, and wrong-mode target keys;
|
|
- safe root-key copy and safe existing target key;
|
|
- successful initial install and replacement of an existing drop-in;
|
|
- invalid candidate rollback;
|
|
- failed reload rollback, including restored validation and reload;
|
|
- rollback failure retaining its recovery artifact;
|
|
- exact command ordering and no install handoff after a transactional failure.
|
|
|
|
## Verified bootstrap and installer inputs
|
|
|
|
### Initial Panama bootstrap
|
|
|
|
README installation instructions use a two-file trust assertion: a full 40-character
|
|
Git commit and the SHA-256 of `boot` at that commit. The command downloads the raw file
|
|
from the commit URL with a 10-second connection timeout, 30-second total timeout, and
|
|
256 KiB maximum, checks SHA-256, and only then executes it.
|
|
|
|
The verified revision is passed as `PANAMA_BOOT_REVISION`. `boot` requires a full
|
|
lowercase hexadecimal commit, fetches that exact object, verifies
|
|
`git rev-parse HEAD^{commit}` equality, and only then executes `install`. A fresh
|
|
clone creates local `main` at that commit with `origin/main` as its upstream. An
|
|
existing checkout must be clean and may only fast-forward to the verified commit;
|
|
Panama never resets or overwrites local work. A mismatch, dirty checkout, divergent
|
|
history, or fetch failure stops before handoff.
|
|
|
|
This uses two commits when refreshing the documented bootstrap: the implementation is
|
|
committed first; a following documentation commit records the preceding full commit
|
|
and `git show COMMIT:boot | sha256sum`. A hermetic contract recomputes the documented
|
|
digest from that committed object. A digest copied from the same hosting origin is not
|
|
a substitute for a long-lived publisher signing key, so Package 3 owns the stronger
|
|
authenticated-update/release-manifest design.
|
|
|
|
### Verification helpers and provenance data
|
|
|
|
Create `setup/lib/artifact-provenance` with only these public shell functions:
|
|
|
|
```text
|
|
key_fingerprint_matches FILE EXPECTED_FINGERPRINT
|
|
download_sha256 URL EXPECTED_SHA256 DESTINATION
|
|
verify_detached_signature KEY_FILE SIGNATURE_FILE CONTENT_FILE
|
|
rpm_signature_matches PACKAGE_FILE KEY_FILE EXPECTED_FINGERPRINT
|
|
```
|
|
|
|
`rpm_signature_matches` imports only the expected key into a temporary RPM database
|
|
and requires `rpmkeys --dbpath ... --checksig` to report a valid package signature;
|
|
it does not trust or modify the host RPM keyring.
|
|
|
|
Create `setup/provenance/installers.conf` as non-executable data. It uses one
|
|
`NAME=value` assignment per line, rejects unknown/duplicate keys, and is parsed without
|
|
`source` or `eval`. Per-architecture URLs, SHA-256 values, and maximum byte counts are
|
|
explicit.
|
|
|
|
Vendored public keys live under `setup/provenance/keys/`. The accompanying README
|
|
records the official source URL, retrieval date, complete primary fingerprint,
|
|
verification command, and rotation policy. Runtime verification compares the complete
|
|
primary fingerprint, not a short key ID.
|
|
|
|
As reviewed on 2026-08-27, the trust anchors are:
|
|
|
|
| Source | Primary fingerprint |
|
|
|---|---|
|
|
| Terra 44 | `AE09157A4DE88B497EA1D5D300CDAB43DE226D6F` |
|
|
| Anthropic Claude Code | `31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE` |
|
|
| Bun releases | `F3DCC08A8572C0749B3E18888EAB4D40A7B22B59` |
|
|
| RPM Fusion free | `E9A491A3DE247814E7E067EAE06F8ECDD651FF2E` |
|
|
| RPM Fusion nonfree | `79BDB88F9BBF73910FD4095B6A2AF96194843C65` |
|
|
| lionheartp/Hyprland COPR | `97E23476C89635135407C7D5E9BA41342C4B2995` |
|
|
| Flathub | `6E5C05D979C76DAF93C081354184DD4D907A7CAE` |
|
|
|
|
The initial reviewed artifact pins are:
|
|
|
|
| Artifact | Architecture | SHA-256 |
|
|
|---|---|---|
|
|
| Bun 1.4.0 | x86_64 | `2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452` |
|
|
| Bun 1.4.0 | aarch64 | `4b1a332ee861983eb93bcfe6f770fff94e3e31b2c388bdaea3c8ed35e58eed0e` |
|
|
| Node 24.20.0 | x86_64 | `2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2` |
|
|
| Node 24.20.0 | aarch64 | `5f4ddab610c1ab2016b3c227cebdbf6d9495161487e4739c7b90090595f465f7` |
|
|
| Codex 0.150.1 package | x86_64 musl | `00aba704f029f6dc0d948be407a756e0c97cc840132fd691353b2c6b0a505b17` |
|
|
| Codex 0.150.1 package | aarch64 musl | `1ecac3f87823efb98153233b076ea3d6e34a7a8cebe43c5285dc5f79e1514639` |
|
|
| RustDesk 1.4.9 RPM | x86_64 | `eb1b053ac5b2f774f2271f7fbbfd2ea475899f7a55135c5e172bc54b9388f108` |
|
|
|
|
Unsupported architectures fail before download. Updating any version, URL, digest, or
|
|
key is an explicit reviewed repository change.
|
|
|
|
The provenance README cites these publisher-controlled records:
|
|
|
|
- Terra package instructions, key, and bootstrap limitation:
|
|
`https://github.com/terrapkg/packages/blob/frawhide/README.md`,
|
|
`https://repos.fyralabs.com/terra44/key.asc`, and
|
|
`https://github.com/terrapkg/packages/discussions/7736`;
|
|
- Anthropic setup and signing key: `https://code.claude.com/docs/en/setup` and
|
|
`https://downloads.claude.ai/keys/claude-code.asc`;
|
|
- Bun release and release-key usage:
|
|
`https://github.com/oven-sh/bun/releases/tag/bun-v1.4.0` and
|
|
`https://github.com/oven-sh/bun/blob/main/dockerhub/distroless/Dockerfile`;
|
|
- RPM Fusion keys:
|
|
`https://download1.rpmfusion.org/free/fedora/RPM-GPG-KEY-rpmfusion-free-fedora-2020`
|
|
and
|
|
`https://download1.rpmfusion.org/nonfree/fedora/RPM-GPG-KEY-rpmfusion-nonfree-fedora-2020`;
|
|
- Hyprland COPR key:
|
|
`https://download.copr.fedorainfracloud.org/results/lionheartp/Hyprland/pubkey.gpg`;
|
|
- Flathub descriptor: `https://flathub.org/repo/flathub.flatpakrepo`;
|
|
- Node release and verification instructions:
|
|
`https://github.com/nodejs/node/releases/tag/v24.20.0` and
|
|
`https://github.com/nodejs/node/blob/main/README.md`;
|
|
- Codex release and signing workflow:
|
|
`https://github.com/openai/codex/releases/tag/rust-v0.150.1` and
|
|
`https://github.com/openai/codex/blob/main/.github/workflows/rust-release.yml`;
|
|
- RustDesk release: `https://github.com/rustdesk/rustdesk/releases/tag/1.4.9`.
|
|
|
|
### Repository roots
|
|
|
|
- RPM Fusion release RPMs are downloaded first, verified against the vendored free
|
|
and nonfree keys, then installed as local files with
|
|
`--setopt=localpkg_gpgcheck=1`.
|
|
- Terra installs no package with `--nogpgcheck`. Panama verifies the Terra 44 key,
|
|
installs it atomically, and uses `terra.pkg_gpgcheck=1`,
|
|
`terra.repo_gpgcheck=1`, and the local pinned key with the official repository.
|
|
Failure stops before initial, desktop, or Hyprland package transactions.
|
|
- Hyprland COPR configuration is written from reviewed local data with the exact
|
|
base URL, `gpgcheck=1`, `repo_gpgcheck=1`, and pinned project key. Panama does not
|
|
use interactive/TOFU `dnf copr enable -y`.
|
|
- Flathub's descriptor is downloaded as data. Panama decodes and verifies its embedded
|
|
primary key and requires GPG verification before adding or retaining the remote.
|
|
A mismatch preserves an existing remote and skips Flathub transactions.
|
|
|
|
The exact Terra DNF command receives a disposable Fedora 44 container smoke test. It
|
|
is never tried on the daily-driver host. If the signed bootstrap cannot be made to
|
|
work, Terra is reported unavailable and desktop installation stops before packages
|
|
that depend on it.
|
|
|
|
### User tools
|
|
|
|
- Node 24.20.0 is installed into the nvm version layout from the reviewed per-arch
|
|
archive and selected as the default. `nvm install --lts` is removed.
|
|
- pnpm comes from Fedora's signed `pnpm` package. If unavailable, Panama reports a
|
|
soft failure; it does not run npm or a remote installer as fallback.
|
|
- Bun is installed from the reviewed archive after SHA-256 verification; the remote
|
|
install script is removed.
|
|
- Claude Code uses Anthropic's signed RPM repository after exact key and repository
|
|
validation; `claude.ai/install.sh` is removed.
|
|
- Codex uses the reviewed per-arch package archive and an atomic user-owned install;
|
|
unversioned `npm install -g` is removed.
|
|
- RustDesk uses the reviewed versioned RPM URL and SHA-256 before the narrow sudo DNF
|
|
install. It never resolves `latest` at runtime.
|
|
- Claude Desktop repository setup is never downloaded or executed. If an existing
|
|
repository has the expected base URL, key, `gpgcheck=1`, and `repo_gpgcheck=1`,
|
|
Panama may install from it. Otherwise it prints one optional manual step and
|
|
continues successfully.
|
|
- Vicinae extensions use `npm ci`; lock mismatch fails the extension build without
|
|
modifying the tracked lockfile.
|
|
|
|
Changing installer code, provenance data, keys, or package lists invalidates the
|
|
installer-stage hash so the next upgrade re-runs verification.
|
|
|
|
### Hermetic contract
|
|
|
|
`tests/setup/package-provenance-contract` runs real `gpg`/`sha256sum` against a test
|
|
key and signed tiny fixture manifest. It stubs curl, sudo, DNF, rpmkeys, Git, and
|
|
Flatpak. It proves:
|
|
|
|
- correct signature/digest success and verify-before-install ordering;
|
|
- wrong fingerprint, wrong signer, bad signature, absent checksum, truncation,
|
|
tampering, unsupported architecture, and interrupted download refusal;
|
|
- known-good target preservation and temporary cleanup;
|
|
- exact RPM Fusion, Terra, COPR, Flathub, Claude Code, and Claude Desktop policies;
|
|
- no `curl | shell`, unpinned npm global install, moving `latest` lookup, or
|
|
unverified root package input remains in the base installer;
|
|
- both x86_64 and aarch64 select only their reviewed URL and digest.
|
|
|
|
The existing `boot`, README, package-list, desktop-first, apps, launcher-search, and
|
|
update-command contracts are updated where their old expectations contradict the new
|
|
trust boundary.
|
|
|
|
## Server firewall transaction
|
|
|
|
### Files and policy
|
|
|
|
Create `server/scripts/update-firewall` as a standard-library Python executable and
|
|
invoke it from the real `setup/scripts/setup-server` stage. It is server-only and does
|
|
not reuse the desktop `panama-firewall` helper.
|
|
|
|
The repository carries LF-terminated canonical CIDR files:
|
|
|
|
- `server/firewall/cloudflare-v4.cidrs`
|
|
- `server/firewall/cloudflare-v6.cidrs`
|
|
|
|
They begin with the current official ranges published at
|
|
`https://www.cloudflare.com/ips-v4` and `https://www.cloudflare.com/ips-v6`.
|
|
|
|
Each refresh uses a 10-second connection timeout and 30-second total timeout, and
|
|
downloads at most 64 KiB per response without sudo. It requires:
|
|
|
|
- valid UTF-8/ASCII with one value per line;
|
|
- no blanks, whitespace, comments, or trailing fields;
|
|
- `ipaddress.ip_network(value, strict=True)` success;
|
|
- matching file family, canonical string form, no `/0`, no duplicate, and nonempty
|
|
IPv4 and IPv6 sets.
|
|
|
|
The pair is indivisible. One invalid response changes nothing. With an existing valid
|
|
Panama policy, refresh failure warns and preserves it. On first setup, the validated
|
|
committed pair is the fallback.
|
|
|
|
### Desired state
|
|
|
|
Content-addressed ipsets are named:
|
|
|
|
```text
|
|
panama-cf4-<first 12 SHA-256 hex>
|
|
panama-cf6-<first 12 SHA-256 hex>
|
|
```
|
|
|
|
The selected public zone receives exactly four Panama-owned rich rules: IPv4 and IPv6
|
|
sources for each of `80/tcp` and `443/tcp`. The selected WireGuard zone receives only
|
|
`81/tcp`. Panama adds no service-specific port.
|
|
|
|
Zone selection is stored without shell evaluation in
|
|
`${XDG_CONFIG_HOME:-$HOME/.config}/panama/server-firewall.conf`:
|
|
|
|
```text
|
|
public_zone=public
|
|
wireguard_zone=wireguard
|
|
```
|
|
|
|
On first run, the public zone is firewalld's default zone. The WireGuard zone is the
|
|
unique active zone whose interfaces are all named `wg*`. Zero matches leaves port 81
|
|
closed and omits the setting. More than one match, identical public/WireGuard zones,
|
|
or a candidate containing a non-WireGuard interface fails before mutation. Explicit
|
|
stored zones must still exist and satisfy those invariants.
|
|
|
|
Before mutation, Panama refuses unrelated services, port ranges, ACCEPT zone targets,
|
|
or rich rules that broadly admit 80/443. It prints exact inspection commands. It
|
|
removes the exact direct 80/443/81 legacy rules only during the documented migration.
|
|
|
|
### Recoverable transaction
|
|
|
|
The updater acquires a nonblocking user-state lock and writes a checked journal to
|
|
`${XDG_STATE_HOME:-$HOME/.local/state}/panama/firewall/pending.json` containing only
|
|
Panama-owned rules/ipsets, the legacy direct ports, the WireGuard 81 rule, and selected
|
|
zones.
|
|
|
|
It then:
|
|
|
|
1. creates and fills the new permanent generation ipsets;
|
|
2. adds the four new public rules;
|
|
3. adds WireGuard 81 when a valid zone exists;
|
|
4. removes old Panama rules, exact legacy ports, and unreferenced Panama ipsets;
|
|
5. runs `firewall-cmd --check-config`;
|
|
6. performs one reload;
|
|
7. reads permanent and runtime state back;
|
|
8. writes selected-zone configuration and removes the journal only after equivalence.
|
|
|
|
Old live rules remain active until the single reload. A partial permanent transaction
|
|
therefore does not create an outage. Any error restores the snapshot and reloads.
|
|
Rollback success still returns nonzero. Rollback failure retains the journal and
|
|
prints its path plus the exact retry command. A later run restores a pending journal
|
|
before considering new inputs.
|
|
|
|
`setup-server` attempts `sudo systemctl enable --now firewalld`, then requires both
|
|
`is-enabled` and `is-active`. Missing commands or failed postconditions stop before
|
|
the updater. The updater itself is never run through sudo; only its narrow systemctl
|
|
and firewall-cmd mutations use sudo.
|
|
|
|
### Stateful contract
|
|
|
|
`tests/setup/server-firewall-contract` invokes the real `setup-server`. Its stateful
|
|
stubs keep separate permanent/runtime JSON and exact argv logs. `firewall-cmd` supports
|
|
only the queried zone, service, port, rich-rule, ipset, check-config, and reload
|
|
operations. Reload copies permanent to runtime. Exact one-shot failure selectors leave
|
|
state unchanged.
|
|
|
|
Required cases are:
|
|
|
|
- missing/inactive firewalld that cannot become enabled and active;
|
|
- first setup with legacy rules, exact desired ipsets/rules, WireGuard-only 81,
|
|
unrelated SSH/manual rules preserved, and one reload;
|
|
- every malformed CIDR class and indivisible-pair behavior;
|
|
- no WireGuard zone, ambiguous zones, identical zones, and mixed interfaces;
|
|
- unrelated broad exposure conflict refusal;
|
|
- reload failure with successful rollback and rollback-reload failure with journal;
|
|
- identical second run with no mutation/reload and valid two-family refresh;
|
|
- interrupted journal recovery before new evaluation.
|
|
|
|
## Scope ledger amendments
|
|
|
|
Reconnaissance added findings that the parent ledger did not name. Ownership is:
|
|
|
|
| Finding | Package |
|
|
|---|---|
|
|
| Mutable root bootstrap and mutable initial checkout | 2 |
|
|
| Unverified RPM Fusion/COPR/Flathub trust roots | 2 |
|
|
| Moving Node/pnpm/Bun/Claude/Codex/RustDesk inputs | 2 |
|
|
| Mutable npm install during Vicinae extension build | 2 |
|
|
| Unauthenticated fetched revision used by `panama update` | 3 |
|
|
| Mutable source-app build inputs | 5 |
|
|
| Mutable Neovim bootstrap/plugin graph | 5 |
|
|
|
|
Package 3 must define an authenticated approved revision or signed release manifest
|
|
before fetched Panama code reaches `install --upgrade`. Package 5 must pin or refuse
|
|
the ChatGPT source application and Neovim bootstrap/plugin graph. Package 3 retains
|
|
container image trust, and Package 4 retains the Whisper image/model pins already
|
|
assigned by the parent design.
|
|
|
|
## Documentation
|
|
|
|
Package 2 updates:
|
|
|
|
- README bootstrap commands and root-server narrative;
|
|
- README/server README firewall policy and port exposure;
|
|
- installer comments that currently defend moving or unsigned inputs;
|
|
- package/provenance documentation and key-rotation procedure;
|
|
- Panama development/operator skills when command behavior changes.
|
|
|
|
Documentation never claims a real host was cut over or a live reload succeeded.
|
|
|
|
## Verification
|
|
|
|
Each implementation plan uses red-green cycles through public interfaces. The package
|
|
gate includes:
|
|
|
|
```bash
|
|
bash -n boot install setup/scripts/install-packages setup/scripts/setup-server \
|
|
setup/scripts/link-vicinae-scripts setup/lib/artifact-provenance \
|
|
tests/setup/root-server-bootstrap-contract \
|
|
tests/setup/package-provenance-contract \
|
|
tests/setup/server-firewall-contract
|
|
|
|
python3 -m py_compile server/scripts/update-firewall
|
|
|
|
tests/setup/root-server-bootstrap-contract
|
|
tests/setup/package-provenance-contract
|
|
tests/setup/server-firewall-contract
|
|
tests/setup/boot-contract
|
|
tests/setup/role-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
|
|
```
|
|
|
|
If available, ShellCheck covers every touched shell file. The signed Terra command
|
|
also receives one disposable Fedora 44 container smoke test. The container has no host
|
|
mounts, host package database, system bus, SSH daemon, firewall access, or production
|
|
credentials.
|
|
|
|
## Completion criteria
|
|
|
|
Package 2 is complete when:
|
|
|
|
- no unverified mutable network response is executed by root or while relying on the
|
|
installer's sudo keepalive;
|
|
- the initial Panama revision and boot digest are checked before handoff;
|
|
- every automatic third-party executable input is signature-verified or pinned by a
|
|
reviewed immutable SHA-256;
|
|
- untrusted Claude Desktop setup is optional and never automatic;
|
|
- SSH hardening cannot remove the available login path and rolls back every tested
|
|
validation/reload failure;
|
|
- server firewall setup establishes the documented Cloudflare/WireGuard policy or
|
|
returns nonzero without losing the last known good policy;
|
|
- the full hermetic gate passes without live capability grants;
|
|
- independent package review has no unresolved Critical or Important finding.
|