Trust Lists
A trust list names the issuers you accept. EUDIPLO uses Lists of Trusted Entities (LoTE, ETSI TS 119 602) as signed JWTs: it publishes lists you manage and reads lists published by others. ETSI TS 119 612 XML trusted lists are not supported. For a complete walkthrough, follow the trusted issuers cookbook.
How a trust list is used
Each trusted entity has an issuance certificate and a revocation certificate. When a credential is presented, EUDIPLO:
- loads the lists referenced in the credential query and checks their signature and
NextUpdate, - builds a certificate path from the credential's
x5cchain to a listed issuance certificate, - requires the matching entity to be listed with a PID or EAA issuance service (
http://uri.etsi.org/19602/SvcType/PID/Issuanceor.../EAA/Issuance); other service types are ignored for credentials, - if status checks are enabled, requires the credential's status list to be signed by the revocation certificate of the same entity.
A listed CA certificate accepts every credential issued below it; a listed end-entity certificate only accepts credentials signed with exactly that certificate. Wallet-provider lists, used to trust wallet and key attestations during issuance, are described in Wallet and Key Attestation.
Publish a managed trust list
Create the list in the Web Client under Credential Issuance → Trust Lists, or with POST /api/trust-list (role issuance:manage or presentation:manage):
{
"id": "membership-issuers",
"description": "Issuers of membership credentials",
"entities": [
{
"type": "internal",
"issuerKeyChainId": "<attestation key chain id>",
"revocationKeyChainId": "<status list key chain id>",
"info": { "name": "Example Club", "country": "DE" }
},
{
"type": "external",
"issuerCertPem": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
"revocationCertPem": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
"info": { "name": "Partner Club" }
}
]
}
- Internal entities reference key chains of this tenant. EUDIPLO lists the last certificate of each chain, the root CA for internal and external CA chains, so the entry survives key rotation.
- External entities carry the PEM certificates of issuers outside this tenant.
- The list is signed with the
trustListkey chain inkeyChainId, or with atrustListkey chain of the tenant if omitted; create one first (Keys and Certificates).
EUDIPLO publishes the signed JWT at GET /issuers/{tenantId}/trust-list/{id} without authentication, so others can use your list.
| Task | Endpoint |
|---|---|
| Replace entities | PUT /api/trust-list/{id} with the complete body; publishes the next sequence number |
| Version history | GET /api/trust-list/{id}/versions, GET /api/trust-list/{id}/versions/{versionId} |
| List, read, export, delete | GET /api/trust-list, GET /api/trust-list/{id}, GET /api/trust-list/{id}/export, DELETE /api/trust-list/{id} |
To manage lists as files, put the same JSON into config/<tenant>/trust-lists/ (Configuration as Code).
Validity and renewal
A managed list is valid for 30 days (NextUpdate). EUDIPLO renews it automatically in the last 10 days: it re-signs the unchanged entities with the next sequence number and a new NextUpdate, and keeps the previous version in the history. The check runs every hour and at startup, so lists that expired while EUDIPLO was stopped are renewed when it starts. Verifiers reject a list whose NextUpdate has passed (trust_list_unavailable).
Renewals and updates never publish the same sequence number twice. If a renewal or another update was published between reading and writing, PUT fails with 409; read the list again and retry.
Fields
| Field | Required | Type / allowed values | Description |
|---|---|---|---|
id | no | string | Optional trust list id. If omitted, one may be generated. |
description | no | string | Optional trust list description. |
keyChainId | no | string | Optional key chain id used to sign trust list payloads. |
entities | yes | array of one of 2 shapes | One or more entities included in this trust list. |
entities[].type | yes | string: internal | Only in shape 1 of 2. Use locally managed key chains for trust material. |
entities[].issuerKeyChainId | yes | string | Only in shape 1 of 2. Key chain id for issuer certificate material. |
entities[].revocationKeyChainId | yes | string | Only in shape 1 of 2. Key chain id for revocation/status list certificate material. |
entities[].providerType | no | string: attestation-provider | wallet-provider | Only in shape 1 of 2. Provider role; defaults to attestation-provider. |
entities[].info | yes | object | Only in shape 1 of 2. Entity metadata. |
entities[].info.name | yes | string | Only in shape 1 of 2. Display name of the trusted entity. |
entities[].info.lang | no | string | Only in shape 1 of 2. Optional language tag for entity info. |
entities[].info.locale | no | string | Only in shape 1 of 2. Optional locale identifier. |
entities[].info.uri | no | string | Only in shape 1 of 2. Optional entity URI. |
entities[].info.country | no | string | Only in shape 1 of 2. Optional country name or code. |
entities[].info.locality | no | string | Only in shape 1 of 2. Optional locality or city. |
entities[].info.postalCode | no | string | Only in shape 1 of 2. Optional postal code. |
entities[].info.streetAddress | no | string | Only in shape 1 of 2. Optional street address. |
entities[].info.contactUri | no | string | Only in shape 1 of 2. Optional contact URI for the entity. |
entities[].type | yes | string: external | Only in shape 2 of 2. Provide external PEM certificates directly. |
entities[].issuerCertPem | yes | string | Only in shape 2 of 2. Issuer certificate in PEM format. |
entities[].revocationCertPem | yes | string | Only in shape 2 of 2. Revocation/status certificate in PEM format. |
entities[].providerType | no | string: attestation-provider | wallet-provider | Only in shape 2 of 2. Provider role; defaults to attestation-provider. |
entities[].info | yes | object | Only in shape 2 of 2. Entity metadata. |
entities[].info.name | yes | string | Only in shape 2 of 2. Display name of the trusted entity. |
entities[].info.lang | no | string | Only in shape 2 of 2. Optional language tag for entity info. |
entities[].info.locale | no | string | Only in shape 2 of 2. Optional locale identifier. |
entities[].info.uri | no | string | Only in shape 2 of 2. Optional entity URI. |
entities[].info.country | no | string | Only in shape 2 of 2. Optional country name or code. |
entities[].info.locality | no | string | Only in shape 2 of 2. Optional locality or city. |
entities[].info.postalCode | no | string | Only in shape 2 of 2. Optional postal code. |
entities[].info.streetAddress | no | string | Only in shape 2 of 2. Optional street address. |
entities[].info.contactUri | no | string | Only in shape 2 of 2. Optional contact URI for the entity. |
data | no | object | Optional additional custom payload data. |
data is accepted but ignored; EUDIPLO builds the list content from entities.
Use a trust list in a presentation
Reference trust lists in the trusted_authorities of a DCQL credential query:
{
"trusted_authorities": [
{
"type": "etsi_tl",
"values": [
{ "trustListId": "membership-issuers" },
{ "url": "https://trust.example.org/lists/pid.jwt", "verifierX509Der": "MIIB..." }
]
}
]
}
trustListIdreferences a managed list of this tenant. EUDIPLO fetches it from<INTERNAL_URL or PUBLIC_URL>/issuers/<tenant>/trust-list/<id>and pins the certificate that signs it. SetINTERNAL_URLif the backend cannot reach its ownPUBLIC_URL.urlreferences an external LoTE JWT.verifierX509Der(base64 DER certificate) orverifierKey(public JWK) is required to check its signature; without them loading the list fails (trust_list_unavailable).<TENANT_URL>in the URL is replaced with<PUBLIC_URL>/issuers/<tenant>.- Put several lists into the
valuesof oneetsi_tlentry: only the firstetsi_tlentry of a credential query is used for verification.
A credential query without trusted_authorities is verified without any issuer check.
What the wallet receives
Wallets match credentials by key identifier, without fetching the list. EUDIPLO therefore replaces each etsi_tl entry in the request with an aki entry whose values are the base64url-encoded key identifiers of the listed PID and EAA issuance certificates: the Subject Key Identifier of every certificate, plus the Authority Key Identifier of end-entity certificates. If a list cannot be loaded, or an issuer certificate has no usable identifier (for example a self-signed certificate without Authority Key Identifier), the request additionally keeps an etsi_tl entry with the plain URL of that list. Verification always uses the stored configuration.
Some wallets do not handle trusted_authorities yet. VP_REMOVE_TA=true removes it from all requests sent to wallets; EUDIPLO still verifies against the configured lists.
Caching
Loaded trust lists are cached for five minutes. After changing a list, clear the cache with DELETE /api/cache/trust-list (it also clears the OpenID Federation cache) to use it immediately; GET /api/cache/stats shows what is cached. Fetching a list times out after four seconds. Outside NODE_ENV=production, the TLS certificate of the list's host is not checked; the list signature always is. Lists outside EUDIPLO's own PUBLIC_URL and INTERNAL_URL must pass the outbound URL policy: HTTPS and public addresses only, unless OUTBOUND_URL_ALLOW_HTTP or OUTBOUND_URL_ALLOW_PRIVATE_NETWORK is set. The same applies to the status lists named in presented credentials and wallet attestations. EUDIPLO caches a status list for its ttl, five minutes without one, at most one hour and not past its exp; DELETE /api/cache/status-list clears the cached status lists.