Sigil Anchoring
Committing a Weft root on the Sigil chain so verifiers can prove an artifact existed at a specific block.
What this page covers
The "off-chain bytes, on-chain commitment" pattern. Weft keeps the bytes; Sigil keeps a small commitment that lets a verifier prove the artifact at a given root existed at a given block.
This page describes the model and the data plumbing. The actual transaction shape lives in the Sigil architecture overview, which covers how on-chain commitments compose with DID-bearing identity.
Why anchor a Weft root
Three properties become provable once a root is anchored:
- Existence — at block
N, the producer DID committed rootR. Verifiers can check the chain for the inclusion of that commit. - Authorization — the commit transaction is signed by the producer's identity (typically the same DID as
ManifestProducer.did). - Order — across multiple roots, block ordering establishes which was produced first.
What is not added by anchoring:
- Availability. Sigil does not store the bytes. Anchoring proves a root existed; it does not prove anyone is serving it.
- Validity. Sigil does not run
WeftManifest::validate. A producer can anchor a malformed manifest; consumers must still validate when fetching.
Anchoring is existence-only. A consumer must independently fetch the manifest from the DHT, verify the signature, and verify every chunk on retrieval. Anchoring does not relieve consumers of any verification step.
The data flow
Producer
────────
1. publish to a WeftDhtStore → manifest, chunks, availability on the DHT
2. assemble a Sigil commitment transaction:
- Weft root (32 bytes)
- producer DID
- optional MARS asset id
- optional artifact metadata commitment (hash of canonical bytes)
3. sign + submit the transaction
4. wait for inclusion
5. record the chain reference in ArtifactMetadata.sigil_commitment_cid
Consumer
────────
1. read the Sigil commitment for the artifact id
2. extract the Weft root
3. fetch the manifest from the DHT (manifest_key(root))
4. verify the manifest signature
5. fetch chunks; verify each chunk id; assemble payloadCarrying the Sigil reference in the manifest
ArtifactMetadata has a sigil_commitment_cid: Option<String> field. Once a root has been anchored, the producer can re-publish with the field populated:
use weft::manifest::ArtifactMetadata;
let artifact = ArtifactMetadata {
artifact_id: Some("dataset-2026-05-14".into()),
media_type: "application/x-zip".into(),
schema: "weave.dataset.v1".into(),
mars_asset_id: Some("mars://asset/abc123".into()),
sigil_commitment_cid: Some("sigil:block:42/tx:7".into()),
};The sigil_commitment_cid is opaque to Weft — it is a verifiable pointer the consumer follows. The format is defined by the Sigil chain.
Verifier flow
use weft::manifest::WeftManifest;
async fn verify_with_chain(
manifest_bytes: &[u8],
chain_client: &impl ChainClient,
) -> Result<(), Box<dyn std::error::Error>> {
let manifest = WeftManifest::from_canonical_bytes(manifest_bytes)?;
// 1) Validate the manifest is internally consistent.
manifest.validate()?;
// 2) Verify the producer signature.
let pubkey = chain_client.resolve_did(&manifest.producer.did).await?;
let signing_payload = manifest.signing_payload()?;
let sig = ed25519_dalek::Signature::from_slice(&manifest.producer.signature)?;
ed25519_dalek::Verifier::verify(&pubkey, &signing_payload, &sig)?;
// 3) Verify the chain commitment, if present.
if let Some(commit_ref) = &manifest.artifact.sigil_commitment_cid {
let commit = chain_client.read_commitment(commit_ref).await?;
if commit.weft_root != manifest.root {
return Err("chain commitment root != manifest root".into());
}
if commit.producer_did != manifest.producer.did {
return Err("chain commitment producer != manifest producer".into());
}
}
Ok(())
}
trait ChainClient {
async fn resolve_did(&self, did: &str) -> Result<ed25519_dalek::VerifyingKey, Box<dyn std::error::Error>>;
async fn read_commitment(&self, cid: &str) -> Result<ChainCommitment, Box<dyn std::error::Error>>;
}
struct ChainCommitment {
weft_root: weft::WeftHash,
producer_did: String,
}Replace ChainClient with the Sigil RPC client your application uses; the shape above is the minimum the verifier needs.
When to anchor
| Workload | Anchor? |
|---|---|
| Public model release; reputation matters | Yes |
| Training corpus shared with downstream auditors | Yes |
| Internal dataset shared between agents you control | Optional |
| Ephemeral cache content | No |
| Anything you might want to revoke later | Anchor with a revocation predicate in MARS or a separate Sigil contract |
What gets revoked
Sigil anchoring is append-only. To "revoke" an artifact, anchor a second commitment that supersedes the first. Verifiers that follow the chain see both records and apply whatever revocation policy your application defines.
The Weft data itself stays accessible as long as peers serve it. Anchoring revocation is a policy layer above Weft, not a delete operation on the DHT.
Coordinating with MARS
MARS is the universal asset registry. The recommended flow:
- Mint the asset in MARS, receiving a
mars_asset_id. - Publish to Weft with
ArtifactMetadata.mars_asset_idset. - Anchor on Sigil with both
weft_rootandmars_asset_idin the commitment. - Republish the manifest with
sigil_commitment_cidset so the in-band manifest is self-describing.
After this, a verifier with just the manifest bytes can reach MARS, Sigil, and the producer's DID without out-of-band context.