> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sigmora.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Provenance & the public verifier

> Every asset Sigmora generates carries a signed record of what made it. Verify one yourself, without an account.

<Card title="Verify a file now" icon="badge-check" href="https://www.sigmora.org/verify" horizontal>
  Public, no login, no upload. Drop in a file and read its record.
</Card>

## What a receipt is

Every generated asset carries a **manifest**: a canonical, SHA-256-bound record
of how it was made — provider, model, prompt, seed, per-step cost, retries and
lineage — embedded directly into the delivered file.

The cost on that record is not an estimate. It comes from the same cost model
that bills customers, exported from TypeScript into the Python pipeline and
drift-guarded in CI, so a manifest line and an invoice line are the same number.

## Verifying without uploading

The file never leaves your browser. It is parsed locally, and only the few-kB
record is sent for the hash check — so a 4 GB master and a thumbnail cost the
same to verify.

<Note>
  The browser reader parses **PNG** and **MP4**. The pipeline can also embed
  into MP3, WAV, AAC, JPEG and WebP; those verify through the service endpoint
  rather than in-page.
</Note>

Hash verification is deliberately **not** reimplemented in TypeScript.
Genblaze's canonical hash is version-aware and canonicalises through Pydantic's
dump semantics, so a second implementation would drift — and would drift by
declaring genuine assets tampered. The browser extracts; the reference
implementation judges.

| Surface                                     | Auth    | What it does                               |
| ------------------------------------------- | ------- | ------------------------------------------ |
| [`/verify`](https://www.sigmora.org/verify) | none    | Extract and verify in the browser          |
| `POST /api/genblaze/verify`                 | none    | Verify a manifest, returns `ok` / `hashOk` |
| `POST /genblaze/estimate`                   | service | Quote a run before any provider is called  |
| `POST /genblaze/generate`                   | service | Run the pipeline; `202` + `jobId`          |
| `GET /genblaze/runs/{id}/manifest`          | service | The record, verbatim                       |
| `GET /genblaze/runs/{id}/download`          | service | The delivered file, receipt embedded       |

A tampered record returns `422` with a verdict about the file, not a generic
error.

## Providers and models

The `brief → video` pipeline is one Genblaze `Pipeline` across three modalities.
Model ids are checked against each adapter's live registry — a slug that leaves
a vendor's catalogue fails the build rather than silently routing to something
older.

| Step | Modality | Provider   | Model                      | Fallbacks                                    |
| ---- | -------- | ---------- | -------------------------- | -------------------------------------------- |
| 0    | Image    | OpenAI     | `gpt-image-2`              | `gpt-image-1.5`                              |
| 1    | Video    | GMI Cloud  | `seedance-2-0-260128`      | `seedance-1-0-pro-fast-251015`, `wan2.6-r2v` |
| 1    | Video    | Google     | `veo-3.1-generate-preview` | `veo-3.1-fast`, `veo-3.1-lite`               |
| 2    | Audio    | ElevenLabs | `eleven_v3`                | `eleven_multilingual_v2`                     |
| 2    | Audio    | Speechify  | `simba-3.2`                | `simba-english`                              |

Two steps carry a **vendor switch** rather than a single vendor, because both
have failed in production for reasons that were not bugs: narration on an
exhausted character quota, video on a credential the service did not hold.
`videoProvider` and `narrationProvider` pick per request; `VC_GENBLAZE_VIDEO`
and `VC_GENBLAZE_TTS` set the box default.

<Note>
  Fallbacks are same-vendor by design — one that also changes vendor changes
  the auth, the parameter shape and the price at the same time, which is a
  worse thing to discover mid-incident. The two cross-vendor switches above are
  explicit, quoted and preflighted instead.
</Note>

### Keeping up with the catalogue

Pinned model ids rot, and in generative media a stale pin is not a stale
constant — it is a standing decision to ship last season's output quality. Two
things prevent that here:

* **Pattern-keyed registries.** The Veo family is matched on `^veo-`, so a new
  generation resolves without a code change and a liveness probe decides
  whether it is real.
* **A retirement guard in CI.** Superseded ids are deleted rather than demoted
  to fallbacks, and a test fails if one reappears in either the pipeline pins or
  the price map.

## Where the bytes live

Generated media is stored on **Backblaze B2** and served through Cloudflare
under the Bandwidth Alliance, so delivery costs **\$0 in egress**. Uploads go
direct to the bucket: the app server mints a presigned URL and the browser PUTs
straight to B2, in 5 parallel parts with per-part retry, so no generated byte
transits our compute.

Provenance records are written to a separate Object-Locked bucket. Manifests are
immutable by construction — the retention mode is `GOVERNANCE` rather than
`COMPLIANCE` deliberately, so the record is tamper-evident without making a
deletion request impossible to honour.
