# app.zeens.c2pa

> Published by [zeens.app](https://lexicon.garden/identity/did:plc:pwpuqwge6g7xrjmfbw73uycw)

✓ This is the authoritative definition for this NSID.

## Links

- [View on Lexicon Garden](https://lexicon.garden/lexicon/did:plc:pwpuqwge6g7xrjmfbw73uycw/app.zeens.c2pa)
- [Documentation](https://lexicon.garden/lexicon/did:plc:pwpuqwge6g7xrjmfbw73uycw/app.zeens.c2pa/docs)
- [Examples](https://lexicon.garden/lexicon/did:plc:pwpuqwge6g7xrjmfbw73uycw/app.zeens.c2pa/examples)

## Definitions

### `app.zeens.c2pa#action`

**Type**: `object`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `action` | `string` | Yes | The C2PA action identifier, e.g. c2pa.color_adjustments, c2pa.cropped, or a vendor's own such as com.adobe.*. Stored raw so a reader maps it to words at render time. |
| `description` | `string` | No | The producer's own description of the action, when it gave one. The only words a reader has for a vendor action it does not know. |
| `softwareAgent` | `string` | No | The software that performed this action, when it differs from the manifest's generator. |

### `app.zeens.c2pa#capture`

**Type**: `object`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `device` | `string` | No | The capture device that signed, e.g. "Leica M11-P". |
| `signedAt` | `string` (datetime) | No |  |
| `signer` | `string` | No | Organisation on the capture manifest's signing certificate. |

### `app.zeens.c2pa#credential`

**Type**: `object`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `actions` | `array` | No | Actions from the active manifest, in the order recorded. |
| `blob` | `string` (cid) | Yes | CID of the blob as stored on the PDS. Binds the entry to one exact image, so a reordered or replaced photo cannot inherit another photo's credential. The stored blob is usually a re-encode of the original, which is what `embedded` reports. |
| `capture` | `ref` → `#capture` | No | Present when the photo's provenance reaches back to a camera that signed the capture, either because the active manifest is the camera's own or because an ingredient's is. |
| `digitalSourceType` | `string` | No | IPTC digital source type of the asset, as a URI, e.g. http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture or .../trainedAlgorithmicMedia. The strongest signal of generative AI involvement. |
| `embedded` | `boolean` | No | True when the stored blob is the original file, byte for byte, so anyone can re-verify its credential from the PDS: from the manifest inside it, or for a cloud manifest by following the file's pointer. False when the upload was re-encoded, which strips both. |
| `generator` | `string` | No | The app or device that produced the active manifest, with its version, e.g. "Adobe Lightroom Classic 14.3". |
| `index` | `integer` | Yes | Position of the photo in the post's embed. |
| `ingredients` | `array` | No | Source files the active manifest names, e.g. the raw file a Lightroom export was made from. Titles and whether each carries a credential of its own, never thumbnails: an ingredient's thumbnail is often the uncropped original. |
| `signedAt` | `string` (datetime) | No | Time of the manifest's signature, from its trusted timestamp when it has one. |
| `signer` | `string` | Yes | Organisation on the signing certificate, e.g. "Adobe Inc." or "Leica Camera AG". |
| `status` | `string` | Yes | What validation found. trusted: the signer chains to the official C2PA Trust List. legacy: the signer chains only to the frozen Interim Trust List. unrecognised: the manifest is intact but its signer is on neither list. A manifest that failed validation is never written. |

### `app.zeens.c2pa#ingredient`

**Type**: `object`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `credential` | `boolean` | Yes | True when the ingredient carries a Content Credential of its own. |
| `title` | `string` | No | The ingredient's title as its manifest records it, usually a file name. |

### `app.zeens.c2pa`

**Type**: `record`

Content Credentials (C2PA) read from a post's original photos at upload, for the photos the creator chose to show them on. The rkey matches the rkey of the associated post record, as with app.zeens.postMeta. This is a summary of what the C2PA manifest said, not the manifest itself: the manifest can carry an uncropped ingredient thumbnail and the photographer's GPS, so it is deliberately not republished. The summary is the creator's statement, signed by their repo; `status` records what the validator found, and a labeler may attest it independently.

**Key**: `any`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `createdAt` | `string` (datetime) | Yes |  |
| `images` | `array` | Yes | One entry per photo with a credential the creator chose to show. Sparse: a photo without one, or with the toggle off, has no entry. `index` and `blob` say which photo an entry is for. |
| `subject` | `string` (at-uri) | Yes | AT-URI of the post these credentials belong to. |

## Raw Schema

```json
{
  "$type": "com.atproto.lexicon.schema",
  "defs": {
    "action": {
      "properties": {
        "action": {
          "description": "The C2PA action identifier, e.g. c2pa.color_adjustments, c2pa.cropped, or a vendor's own such as com.adobe.*. Stored raw so a reader maps it to words at render time.",
          "maxLength": 128,
          "type": "string"
        },
        "description": {
          "description": "The producer's own description of the action, when it gave one. The only words a reader has for a vendor action it does not know.",
          "maxLength": 300,
          "type": "string"
        },
        "softwareAgent": {
          "description": "The software that performed this action, when it differs from the manifest's generator.",
          "maxLength": 256,
          "type": "string"
        }
      },
      "required": [
        "action"
      ],
      "type": "object"
    },
    "capture": {
      "properties": {
        "device": {
          "description": "The capture device that signed, e.g. \"Leica M11-P\".",
          "maxLength": 256,
          "type": "string"
        },
        "signedAt": {
          "format": "datetime",
          "type": "string"
        },
        "signer": {
          "description": "Organisation on the capture manifest's signing certificate.",
          "maxLength": 256,
          "type": "string"
        }
      },
      "type": "object"
    },
    "credential": {
      "properties": {
        "actions": {
          "description": "Actions from the active manifest, in the order recorded.",
          "items": {
            "ref": "#action",
            "type": "ref"
          },
          "maxLength": 32,
          "type": "array"
        },
        "blob": {
          "description": "CID of the blob as stored on the PDS. Binds the entry to one exact image, so a reordered or replaced photo cannot inherit another photo's credential. The stored blob is usually a re-encode of the original, which is what `embedded` reports.",
          "format": "cid",
          "type": "string"
        },
        "capture": {
          "description": "Present when the photo's provenance reaches back to a camera that signed the capture, either because the active manifest is the camera's own or because an ingredient's is.",
          "ref": "#capture",
          "type": "ref"
        },
        "digitalSourceType": {
          "description": "IPTC digital source type of the asset, as a URI, e.g. http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture or .../trainedAlgorithmicMedia. The strongest signal of generative AI involvement.",
          "maxLength": 256,
          "type": "string"
        },
        "embedded": {
          "description": "True when the stored blob is the original file, byte for byte, so anyone can re-verify its credential from the PDS: from the manifest inside it, or for a cloud manifest by following the file's pointer. False when the upload was re-encoded, which strips both.",
          "type": "boolean"
        },
        "generator": {
          "description": "The app or device that produced the active manifest, with its version, e.g. \"Adobe Lightroom Classic 14.3\".",
          "maxLength": 256,
          "type": "string"
        },
        "index": {
          "description": "Position of the photo in the post's embed.",
          "maximum": 3,
          "minimum": 0,
          "type": "integer"
        },
        "ingredients": {
          "description": "Source files the active manifest names, e.g. the raw file a Lightroom export was made from. Titles and whether each carries a credential of its own, never thumbnails: an ingredient's thumbnail is often the uncropped original.",
          "items": {
            "ref": "#ingredient",
            "type": "ref"
          },
          "maxLength": 16,
          "type": "array"
        },
        "signedAt": {
          "description": "Time of the manifest's signature, from its trusted timestamp when it has one.",
          "format": "datetime",
          "type": "string"
        },
        "signer": {
          "description": "Organisation on the signing certificate, e.g. \"Adobe Inc.\" or \"Leica Camera AG\".",
          "maxLength": 256,
          "type": "string"
        },
        "status": {
          "description": "What validation found. trusted: the signer chains to the official C2PA Trust List. legacy: the signer chains only to the frozen Interim Trust List. unrecognised: the manifest is intact but its signer is on neither list. A manifest that failed validation is never written.",
          "knownValues": [
            "trusted",
            "legacy",
            "unrecognised"
          ],
          "type": "string"
        }
      },
      "required": [
        "index",
        "blob",
        "status",
        "signer"
      ],
      "type": "object"
    },
    "ingredient": {
      "properties": {
        "credential": {
          "description": "True when the ingredient carries a Content Credential of its own.",
          "type": "boolean"
        },
        "title": {
          "description": "The ingredient's title as its manifest records it, usually a file name.",
          "maxLength": 256,
          "type": "string"
        }
      },
      "required": [
        "credential"
      ],
      "type": "object"
    },
    "main": {
      "description": "Content Credentials (C2PA) read from a post's original photos at upload, for the photos the creator chose to show them on. The rkey matches the rkey of the associated post record, as with app.zeens.postMeta. This is a summary of what the C2PA manifest said, not the manifest itself: the manifest can carry an uncropped ingredient thumbnail and the photographer's GPS, so it is deliberately not republished. The summary is the creator's statement, signed by their repo; `status` records what the validator found, and a labeler may attest it independently.",
      "key": "any",
      "record": {
        "properties": {
          "createdAt": {
            "format": "datetime",
            "type": "string"
          },
          "images": {
            "description": "One entry per photo with a credential the creator chose to show. Sparse: a photo without one, or with the toggle off, has no entry. `index` and `blob` say which photo an entry is for.",
            "items": {
              "ref": "#credential",
              "type": "ref"
            },
            "maxLength": 4,
            "minLength": 1,
            "type": "array"
          },
          "subject": {
            "description": "AT-URI of the post these credentials belong to.",
            "format": "at-uri",
            "type": "string"
          }
        },
        "required": [
          "subject",
          "images",
          "createdAt"
        ],
        "type": "object"
      },
      "type": "record"
    }
  },
  "id": "app.zeens.c2pa",
  "lexicon": 1
}
```
