One canonical object. Adapters in, standards bridged.
Every structured Satsignal provenance
anchor — from a CI build to an MCP-driven agent run —
normalizes into the same canonical evidence object:
satsignal.provenance.v1. One schema. One set of
canonical bytes (SCJ-v1 + sha256 — Satsignal Canonical JSON,
deliberately not RFC 8785/JCS; see §01 below).
One on-chain commitment. Adapters
(LangChain, MCP, OpenTelemetry, GitHub Action, webhook, blob, CLI,
API) are ingestion routes into this model, not separate products.
Raw file, blob, and raw-body webhook anchors share the same
on-chain commitment primitive but hash bytes directly; they enter
the canonical model when the caller supplies structured provenance
metadata. Standards (C2PA, Sigstore, SLSA) bridge in by digest.
This page is the public spine of the proof system.
A small, structured, canonicalizable record.
The current satsignal.provenance.v1 shape is seven
top-level keys, three required, four optional, no unknown keys
permitted. The record is canonicalized with Satsignal Canonical
JSON v1 (SCJ-v1): every string (keys and values) is UTF-8
NFC-normalized, object keys are sorted by Unicode code point, the
JSON is serialized with minimal separators and no whitespace,
integers are bare decimals, and floats are rejected rather than
rounded. The result is hashed with sha256 and the digest is
committed to the public Bitcoin SV chain. The full canonical
record ships back inside the .mbnt bundle so anyone
can re-derive the bytes and verify the anchor without calling
Satsignal again.
SCJ-v1 is deliberately not
RFC 8785 (JCS).
The two rules diverge in two places — SCJ-v1 sorts keys by code
point where RFC 8785 sorts by UTF-16 code unit (they differ for
supplementary-plane keys), and SCJ-v1 NFC-normalizes strings where
RFC 8785 does not — so an off-the-shelf JCS library will
compute a different hash on some inputs and a valid anchor will
appear to fail. The normative rule, reference implementations, and
the cross-language byte-parity corpus live at
/spec-provenance
§3. (RFC 8785 is used only by the separate
satsignal.json.field.v1 selective-disclosure profile
— a distinct canonicalization context.)
{
"schema": "satsignal.provenance.v1",
"source": { "type": "gitlab", "id": "acme/widgets" },
"subject": { "type": "commit", "digest": "sha256:9f86d0..." },
"identity": { "actor": "ci-bot", "repo": "acme/widgets", ... },
"attestations": [ { "type": "slsa", "digest": "sha256:1b4f0e..." } ],
"claims": { ... },
"privacy": { ... }
}
The three required keys, four optional today, more arriving additively.
schema— required- The literal string
satsignal.provenance.v1. Verifiers reject if missing. source— required- Where the event came from:
type+id. Examples:gitlab/github/otel-span/langfuse/webhook. subject— required- What is being anchored:
type+digest. Examples:commit/span_batch/file/policy_snapshot. identity- Who / what acted: today an open string→string bag
(
actor,repo,agent_id,org_id, ...). In v1.x this will be broken out into typed slots — see "what’s coming" below. attestations- External signatures Satsignal points at by digest: SLSA, in-toto, Sigstore / cosign, Rekor entries, npm provenance, PEP 740, C2PA manifests. Satsignal anchors the reference, not the payload — the signing standard stays the source of truth for who signed what.
claims- Caller-defined structured claims about the run (e.g. eval scores, decision categories, expected outputs). Free-form, but the canonical bytes are still byte-stable.
privacy- Declaration of what is and isn’t public in the manifest. Used by sealed-mode and selective-disclosure flows.
What’s coming additively in v1.x
The current identity bag will be supplemented by
typed slots that name what an evidence reviewer cares about. All
optional, all payload-free (digests and references only). Landing
behind the same backward-compat rule: existing .mbnt
bundles keep verifying byte-identically.
authority,principal,organization,agent— typed identifiers (NIST NCCoE software-agent identity/authorization pillars).delegation_grant_digest+scopes— what the agent was authorized to do, by hash of the grant.policy_snapshot_digest,run_scope,capture_policy— the precommitment hooks that turn "events were anchored" into "the declared capture policy was followed for this scope."artifact_roles— input / output / intermediate / policy per referenced attestation.signature_ref— reference to an OUTSIDE signature over the canonical manifest (cosign / JWS / VC / similar). Not an operator-signed Satsignal claim.extensions— explicit container for vendor / custom data. Verifier may hash and preserve; MUST label "not interpreted by Satsignal." Unknown top-level keys remain invalid.
Same canonical packet, many ways in.
The judgement rule for any adapter is one line: does it
produce the same satsignal.provenance.v1 object as
every other adapter? If yes, it’s a thin ingestion
route. If it invents its own proof shape, that’s a bug
against this page.
- Direct API —
POST /api/v1/anchors(and the structured wrapperPOST /api/v1/provenance/anchor). - CLI / Python helper —
satsignal-cli
and the stdlib
agent_anchor.pyhelper. - MCP —
satsignal-mcp
server exposing
anchor_*/lookup_hash/verify_bundleover stdio. - OpenTelemetry — satsignal-otel processor emitting anchors for marked spans.
- LangChain — langchain-satsignal helpers for the policy-snapshot / commitment / evidence-bundle pattern.
- GitHub Action — Steleet/satsignal-action@v0; analogous bash adapters for GitLab CI, Bitbucket Pipelines, Docker BuildKit, npm, PyPI.
- Webhook — raw-body anchoring with per-source signature verifiers (Stripe, GitHub, Langfuse).
- Object storage —
satsignal-blob
writes
.mbnt+.proof.jsonsidecars next to objects in S3 / R2 / GCS / Azure Blob.
None of the above is a Satsignal product category. They are capture paths. The product is the canonical model on this page.
C2PA, Sigstore, SLSA, OpenTelemetry, MCP — complements.
Satsignal does not replace content-provenance metadata, keyless
code signing, supply-chain attestation frameworks, observability
standards, or agent-tool transports. It is the external time-and-integrity
layer those standards leave open. The bridge for each is the same
shape: take the canonical bytes the standard already produces,
hash them, and anchor the digest as a satsignal.provenance.v1
record (the attestations array carries the reference).
See standards bridges for the per-standard worked patterns (C2PA / Sigstore / SLSA / OTel / MCP) and where each one’s authority ends and Satsignal’s begins.
Integrity. Timing. Inclusion. Commitment. Nothing more — unless precommitted.
The four things this model establishes — and the things it deliberately does not — are documented as the canonical scope statement on what a proof proves. That page restates the same claims the verifier’s own “what this verifies” panel makes, so neither page can read as a stronger claim than the verifier itself makes.
The single line worth re-reading: a proof binds bytes, not
truth. Wider claims (completeness, capture-policy adherence,
authority) are possible only when the manifest precommitted them
via run_scope, capture_policy, or
policy_snapshot_digest before the run began.
Everything an external implementer needs.
- /spec-provenance — normative implementer spec.
- /spec-mbnt
— the
.mbntbundle format that carries canonical manifests + chain references. - /spec-merkle-row — selective row reveal scheme for manifests + sealed sets.
- /verify — offline-capable verifier (single static HTML file you can save and audit; a saved copy runs the hash and fingerprint checks offline — the on-chain lookup still needs a public block explorer).
- /agents — agent-run integration recipe (policy-snapshot, per-decision commitment, evidence-bundle manifest, handoff JSON).