# IXO Credentials

> How IXO issues, holds, shares and revokes verifiable credentials with SD-JWT VC, OpenID4VCI and OpenID4VP: the parts, the live hosts on devnet, testnet and mainnet, and where to start as an issuer, a verifier or a wallet.

IXO Credentials is the system IXO uses to issue verifiable credentials to people, keep them in the person's encrypted Vault, share only the claims a service needs, and revoke them. Read this page to understand the parts and how they connect before you integrate as an issuer, a verifier or a wallet. To try the whole chain first, follow [Try it on devnet](/guides/dev/credentials/quickstart). For the general model of Decentralized Identifiers (DIDs) and credentials on IXO, see [Identity and credentials](/articles/identity-and-credentials).

## Where it runs

IXO Credentials is live on all three networks. Each network has its own Credentials worker and its own Auth Hub wallet; nothing is shared between networks.

| Network | Credentials worker | Worker DID | Auth Hub wallet |
| --- | --- | --- | --- |
| Devnet | `https://dev.credentials.ixo.earth` | `did:web:dev.credentials.ixo.earth` | `https://dev.auth.ixo.earth/credentials` |
| Testnet | `https://test.credentials.ixo.earth` | `did:web:test.credentials.ixo.earth` | `https://test.auth.ixo.earth/credentials` |
| Mainnet | `https://credentials.ixo.earth` | `did:web:credentials.ixo.earth` | `https://auth.ixo.earth/credentials` |

Every worker host describes itself at `/docs` (interactive API reference), `/openapi.json` (the full contract) and `/llms.txt` (a short summary for agents), for example [dev.credentials.ixo.earth/docs](https://dev.credentials.ixo.earth/docs). The library for issuers, verifiers and wallets is [`@ixo/credentials-kit`](https://www.npmjs.com/package/@ixo/credentials-kit) on npm.

Devnet also has a test issuer and a test verifier for trying the flow. They issue and accept fake credentials only; never use them for real people. See [Try it on devnet](/guides/dev/credentials/quickstart).

<Note>
  **Self is not live yet.** Self, IXO's identity verification service, is the reference issuer of the `IdentityLevel1` credential. It is built but not deployed on any network, so no `IdentityLevel1` credentials exist yet. The pages mark everything that depends on Self.
</Note>

## What it is

IXO Credentials uses open standards end to end:

| Piece | Standard |
| --- | --- |
| Credential format | SD-JWT-based Verifiable Digital Credentials (SD-JWT VC), format `dc+sd-jwt`, on Selective Disclosure for JWTs (SD-JWT, RFC 9901), signed with Ed25519 |
| Issuance | OpenID for Verifiable Credential Issuance (OpenID4VCI) 1.0, pre-authorized code flow, offers by reference |
| Presentation | OpenID for Verifiable Presentations (OpenID4VP) 1.0, a subset of the Digital Credentials Query Language (DCQL), encrypted `direct_post.jwt` responses |
| Revocation | IETF Token Status List |
| Identifiers | `did:ixo` for people and entities, `did:web` for services |

IXO implements a profile of each standard, not all of it. For example: no authorization code flow, no transaction codes, no batch issuance, and requests outside the DCQL subset are refused. The exact versions, what is implemented, and how this compares with the European Digital Identity (EUDI) Wallet are on [Standards and conformance](/guides/dev/credentials/standards).

On top of the standards, IXO adds one shared service and one set of rules:

- **The Credentials worker** lets issuers and verifiers reach a person by their DID. It has four jobs: an **offer inbox** (credential offers addressed to a DID), a **presentation broker** (signed proof requests addressed to a DID), a **status-list registry** (issuer-signed revocation lists) and a public **catalog** (what each issuer says it issues and what each verifier says it accepts, with the name, logo and card look wallets show). It also keeps each person's **block list**: the senders that person no longer accepts offers or requests from. It never holds a claim or a credential, and never signs one.
- **The IXO Credential Profile v1** narrows each standard to one way of doing things, so every IXO issuer, verifier and wallet checks the same things.

Credential types (`vct` values) are not hosted on the Credentials worker. New types follow the existing IXO pattern: a type document in the GitHub repository `ixofoundation/ns` and a protocol entity, as the `KYCAMLLevel1` type does. Self's `IdentityLevel1` keeps its current `vct` (`https://credentials.ixo.world/types/IdentityLevel1`).

## The roles

| Role | Does | On IXO |
| --- | --- | --- |
| Issuer | Checks facts, signs credentials, keeps status lists | Any service with a `did:ixo` entity DID or a `did:web` DID and an Ed25519 key under `assertionMethod`. Self (not live yet) and the devnet test issuer are two. |
| Wallet (holder) | Collects, stores and presents credentials with the person's consent | The Auth Hub's `/credentials` pages, acting with the person's own key, or a wallet a person or team runs themselves with the kit and the person's own keys |
| Verifier | Asks for specific claims from specific issuers and checks the answer | Any service with a `did:ixo` or `did:web` DID |
| Registry | Publishes the keys everyone checks against | DID documents on the IXO chain, read through [IXO Blocksync](/articles/ixo-blocksync), and `did:web` documents on each service's own host |

Credentials are stored only in the person's **Vault**, an end-to-end encrypted room on [IXO Matrix](/articles/ixo-matrix). The Auth Hub keeps its working data for the wallet (cached offers and requests, the cached Vault index, standing consents, the activity log, its copy of the block list) in a credentials database of its own, separate from the Hub's account database.

## How it fits together

```mermaid
flowchart LR
  ISS["Issuer"] -- "deposit offer" --> CW["Credentials worker"]
  VER["Verifier"] -- "deposit request" --> CW
  ISS -- "publish status list" --> CW
  CW -- "signed webhook" --> HUB["Auth Hub wallet"]
  HUB -- "read own offers and requests, manage blocks" --> CW
  APP["Own wallet (kit + own keys)"] -- "same endpoints, polled" --> CW
  HUB -- "OpenID4VCI" --> ISS
  APP -- "OpenID4VCI" --> ISS
  HUB -- "OpenID4VP response" --> VER
  APP -- "OpenID4VP response" --> VER
  HUB -- "encrypted write and read" --> VAULT[("Vault")]
  APP -- "encrypted write and read" --> VAULT
  VER -- "read status list" --> CW
```

The credential travels from the issuer to the wallet, and the presentation from the wallet to the verifier, encrypted to the verifier's key. Neither passes through the Credentials worker. Only the Auth Hub receives webhooks; any other wallet polls the worker as the person.

## When to use it

Use IXO Credentials when:

- your service checks something about a person once (identity, age, a qualification) and other services need to rely on that check;
- your service needs proof of a fact about a person without collecting the underlying data, for example "over 18" without a birth date;
- you need to revoke what you issued.

Do not use it for:

- claims about entities, projects or impact outcomes: use [IXO claims](/guides/dev/ixo-claims);
- service-to-service authorization: use UCAN delegations and the patterns in [Authentication](/guides/dev/authentication).

## Core capabilities

<AccordionGroup>
  <Accordion title="Issue to a DID" icon="pen-nib">
    An issuer deposits a credential offer for a person's DID, by reference: a `credential_offer_uri` at the issuer that mints a fresh, short-lived, single-use pre-authorized code each time the wallet fetches it. The wallet collects the credential over OpenID4VCI. The issuer binds the credential only to a key on the person's DID document, so a leaked code yields nothing without that key. [Issue a credential](/guides/dev/credentials/issue-a-credential).
  </Accordion>
  <Accordion title="Ask a DID for proof" icon="clipboard-check">
    A verifier deposits a signed OpenID4VP request for a person's DID, sends the person a link, and receives an encrypted response at its own endpoint. [Request proof](/guides/dev/credentials/request-proof).
  </Accordion>
  <Accordion title="Hold and share with consent" icon="wallet">
    The Auth Hub wallet stores credentials in the Vault and shares only what each request asks for, after the person approves or under a standing consent the person created. The person can block any sender. A person or team can run their own wallet with the kit and the standard `openid4vp://` links, without the Hub. [Hold and share credentials](/guides/dev/credentials/hold-and-share).
  </Accordion>
  <Accordion title="Revoke and discover" icon="list-check">
    Issuers publish signed status lists to the registry; verifiers read one bit per credential. The catalog tells anyone what an issuer says it issues and what a verifier says it accepts. [Status lists and the catalog](/guides/dev/credentials/status-lists-and-catalog).
  </Accordion>
</AccordionGroup>

## What IXO adds and what stays standard

| IXO addition | Why | Effect on the standards |
| --- | --- | --- |
| Offers and requests addressed to a DID | The standards assume a QR code or link in front of a person | None: the offers and requests are standard objects |
| Issuer key from a `did:ixo` or `did:web` | SD-JWT VC lets an ecosystem define its key discovery | One mechanism only |
| Subject binding at issuance | Pre-authorized codes are bearer secrets | Issuer-side check, invisible to wallets |
| A DCQL subset and an issuer filter on `iss` | Every wallet must handle every request the same way | Requests beyond the subset are refused |
| Mandatory key binding and status checks | Replay and revocation protection | Stricter than the standards require |
| Catalog | Find what an issuer says it issues or a verifier says it accepts, starting from a DID; the only source of names, logos and card looks | Ignoring it changes nothing |
| Block list at the worker | A person can stop any sender from reaching them | None: only the inbox and the broker apply it |
| UCAN authentication at the worker | Every call is signed by the caller's DID, bound to one request and used once | None: only the worker uses it |

## What protects a person

There is no central trust list. The same rules apply to every sender, IXO's own services included. Every DID is its own sender, exactly as written: `did:web:example.com`, `did:web:a.example.com` and `did:web:example.com:user:alice` are three senders, each with its own limits, its own block and its own catalog entry. There is no grouping by domain.

Names, logos and card looks are self-declared. Each comes only from the sender's own catalog entry, never from an offer or a request. Nobody checks them, and a sender can copy another sender's name and look. So none of the protections below depends on a name or a look.

The Auth Hub never shows a web address that a sender supplied, not as a link and not as text to copy. A harmful link would be blamed on IXO. A sender appears only by its name, logo and card look, and the Hub loads logos only through its own media proxy (png, jpeg or webp).

What protects a person:

- **The person's block list.** A person can block any issuer or verifier. The Credentials worker then refuses that sender's offers and requests for them, and closes the ones already waiting. See [Block a sender](/guides/dev/credentials/hold-and-share#block-a-sender).
- **Two waiting items per sender.** A sender can have at most 2 offers and, separately, 2 requests waiting for one person at a time. A sender cannot fill a person's inbox.
- **The cryptographic check of every credential.** A wallet stores a credential only if the issuer's signature is valid, it is about the person (`sub`) and it is bound to the person's key (`cnf`). A verifier checks the same on every presentation, plus status and key binding.
- **Each verifier's own choice of accepted issuers.** A verifier names the issuer DIDs it accepts for each type. A credential from any other issuer fails the check, whatever name or look it carries.

## What a verifier always sees

Selective disclosure hides only the claims the issuer made disclosable and the request did not ask for. Every presentation also shows the verifier, in the clear:

- the person's IXO account ID (`sub`, their `did:ixo`, the same at every service);
- the holder key (`cnf.jwk`, also the same at every service);
- the status reference (the status list URL and the person's index in it);
- the issuer (`iss`), the type (`vct`) and the dates (`iat`, `exp`, and `nbf` when set);
- every claim the issuer did not make selectively disclosable (for Self, for example `identity_level` and `document_verified`).

So two services that compare notes can tell they saw the same person. This is what every presentation carries; it is not a list the wallet shows on its sharing screen.

## Next steps

<CardGroup cols={2}>
  <Card title="Try it on devnet" icon="rocket" href="/guides/dev/credentials/quickstart">
    Look at the live services, save a test credential in the devnet Hub, and answer a test request.
  </Card>
  <Card title="Issue a credential" icon="pen-nib" href="/guides/dev/credentials/issue-a-credential">
    Serve OpenID4VCI, bind to the subject's key, publish status, deposit offers.
  </Card>
  <Card title="Request proof" icon="clipboard-check" href="/guides/dev/credentials/request-proof">
    Build a signed request, deposit it, verify the encrypted response.
  </Card>
  <Card title="Hold and share credentials" icon="wallet" href="/guides/dev/credentials/hold-and-share">
    The Auth Hub wallet, blocking a sender, and the self-sovereign path for apps with their own keys.
  </Card>
  <Card title="Standards and conformance" icon="scale-balanced" href="/guides/dev/credentials/standards">
    Every specification and version, what is implemented, and the EUDI Wallet comparison.
  </Card>
  <Card title="Credentials API reference" icon="code" href="/guides/dev/credentials/api-reference">
    Authentication, endpoints, limits and error codes of the Credentials worker and the Hub.
  </Card>
</CardGroup>
