# Document Trust Payload v1

## 1. Purpose

This specification defines a signed payload that can be embedded in a document, QR code, or other machine-readable carrier so a recipient can verify:

- who issued the payload,
- what critical data was approved by the issuer,
- whether the payload was altered,
- whether the payload is still valid.

## 2. Design goals

- Simple enough to implement in many languages
- Stable canonical form for signing
- Verifiable offline
- Suitable for QR code transport
- Extensible without breaking v1 verifiers

## 3. Non-goals

- Full document authenticity for arbitrary unstructured scans
- Human identity proof without a trust registry
- Preventing social engineering outside the signed payload

## 4. Payload structure

The signed payload MUST be a JSON object with the following top-level members:

- `version`
- `issuer`
- `document`
- `intent`
- `issued_at`
- `expires_at`
- `nonce`
- `alg`
- `sig`

### 4.1 Required fields

- `version`: string, fixed to `"1"`
- `issuer`: object describing the signer
- `document`: object describing the document or payment instruction
- `intent`: string describing the allowed action, for example `"payment"` or `"invoice"`
- `issued_at`: RFC 3339 timestamp in UTC
- `expires_at`: RFC 3339 timestamp in UTC
- `nonce`: random unique string
- `alg`: signature algorithm identifier
- `sig`: base64url-encoded detached signature over the canonical payload body, excluding `sig`

### 4.2 Issuer object

The `issuer` object MUST include:

- `issuer_id`: stable identifier for the entity
- `display_name`: human-readable name
- `trust_anchor_id`: identifier for the trust registry or certificate chain

Optional fields:

- `country`
- `vat_id`
- `website`

### 4.3 Document object

The `document` object SHOULD include the fields that matter for anti-fraud validation.

Recommended fields:

- `document_id`
- `document_type`
- `beneficiary_name`
- `iban`
- `amount`
- `currency`
- `reference`
- `due_date`

Optional fields:

- `customer_id`
- `invoice_number`
- `purchase_order`
- `purpose`
- `transaction_id`
- `communication`

### 4.4 Invoice profile

For invoice and payment documents, implementations SHOULD use a minimal shared profile to remove ambiguity during machine verification.

If `intent` is `"payment"` and `document.document_type` is `"invoice"`, the following fields SHOULD be present and validated:

- `document_id`
- `document_type`
- `beneficiary_name`
- `iban`
- `amount`
- `currency`
- `reference`
- `due_date`

Verification software SHOULD compare these fields against the payment context or the visible invoice content before authorizing a transfer.

### 4.5 Payment profile

For bank transfers, implementations SHOULD treat the signed payload as a payment instruction profile.

At minimum, the verifier SHOULD require:

- `issuer.issuer_id`
- `issuer.trust_anchor_id`
- `document.document_id`
- `document.document_type`
- `document.beneficiary_name`
- `document.iban`
- `document.amount`
- `document.currency`
- `document.reference`
- `document.due_date`
- `document.transaction_id`
- `document.communication`

Recommended semantics:

- `document.transaction_id` is a unique identifier for correlation, reconciliation, or anti-replay tracking.
- `document.communication` is the payment reference shown to the user and/or carried in the bank transfer.
- `document.reference` MAY contain a structured reference, an end-to-end ID, or another bank-facing remittance identifier.

Payment software SHOULD require exact matching of:

- beneficiary name
- IBAN
- amount
- currency
- reference or communication

Payment software SHOULD reject or warn on any mismatch unless an explicit user override policy exists.

## 5. Canonicalization

Before signing, the JSON payload body MUST be canonicalized with a deterministic serialization format.

For v1, this specification recommends:

- UTF-8 encoding
- JSON Canonicalization Scheme style ordering
- no insignificant whitespace
- escaped characters normalized according to the canonical serializer

Implementations MUST sign the canonical bytes of the payload body without the `sig` member, not the pretty-printed JSON.

## 6. Signature algorithm

v1 supports:

- `Ed25519`
- `ES256`

The `alg` field MUST match the algorithm used to create `sig`.

## 7. Verification rules

A verifier MUST:

1. Parse the payload.
2. Check `version == "1"`.
3. Confirm `issued_at <= now <= expires_at`.
4. Verify the signature over the canonical payload bytes.
5. Resolve `issuer.trust_anchor_id` to a trusted public key.
6. Confirm the issuer key is not revoked.
7. Check that the document fields match the expected transaction or recipient context.

If any step fails, the verifier MUST reject the payload.

## 8. Trust model

The payload signature proves that the holder of the private key approved the data.

The trust registry proves that the public key belongs to the named issuer.

The recipient application MUST NOT assume trust from the QR code alone.

### 8.1 Local trust registry

For offline or embedded verification, implementations MAY use a local trust registry file.

A trust registry SHOULD:

- identify trust anchors by `trust_anchor_id`
- bind each trust anchor to an `issuer_id`
- list one or more active public keys
- mark revoked keys and inactive anchors explicitly

The registry format is implementation-defined in v1, but it MUST allow deterministic lookup of the issuer public key from `issuer.trust_anchor_id`.

## 9. QR code profile

The QR code MAY carry either:

- the full signed JSON payload,
- or a compact binary encoding of the same payload.

For v1, the full JSON form is acceptable for prototyping.

### 9.1 Compact transport envelope

Implementations MAY use a compact QR transport envelope with the following format:

- prefix: `dtp1z.`
- body: base64url-encoded zlib-compressed UTF-8 JSON of the full signed payload

The compressed JSON MUST still contain all standard payload members, including `sig`.

Verifiers SHOULD accept both raw JSON and the compact `dtp1z.` transport form.

The transport envelope is separate from the signature; it only reduces QR size.

## 10. Versioning

Breaking changes require a new major version.

Backward-compatible additions SHOULD use optional fields.

## 11. Examples

See `examples/invoice.json`.
