PRE-GEN
Docs / Overview

PRE-GEN protocol overview

License: Apache-2.0 · Test vectors: vectors v2 · Where prose and vectors disagree, the vectors win.

PRE-GEN is a protocol for pre-generation authorization: before an AI model produces content depicting a registered person, brand, voice, style, or creative work, the provider asks a registry whether the use is licensed — and receives a cryptographically signed decision that can be stored and independently verified forever.

The specification is licensed under the Apache License 2.0, separately from any service that implements it. Anyone may implement PRE-GEN without permission. The parts every implementation must agree on are pinned as byte-level test vectors, not prose alone: if an implementation’s canonicalization drifts, its vector test fails.

The crypto invariant

Four rules, agreed on byte for byte by every implementation:

  1. Canonical JSON. Keys sorted by Unicode code point, no whitespace, non-ASCII values emitted as raw UTF-8 (never escaped), integers only — no floats, because float serialization is not portable.
  2. Hashing. SHA-256, lowercase hex.
  3. Signatures. Ed25519 (RFC 8032, deterministic), hex-encoded, over the canonical JSON bytes of the body excluding the signature field itself.
  4. Audit chain. Each event’s prev_hash is the SHA-256 of the previous event’s canonical bytes; the first event uses the literal string genesis.
Unicode warning Signatures are computed over raw UTF-8 bytes. NFC- and NFD-normalized strings that look identical produce different signatures — this is by design. Never normalize a signed body before verification; verify the bytes you received.
canonical_json(obj) = UTF8( json.dumps(obj,
    sort_keys=True, separators=(",", ":"), ensure_ascii=False) )

signature = Ed25519.sign( canonical_json(body minus signature) )
key_fingerprint = "pg-ed25519:" + hex( SHA256(raw 32-byte public key) )[0:32]

Signed objects

Five object types cross the wire, each carrying its own schema version marker inside the signed bytes. See Versioning for what bumps each marker and which changes are additive-safe.

ObjectMarkerRole
Decisionpg.decision.v1The /verify answer — signed by the registry operator
Licensepg.license.v2Scoped permission — signed by the rights holder, countersigned by the operator
Receiptpg.receipt.v2Post-generation proof — signed by the provider, hashed by the operator
Evidence bundlepg.evidence.v1Verifiable offline package of decision + license + audit chain
Assertionpg.assertion.v1Portable provenance claim, embeddable in C2PA manifests

The flow

  1. Detect — poll GET /v1/subjects/index (ETag-cached) and match user prompts against registered subjects with the SDK matcher.
  2. VerifyPOST /v1/verify/ returns a signed decision: allowed (with obligations) or refused (with a machine-readable PG_* code — see Refusal codes).
  3. Prove — verify the signature against GET /keys (rotation-aware), store the decision, and file a receipt after generation.

Implementations pinned by vectors

ImplementationVector test
Registry backend (source of truth)test_spec_vectors.py
Reference single-file registryTestReferenceImplementation
Python SDK (prampta)test_spec_vectors.py
TypeScript SDK (@prampta/sdk)vectors.test.ts

The vectors use a fixed, public test key derived from a known seed — never use it in any deployment. Regenerating vectors is allowed only after an intentional protocol change: the generator is deterministic, so regenerating without a protocol change produces a byte-identical file. A noisy diff means the invariant moved.