KERIOX is a Rust implementation of the Decentralized Key Management System (DKMS) that under the hood uses the Key Event Receipt Infrastructure (KERI) protocol.
The Human Colossus Foundation has been developing and maintaining KERIOX since 2020.
- For running example infrastructure (Witnesses, etc.), see https://github.com/THCLab/ambient-infrastructure
- Connect to the Infrastructure
- for Command-Line Interface (CLI)-based interaction, use
dkms-bin - for Rust client, see test.
- for FFI bindings (clients for different programming languages), see https://github.com/THCLab/keri-bindings
- for Command-Line Interface (CLI)-based interaction, use
KERIOX is an open-source Rust implementation of the Key Event Receipt Infrastructure (KERI) , a system designed to provide a secure identifier-based trust spanning layer for any stack. The current version of the KERI paper can be found here.
KERI provides the same security and verifiability properties for transactions as a blockchain or distributed ledger can, without the overhead of requiring an absolute global ordering of transactions. Because of this, there is no need for a canonical chain and thus there is no "KERI Chain" or "KERI Network". KERI Identifiers can be generated independently in a self-sovereign and privacy-preserving manner and are secured via a self-certifying post-quantum resistant key management scheme based on blinded pre-rotation, auditable and flexible key events and a distributed conflict resolution algorithm called KAACE.
KERIOX is designed around pluggable abstractions that allow it to run in diverse environments. The EventDatabase trait abstracts storage so that backends can be swapped at compile time; redb is the default (feature-flagged as storage-redb), and an in-memory implementation is available for testing or custom backends. Notification dispatch is also pluggable via NotificationBus, which supports injectable dispatch strategies suitable for serverless environments such as SQS-backed message routing. The KeriRuntime<D> struct bundles the processor, storage, escrows, and notification bus into a single composable unit, enabling thin Lambda handlers or other lightweight entry points.
Following the KERI specification,
an identifier is abandoned by a rotation (rot, or drt for a delegated
identifier) that commits to no next keys (n=[], nt=0); an inception that
does so makes it non-transferable instead. Either way its key event log is
final and no more key events are allowed in it:
- Core refuses every later event (
Error::AbandonedIdentifierorError::NonTransferableIdentifier). For an abandoned identifier it also refuses replies, exchanges and transferable receipts signed by it, an extension beyond the specification: abandoning signals that its controller no longer speaks for it. Queries are still answered, so the owner can collect receipts for the abandonment. A non-transferable identifier keeps speaking for itself. - As the specification requires, an establishment event's key list
kmust not be empty, an integer threshold (kt,nt) may not need more keys than its list has, and a weighted one has exactly one weight per key (KERIpy only checks for at most one). So no next keys meansnt=0. A key event without a single controller signature is dropped, not escrowed, and answered with400. - Witnesses receipt the abandonment but answer
410 Goneto anything after it. - Watchers serve the final log, stop polling the witnesses for it, and answer full-log queries without waiting on them.
- Both serve
GET /identifiers/{aid}/statewithout authentication: the identifier's sn, last event, witnesses, whether it istransferableand, when abandoned,"abandoned": {"sn", "said"}. It is a hint for operators and fresh clients; whoever relies on it verifies the KEL through an authenticated watcher query. Unknown identifiers get404. - Both list
"features": ["abandonment"]onGET /info. Check every witness and watcher before abandoning an identifier: an older one would still receipt or serve events that follow the abandonment. - The controller builds the event (
Identifier::abandon,abandon_group), and the SDK wraps it (Identity::abandon,KeriStore::abandon,operations::abandon,Keri::remove_identity). The SDK refuses to verify anything signed with an abandoned identifier's final keys.
keriox reads KERI 1.0 and KERI 2.0 messages, and writes KERI 1.0 unless told otherwise, so a node or client can be upgraded before its peers.
Versions are the KERI protocol versions of the version string, in the dotted-decimal notation of the CESR specification:
| Version | Version string | Attachments |
|---|---|---|
1.0 |
KERI10JSON…_ (vv = 10) |
CESR 1.0 code table (-_AAABAA) |
2.0 |
KERICAACAAJSON…. (protocol CAA = 2.0, genus CAA = 2.0) |
CESR genus 2.0 code table (-_AAACAA) |
Any minor version of protocol 1 or 2 is read by that major's rules; other majors, and 2.x messages on a CESR genus other than 2.x, are refused. These are protocol versions, not the versions of the specification documents.
- KERI 2.0 key events, receipts,
qry,rpyandexnare written with a 2.0 version string and CESR 2.0 attachments. Routed messages (qry,rpy) name their sender ini, andexncarriesi,ri,xandp; the sender must be the signer. - What a node writes in answer to something follows what it answers: a receipt follows the event's version, a key state notice the query's.
- The controller writes its own messages in
ControllerConfig::keri_version, the SDK inKeriStore::with_keri_version, and witnesses and watchers inkeri_versionin their configuration file ("1.0"or"2.0";WITNESS_KERI_VERSIONandWATCHER_KERI_VERSIONin the environment). - Switch a writer to 2.0 only once everything that reads its messages runs a
keriox that reads 2.0. Older releases refuse KERI 2.0 messages. Witnesses
and watchers that read 2.0 list
"keri2"onGET /info(supports_keri2in the SDK). - Witnesses and watchers report what they write on
GET /infoas"keri": {"protocol": "2.0", "cesr_genus": "2.0"}(server_keri_versionin the SDK). Operators switch the servers first; a client that follows them asksagreed_keri_versionwith the witnesses and watchers it uses, which answers 2.0 only once every one of them writes 2.0 and 1.0 if any writes 1.0 or cannot be reached. - keriox clients name their release in every request (
User-Agent: keriox/<release>), and witnesses and watchers count requests by it (keri_witness_requests_by_client_total,keri_watcher_requests_by_client_total), so operators can tell when old clients are gone. - A witness or watcher with
min_client_versionset (e.g."0.20.0";WITNESS_MIN_CLIENT_VERSION,WATCHER_MIN_CLIENT_VERSION) answers426 Upgrade Requiredto everyPOSTfrom an older keriox client or from a client that names no release.GETrequests (/info, OOBIs) stay open. - TEL events stay KERI 1.0: the KERI specification defines no TEL messages, and registries in KERI 2.0 come with ACDC 2.0.
- An identifier incepted in KERI 1.0 moves to a new one with
KeriStore::reincept: the new identifier is incepted (in the store's version, withKeriStore::same_witnessesfor the same witnesses) and the old one abandoned with a final rotation anchoring the new inception. The SDK follows that seal when it imports a contact (Keri::import_contact_fromreturns the new identifier,Keri::successor_ofrecords the move).
EUPL 1.2
We have distilled the most crucial license specifics to make your adoption seamless: see here for details.
KERIOX implementation is in progress and ongoing. We support all the KERI protocol's significant features and provide the second most advanced implementation right after the keripy reference implementation.
We furthermore support bindings to NodeJS and Dart. See our keri-bindings repository.
This repository provides the implementation of the KERI protocol. keriox_core brings the core protocol features that are further consumed by the following concepts:
- Witness: the KERI Witness
- Watcher: the KERI Watcher
- Controller: the client for accessing the infrastructure
- SDK: high-level SDK providing
KeriRuntimeandControllerfor KERI+TEL operations - Key Provider: pluggable cryptographic signing abstraction (software, file-encrypted, OS keychain, host-callback for mobile)
KERIOX supports mobile targets (Android API 30+ and iOS) via:
mobilefeature flag onkeri-sdk— enableshost+softwarekey providers, usesrustlsfor TLSHostCallbackKeyProviderinkeri_keyprovider— delegates all signing to host platform callbacks (Android Keystore / iOS Keychain). Private keys never enter Rust memory.rustlsTLS backend — pure Rust, no OpenSSL dependencyredbstorage — pure Rust, no mmap, works on both Android and iOS app sandboxes
See the mobile SDK CI workflow for cross-compilation verification.