Axle is a headless, from-scratch SDK for building EU Digital Identity (EUDI) wallets — issuing, storing, and presenting digital credentials under eIDAS 2.0 (ARF · HAIP). Embed it in your own app and you own the UI; the SDK owns the protocols, cryptography, and trust. The reference wallet built with it is Axle Wallet.
It ships as two native implementations — Kotlin and Swift — that share only an API contract, with a pure core that builds and tests on plain Linux. New to EUDI? Start with Concepts.
- Headless (UI-less). A B2B library your app embeds — no screens, no opinions about your UX. You wire it to your product; you keep full control of the user experience.
- Dependency-injected, no framework. The core is pure and platform-agnostic. Every platform capability — secure-key hardware, storage, HTTP, Bluetooth/NFC — is a small port you supply an adapter for (plain constructor injection). Swap any piece; test the core on Linux.
- Batteries included, not required. Production Android adapters (
android/) and iOS adapters (ios/) implement every port — use them as-is or as a reference for your own. Full demo wallets ship for both: Android (Jetpack Compose) indemo/, iOS (SwiftUI, Axle Wallet) indemo-ios/. - Full scratch, standards-first. The EU reference wallet is an interop target, not a dependency. Every layer — CBOR/COSE, SD-JWT VC, ISO mdoc, OpenID4VCI/VP, X.509 trust, Token Status List — is implemented in-house against the source specifications (see SPEC-MATRIX.md and the specifications reference).
Everything is reached through one assembled Wallet facade — credentials, issuance, presentation,
proximity, reader, transactions.
A live end-to-end EUDI ecosystem you can point the wallet at — issue a PID, present it to a verifier, all against real trusted-list-anchored trust:
| Service | URL | Role |
|---|---|---|
| PID Issuer | https://pid-issuer.vercel.app/ | Issues PID (SD-JWT VC & mdoc) and mDL — OpenID4VCI |
| Verifier | https://eudi-verifier.vercel.app/ | Requests & verifies presentations — OpenID4VP + DC API |
| RP Registrar | https://demo-registrar.vercel.app/ | Registers relying parties; issues WRPAC/WRPRC (ETSI TS 119 475) |
| Trusted List | https://trusted-list.vercel.app/ | Scheme Operator — JAdES-signed trust lists (ETSI TS 119 602) |
Both demo wallets are pre-wired to these — Android in demo/ (cd demo && ./gradlew :app:assembleDebug; see
demo/RELEASE.md for signed AAB + Play distribution) and iOS in demo-ios/ (open
demo-ios/AxleWallet/AxleWallet.xcodeproj in Xcode 26+ and run on an iOS 26 device).
| Path | What |
|---|---|
kotlin/ |
Kotlin SDK (pure JVM, Gradle multi-module) — the reference core |
swift/ |
Swift package mirroring the Kotlin modules 1:1 (no Apple-framework imports; Linux-buildable) |
android/ |
Android platform-adapter presets (com.hopae.eudi.android:core/proximity/dcapi/attestation) — Keystore/StrongBox SecureArea, file storage, OkHttp, BLE + NFC transports, DC API glue, Play Integrity attestation |
demo/ |
Android wallet app (Compose) consuming kotlin/ + android/ — the reference assembly; release guide in demo/RELEASE.md |
ios/ |
iOS platform-adapter package (SwiftPM: AppleCore / AppleProximity / AppleDcApi / AppleAttestation) — Secure Enclave SecureArea, Keychain storage, URLSession, ISO 18013-5 BLE transports, Digital Credentials API provider extension, App Attest attestation |
demo-ios/ |
iOS wallet app (SwiftUI, Axle Wallet) consuming swift/ + ios/ — the reference iOS assembly |
docs/ |
Docusaurus developer docs (English + 한국어) — see below |
ecosystem/ |
The reference sandbox services (issuer, verifier, trusted list) — see below |
wallet-provider/ |
NestJS Wallet Provider backend — Wallet Unit Attestation (WUA) + key attestation + Play Integrity |
vectors/ |
Shared golden test vectors consumed by both test suites |
Core rule: everything under kotlin/ and swift/ builds and tests on plain Linux. Platform features
(secure hardware, storage, BLE, DC API) live strictly behind ports.
The core is pure; the host injects capabilities. Assembling a wallet is: pick adapters, set config, call
Wallet.create(config, ports).
your app (UI) ← you build this
│
┌────▼─────────────────────────┐
│ Wallet (facade) │ credentials · issuance · presentation
│ │ proximity · reader · transactions
└────┬─────────────────────────┘
pure core │ OpenID4VCI/VP · SD-JWT VC · ISO mdoc · X.509 trust · CBOR/COSE
┌────▼─────────────────────────┐
│ Ports (SPI) │ SecureArea · StorageDriver · HttpTransport
│ │ ProximityTransport · WalletAttestationProvider
└────┬─────────────────────────┘
adapters │ ← android/ or ios/ presets (or your own)
android: Keystore · files · OkHttp · BLE/NFC · Play Integrity
ios: Secure Enclave · Keychain · URLSession · BLE · DC API ext · App Attest
The android/ and ios/ adapters are ready-made presets;
demo/app/.../DemoWallet.kt and
demo-ios/.../DemoWallet.swift are the canonical
"assemble from adapters + config" examples. Full walkthrough in
docs/ → Getting Started and Architecture.
// Assemble once from platform adapters + config (Android preset shown).
val wallet = Wallet.create(
config = WalletConfig(trust = TrustConfig(issuerAnchorsDer, readerAnchorsDer, registrarAnchorsDer)),
ports = WalletPorts(
secureAreas = listOf(AndroidKeystoreSecureArea()), // your SecureArea adapter
storage = FileStorageDriver(dir), // your StorageDriver adapter
http = OkHttpTransport(), // your HttpTransport adapter
),
)
wallet.issuance.resolveOffer(offerUri) // OpenID4VCI — resolve → start session → store credential
wallet.presentation.start(requestUri) // OpenID4VP — resolve → select credentials → submit
wallet.proximity.present(transport) // ISO 18013-5 — present in person over BLE/NFC
wallet.credentials.list() // stored credentials (+ DCQL match, status)
wallet.transactions.history() // audit logSwap AndroidKeystoreSecureArea / FileStorageDriver / OkHttpTransport for your own adapters (or the
testkit software ones) to run the same core anywhere. Full walkthrough — Kotlin + Swift — in
Getting Started.
Each concern is a separate module (Kotlin name / Swift target), tested in isolation.
| Module | Purpose | Key types |
|---|---|---|
cbor / CborCose |
CBOR (RFC 8949) + COSE primitives | Cbor, CborEncoder, cose/CoseSign1, CoseKey, EcPublicKey, Ecdsa, Der |
sdjwt / SdJwt |
SD-JWT VC + JOSE (JWS/JWE/JWK) | SdJwt, SdJwtIssuer/Holder/Verifier, SdJwtVcVerifier, Jws, Jwe, SecureAreaJwsSigner |
mdoc / MDoc |
ISO 18013-5 mdoc / mDL | IssuerSigned/MobileSecurityObject, DeviceRequest, DeviceResponse, MdocPresenter, MdocReader, ReaderAuthSigner, Hpke |
openid4vci / OpenID4VCI |
OpenID4VCI issuance | Openid4VciClient, CredentialOffer, CredentialIssuerMetadata, DpopProver, KeyAttestationSource |
openid4vp / OpenID4VP |
OpenID4VP presentation + DCQL | Openid4VpClient, DcqlQuery, DcqlMatchResult, TransactionData, RegistrationInfo |
trust / Trust |
X.509 PKIX trust + WRPRC | X509ChainValidator, X5cMdocIssuerTrust/ReaderTrust, X509RequestVerifier, WRPRCVerifier, RegistrarApiClient |
statuslist / StatusList |
IETF Token Status List (revocation) | StatusListClient, CwtStatusListClient, CredentialStatus |
credential-store / CredentialStore |
Persisted credential store | CredentialStore, CredentialEnvelope, EnvelopeCodec (deterministic CBOR) |
proximity / Proximity |
ISO 18013-5 engagement + NFC handover | DeviceEngagement, ProximitySessionTranscript, SessionMessages, MdocNfcEngagement, Tnep |
txlog / TransactionLog |
Audit log (ARF/GDPR) | TransactionLog, TransactionLogStore (port), TransactionLogEntry, RelyingParty |
wallet-api / WalletAPI |
Port SPI + shared types | spi/ ports (SecureArea, StorageDriver, HttpTransport, ProximityTransport, WalletAttestationProvider), Types, SecureAreaCoseSigner |
wallet / Wallet |
The facade + composition root | Wallet, WalletConfig, WalletPorts, CredentialsService, IssuanceService, PresentationService, ProximityService, ProximityReaderService |
testkit / WalletTestKit |
Test doubles + adapter contracts | SoftwareSecureArea, InMemoryStorageDriver, SecureAreaContract, StorageDriverContract |
# Kotlin — pure JVM
cd kotlin && ./gradlew test
# Swift — on this Linux host, point clang at a GCC dir that has libstdc++-dev:
cd swift && swift test \
-Xcc --gcc-install-dir=/usr/lib/gcc/x86_64-linux-gnu/11 \
-Xcxx --gcc-install-dir=/usr/lib/gcc/x86_64-linux-gnu/11 \
-Xlinker -L/usr/lib/gcc/x86_64-linux-gnu/11
# (CI images `swift:6.x` need no extra flags.)
# Android demo → APK
cd demo && ./gradlew :app:assembleDebug # app/build/outputs/apk/debug/app-debug.apkFull developer documentation (guides + API reference, Kotlin + Swift examples, English + 한국어) lives in
docs/ as a Docusaurus site:
- Concepts — EUDI/eIDAS vocabulary for developers new to the domain
- Architecture · Getting Started — assemble the SDK from ports & adapters
- Guides — Issuance · Presentation · Digital Credentials API — Android · Digital Credentials API — iOS · Proximity · Trust & Audit
- Platform adapters — Android adapter modules · iOS adapter modules — and the demo assemblies: Android · iOS
- Reference — Facade · Ports · Specifications
cd docs && npm install
npm start # dev server (English)
npm start -- --locale ko # dev server (한국어)
npm run build # static build of both localesA complete EUDI sandbox — every counterpart the wallet talks to — lives in this repo (except the Registrar, a separate project at github.com/hopae-official/registrar; sandbox at demo-registrar.vercel.app):
| Service | Path | Role |
|---|---|---|
| Issuer backend | ecosystem/issuer-be |
OpenID4VCI 1.0 + HAIP — issues PID (SD-JWT VC & mdoc) and mDL |
| Issuer frontend | ecosystem/issuer-fe |
Issuance-consent screen (authorization-code flow) |
| Verifier backend | ecosystem/verifier-be |
OpenID4VP 1.0 + HAIP — builds & verifies presentations (QR + DC API) |
| Verifier frontend | ecosystem/verifier-fe |
Relying-party UI (request QR / DC API, show result) |
| Trusted List | ecosystem/trusted-list |
Scheme Operator — JAdES-signed trust lists + ecosystem CAs (KEYS.md) |
| Wallet Provider | wallet-provider |
Wallet Unit Attestation (WUA) + key attestation + Play Integrity (PLAY-INTEGRITY.md) |
See ecosystem/README.md for the trust model overview.
Reference / sandbox implementation for eIDAS 2.0 EUDI interoperability. The Kotlin and Swift cores, the Android adapters + demo, and the iOS adapters + demo (Axle Wallet) are functional and device-tested. CI runs both core suites on Linux; the Apple / Android adapter layers build under Xcode / Gradle. The hosted services above are a non-production sandbox.
Licensed under the Apache License 2.0 — see LICENSE.