Skip to content

refactor(wcm): adopt SDK 0.27.0, and fix six demos it broke - #91

Merged
imran-siddique merged 1 commit into
mainfrom
refactor/wcm-sdk-artifact-digest
Aug 27, 2026
Merged

refactor(wcm): adopt SDK 0.27.0, and fix six demos it broke#91
imran-siddique merged 1 commit into
mainfrom
refactor/wcm-sdk-artifact-digest

Conversation

@imran-siddique

Copy link
Copy Markdown
Member

Two things. The second was not planned and is the one to read.

1. The digest recipe comes from the SDK now

It was implemented here and copied into two Marketplace integrations, with nothing keeping the three in step. weight-custody-manifest 0.27.0 publishes wcm.artifact_digest, so sha256_artifact becomes a thin wrapper.

follow_symlinks=True, because a Hugging Face snapshot directory is a symlink tree: snapshot_download populates snapshots/<revision>/ with links into a content-addressed blobs/ directory. The SDK refuses symlinks by default, which is right for an artifact somebody handed you and wrong for a cache you just populated yourself.

flip_first_byte is gone. It existed so the demo could show a tampered hash without writing to the model somebody just downloaded, which is worth keeping, but it meant maintaining a second hashing path that existed only to fake tampering. tampered_digest copies the artifact, flips a byte, hashes the copy and discards it. Costs disk on a demo that already downloaded the model, and buys a demonstration where the bytes genuinely differ. The test asserting the original is untouched is kept and strengthened.

2. Six demos pass on 0.26.0 and fail on 0.27.0

None of them touch the digest recipe. This is the release itself, and it is the security work in it doing exactly its job.

Demo Cause
refuse_and_wipe, open_model_e2e, closed_model_e2e, revocation_kill_switch, channel_binding, real_open_model #98: release now refuses unless the manifest identity is pinned out of band
sovereign_self_custody #95: the memory-fingerprint challenge now requires a signed sweep

Every one of the first six built a broker that would have released against any manifest reusing a held weights hash. That is precisely what #98 closed. Fixed by pinning manifest_identity(manifest), which is the correct usage and what these demos should have been modelling all along.

For sovereign_self_custody, an unsigned fingerprint is now refused rather than accepted on trust, because an aliasing attack that can fake a readback can also fake an unsigned claim about it. Replaced the mock fingerprint with a real run_memory_sweep over a BytearrayMemoryRange and gave the broker the sweep key. The demo is better for it: it demonstrates the control instead of stubbing it.

A latent bug that surfaced with it

sovereign_self_custody called decision.failures() on a property. It had never run, because the gate had never refused. An error path that only executes when something is wrong is exactly the one worth having correct.

CI never ran the tests in this directory

The job runs the demo scripts and stops there. The tests cover the artifact digest, which is the value a manifest binds, so a change to it that no demo happens to exercise would land unnoticed. Added a pytest step, and bumped the stale >=0.21.0 floor in the model-signing step to match requirements.txt.

Verified

Against published 0.27.0 in a clean venv: 13 demos run, 10 tests pass. Confirmed the six failures reproduce on 0.26.0-vs-0.27.0 before fixing, so the attribution is not a guess.

🤖 Generated with Claude Code

https://claude.ai/code/session_014NL8o3PXq6kfs2SdmBv6ak

Two things, and the second was not planned.

The digest recipe now comes from wcm.artifact_digest. It was implemented here
and copied into two Marketplace integrations, with nothing keeping the three in
step; a recipe that exists three times stops being one recipe, and the drift
shows up as weights_hash not matching, which reads as tampered weights.
sha256_artifact becomes a thin wrapper passing follow_symlinks=True, because a
Hugging Face snapshot directory is a symlink tree and the SDK refuses symlinks
by default.

flip_first_byte is gone. It existed so the demo could show a tampered hash
without writing to the model somebody just downloaded, which is a property worth
keeping, but it meant maintaining a second hashing path that existed only to
fake tampering. tampered_digest copies the artifact, flips a byte, hashes the
copy and discards it. Costs disk on a demo that already downloaded the model,
and buys a demonstration where the bytes genuinely differ. The test asserting
the original is untouched is kept and strengthened.

Then, verifying against the published package: SIX demos that pass on 0.26.0
fail on 0.27.0. None of them touch the digest recipe, so this is the release
itself, and it is the security work in that release doing its job.

  refuse_and_wipe, open_model_e2e, closed_model_e2e, revocation_kill_switch,
  channel_binding, real_open_model
      #98 refuses release unless the manifest identity is pinned out of band.
      Every one of these built a broker that would have released against any
      manifest reusing a held weights hash. Fixed by pinning
      manifest_identity(manifest), which is the correct usage and what these
      demos should have been modelling.

  sovereign_self_custody
      #95 requires a SIGNED memory sweep; an unsigned fingerprint is refused
      rather than accepted on trust, because an aliasing attack that can fake a
      readback can also fake an unsigned claim about it. Replaced the mock
      fingerprint with a real run_memory_sweep over a BytearrayMemoryRange and
      gave the broker the sweep key. The demo is better for it: it now
      demonstrates the control instead of stubbing it.

      Also fixed a latent bug this exposed: the failure branch called
      decision.failures() on a property. It had never run, because the gate had
      never refused. An error path that only executes when something is wrong is
      exactly the one worth checking.

CI ran the demo scripts and never ran the tests in this directory. They cover
the artifact digest, which is the value a manifest binds, so a change to it that
no demo happens to exercise would land unnoticed. Added a pytest step, and
bumped the stale >=0.21.0 floor in the model-signing step to match.

Verified against published 0.27.0 in a clean venv: 13 demos run, 10 tests pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014NL8o3PXq6kfs2SdmBv6ak
@imran-siddique
imran-siddique requested a review from a team as a code owner August 27, 2026 16:01
@imran-siddique
imran-siddique merged commit 04943c5 into main Aug 27, 2026
18 checks passed
@imran-siddique
imran-siddique deleted the refactor/wcm-sdk-artifact-digest branch August 27, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant