• attic/idunn-cut1-fix5 ac7824b482

    Ghost released this 2026-09-23 03:48:48 +00:00 | 324 commits to main since this release

    Land R-I25: version the artifact digest, and the frozen-source digest too

    Self's finding: R-I20 changed digest_artifact's bare-file shape, but three
    stored values are compared against a fresh recompute forever after they are
    sealed -- expected.artifact_sha256 (IdunnExpectedIncarnationRecord),
    ArtifactReceipt::sha256 (SealedRelease, in host.rs's verify_artifacts), and
    HostWorkloadObservation::executable_sha256. The first Idunn upgrade past
    R-I20 would have refused every already-installed and already-running
    workload sealed before it.

    The ruling asked for an explicit version tag recorded with the stored
    value. That channel does not exist here: expected.artifact_sha256 and
    ArtifactReceipt::sha256 are both validated to a bare sha256-<64 lower hex>
    shape with no room for one -- IdunnExpectedIncarnationRecord's validator
    lives in the pinned cultnet-rs git dependency, a separate repo this
    worktree has no authority to widen, and ArtifactReceipt::sha256's own local
    validator enforces the identical shape. There has only ever been one prior
    shape (raw_sha256 of the file's bytes alone, nothing else), so
    verify_artifact_digest recomputes under the current algorithm first and
    falls back to that exact legacy shape -- the same effect as reading an
    implicit version, without a tag this worktree cannot add. New seals always
    call digest_artifact directly, so they always use the current shape;
    nothing here ever rewrites a stored value to match.

    host.rs's start(), observe() and verify_artifacts() now go through
    verify_artifact_digest instead of comparing digest_artifact's output to the
    stored string directly. start() carries expected.artifact_sha256 forward
    into the returned observation rather than a fresh recompute, so a
    legacy-shaped receipt is echoed, not silently upgraded to a value it was
    never sealed against.

    Checked whether frozen_source_sha256 (the frozen-source digest,
    observe_frozen's stored-value comparison against FrozenSourceReceipt) has
    the same exposure: yes. Its algorithm changed twice before this batch
    touched anything -- R-I9 added mode and gid, R-I18 added xattrs -- and a
    FrozenSourceReceipt is persisted in DeploymentTransaction.frozen_source
    between the freeze() step and the step that calls observe_frozen, each a
    separate advance_sealing call that can cross a restart. The window is
    narrower than the artifact side (one in-flight Sealing-phase transaction,
    not every installed workload), but this cut's own batches 6 through 8
    already crossed it twice. verify_frozen_source_digest applies the same
    answer against both prior shapes, which are reproduced verbatim in
    frozen_source_sha256_legacy from git history (f873b54^ and f873b54).

    Tests: verify_artifact_digest_accepts_a_receipt_sealed_under_the_pre_r_i20_shape
    and verify_frozen_source_digest_accepts_both_shapes_that_predate_this_cuts_sixth_batch
    construct a stored value the old code would have produced and confirm the
    new code still accepts it, confirm the current-algorithm-only comparison
    they replace would have refused it, and confirm a real content tamper is
    still caught. verify_artifacts_accepts_a_receipt_sealed_under_the_pre_r_i20_shape
    exercises the same thing through host.rs's own verify_artifacts. Manually
    mutated (fallback removed, then restored) on Yggdrasil; see the verification
    report.

    Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com

    Downloads