This is an experimental implementation of KERI protocol that aims to be compatible with the vLEI ecosystem
The goal of this project is to build a "validator" component that allows to monitor KERI AID
s and maintain up to date key state.
It currently consists of two parts:
Kerilex
- low-level functionality to parse and validate KELs, plus some ability to create and sign certain type of events.Watcher
- building blocks that can be used to create a continuously running validator.
Alpha - expect continuous changes as I explore various ways to deal with asynchronous nature of KERI and its infrastructure components.
The Modules and their "shape" are changing as I try to find the optimal decomposition. Documentation is sparse and typespecs
are not always defined.
- parses KERI 1.0 KELs -
icp
,dip
,rot
,drt
andixn
messages along with the typical CESR attachments - allows creation of
icp
,rpy
( endpoint),qry
messages along with signing. - Generates signers based on
CESR
salt, which allows to have signers identical to those generated bykeripy
'skli
. Watcher
has asynchronous qry/response component (Watcher.WitnessClient
) that can send and process variousqry
messages and collects indexed response from amailbox
Watcher
also hasOOBI
processor that will take the output of aKerilex.KELparser.parse
and, if all checks run though, will fillmnesia
based tables with the processed events and output a%KeyStateCache{}
containing calculated key states ofAID
s that were included in theOOBI
response.
- Elixir - 1.17 and OTP27
- dev version of
libsodium
c
toolchain to compilelibsodium
nif- Rust to compile
blake3
- setup proper variables pointing to the installed libs
- E.g. for
mac
, if usingbrew
(works onm1
/m3
)LDFLAGS=-L/opt/homebrew/include C_INCLUDE_PATH=/opt/homebrew/include CPLUS_INCLUDE_PATH=/opt/homebrew/include LIBRARY_PATH=/opt/homebrew/lib
- E.g. for
from the project's root directory get and compile dependencies.
mix deps.get
mix deps.compile
You can either use the
iex
to paly with the API or uselivebook
to see how theWatcher
functionality works.
mkdir -p tmp/db
iex -S mix
:mnesia.stop
:mnesia.create_schema([node()])
:mnesia.start
Watcher.KeyStateStore.init_tables
alias Watcher.{OOBI.LogsProcessor, EventEscrow}
gleif_geda_kel = File.read!("test/data/gleif-kel-july-23-24") |> Kerilex.KELParser.parse
gleif_geda_kel |> LogsProcessor.process_kel(EventEscrow.new())
output
18:23:26.910 [debug] [type: "icp", msg: "added event", result: "updated KEL", pre: "EDP1vHcw_wc4M__Fj53-cJaBnZZASd-aMTaSyWEQ-PC2", sn: 0]
18:23:26.912 [debug] [type: "rot", msg: "added event", result: "updated KEL", pre: "EDP1vHcw_wc4M__Fj53-cJaBnZZASd-aMTaSyWEQ-PC2", sn: 1]
18:23:26.913 [debug] [type: "rot", msg: "added event", result: "updated KEL", pre: "EDP1vHcw_wc4M__Fj53-cJaBnZZASd-aMTaSyWEQ-PC2", sn: 2]
18:23:26.914 [debug] [type: "dip", msg: "added event", result: "updated KEL", pre: "EINmHd5g7iV-UldkkkKyBIH052bIyxZNBn9pq-zNrYoS", sn: 0]
18:23:26.915 [debug] [type: "ixn", msg: "added event", result: "updated KEL", pre: "EINmHd5g7iV-UldkkkKyBIH052bIyxZNBn9pq-zNrYoS", sn: 1]
18:23:26.915 [debug] [type: "ixn", msg: "added event", result: "updated KEL", pre: "EINmHd5g7iV-UldkkkKyBIH052bIyxZNBn9pq-zNrYoS", sn: 2]
18:23:26.916 [debug] [type: "ixn", msg: "added event", result: "updated KEL", pre: "EINmHd5g7iV-UldkkkKyBIH052bIyxZNBn9pq-zNrYoS", sn: 3]
18:23:26.916 [debug] [type: "ixn", msg: "added event", result: "updated KEL", pre: "EINmHd5g7iV-UldkkkKyBIH052bIyxZNBn9pq-zNrYoS", sn: 4]
18:23:26.916 [debug] [type: "ixn", msg: "added event", result: "updated KEL", pre: "EINmHd5g7iV-UldkkkKyBIH052bIyxZNBn9pq-zNrYoS", sn: 5]
18:23:26.917 [debug] [type: "ixn", msg: "added event", result: "updated KEL", pre: "EINmHd5g7iV-UldkkkKyBIH052bIyxZNBn9pq-zNrYoS", sn: 6]
18:23:26.917 [debug] [type: "ixn", msg: "added event", result: "updated KEL", pre: "EINmHd5g7iV-UldkkkKyBIH052bIyxZNBn9pq-zNrYoS", sn: 7]
18:23:26.918 [debug] [type: "ixn", msg: "added event", result: "updated KEL", pre: "EINmHd5g7iV-UldkkkKyBIH052bIyxZNBn9pq-zNrYoS", sn: 8]
18:23:26.918 [debug] [type: "rpy", msg: "added event", result: "added witness", url: "http://65.21.253.212:5623/"]
18:23:26.918 [debug] [type: "rpy", msg: "added event", result: "added witness", url: "http://8.210.213.186:5623/"]
18:23:26.919 [debug] [type: "rpy", msg: "added event", result: "added witness", url: "http://51.79.54.121:5623/"]
18:23:26.919 [debug] [type: "rpy", msg: "added event", result: "added witness", url: "http://102.37.159.99:5623/"]
18:23:26.919 [debug] [type: "rpy", msg: "added event", result: "added witness", url: "http://54.233.109.129:5623/"]
18:23:26.919 [debug] [msg: "finished processing KEL messages", kel_length: 17]
{:ok, %Watcher.EventEscrow{store: %{}},
%Watcher.KeyStateCache{
cache: %{
"EDP1vHcw_wc4M__Fj53-cJaBnZZASd-aMTaSyWEQ-PC2" => %Watcher.KeyState{
pe: "ECphNWm1_jZOupeKh6C7TlBi81BlERqbnMpyqpnS4CJY",
te: "rot",
se: 2,
de: "EHsL1ldIafZC-M9-3RgLQB3m2_2F0aYIiNBGnTVoFDH2",
fs: "2024-10-25T16:23:26.910544Z",
k: ["DNLdWqTBKOhDO8YfE5uIaTvN-n_Jv20-5ZwK609BvG0b",
"DL68G7IW4zT2ryLRDziYiRyvwIDyq9xssVuZ3u6w-30Y",
"DH63RGGv_r8pQ5Di9MVblcofkBm0O8r6SUY0cqNAYqne"],
kt: %Kerilex.Crypto.WeightedKeyThreshold{
size: 3,
weights: [Ratio.new(1, 3), Ratio.new(1, 3), Ratio.new(1, 3)],
sum: Ratio.new(1, 1),
ind_ranges: [0..2]
},
n: ["EPHYDUnxDH7xcAim3aYvS9bvh7JmdBDKc__w2_McXr6I",
"EHgOexUh8AvN7rXblsSr6MJE5Gn1HPq5Mv9KFpCpllKN",
"ECH4pTtUI653ykKb_capPBkKF3RvBZRzyb5dPfuJCfOf",
"ELXXiPwoaWOVOTLMOAmg4IKkjFHFs3q2hsL9tHvuuC2D",
"EAcNrjXFeGay9qqMj96FIiDdXqdWjX17QXzdJvq58Zco",
"EF1IPGq_uF3FmywFdIQXSO4jy0QhtzREVMlPQ8PEy_As",
"EHGlZciB0cZ627-MJrQxyw5niNzN1nKnNMDaJO7sCEvF"],
nt: %Kerilex.Crypto.WeightedKeyThreshold{
size: 7,
weights: [Ratio.new(1, 3), Ratio.new(1, 3), Ratio.new(1, 3),
Ratio.new(1, 3), Ratio.new(1, 3), Ratio.new(1, 3), Ratio.new(1, 3)],
sum: Ratio.new(7, 3),
ind_ranges: [0..6]
},
b: ["BNfDO63ZpGc3xiFb0-jIOUnbr_bA-ixMva5cZb3s4BHB",
"BDwydI_FJJ-tvAtCl1tIu_VQqYTI3Q0JyHDhO1v2hZBt",
"BGYJwPAzjyJgsipO7GY9ZsBTeoUJrdzjI2w_5N-Nl6gG",
"BM4Ef3zlUzIAIx-VC8mXziIbtj-ZltM8Aor6TZzmTldj",
"BLo6wQR73-eH5v90at_Wt8Ep_0xfz05qBjM3_B1UtKbC"],
bt: 4,
c: ["EO"],
di: false,
last_event: {"rot", 2, "EHsL1ldIafZC-M9-3RgLQB3m2_2F0aYIiNBGnTVoFDH2"}
},
"EINmHd5g7iV-UldkkkKyBIH052bIyxZNBn9pq-zNrYoS" => %Watcher.KeyState{
pe: nil,
te: "dip",
se: 0,
de: "EINmHd5g7iV-UldkkkKyBIH052bIyxZNBn9pq-zNrYoS",
fs: "2024-10-25T16:23:26.914180Z",
k: ["DEO7QT90CzPeCubjcAgDlYI-yudt0c_4HeAb1_RbrGiF",
"DKu6Q_Qth7x-pztt11qXDr42B9aUjkp_v9Rq8-xXcQjF",
"DEiPSxcuILZFxJscr_Lt8fuiidhB_HrqKxoCbZr9tQfp",
"DIqrjqqwArsSHIX3n510DnSrYL9ULbYOpi14hEencBSC",
"DAB9Tl0T8-638H65GMFj2G7CAr4CoExZ5xH-U1ADldFP"],
kt: %Kerilex.Crypto.WeightedKeyThreshold{
size: 5,
weights: [Ratio.new(1, 2), Ratio.new(1, 2), Ratio.new(1, 2),
Ratio.new(1, 2), Ratio.new(1, 2)],
sum: Ratio.new(5, 2),
ind_ranges: [0..4]
},
n: ["EObLskWwczY3R-ALRPWiyyThtraelnbh6MMeJ_WcR3Gd",
"ENoI2e5f59xEF83joX__915Va-OIE7480wWyh2-8bJk7",
"EElSAVDf2vU8aoxN50eSMNm6MrQ-Hv_2xOWC02tFrS3M",
"EHX0Re-hExzl7mvLuRwQHEew-8oPOQh4rqXJNHBo9EyW",
"EBGeYe1_ZgN_ly0qVY-Y1FayZkNA5Yq9LTujrh2ylKbm"],
nt: %Kerilex.Crypto.WeightedKeyThreshold{
size: 5,
weights: [Ratio.new(1, 2), Ratio.new(1, 2), Ratio.new(1, 2),
Ratio.new(1, 2), Ratio.new(1, 2)],
sum: Ratio.new(5, 2),
ind_ranges: [0..4]
},
b: ["BDkq35LUU63xnFmfhljYYRY0ymkCg7goyeCxN30tsvmS",
"BLmvLSt1mDShWS67aJNP4gBVBhtOc3YEu8SytqVSsyfw",
"BHxz8CDS_mNxAhAxQe1qxdEIzS625HoYgEMgqjZH_g2X",
"BGYJwPAzjyJgsipO7GY9ZsBTeoUJrdzjI2w_5N-Nl6gG",
"BFl6k3UznzmEVuMpBOtUUiR2RO2NZkR3mKrZkNRaZedo"],
bt: 4,
c: [],
di: "EDP1vHcw_wc4M__Fj53-cJaBnZZASd-aMTaSyWEQ-PC2",
last_event: {"ixn", 8, "EDxDCjQoH82EgDEcSAU1SD__VKoebRUgr95nFweJxMgu"}
}
},
recoveries: []
}, 17}
-
Install
Elixir
as outlined above -
Install livebook
If you are not familiar with it,
livebook
is analogous to the Python's Jupiter Notebook -
From the root directory of the
Kerilex
repo startiex
as follows:iex --name kerilex@127.0.0.1 --cookie demo -S mix
-
Start
livebook
-
Connect
livebook
to the running node usingname
andcookie
as parameters. -
open
experimental/demo/watcher.livemd
file.