Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,19 @@ jobs:
sh -c 'exit 3' | cat || rc=$?
[ "$rc" = "3" ] || { echo "pipefail is NOT active: a failing left-hand side reported $rc — piped gates can report green while failing"; exit 1; }
echo "pipefail active: a failing left-hand side propagates (rc=$rc)"
# varve's rolling trust root rotated in v0.32.1. The published varve-realms.toml is
# byte-identical to ours EXCEPT the trust-root line, so vendoring it reads as a
# one-line no-op diff and would orphan our pin (`No valid signatures`) until layer
# 2026.09.2 exists. This loop bumps pins on evidence; a rule that lives only in a
# release note is one it will not see.
- name: varve realm/pin agree (trust root can verify the pinned layer)
# This job checks out to `path: jess`, so every step that touches the repo needs
# working-directory. Omitting it cost a red run: exit 127, "No such file or
# directory", which reads like a missing script rather than a wrong cwd.
working-directory: jess
run: |
tools/varve/check-realm.sh --self-test
tools/varve/check-realm.sh
- name: rivet sync + validate
working-directory: jess
run: |
Expand Down
69 changes: 69 additions & 0 deletions artifacts/findings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6261,3 +6261,72 @@ artifacts:
detected-by: clean-room verification of AFD-109/AFD-110 plus a retrospective check-drift run, 2026-09-07
severity: critical
triage-status: confirmed

- id: AFD-112
type: ai-found-defect
title: "varve v0.32.1 rotated the rolling trust root and the correct consumer action is DO NOTHING — but the new realms file differs from ours by one line, so taking it reads as a no-op diff and would orphan the pin; guarded"
status: open
description: |-
2026-09-07. varve v0.32.1 (published 11:36) is NOT the consumer-API release jess asked for on
varve#130. Its own notes: "There is no code change from v0.32.0 — no behaviour, no CLI, no
format." It exists to publish a rotated rolling trust root, 4e771dc6... -> 7d3b892e...

*** JESS'S CORRECT ACTION IS TO DO NOTHING, AND THAT IS VARVE'S OWN INSTRUCTION ***
Layers 2026.08.0 .. 2026.09.1 are signed by the OLD root; 2026.09.2 will be the first signed
by the new one. jess pins layer 2026.08.4 and carries the OLD root, which is the CORRECT
pairing. Verified rather than assumed: `varve verify` reports "layer 2026.08.4 ... verified:
signature OK, 11 tool(s) match their signed digests". (It exits 1 only on REQ-SHADOW-001 PATH
shadowing of 7 tools — the ADVISORY class AFD-111 just separated from blocking drift, so the
two checkers agree.)
Ran varve's own falsification test from the release notes: the published rolling.pub and the
published varve-realms.toml name the same root — AGREE.

*** THE HAZARD, AND WHY IT WARRANTS EXECUTABLE GUARDING ***
The published varve-realms.toml is BYTE-IDENTICAL to jess's except the trust-root line
(measured: diff is empty once that line is masked). So "vendor the new realms file" reads in
review as a one-line no-op, and produces:
error: manifest signature verification failed: ... No valid signatures
This repo runs an autonomous 4h loop that bumps pins on evidence — it bumped rivet and spar to
the pin in AFD-111 the same day. A rule that exists only in a release note is a rule that loop
will not see. tools/varve/check-realm.sh makes it executable: it refuses a realm/pin pairing
whose root cannot verify the pinned layer, in EITHER direction, plus an unrecognised root.
--self-test exercises five rows and BOTH failure modes are OBSERVED failing, including the
trap itself. The layer comparison is numeric, not lexical: 2026.09.10 must sort ABOVE the
2026.09.2 boundary and a string compare puts it below.

*** varve-core 0.32.1 AGAINST JESS'S FOUR ASKS: TWO LANDED ***
(a) generic held-payload kind + locate -> LANDED. `kind::PayloadKind` has 8 variants
(Tool/Crate/Wit/ZephyrModule/Sdk/WasmComponent/Vsix/Layer), `is_dispatchable()` true only
for Tool, and `UnknownKind` for forward compatibility. Its doc restates jess's own
argument: "Every other kind is held, not dispatched — its identity is (name, version,
platform)". Locate exists at the LIBRARY level via `store::payload_rel_path` and
`Store::find_anywhere`, which reach kinds the CLI has no export verb for (there are
export-cargo/-vsix/-bazel/-crates-vendor/-bazel-distdir, but none for wit/sdk/zephyr).
That is exactly the "consume the crate instead of parsing a command" the operator asked
for.
(b) the crate must be able to say `verified` at ALL -> LANDED. `reverify::verify_installed(
store, layer, verifier, platform) -> Result<usize, ReverifyError>` re-verifies an
installed layer against the trust root and returns the number of binaries checked, and
`verify::PinnedKeyVerifier` is a PUBLIC constructible impl of the `ManifestVerifier`
trait. So a jess reader can make a real verified claim instead of the weaker sentence
jess promised on varve#130 ("layer resolved, not re-verified at read time").
(c) crate/format version self-report -> NOT LANDED. No `pub fn version`, no FORMAT_VERSION
constant; the only version is the Cargo package version, which a consumer reads for its
OWN build, not for the crate's contract. This was the operator's framing: "with device
version x and varve crate y, and if varve changes to z we have one way to detect".
(d) query by type/field -> NOT LANDED beyond `discover::find_pin`.

*** THE CATALOGUE-READER PREMISE IS STILL NOT MET — CHECKED, NOT ASSUMED ***
The plan recorded on varve#130 was to build jess's catalogue reader against `resolve()`. No
layer carries a hardware-catalogue payload. The pinned layer's only held payloads are
`rivet-sdlc/0.34.0` (966,866 B) and `spar-aadl/0.40.0` (4,673,917 B), single .crate files that
jess consumes NEITHER of today. Building "consume the catalogue from varve" now would be
building against nothing — the same stale-premise error as AFD-107, checked this time before
writing code rather than after.
Incidental confirmation: those held payload versions are spar 0.40.0 and rivet 0.34.0, the
exact versions AFD-111 aligned CI to.
tags: [varve, trust-root, supply-chain, varve-core, premise-check, afd-111, varve-130]
fields:
detected-by: assessing varve v0.32.1 and varve-core 0.32.1 against jess's four consumer asks, 2026-09-07
severity: major
triage-status: confirmed
80 changes: 80 additions & 0 deletions tools/varve/check-realm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env bash
# Refuse the ONE combination varve v0.32.1's rotation makes silently fatal:
# a trust root that cannot verify the layer this project is pinned to.
#
# WHY THIS EXISTS. On 2026-09-07 varve rotated the rolling trust root
# (4e771dc6... -> 7d3b892e...). Layers 2026.08.0 .. 2026.09.1 are signed by the OLD
# root; 2026.09.2 will be the first signed by the NEW one. varve's own guidance to a
# consumer pinned below that boundary is: DO NOTHING.
#
# The hazard is that doing the wrong thing looks trivial. The published
# varve-realms.toml is BYTE-IDENTICAL to the one in this repo except for the
# trust-root line — verified, `diff` is empty once that line is masked. So "vendor the
# new realms file" reads as a one-line no-op diff in review, and produces:
#
# error: manifest signature verification failed: ... No valid signatures
#
# This repo runs an autonomous loop that bumps pins on evidence. A rule that lives only
# in a release note is a rule the loop will not see. This is that rule, executable.
set -uo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd -P)"
REALMS="${REALMS:-$ROOT/varve-realms.toml}"
PIN="${PIN:-$ROOT/varve.toml}"

OLD_ROOT=4e771dc62a08be89e3450f8cd807da58ff70af4a4e124ebf2d2b71684cfd9973
NEW_ROOT=7d3b892e6a33c70043becc708e08042e1cef0d54dd5ae6f23d7d4c68de1da1a0
# The boundary is a varve FACT, not a jess preference: the first layer signed by the
# new root. Layers sort lexically here because they are zero-padded YYYY.MM.N.
FIRST_NEW_ROOT_LAYER=2026.09.2

verdict() { # $1=root $2=layer -> prints OK/FAIL reason, returns 0/1
local root="$1" layer="$2" era
if [ "$root" = "$OLD_ROOT" ]; then era=old
elif [ "$root" = "$NEW_ROOT" ]; then era=new
else echo "UNKNOWN trust root $root — neither the pre- nor post-rotation value"; return 1; fi
# String compare is wrong across a component boundary (2026.09.10 vs 2026.09.2), so
# compare the numeric triple.
local a b; a=$(printf '%s' "$layer" | awk -F. '{printf "%04d%02d%03d",$1,$2,$3}')
b=$(printf '%s' "$FIRST_NEW_ROOT_LAYER" | awk -F. '{printf "%04d%02d%03d",$1,$2,$3}')
if [ "$a" -ge "$b" ]; then
[ "$era" = new ] && { echo "OK: layer $layer is post-rotation and the realm carries the NEW root"; return 0; }
echo "FAIL: layer $layer is signed by the NEW root, but this realm carries the OLD one — it cannot verify"; return 1
else
[ "$era" = old ] && { echo "OK: layer $layer is pre-rotation and the realm carries the OLD root"; return 0; }
echo "FAIL: this realm carries the NEW trust root, but layer $layer was signed by the OLD one.
varve: 'Every layer published from 2026.08.0 through 2026.09.1 was signed by the old
root and does not verify against the new one.' Taking the realms file WITHOUT moving
the pin to >= $FIRST_NEW_ROOT_LAYER leaves this project pinned to a layer its own realm
cannot verify. Until that layer exists, the correct action is to do NOTHING."; return 1
fi
}

if [ "${1:-}" = "--self-test" ]; then
ok=0
# Every row must be OBSERVED to give its stated verdict — including the two failures.
# A guard whose failing cases were never executed is the vacuity this campaign keeps
# finding in checkers rather than in code.
for row in "$OLD_ROOT|2026.08.4|0|pre-rotation pin, old root (jess today)" \
"$NEW_ROOT|2026.09.2|0|post-rotation pin, new root" \
"$NEW_ROOT|2026.08.4|1|THE TRAP: new realms file, pin not moved" \
"$OLD_ROOT|2026.09.2|1|pin moved past the boundary, realm not updated" \
"deadbeef|2026.08.4|1|unrecognised root"; do
IFS='|' read -r r l want label <<EOF
$row
EOF
got=0; out=$(verdict "$r" "$l") || got=1
if [ "$got" = "$want" ]; then printf " [ok ] %-42s -> %s\n" "$label" "$(echo "$out" | head -1)"
else ok=1; printf " [FAIL] %-42s want rc=%s got rc=%s\n" "$label" "$want" "$got"; fi
done
echo "SELF-TEST: $([ $ok = 0 ] && echo PASS || echo FAIL)"; exit $ok
fi

root=$(sed -n 's/^trust-root = "\(.*\)"/\1/p' "$REALMS" | head -1)
layer=$(sed -n 's/^layer[[:space:]]*=[[:space:]]*"\(.*\)"/\1/p' "$PIN" | head -1)
[ -n "$root" ] || { echo "no trust-root in $REALMS" >&2; exit 2; }
[ -n "$layer" ] || { echo "no layer in $PIN" >&2; exit 2; }
echo "realm trust-root: $root"
echo "pinned layer: $layer"
out=$(verdict "$root" "$layer"); rc=$?
echo "$out"
exit $rc
Loading