Integrity: 1,080 historical commit signatures invalid after ID migrations
Summary
Whole-history verification of the current canonical gabriel/muse ancestry reports 1,080 invalid Ed25519 signatures. Current objects, snapshots, commit IDs, and recent signatures verify; the failures are historical and align with the May 20–21 commit-ID/unified-store migrations.
This is not currently tracked by an issue. Related issues #10/#11 intentionally changed all object IDs but did not specify signature preservation. Issue #83 later established the correct invariant—every changed-ID commit must be re-signed—but it addressed a different snapshot-repair case and is closed.
Exact observation
- Staging repo ID:
sha256:200e8689fe34a831289bc1eca17633b2069d595379b7c2f57a158e35d8291bec - Production repo ID:
sha256:f97e3518b7887284682a8a7b357b514c3383f5565d4039422598936d354fc83c - Both
mainanddev:sha256:c606568badb1bd51b0da285b5c395370f5e6148b205cd3948de4113c5714ba70 - Snapshot:
sha256:20f25c55ab64ce347280522b2eefbb937bf90dc3241967d6918bd3fac10ce0cd muse verify --branch dev --json: 1,550 commits, 1,518 snapshots, 1,204 rehashed objects, 1,464 signatures checked, 1,080 signature failures, zero other failure kinds- Oldest failure:
sha256:1c4b3e3a9a1f300774c3ee662b572a698d5fd405bf765a71e6011a2e9c3eaaaa - Newest failure:
sha256:2f17e90958a3a39216322972274314b4c495ed995ea0edf4d24a777d171226f7 - Valid signing resumes at
sha256:1900655993c83c4107067375548a7be823e471d2515830842f1a12cba4bd3cdf
muse/core/migrate.py recomputes commit/snapshot/parent IDs and copies the old signature. Since the provenance payload includes commit_id, the copied signature becomes invalid. Current records reproduce their current IDs, but the original pre-migration signed objects and a complete immutable original-ID→current-ID map have not been recovered; migration-only transformation therefore cannot be cryptographically proved for every affected record.
Impact
- Adding Timeline, Knowtation/Notation, or any other built-in domain to this source repository inherits a failing whole-history gate even when the new code is correct.
- New user repositories do not inherit these failures.
- Runtime use is not known to be broken, but maintainers, contributors, release verification, and anyone auditing
gabriel/museencounter an unresolved provenance result. - A staging-only exception is insufficient because production publishes the same head under a different repository ID.
Recommended closure: signed history boundary
Preserve all old records unchanged and add a versioned, maintainer-signed history-boundary/v1 attestation. This is a new trust root, not a claim that the 1,080 old signatures were recovered or verified.
The attestation and verifier must bind and validate:
- Repository identity (or an explicit signed staging↔production mirror relationship), exact boundary commit and snapshot.
- Content digest of the exhaustive affected-current-ID manifest and exact failure count.
- Classification of that bounded set as
legacy_unverifiedcaused by an ID-changing migration whose original mapping/proof is unavailable. - Migration code/version and observation time.
- Gabriel's maintainer key and revocation state.
- Normal object, snapshot, commit-ID, and signature verification for every descendant after the boundary.
The verifier must report the bounded exception explicitly and fail closed for a wrong repo/head/snapshot, altered or extra affected ID, new pre/post-boundary failure, invalid/revoked attestation, or untrusted signer. A blanket --ignore-signatures mode is out of scope.
Other defensible options
- Recover authoritative original objects/backups plus an immutable old→current map, verify every original signature, and perform a reviewed canonical repair.
- Archive the current repository and establish a clean official successor from reviewed current bytes, with a signed link to the archive.
Acceptance criteria
- Maintainer chooses and documents the trust policy before implementation.
- Machine-readable exhaustive manifest and deterministic digest are generated.
- Tests cover every binding, tamper, omission, extra-failure, signer, revocation, offline/cache, and mirror case.
- Staging and production identities are both covered.
- Current legacy failures remain visible and are never labeled valid.
- All post-boundary commits verify normally.
- Independent review reproduces the result before this is used as a release/domain-development gate.
Aaron can prepare the implementation, tests, documentation, evidence manifest, and proposal on an isolated feature branch after Gabriel confirms the policy. Gabriel would review/merge and issue the final maintainer signature.
Resolved — repaired, not left as a formally-unverified trust boundary.
The ticket's "original pre-migration signed objects... have not been recovered" premise turned out to be incorrect. Canonical local
gabriel/musehistory has (and apparently always had) 0 signature failures — every commit's current, valid signature was never actually lost. What was actually broken was the same bug we just found and fixed ingabriel/musehubthis week: staging's stored copy of historical commit rows got corrupted by the rc10 object-store migration (spurioussigner_public_key/signaturestamped on rows without recomputingcommit_id), but the correct data was sitting right there in canonical local history the whole time.Repaired directly via
wire_repair_commit(recomputescommit_idfrom the canonical record, fails closed on any mismatch — no "trust me" step, every repair is independently re-verified). Final result:muse verifyacross multiple follow-up passes (0 failures except the one noted below).sha256:2f17e90958a3a39216322972274314b4c495ed995ea0edf4d24a777d171226f7has a 15.6 MBstructured_delta, which exceeds the/repair-commitendpoint's request body size limit (HTTP 413). Needs a scoped follow-up to raise or chunk that limit — filing separately, not blocking this closure.The elaborate
history-boundary/v1attestation system this ticket proposed is not needed — there's no "legacy_unverified" class of commit here, just a repair that was never run against the full affected set. Full detail and cross-reference posted on muse#108 (Gate-1 L1), since this is the same 1,080 rows that surfaced there during the real-history migration spike.Closing.