Protocol overview
PRE-GEN is a protocol for pre-generation authorization. Before a model produces content depicting a registered person, brand, voice or work, the provider asks an authorization registry whether the use is permitted and receives a signed decision it can store and anyone can verify.
A small core, and room for each registry
The core is what every registry, provider and verifier implements: the decision, the licence envelope, the receipt, 17 core refusal codes, PG codes and the signed registry directory. It also fixes a floor of protections no registry may lower: an opt-out beats any licence, a refused subject status beats any licence, minors stay protected, and an unknown subject is not a prohibition.
- Floors only go up. A registry may refuse more than the core, never allow more.
- Own members and codes. A registry may add signed members, which verifiers ignore unless marked
critical, and its own refusal codes, prefixed with its issuer:PG_<ISSUER>_…. - Published policy. Each registry publishes its floors, codes, extensions, decision lifetime and opt-out waiting period.
- Several subjects. One output with a person, a voice and a brand needs an allow for each, under one
generation_id. - Trusted registries only. Signatures count only from registries the steward-signed directory lists; the steward admits them on published conditions and can revoke a registry or a key. When two registries hold the same person, neither registry's answer decides by itself: the provider keeps both, tells both, decides under its own policy and does not call the output licensed; the registries resolve it, and the steward revokes one that misstates who holds the rights. A matching name is not evidence of the same subject.
What the origin registry, PRAMPTA, adds — end-user binding, decision caching, evidence bundles, C2PA assertions, observations — is in PRE-GEN-EXTENSIONS.md.
The crypto invariant
- Canonical JSON. Keys sorted by code point, no whitespace, non-ASCII emitted as raw UTF-8, integers only (no floats).
- Hashing. SHA-256, lowercase hex.
- Signatures. Ed25519 (RFC 8032), hex, over the canonical bytes of the body without its signature field.
- Audit chain. Each event's
prev_hashis the SHA-256 of the previous event's canonical bytes; the first usesgenesis.
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]
Never Unicode-normalize a signed body before verifying. NFC and NFD strings that look the same produce different signatures. Verify the bytes you received.
Signed objects
| Object | Marker | Signed by | Proves |
|---|---|---|---|
| Decision | pg.decision.v1 | Registry | The answer to one request and its policy version |
| Licence | pg.license.v2 | Rights holder, countersigned by the registry | A scoped permission: use, modality, channel, territory, time, project |
| Receipt | pg.receipt.v2 · v3 | Provider (v3 signs the event type) | That a generation under a decision was reported |
| Evidence bundle (extension) | pg.evidence.v1 | Registry | Decision, licence and audit chain, verifiable offline |
| Assertion (extension) | pg.assertion.v1 | Registry | A pointer to the decision, embeddable in a C2PA manifest |
The flow
- Detect. Poll
GET /v1/subjects/index(ETag-cached) and match prompts against registered names and aliases. Matching yields candidates, not proof. - Verify.
POST /v1/verify/returns a decision with adisposition:allow,not_blocked,reviewordeny, plus a reason code. - Prove. Check the signature against the registry's key set (
GET /keys, rotation-aware), store the decision, and after anallowfile a receipt.
Conformance
An implementation conforms when it reproduces every case in the published test vectors: canonicalization, signatures, audit chain and PG codes. The vectors use a fixed public test key — never use it in a deployment.