An unofficial developer toolkit for the EUDI and OpenID4VC ecosystem. Decode, issue, and present verifiable credentials, run a testing wallet, or proxy live wallet traffic for debugging. The CLI command is eudi.
Try it online: a shared public demo of the wallet and decoder runs at https://eudi-test.dev, no install needed. Issue, present, and decode test credentials right in the browser (state is shared between all visitors and resets daily, so do not enter personal data).
- Testing Wallet: stateful CLI wallet with file persistence, OID4VP/VCI flows, QR scanning, and OS URL scheme registration (wallet)
- Reverse Proxy: intercept, classify, and decode OID4VP/VCI wallet traffic in real time (proxy)
- Web UI: paste, decode, and validate credentials in a split-pane browser interface (serve)
- Unified Decode: a single
decodecommand handles SD-JWT, JWT VC, JWT, mDOC, OID4VCI offers, OID4VP requests, and ETSI trust lists - QR Screen Capture: scan a QR code straight from your screen to decode credentials or OpenID requests (decode --screen)
- Offline Decode & Validate: SD-JWT, JWT VC, mDOC, JWT with signature verification and trust list support
- DCQL Generation: generate Digital Credentials Query Language queries from existing credentials
Most EUDI test tooling is an issuer or a verifier that you point a wallet at. This is the wallet, so the thing under test is your issuer or your verifier.
| Tool | What it tests | Runs locally | Scriptable |
|---|---|---|---|
| eudi-dev | your issuer or verifier | yes | CLI and HTTP API |
| Animo OpenID4VC Playground | your wallet | self-hostable | no, web UI |
| EUDI reference issuer and verifier | your wallet | issuer only | no, web UI |
| EUDI Web Wallet Tester | your issuer, OID4VCI only | yes | no, web UI |
| EUDI reference wallet (Android, iOS) | your issuer or verifier | on a device | no |
| Procivis One trial apps | your issuer or wallet | on a device | no |
| Multipaz | your wallet or issuer, and proximity | SDK, apps, hosted issuer and verifier | no |
| OIDF conformance suite | your wallet or verifier, for certification | yes, Docker | yes |
| Paradym debuggers | one credential, decoded | no | no |
| SDKs: walt.id, Sphereon, Credo, Procivis One | whatever you build | yes | as you write it |
Reach for something else when you need certification (the OIDF suite is the authority, a passing run here is not one, though this repository runs its plans: see conformance), when the wallet is what you are testing (point it at the Animo or EUDI services), when you are shipping a product (use an SDK, everything here is under internal/), when the flow is proximity rather than remote (this speaks OID4VP over HTTP only, Multipaz covers BLE and NFC), or when you just want to read one credential (a hosted decoder needs no install). Never with real credentials: see SECURITY.md.
brew install dominikschlosser/tap/eudi-devInstalls the eudi command with shell completion (plus oid4vc-dev as a legacy alias).
Download the latest binary for your platform from Releases.
go install github.com/dominikschlosser/eudi-dev@latestThis installs the binary as eudi-dev (Go names it after the module). The documentation calls the command eudi, so link it if you want the shorter name: ln -s "$(go env GOPATH)/bin/eudi-dev" "$(go env GOPATH)/bin/eudi".
The module path is github.com/dominikschlosser/eudi-dev. Installing through the old oid4vc-dev path fails with a version constraints conflict: the repository redirects, but a module declares exactly one path and this one declares the new name.
git clone https://github.com/dominikschlosser/eudi-dev.git
cd eudi-dev
go build -o eudi .docker pull ghcr.io/dominikschlosser/eudi-dev:latest
docker run -p 8085:8085 -p 8086:8086 ghcr.io/dominikschlosser/eudi-devThe default CMD starts the wallet server with pre-loaded PID credentials in headless mode. Ready for automated verifier testing out of the box.
→ Full Docker & verifier testing guide → OIDF conformance status, runbook, and results → Examples
eudi [--json] [--no-color] [-v] <command> [flags] [input]
Input can be a file path, URL, raw credential string, or piped via stdin.
Shell completion covers all subcommands, flags, and known values (template names, credential IDs, running wallet instances). Install it into your shell init with one command (bash, zsh, and fish, detected from $SHELL when no argument is given):
eudi completion install| Command | Purpose |
|---|---|
wallet |
Stateful testing wallet with CLI-driven OID4VP/VCI flows |
issue |
Generate test SD-JWT, JWT, or mDOC credentials for development |
proxy |
Debugging reverse proxy for OID4VP/VCI wallet traffic |
serve |
Web UI for decoding and validating credentials in the browser |
decode |
Auto-detect & inspect credentials, OpenID4VCI/VP, and trust lists. May auto-verify issuer metadata when resolvable |
validate |
Verify signatures, check expiry, and check revocation status |
dcql |
Generate a DCQL query from a credential's claims |
completion |
Generate or install shell completion (completion install) |
version |
Print version |
A stateful testing wallet with file persistence, CLI-driven OID4VP/VCI flows, QR scanning, and OS URL scheme registration.
eudi issue sdjwt --wallet --template german-pid-sdjwt # Issue a PID into the wallet
eudi wallet serve # Start web UI + OID4VP endpoints
eudi wallet ca-cert --out wallet-ca-cert.pem
eudi wallet tls-cert --out wallet-tls-cert.pem
eudi wallet accept 'openid4vp://authorize?...'
eudi wallet scan --screen # QR scan → auto-dispatch
eudi wallet logs -f # Follow persisted wallet interactionsSecurity: By default the wallet server has no authentication: anyone who can reach its port controls the wallet and its credentials. Run it on localhost or an isolated test network, and never put real credentials in it. Localhost alone does not keep a web page out (every page you visit can reach it), so the
/api/endpoints refuse requests carrying anOriginfrom another site. The one exception is/api/dc-api, which a verifier's page calls from its own origin by design, and which is protected by that origin check and the consent dialog instead. Internet-facing hosting has its own profile,--demo, which disables the process and filesystem endpoints and blocks fetches into private networks. That is what runs on eudi-test.dev. See public demo hosting.
wallet serve starts the local wallet UI plus HTTP and HTTPS wallet endpoints for presentation, issuer metadata, trust lists, status lists, and test registrar responses. issue ... --wallet --template german-pid-sdjwt gives you a ready-to-use PID wallet and adds new credentials into the same wallet context (wallet generate-pid still works but is deprecated), and wallet ca-cert / wallet tls-cert export the trust root or exact HTTPS leaf certificate when a verifier needs them. All of these wallet operations are also available on the server's unauthenticated HTTP API. This lets automated tests manage and drive a hosted or containerized wallet entirely over HTTP.
For day-to-day use, the main commands are:
wallet serveto run the walletissue ... --wallet(with--templateor--pid) to preload credentialswallet instancesto find running wallet servers,wallet instances use <url>to manage one remotely over its REST API, andwallet instances killto stop one (when a server already runs for the same wallet directory, CLI commands route through it automatically). Discovery only sees instances running directly on this system plus the active remote target, so a wallet inside a Docker container shows up afterwallet instances use <url>and clicked credential-offer or presentation links route to it.wallet trust-listto get the verifier trust-list URL or JWTwallet logsto inspect wallet-side OID4VP/OID4VCI interactionswallet ca-certandwallet tls-certto export certificate materialwallet --mode debug|strictand--preferred-format ...to control runtime behaviorwallet serve --haipto hold verifiers and issuers to HAIP 1.0
--mode and --haip are separate switches. --mode strict versus --mode debug decides whether a specification finding stops the flow or is only reported, and both modes report their findings. --haip decides whether the counterparty is held to the HAIP 1.0 profile, and a violation of it is an error in either mode, because asking for HAIP asserts that the counterparty follows it. See HAIP 1.0 enforcement.
When a wallet exposes multiple trust-list profiles, /api/trustlists gives you the available IDs and routes. Use the entry's relative path when you access the wallet through Docker port mappings or similar local indirection. The web UI lists the same trust-list URLs with copy buttons above the certificate downloads.
→ Full documentation: subcommands, flags, endpoints, logs, trust lists, storage, URL scheme registration
→ Public demo hosting: run a shared internet-facing demo with --demo (hardened endpoints, periodic reset, imprint page)
→ Flow diagrams: GitHub-rendered OID4VP / OID4VCI interaction diagrams and parameter checklists
Generate test SD-JWT, JWT, or mDOC credentials for development and testing.
eudi issue sdjwt --pid
eudi issue sdjwt --template employee-card --claims '{"employee_id": "E-42"}'
eudi issue sdjwt --pid --always-disclosed issuing_country,address.country
eudi issue jwt --claims '{"name":"Test","age":30}'
eudi issue mdoc --claims '{"name":"Test"}' --doc-type com.example.test
eudi issue sdjwt | eudi decodeReusable claim sets live in credential templates (templates list|show|save|import|delete). Templates carry the credential type, default claims, and the claims to issue without selective disclosure. They work in the CLI, the HTTP API, and the wallet UI.
→ Full documentation: all flags, round-trip examples → Credential templates: template files, management commands, always disclosed claims
Intercept and debug OID4VP/VCI traffic between a wallet and a verifier/issuer with a live web dashboard.
eudi proxy --target http://localhost:8080Wallet <--> Proxy (:9090) <--> Verifier/Issuer (:8080)
|
Live dashboard (:9091)
→ Full documentation: traffic classification, features, flags
Start a local web UI for decoding and validating credentials in the browser.
eudi serve
eudi serve --port 3000
eudi serve credential.txtOpens a split-pane interface at http://localhost:8080 (default) with auto-decode on paste, format detection, collapsible sections, signature verification, and dark/light theme. Pass a credential as an argument to pre-fill the input on load. Use --imprint-file to serve a legal notice at /imprint when hosting it publicly.
Warning: Credentials are sent to the server for decoding. Run it locally, or see public demo hosting for an internet-facing setup.
Auto-detect and decode credentials (SD-JWT, JWT VC, mDOC), OpenID4VCI/VP requests, and ETSI trust lists.
eudi decode credential.txt
eudi decode 'openid4vp://authorize?...'
eudi decode --screen # QR scan from screen→ Full documentation: auto-detection order, format override, QR scanning, flags
Verify signatures, check expiry, and check revocation status.
eudi validate --key issuer-key.pem credential.txt
eudi validate --trust-list trust-list.jwt credential.txt
eudi validate credential.txt→ Full documentation: flags, trust list explanation
Generate a DCQL (Digital Credentials Query Language) query from a credential's claims. Always outputs JSON.
eudi dcql credential.txtThe wallet evaluates credential_sets constraints when processing DCQL queries, selecting the best matching option from each set.
Example output (SD-JWT):
{
"credentials": [
{
"id": "urn_eudi_pid_1",
"format": "dc+sd-jwt",
"meta": { "vct_values": ["urn:eudi:pid:1"] },
"claims": [
{ "path": ["birth_date"] },
{ "path": ["family_name"] },
{ "path": ["given_name"] }
]
}
]
}| Format | Description |
|---|---|
SD-JWT (dc+sd-jwt) |
Header/payload, disclosures, _sd resolution, key binding JWT. Signature: ES256/384/512, RS256/384/512, PS256 |
JWT VC (jwt_vc_json) |
Plain JWT Verifiable Credentials (W3C JWT VC format). Presented as-is without selective disclosure |
mDOC (mso_mdoc) |
CBOR IssuerSigned & DeviceResponse (hex/base64url), COSE_Sign1 issuerAuth, MSO |
| OpenID4VCI / VP | Credential offers, authorization requests, URI schemes (openid-credential-offer://, haip-vci://, openid4vp://, haip-vp://, eudi-openid4vp://) |
| ETSI Trust Lists | TS 119 602 trust list JWTs with entity names, identifiers, and service types |
See docs/spec-compliance.md for detailed compliance status against OID4VP 1.0, OID4VCI 1.0, the currently implemented HAIP 1.0 subset, SD-JWT (RFC 9901) and SD-JWT VC, mDoc (ISO 18013-5), ETSI trust lists, and Token Status List. For a system-level view of the implemented issuer and verifier interactions, see docs/diagrams/README.md.
| Flag | Description |
|---|---|
--json |
Output as JSON |
--no-color |
Disable colored output |
-v |
Verbose output (x5c chain, device key, digest IDs) |
No EU affiliation: This is an independent open source project. It is not an official repository of the European Commission or the European Union, has no affiliation with them, and is not endorsed by them. "EUDI" is used descriptively (a developer tool for the European Digital Identity ecosystem). For official EUDI Wallet resources see the eu-digital-identity-wallet organization.
Renamed from oid4vc-dev: The old name keeps working for the time being. A binary named oid4vc-dev behaves identically (help and completion adapt to the invoked name), the legacy ~/.oid4vc-dev state directory and OID4VC_DEV_HOME variable are still honored, and the ghcr.io/dominikschlosser/oid4vc-dev image keeps receiving releases.
Apache-2.0