# social.smallbury.actor.trust

> Published by [lexicon.pds.smallbury.social](https://lexicon.garden/identity/did:plc:wnagcp6h73nxmvmwid2xxukx)

## Links

- [View on Lexicon Garden](https://lexicon.garden/lexicon/did:plc:wnagcp6h73nxmvmwid2xxukx/social.smallbury.actor.trust)
- [Documentation](https://lexicon.garden/lexicon/did:plc:wnagcp6h73nxmvmwid2xxukx/social.smallbury.actor.trust/docs)
- [Examples](https://lexicon.garden/lexicon/did:plc:wnagcp6h73nxmvmwid2xxukx/social.smallbury.actor.trust/examples)

## Definitions

### `social.smallbury.actor.trust`

**Type**: `record`

Contacts this actor trusts, content-key epochs for E2EE, and optional in-flight pairwise-secret delivery during trust establishment. Trusted DIDs are cleartext for AppView indexing. Mutual trust is derived: A trusts B and B trusts A (each side's trusted array). Feed visibility requires mutual trust on both records. Per-epoch content keys use opaque slots (see #contentKeyWrap).

**Key**: `literal:self`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `trusted` | `array` | Yes | DIDs this actor trusts. Cleartext for AppView; does not by itself reveal per-post subset audiences. Listing a DID here may occur before they reciprocate (pre-emptive trust). |
| `createdAt` | `string` (datetime) | No |  |
| `keyEpochs` | `array` | Yes | Ordered content-key epochs. Only the latest epoch (no endedAt) is used for new posts and comments. Earlier epochs remain for decrypting historical content. |
| `updatedAt` | `string` (datetime) | No |  |
| `pendingPairwiseSecrets` | `array` | No | Temporary delivery of a pairwise trust secret to a peer during establishment (encrypted to the peer's profile encryptionPublicKey). Removed after the peer has retrieved and acknowledged. Used when the acceptor generates the secret (new-user path) or when the initiator pre-delivers it (existing-user path). |

### `social.smallbury.actor.trust#keyEpoch`

**Type**: `object`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `endedAt` | `string` (datetime) | No | Set when a newer epoch is created. Omitted on the current epoch. |
| `startedAt` | `string` (datetime) | Yes |  |
| `keyEpochId` | `string` | Yes | Stable identifier for this epoch (e.g. sequential "0", "1", …). Referenced by encrypted posts and comments. |
| `contentKeyWraps` | `array` | Yes | One wrapped copy of this epoch's symmetric content key per intended recipient. Opaque slots so wraps are not labeled with recipient DIDs. May include dummy wraps. |

### `social.smallbury.actor.trust#trustedEntry`

**Type**: `object`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `did` | `string` (did) | Yes |  |
| `since` | `string` (datetime) | Yes | When this actor added the contact to trusted. |

### `social.smallbury.actor.trust#contentKeyWrap`

**Type**: `object`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `slot` | `string` | Yes | Opaque lookup key. Recipients compute slot = Base64URL(HMAC-SHA256(pairwiseTrustSecret, keyEpochId)). pairwiseTrustSecret is agreed at trust establishment and stored only on the two clients—not published on the PDS. |
| `wrappedKey` | `bytes` | Yes | This epoch's symmetric content encryption key, encrypted to the recipient's encryptionPublicKey from social.smallbury.actor.profile. |

### `social.smallbury.actor.trust#pendingPairwiseSecret`

**Type**: `object`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `peerDid` | `string` (did) | Yes | The contact who should decrypt wrappedSecret. |
| `inviteId` | `string` | No | Optional link to social.smallbury.graph.invite on the initiator's repo. |
| `wrappedSecret` | `bytes` | Yes | 32-byte pairwiseTrustSecret for this pair, encrypted to peerDid's encryptionPublicKey from social.smallbury.actor.profile. |

## Raw Schema

```json
{
  "id": "social.smallbury.actor.trust",
  "defs": {
    "main": {
      "key": "literal:self",
      "type": "record",
      "record": {
        "type": "object",
        "required": [
          "trusted",
          "keyEpochs"
        ],
        "properties": {
          "trusted": {
            "type": "array",
            "items": {
              "ref": "#trustedEntry",
              "type": "ref"
            },
            "description": "DIDs this actor trusts. Cleartext for AppView; does not by itself reveal per-post subset audiences. Listing a DID here may occur before they reciprocate (pre-emptive trust)."
          },
          "createdAt": {
            "type": "string",
            "format": "datetime"
          },
          "keyEpochs": {
            "type": "array",
            "items": {
              "ref": "#keyEpoch",
              "type": "ref"
            },
            "description": "Ordered content-key epochs. Only the latest epoch (no endedAt) is used for new posts and comments. Earlier epochs remain for decrypting historical content."
          },
          "updatedAt": {
            "type": "string",
            "format": "datetime"
          },
          "pendingPairwiseSecrets": {
            "type": "array",
            "items": {
              "ref": "#pendingPairwiseSecret",
              "type": "ref"
            },
            "description": "Temporary delivery of a pairwise trust secret to a peer during establishment (encrypted to the peer's profile encryptionPublicKey). Removed after the peer has retrieved and acknowledged. Used when the acceptor generates the secret (new-user path) or when the initiator pre-delivers it (existing-user path)."
          }
        }
      },
      "description": "Contacts this actor trusts, content-key epochs for E2EE, and optional in-flight pairwise-secret delivery during trust establishment. Trusted DIDs are cleartext for AppView indexing. Mutual trust is derived: A trusts B and B trusts A (each side's trusted array). Feed visibility requires mutual trust on both records. Per-epoch content keys use opaque slots (see #contentKeyWrap)."
    },
    "keyEpoch": {
      "type": "object",
      "required": [
        "keyEpochId",
        "startedAt",
        "contentKeyWraps"
      ],
      "properties": {
        "endedAt": {
          "type": "string",
          "format": "datetime",
          "description": "Set when a newer epoch is created. Omitted on the current epoch."
        },
        "startedAt": {
          "type": "string",
          "format": "datetime"
        },
        "keyEpochId": {
          "type": "string",
          "maxLength": 64,
          "minLength": 1,
          "description": "Stable identifier for this epoch (e.g. sequential \"0\", \"1\", …). Referenced by encrypted posts and comments."
        },
        "contentKeyWraps": {
          "type": "array",
          "items": {
            "ref": "#contentKeyWrap",
            "type": "ref"
          },
          "description": "One wrapped copy of this epoch's symmetric content key per intended recipient. Opaque slots so wraps are not labeled with recipient DIDs. May include dummy wraps."
        }
      }
    },
    "trustedEntry": {
      "type": "object",
      "required": [
        "did",
        "since"
      ],
      "properties": {
        "did": {
          "type": "string",
          "format": "did"
        },
        "since": {
          "type": "string",
          "format": "datetime",
          "description": "When this actor added the contact to trusted."
        }
      }
    },
    "contentKeyWrap": {
      "type": "object",
      "required": [
        "slot",
        "wrappedKey"
      ],
      "properties": {
        "slot": {
          "type": "string",
          "maxLength": 128,
          "minLength": 1,
          "description": "Opaque lookup key. Recipients compute slot = Base64URL(HMAC-SHA256(pairwiseTrustSecret, keyEpochId)). pairwiseTrustSecret is agreed at trust establishment and stored only on the two clients—not published on the PDS."
        },
        "wrappedKey": {
          "type": "bytes",
          "maxLength": 512,
          "description": "This epoch's symmetric content encryption key, encrypted to the recipient's encryptionPublicKey from social.smallbury.actor.profile."
        }
      }
    },
    "pendingPairwiseSecret": {
      "type": "object",
      "required": [
        "peerDid",
        "wrappedSecret"
      ],
      "properties": {
        "peerDid": {
          "type": "string",
          "format": "did",
          "description": "The contact who should decrypt wrappedSecret."
        },
        "inviteId": {
          "type": "string",
          "maxLength": 128,
          "minLength": 1,
          "description": "Optional link to social.smallbury.graph.invite on the initiator's repo."
        },
        "wrappedSecret": {
          "type": "bytes",
          "maxLength": 512,
          "description": "32-byte pairwiseTrustSecret for this pair, encrypted to peerDid's encryptionPublicKey from social.smallbury.actor.profile."
        }
      }
    }
  },
  "$type": "com.atproto.lexicon.schema",
  "lexicon": 1
}
```
