Narrow scope: personal middle ground, not a security framework
Adds an explicit Scope/Non-Goals section (curated personal package list, not adversarial-config or compromised-vendor-pipeline defense) and a post-build version sanity check as its own pipeline stage, distinct from the trust tiers — a correctness gate (does the build report the version we expected), not a security control. Trims the next-steps list to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A2FEut5tVMNjeVjqhgVZbr
This commit is contained in:
parent
0fa994f60a
commit
8443ecea69
1 changed files with 71 additions and 7 deletions
78
SPEC.md
78
SPEC.md
|
|
@ -20,6 +20,37 @@ package X" once, and have that declaration stay live — checked periodically,
|
||||||
re-verified on every new upstream release, and fed into a normal
|
re-verified on every new upstream release, and fed into a normal
|
||||||
`pacman`-based workflow without hand-editing a PKGBUILD each time.
|
`pacman`-based workflow without hand-editing a PKGBUILD each time.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This is a personal tool for a small, curated list of non-critical packages
|
||||||
|
— not a general-purpose supply-chain-security framework. The concrete
|
||||||
|
motivating case: software (especially fast-moving AI/ML tooling) that
|
||||||
|
Manjaro's `extra` repo lags weeks behind upstream on, where raw AUR or a
|
||||||
|
vendor's curl|sh script are the only faster alternatives today.
|
||||||
|
|
||||||
|
**Goal**: a middle ground — fresher than Manjaro's lag, with real
|
||||||
|
verification where the vendor actually offers something to check, safer
|
||||||
|
than blindly piping an install script to `sh`.
|
||||||
|
|
||||||
|
**Non-goals**:
|
||||||
|
|
||||||
|
- Defending against a fully compromised vendor signing/release pipeline.
|
||||||
|
If upstream's CI or signing key is itself compromised, pkgwatch cannot
|
||||||
|
and does not try to catch that. Tiers 1–3 (below) raise the bar from
|
||||||
|
"trust the domain" to "trust the vendor's actual release process"; they
|
||||||
|
are not a guarantee against that process being subverted.
|
||||||
|
- Defending against a malicious downgrade specifically. A compromised
|
||||||
|
version-check source reporting an older version as "latest" is a
|
||||||
|
downstream/distro-security problem, out of scope here. (See "post-build
|
||||||
|
version check" below for a related but distinct sanity check — it is
|
||||||
|
not a security control.)
|
||||||
|
- Supporting an adversarial or multi-user config. The tracked-package list
|
||||||
|
is curated by the one person running the daemon on their own machine;
|
||||||
|
config/state file integrity relies on normal filesystem permissions, not
|
||||||
|
a hardened trust boundary. Key-pinning friction on rotation (see tiers
|
||||||
|
below) is acceptable, even desirable, at this scale — it's a personal,
|
||||||
|
low-volume list, not something that needs to scale painlessly.
|
||||||
|
|
||||||
## Vision
|
## Vision
|
||||||
|
|
||||||
A Rust binary, run as a systemd service (service + timer, periodic not
|
A Rust binary, run as a systemd service (service + timer, periodic not
|
||||||
|
|
@ -44,7 +75,10 @@ that those tools don't provide.
|
||||||
|
|
||||||
## Verification trust tiers
|
## Verification trust tiers
|
||||||
|
|
||||||
The central design problem: from the install UX, a cryptographically strong
|
Per the Scope above, the goal is to raise the bar above curl|sh where the
|
||||||
|
vendor gives us something to check — not to build airtight supply-chain
|
||||||
|
defense. The central design problem within that goal: from the install UX,
|
||||||
|
a cryptographically strong
|
||||||
verification and a "trust-me-bro" same-domain checksum look identical. The
|
verification and a "trust-me-bro" same-domain checksum look identical. The
|
||||||
tool's job is to make that difference legible instead of laundering every
|
tool's job is to make that difference legible instead of laundering every
|
||||||
package into an undifferentiated "verified" bucket.
|
package into an undifferentiated "verified" bucket.
|
||||||
|
|
@ -82,6 +116,10 @@ This is the load-bearing decision, not just a cosmetic label:
|
||||||
adds at these tiers is diff-and-alert (notice something changed, surface
|
adds at these tiers is diff-and-alert (notice something changed, surface
|
||||||
the new hash for a human to look at), not verify-and-trust.
|
the new hash for a human to look at), not verify-and-trust.
|
||||||
|
|
||||||
|
The post-build version sanity check (see Architecture below) runs
|
||||||
|
regardless of tier — it's a correctness gate on the build itself, not part
|
||||||
|
of the trust-tier judgment, and doesn't change this tiering.
|
||||||
|
|
||||||
### Surfacing trust, not just gating on it
|
### Surfacing trust, not just gating on it
|
||||||
|
|
||||||
- Every tracked package carries an explicit tier + one-line justification
|
- Every tracked package carries an explicit tier + one-line justification
|
||||||
|
|
@ -106,6 +144,13 @@ tier = 4
|
||||||
method = "same-origin-sha256"
|
method = "same-origin-sha256"
|
||||||
checksum_asset_pattern = "uv-x86_64-unknown-linux-gnu.tar.gz.sha256"
|
checksum_asset_pattern = "uv-x86_64-unknown-linux-gnu.tar.gz.sha256"
|
||||||
|
|
||||||
|
# Post-build sanity check — correctness only, not a security control.
|
||||||
|
# Runs the built binary and confirms it reports the version pkgwatch
|
||||||
|
# believes it just built; mismatch blocks publish.
|
||||||
|
[package.uv.sanity_check]
|
||||||
|
command = "uv --version"
|
||||||
|
version_regex = 'uv (\d+\.\d+\.\d+)'
|
||||||
|
|
||||||
# Tier 1 example:
|
# Tier 1 example:
|
||||||
[package.somepkg]
|
[package.somepkg]
|
||||||
source = "url-with-version-regex"
|
source = "url-with-version-regex"
|
||||||
|
|
@ -144,11 +189,22 @@ Open questions on the schema:
|
||||||
attestation companion) for a resolved version.
|
attestation companion) for a resolved version.
|
||||||
- **Verifier**: tier-specific verification implementations behind a common
|
- **Verifier**: tier-specific verification implementations behind a common
|
||||||
trait; returns a tier + pass/fail + justification string.
|
trait; returns a tier + pass/fail + justification string.
|
||||||
- **Publisher**: for tiers 1–3 on pass, generates/updates the PKGBUILD,
|
- **Builder**: for tiers 1–3 on pass, generates/updates the PKGBUILD
|
||||||
runs `makepkg`, runs `repo-add` against the local repo.
|
(strict validation on any upstream-controlled string — version, filename
|
||||||
|
— before it touches generated shell content; never unescaped
|
||||||
|
interpolation) and runs `makepkg`.
|
||||||
|
- **Sanity checker**: after a successful build, runs the package's
|
||||||
|
declared `sanity_check.command` against the built artifact and confirms
|
||||||
|
the reported version matches what pkgwatch believes it just built.
|
||||||
|
Mismatch = fail loud, do not publish. This is a correctness check, not a
|
||||||
|
security control — it catches checker bugs and mangled/wrong-artifact
|
||||||
|
downloads, not malicious releases.
|
||||||
|
- **Publisher**: runs `repo-add` against the local repo, only after the
|
||||||
|
sanity check passes.
|
||||||
- **Reviewer queue**: for tiers 4–6, records the detected change instead of
|
- **Reviewer queue**: for tiers 4–6, records the detected change instead of
|
||||||
auto-publishing; a separate `pkgwatch review` command lets a human
|
auto-building; a separate `pkgwatch review` command lets a human
|
||||||
approve/reject, which then triggers the publish step.
|
approve/reject, which then triggers the build → sanity-check → publish
|
||||||
|
steps above.
|
||||||
- **Scheduling**: systemd `.service` (oneshot) + `.timer` running it
|
- **Scheduling**: systemd `.service` (oneshot) + `.timer` running it
|
||||||
periodically, matching the pattern already used for other periodic tasks
|
periodically, matching the pattern already used for other periodic tasks
|
||||||
on this box.
|
on this box.
|
||||||
|
|
@ -166,11 +222,19 @@ Open questions on the schema:
|
||||||
|
|
||||||
## Status / next steps
|
## Status / next steps
|
||||||
|
|
||||||
- [ ] Refine config schema further (see open questions above).
|
- [x] Scope decided: personal middle-ground tool for a curated package
|
||||||
|
list, not a general supply-chain-security framework (see Scope
|
||||||
|
above). Downgrade attacks and compromised-vendor-pipeline defense
|
||||||
|
are explicit non-goals.
|
||||||
|
- [ ] Refine config schema further (see open questions above), including
|
||||||
|
the `sanity_check` block per package.
|
||||||
- [ ] Decide version-check strategy: shell out to `nvchecker` vs. own
|
- [ ] Decide version-check strategy: shell out to `nvchecker` vs. own
|
||||||
implementation, for the PoC.
|
implementation, for the PoC.
|
||||||
|
- [ ] Implement PKGBUILD generation with strict upstream-string validation
|
||||||
|
from day one (see Builder, above) — cheap to do right up front,
|
||||||
|
expensive to retrofit.
|
||||||
- [ ] PoC scope: single tier-4 package (e.g. `uv`, ironically) end-to-end —
|
- [ ] PoC scope: single tier-4 package (e.g. `uv`, ironically) end-to-end —
|
||||||
check, fetch, same-origin-checksum verify, flag-for-review, manual
|
check, fetch, same-origin-checksum verify, flag-for-review, manual
|
||||||
approve, PKGBUILD generation, local repo publish.
|
approve, build, post-build version sanity check, local repo publish.
|
||||||
- [ ] Decide on project home: local-only for now, or push to
|
- [ ] Decide on project home: local-only for now, or push to
|
||||||
code.austinschaefer.com (Forgejo) once the spec settles.
|
code.austinschaefer.com (Forgejo) once the spec settles.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue