Skip to main content

Etch

Content provenance on a tamper-evident Merkle chain. Register, verify, and prove content existed at a point in time.

pip install etch-provenance   # imports as `etch`

Why Etch?

C2PA embeds provenance in media files. Blockchain attestation services cost gas and take minutes. Etch gives you cryptographic content provenance with a single HTTP call — sub-millisecond, privacy-preserving (content is never stored), and independently verifiable.

Feature Etch C2PA On-chain attestation
Sub-millisecond registration Yes N/A (embedded) No (block time)
Content never stored Yes No (embedded in file) Varies
Offline verification Yes Yes Requires node
No gas fees Yes N/A No
Tamper-evident chain Yes Yes Yes

Quick start

Start the server

# SQLite (zero config) — serves on :8100
uvicorn etch.server:app --reload --port 8100

# PostgreSQL
ETCH_DATABASE_URL=postgresql+asyncpg://user:pass@localhost/etch uvicorn etch.server:app --port 8100

Interactive API docs: http://localhost:8100/docs (OpenAPI schema at /openapi.json).

Register content

curl -X POST http://localhost:8100/v1/proof \
  -H "Content-Type: application/json" \
  -d '{"content": "My original article text", "label": "Blog: Launch Day"}'

Response:

{
  "proof_id": 0,
  "content_hash": "a3f1...",
  "label": "Blog: Launch Day",
  "timestamp": 1710000000.0,
  "leaf_hash": "...",
  "mmr_root": "...",
  "chain_depth": 1
}

Verify content

curl -X POST http://localhost:8100/v1/proof/0/verify \
  -H "Content-Type: application/json" \
  -d '{"content": "My original article text"}'

Privacy mode (hash only)

# Pre-hash locally — content never leaves your machine
HASH=$(echo -n "secret document" | sha256sum | cut -d' ' -f1)
curl -X POST http://localhost:8100/v1/proof \
  -H "Content-Type: application/json" \
  -d "{\"content_hash\": \"$HASH\"}"

The two chains

Etch exposes one primitive through two front doors:

/v1/proof/* /v1/records/*
Chain One global chain One chain per namespace
Auth None API key (Bearer)
Intended for Demos, public registration, single-tenant System-of-record integrations, multi-tenant
Status Legacy — kept stable Current

Everything else (/v1/c2pa, /v1/dooh, /v1/assent, /g/, watermark) is a domain-specific façade over one of those two.


API

/v1/proof/* — legacy registration (no auth)

Method Endpoint Description
POST /v1/proof Register content → proof receipt
POST /v1/proof/batch Register up to 1000 items in one call
GET /v1/proof/{proof_id} Retrieve receipt by ID
GET /v1/proof/hash/{content_hash} Look up by SHA-256
GET /v1/proof/recent List recent proofs (paginated)
GET /v1/proof/stats Chain statistics
POST /v1/proof/{proof_id}/verify Verify content integrity

/v1/records/* — System of Record API (API key, namespace-isolated)

Method Endpoint Description
POST /v1/records Commit a record → receipt
GET /v1/records List/filter with cursor pagination
GET /v1/records/{record_id} Retrieve receipt
GET /v1/records/{record_id}/proof Self-contained inclusion proof
POST /v1/records/verify Verify a record against the chain
GET /v1/records/stats Append vs. dedup counters for this namespace
GET /v1/chain/root Current namespace chain state

Keys look like etch_{live|test}_sk_{token} and are sent as Authorization: Bearer …. Only the SHA-256 of a key is stored. Provision the first namespace and key with:

from etch.auth import bootstrap_namespace
namespace_id, api_key = await bootstrap_namespace("acme-crm")
curl -X POST http://localhost:8100/v1/records \
  -H "Authorization: Bearer etch_live_sk_…" \
  -H "Content-Type: application/json" \
  -d '{
        "record": {"type": "salesforce.opportunity", "id": "0065g00000XYZ", "data": {"stage": "Closed Won"}},
        "metadata": {"actor": "sync-job"},
        "if_changed": true
      }'

if_changed is opt-in de-duplication. With record.id set, Etch looks up the latest record for that (namespace, external_id, record_type); if the record_hash is unchanged it returns the existing receipt with "deduplicated": true and appends nothing. It defaults to false on purpose — the chain is append-only and re-registering unchanged content is a legitimate timestamped re-attestation, so nothing is ever silently suppressed.

/v1/c2pa/* — C2PA manifest bridge (no auth)

Method Endpoint Description
POST /v1/c2pa/manifest Register a C2PA manifest on the chain
GET /v1/c2pa/manifest/{claim_id} Retrieve manifest + Etch proof
POST /v1/c2pa/verify Verify manifest against the chain
POST /v1/c2pa/bridge Bridge an existing proof into C2PA form

/v1/dooh/* — DOOH playback receipts (API key)

Bilaterally-signed proof that a creative played on a specific screen at a specific time.

Method Endpoint Description
POST /v1/dooh/receipts Anchor a signed bundle
GET /v1/dooh/receipts Query by campaign, screen, time range
POST /v1/dooh/verify Server-side run of the offline verifier

SDK and quickstart: etch/dooh/README.md. Protocol: docs/dooh-spec.md (CC-BY 4.0) — read the trust model first.

/v1/assent/* — Etch Assent (anonymous, rate-limited)

Client-side PDF signing with an anonymous provenance chain. Plaintext PDFs are encrypted in the browser with AES-256-GCM; the key lives in the URL fragment, which browsers never transmit, so Etch cannot decrypt what it stores.

Method Endpoint Description
POST /v1/assent/stamp Commit an assent event (20/hr per IP)
GET /v1/assent/chain/{document_id} Event chain + integrity check
GET /v1/assent/records/{record_id} Single receipt
GET /v1/assent/records/{record_id}/proof Public inclusion proof
GET /v1/assent/verify?hash={sha256} Find events by document hash
POST/GET/PUT/HEAD /v1/assent/document[/{doc_id}] Opaque ciphertext store (10 uploads/hr per IP, 15 MB)

Product + protocol spec: docs/ETCH_ASSENT_SPEC.md. Frontend lives in assent-app/ and builds to site/assent/.

Assent MCP server: an agent asks a human to sign. Three tools. request_signature takes plain text, which it renders to a one-page sign-off PDF, or a path to an existing PDF. It encrypts the document on the agent's machine, uploads only the ciphertext, stamps the plaintext hash as event 0, and returns a sign_url that opens in the normal Assent Sign page. The agent delivers the link itself. The key exists only in that URL and in a 0600 file under ~/.etch/assent/requests/. signature_status reads the public chain and reports one of sent, opened, signing, or finalized. It also flags a signer who was shown different bytes from what was sent. fetch_signed downloads the signed copy, decrypts it locally, saves it only if its SHA-256 equals the chain's finalized hash, and recomputes the inclusion proof.

pip install "etch-provenance[mcp]"
claude mcp add etch-assent -- etch-assent-mcp

Set ETCH_ASSENT_BASE_URL (default https://assent.to) to change the link host, or ETCH_ASSENT_API_URL to send API calls somewhere else, such as a local server.

Glyph — shortcodes, sigils, and the public resolver

A shortcode is 8 Crockford-base32 characters encoding a 32-bit proof_id plus an 8-bit checksum over the content hash, so typos and fabrications are detectable before a DB lookup.

Method Endpoint Description
POST /v1/proof/{proof_id}/glyph Render bar sigil + magic-link bundle
GET /g/{shortcode} Public resolver page (server-rendered HTML)
GET /g/{shortcode}.json Resolver result as JSON
GET /g/{shortcode}.png Bar sigil PNG

Watermark (etchmark) — inaudible audio pointer

Method Endpoint Description
POST /v1/proof/{proof_id}/embed-audio Multipart upload → watermarked 16-bit PCM WAV
POST /v1/proof/extract-audio Multipart upload → recovered shortcode + resolver URL

Embed returns lossless WAV; the spread-spectrum layer survives the artist's own lossy re-encode. Extraction reads WAV/FLAC natively and MP3/AAC/Opus via ffmpeg if it's on PATH.

The watermark is a pointer, not a proof. It tells a verifier which Etch record claims a recording; the chain remains the only source of truth. The PRNG seed is public by design.

Other

Method Endpoint Description
GET /health {"status":"ok","service":"etch","version":"0.3.0"}

How it works

Etch uses a Merkle Mountain Range (MMR) hash chain. Each proof receipt commits to all previous entries:

payload_hash = SHA256(payload_json)          # payload_json names the content hash
leaf_hash    = SHA256(prev_root : action_type : payload_hash : timestamp)
mmr_root     = SHA256(prev_root : leaf_hash)

Every proof returns payload_json, the exact bytes payload_hash was computed over. Check that it hashes to payload_hash and that its record_hash (or content_hash) is the SHA-256 of your content. That ties the proof to your content, not just to some leaf. The SDK's InclusionProof.verify(record_hash=...) runs all four steps.

If any historical entry is tampered with, all subsequent hashes become invalid. Proofs can be verified offline — no server trust required. The chain is append-only: nothing is ever deleted or compacted.


As a library

from etch import AuditChain, verify_inclusion_proof

chain = AuditChain()
entry = chain.append("content_proof", {"content_hash": "a3f1..."})

proof = chain.generate_proof(entry.leaf_index)
assert verify_inclusion_proof(proof)  # offline verification

Python SDK

from etch import EtchClient

async with EtchClient(base_url="http://localhost:8100", api_key="etch_live_sk_…") as etch:
    receipt = await etch.records.create(
        record_type="invoice",
        record_id="INV-1001",
        data={"total": 4200},
        if_changed=True,
    )
    result = await etch.records.verify(receipt.id, data={"total": 4200})

EtchClient.register() / .verify() still target the legacy /v1/proof API and are deprecated in favour of .records.


Configuration

Variable Default Purpose
ETCH_DATABASE_URL / DATABASE_URL sqlite+aiosqlite:///./etch.db Database connection string
ETCH_LOG_LEVEL INFO Level for the etch loggers (dedup suppressions log here)
ETCH_ASSENT_DOC_DIR /var/etch/assent-documents Ciphertext blob storage for Assent send-to-sign
ETCH_ASSENT_IP_SALT etch-assent Salt for hashed client IPs in Assent rate limiting

Optional extras: pip install "etch-provenance[postgres]" (asyncpg), "etch-provenance[qr]" (QR sigils), "etch-provenance[mcp]", "etch-provenance[dev]", "etch-provenance[all]".


Development

python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

pytest                      # asyncio_mode = auto
ruff check etch/ tests/     # line-length 120, py311

uvicorn etch.server:app --reload --port 8100

cd assent-app && npm install && npm run dev   # Assent frontend

Release procedure: docs/RELEASING.md.


License

Rising Sun License v1.0. Free for personal use. Commercial deployments connect to the Nous network.

The DOOH SDK (etch/dooh/) is Apache-2.0; the DOOH spec is CC-BY 4.0.

Metadata

Release files for etch-provenance 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for etch-provenance 0.3.0
File Size Uploaded Provenance Actions
etch_provenance-0.3.0.tar.gz 2.0 MB With provenance Details for etch_provenance-0.3.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for etch-provenance 0.3.0
File Interpreter ABI Platform Actions
etch_provenance-0.3.0-py3-none-any.whl Python 3 none any Details for etch_provenance-0.3.0-py3-none-any.whl

Total release size: 2.1 MB

Release files / etch_provenance-0.3.0.tar.gz

Download URL etch_provenance-0.3.0.tar.gz
Size 2.0 MB
Tags Source
SHA-256 checksum
How to use checksums
6e65f7ce6d2c9e3730a78760345ca85cbf1dca3d4cea43b68afdcba9dee72bb9
BLAKE2b-256 checksum
How to use checksums
a02d27074233c1c4ec14bbdbdc176041a7c4cd2f5ef2ea3cd5f5690fdb71a77d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

Release files / etch_provenance-0.3.0-py3-none-any.whl

Download URL etch_provenance-0.3.0-py3-none-any.whl
Size 98.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cb0de832fd5d56544dfaa13832cce4f1a13487325d9c5e1d3618cb07eb7d4bc9
BLAKE2b-256 checksum
How to use checksums
27b995eccb2be607ca7a282613051f904eec304b9b810b7765d0ae47c4f52900
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

Security for etch-provenance 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for etch_provenance-0.3.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page