> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ditto.live/llms.txt
> Use this file to discover all available pages before exploring further.

# Revocations

> Revoke certificates to remove unwanted access from peers.

## Overview

When using the [Server Connection with Custom Authentication](/sdk/v5/auth-and-authorization/cloud-authentication) identity,
Ditto issues X.509 certificates and JWTs (referred to collectively as "certificates" in this document) to authenticated peers.
Once a certificate is granted, a peer can continue syncing data across the mesh until the certificate expires.

Revocations allow you to invalidate these certificates when a user or device should no longer have access. Rather than revoking individual certificates by ID, you define a **filter expression** using [DQL](/dql) that matches against identity fields. This allows you to revoke access for a single user, a group of users, or any set of identities matching specific criteria.

Revocations are managed centrally via the Big Peer's HTTP API and are automatically propagated to all connected peers -- including Small Peers that communicate only via peer-to-peer connections (Bluetooth, LAN, etc.).

```mermaid theme={null}
sequenceDiagram
    participant Admin as Administrator
    participant BigPeer as Ditto Cloud<br/>(Big Peer)
    participant SmallPeerA as Small Peer A
    participant SmallPeerB as Small Peer B

    Admin->>BigPeer: 1. Create revocation via HTTP API
    Note over BigPeer: 2. Revocation stored and signed,<br/>check peer connections

    BigPeer->>SmallPeerA: 3. Signed revocation propagated
    Note over SmallPeerA: 4. Verify signature,<br/>store revocation,<br/>check peer connections

    SmallPeerA->>SmallPeerB: 5. Propagate via mesh
    Note over SmallPeerB: 6. Verify signature,<br/>store revocation,<br/>check peer connections
```

In the diagram above, "check peer connections" means both existing connections and any future connection attempts are evaluated against the revocation.

## When to use revocations

Common scenarios where revocation is necessary:

* **Deprovisioning a user** -- An employee leaves the organization and their device should no longer sync data.
* **Compromised device** -- A device is lost or stolen and should be immediately excluded from the mesh.
* **Bulk access removal** -- Revoke all users matching certain criteria, such as a specific role or store location.

<Info>
  Revocations only apply to peers whose certificates were issued *before* the revocation was created. Peers who re-authenticate after a revocation will receive new certificates that are not affected by existing revocations.
</Info>

## Revocation filters

Revocations use DQL filter expressions that are evaluated against a peer's identity context. The identity context is derived from two fields set in your [authentication webhook response](/sdk/latest/auth-and-authorization/data-authorization):

| Field                     | Description                                                                                                               |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `userID`                  | The user ID assigned during authentication                                                                                |
| `identityServiceMetadata` | Custom metadata returned by your [authentication webhook](/sdk/latest/auth-and-authorization/cloud-authentication#server) |

### Filter examples

Revoke a single user:

```sql theme={null}
userID == 'alice'
```

Revoke all users at a specific store:

```sql theme={null}
identityServiceMetadata.storeID == 'store-456'
```

Revoke a user at a specific store:

```sql theme={null}
userID == 'bob' AND identityServiceMetadata.storeID == 'store-456'
```

<Warning>
  Only the `userID` and `identityServiceMetadata` fields are allowed in revocation filters. Filters referencing other fields will be rejected by the API.
</Warning>

## Create a revocation

To revoke access, send a POST request with a DQL filter expression and a reason.

<Warning>
  **Reject the matching identities in your authentication webhook before you create the revocation.** Peers may refresh
  their tokens automatically and a revocation only applies to certificates issued before it was created. If your
  [webhook](/sdk/latest/auth-and-authorization/cloud-authentication#server) still returns
  `authenticate: true` for that identity, the peer re-authenticates, receives a fresh certificate that
  the revocation does not match, and regains access.
</Warning>

<Info>
  To use the HTTP API, you need an API key with appropriate permissions. See [HTTP API Authentication](/cloud/http-api/auth-and-params) for details.
</Info>

**Request:**

```
POST /api/v4/auth/revocations
```

The endpoint expects a JSON payload that looks like:

```json theme={null}
{
  "filter": "userID == 'alice'", // DQL filter matching the identities to revoke
  "reason": "Employee offboarded" // why access was revoked, stored with the entry
}
```

Both fields are required, and each must be non-empty and at most 16384 characters.

**Example request:**

```bash theme={null}
curl -X POST "https://{YOUR_CLOUD_URL_ENDPOINT}/api/v4/auth/revocations" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer {your-api-key}" \
  -d '{
    "filter": "userID == '\''alice'\''",
    "reason": "Employee offboarded"
  }'
```

**Response:**

```json theme={null}
{
  "_id": "019502ab-7c3e-7abc-8def-1234567890ab",
  "filter": "userID == 'alice'",
  "reason": "Employee offboarded",
  "createdAt": "2025-01-15T10:00:00Z",
  "transactionId": 17417
}
```

* `_id`: A UUIDv7 that encodes the creation time, enabling chronological ordering.
* `transactionId`: A monotonically increasing identifier for the write operation, consistent with the `transactionId` returned by other [HTTP Data API](/cloud/http-api/api/post-storeexecute) endpoints.

## List revocations

Retrieve the current list of revocations for auditing. The endpoint supports cursor-based pagination. The response includes a `hasMore` field indicating whether additional pages are available, and a `cursor` field to fetch the next page.

**Request:**

```
GET /api/v4/auth/revocations?filter_contains={substring}&cursor={revocation_id}&limit={count}
```

All three query parameters are optional.

**Example request:**

```bash theme={null}
curl -X GET "https://{YOUR_CLOUD_URL_ENDPOINT}/api/v4/auth/revocations?limit=1" \
  -H "Authorization: Bearer {your-api-key}"
```

**Response:**

```json theme={null}
{
  "revocations": [
    {
      "_id": "019502ab-7c3e-7abc-8def-1234567890ab",
      "filter": "userID == 'alice'",
      "reason": "Employee offboarded",
      "createdAt": "2025-01-15T10:00:00Z"
    }
  ],
  "hasMore": true,
  "cursor": "019502ab-7c3e-7abc-8def-1234567890ab"
}
```

**Paginate through results**

When `hasMore` is `true`, pass the returned `cursor` value to fetch the next page, with an optional `limit`:

```bash theme={null}
curl -X GET "https://{YOUR_CLOUD_URL_ENDPOINT}/api/v4/auth/revocations?cursor=019502ab-7c3e-7abc-8def-1234567890ab&limit=50" \
  -H "Authorization: Bearer {your-api-key}"
```

**Response:**

```json theme={null}
{
  "revocations": [
    {
      "_id": "019502ac-8d4f-7def-9abc-234567890cd",
      "filter": "identityServiceMetadata.storeID == 'store-456'",
      "reason": "Store closed",
      "createdAt": "2025-01-15T11:30:00Z"
    }
  ],
  "hasMore": false
}
```

When `hasMore` is `false`, the `cursor` field is omitted from the response since there are no further
pages to retrieve.

### Search revocations

You can search for revocations by filter text using the `filter_contains` query parameter. This
performs a **case-sensitive substring match** against the `filter` field of each revocation. This
parameter can be combined with `cursor` and `limit` for paginated searches.

```bash theme={null}
curl -X GET "https://{YOUR_CLOUD_URL_ENDPOINT}/api/v4/auth/revocations?filter_contains=alice" \
  -H "Authorization: Bearer {your-api-key}"
```

## Error responses

| Endpoint | Status | Condition                                                                                                |
| -------- | ------ | -------------------------------------------------------------------------------------------------------- |
| POST     | 400    | Invalid `filter` — empty, exceeds 16384 characters, invalid DQL syntax, or references unsupported fields |
| POST     | 400    | Invalid `reason` — empty or exceeds 16384 characters                                                     |
| POST     | 500    | Internal server error                                                                                    |
| GET      | 400    | Invalid `cursor` format (must be a valid UUID)                                                           |
| GET      | 500    | Internal server error                                                                                    |

## How revocation propagation works

When you create a revocation through the HTTP API:

1. The Big Peer stores the revocation and signs it with its private key before propagating it.
2. The signed revocation is propagated to connected Small Peers.
3. Each Small Peer **verifies the signature** to confirm the revocation originates from a trusted Big Peer, then stores it locally.
4. Small Peers propagate revocations to other Small Peers they connect with. Each receiving peer **verifies the signature** before storing, ensuring every hop in the mesh is authenticated.

### Revocation checking

Revocations are enforced at two points:

* **New connections**: When a peer attempts to connect, it is evaluated against all active revocation filters. If any filter matches, the connection is rejected.
* **Existing connections**: When a new revocation is added, it is immediately evaluated against all currently connected peers. Any matching connections are terminated.

This dual-check approach ensures that revoked peers are disconnected promptly, not just prevented from establishing new connections.

### Mesh-wide propagation

Revocations are not limited to the Big Peer → Small Peer path. Small Peers also propagate revocations to each other during peer-to-peer sync. This means:

* A Small Peer that received a revocation from the Big Peer will share it with other Small Peers it connects to.
* Peers that are not directly connected to the Big Peer can receive revocations through the mesh, as long as they have authenticated with the Big Peer earlier.

<Warning>
  Revocation propagation depends on peers syncing with the Big Peer or with another peer that already has the revocation. Fully offline peers will not receive revocation updates until they reconnect to the mesh.
</Warning>

### Signature verification

Each revocation entry is individually signed by the Big Peer. Small Peers verify the signature against their trusted CA keys before storing the revocation. This prevents forged revocations -- a compromised Small Peer cannot fabricate revocation entries that other peers would accept.

If signature verification fails, the revocation entry is skipped.

## Revocations collections

Revocations are stored in collections that you can query read-only via [DQL](/dql). The Big Peer holds the authoritative list, while each Small Peer holds a signed copy of the revocations it has received.

### Big Peer

The Big Peer maintains the authoritative list of revocations in the `__revocations` collection. This is where entries created through the HTTP API are stored, including the human-readable `filter`, `reason`, and `created_at` values.

```sql theme={null}
SELECT * FROM __revocations
```

<Warning>
  Manage revocations on the Big Peer only through the [HTTP API](#create-a-revocation). Do not write to the `__revocations` collection directly.
</Warning>

### Small Peer

Small Peers store the revocations they have received in a local `__revocation_cache` collection. You can query this collection to inspect which revocations a peer currently holds -- useful for debugging and auditing on-device state.

```sql theme={null}
SELECT * FROM __revocation_cache
```

Each document is individually signed by the Big Peer. Its contents include the signed revocation payload, the signature, the signing key, and metadata such as the creation time.

<Warning>
  Treat `__revocation_cache` as **read-only**. Do not insert, update, or delete documents in this collection. Because each entry is verified against its signature, a modified entry is treated as invalid.
</Warning>

## Considerations

| Topic                  | Detail                                                                                                                                                                                                                                                                                                                            |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Filter-based           | Revocations match against identity fields (`userID`, `identityServiceMetadata`), not individual certificate IDs. A single revocation can affect multiple users.                                                                                                                                                                   |
| Forward-looking        | Revocations only apply to peers whose certificates were issued before the revocation was created. Re-authenticated peers receive new, unaffected certificates, so reject the matching identities in your [authentication webhook](/sdk/latest/auth-and-authorization/cloud-authentication#server) before creating the revocation. |
| Propagation delay      | Revocations propagate during sync. Fully offline peers won't receive updates until they reconnect to the mesh.                                                                                                                                                                                                                    |
| No delete or un-revoke | Revocations are permanent and cannot be deleted. There is no DELETE endpoint. To restore access for a revoked user, the user must re-authenticate and obtain a new certificate.                                                                                                                                                   |


## Related topics

- [Release Notes](/cloud/release-notes.md)
- [JavaScript Web Release Notes](/sdk/latest/release-notes/javascript-web.md)
- [C++ Release Notes](/sdk/latest/release-notes/cpp.md)
