Skip to content

Pack contracts have no legacy-migration path; a pack-contract re-key orphans all history #64

Description

@sanity

Found while writing tests that exercise the repo-contract legacy fallback for the first time (PR forthcoming). Not a bug in existing code — a gap in what the migration mechanism covers.

The gap

legacy_contracts.toml / LEGACY_REPO_CONTRACT_WASM_HASHES covers the repo contract only. There is no equivalent for the pack contract.

Object bundles live in pack contracts keyed by BLAKE3(BLAKE3(pack_wasm) || pack_hash), and both fetch paths derive that key from the current pack_wasm with no fallback:

  • wsclient::get_pack (crates/freenet-git/src/wsclient.rs) — ContractKey::from_params_and_code(Parameters::from(pack_hash), ContractCode::from(pack_wasm))
  • chunked::fetch_chunked_pack_with_progress — same derivation per chunk

So if contracts/pack-contract.wasm is ever rebuilt, every previously-published bundle becomes unreachable, and unlike the repo contract there is no mechanism to find it.

Why this is reachable

Both WASM artifacts are checked in and both date from 2026-04-30. A rebuild is triggered by a freenet-stdlib bump, a toolchain bump, or any change to either contract crate — and a stdlib/toolchain bump rebuilds both contracts, not just the one whose source changed.

#63 documents the re-key procedure for the repo contract (capture the old BLAKE3, add a legacy_contracts.toml entry, rebuild). Following it exactly still leaves the pack side unhandled.

Symptom if it happens

Worse than a clean failure, because the repo half succeeds:

  1. git clone falls back, finds the repo state at the legacy repo key, migrates it forward.
  2. Refs and object_index list normally.
  3. Every bundle download fails — the packs are at pack-contract keys nobody computes any more.

Existing clones are unaffected (PackCache is keyed by content hash, not contract key) and new pushes self-heal at the new key, so the loss is specifically historical objects for anyone who has not already cloned.

Possible directions (not a proposal)

  • A legacy_pack_contracts registry plus fallback in get_pack / the chunked fetch path, symmetric with the repo side. Cheap, but re-PUT-forward is expensive: it means republishing every bundle in the repo.
  • Or: treat a pack-contract re-key as requiring a freenet-git rescue pass that re-PUTs bundles at the new key, and note in the release procedure that this must happen before the old copies age out. Note rescue currently GETs via get_pack at the current key, so it cannot do this today either.
  • Or: make the pack-contract WASM hash not part of the pack key at all, if the pack contract is a pure content-addressed blob store where the code identity adds nothing. Biggest change, removes the failure class.

Worth deciding before #63's rebuild, since that is the first re-key event on the horizon.

[AI-assisted - Claude]

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions