KERI spec-compliance: make icp/rot events round-trip cleanly through keripy / keria / kerigo
Problem
Auths emits KERI-shaped events under refs/auths/registry at v1/identities/<shard>/<prefix>/events/<seq>.json, but they have several deviations from the KERI IETF draft / Smith whitepaper that prevent strict spec-conformant verifiers (keripy, keria, kerigo) from accepting them. None of these are silent-correctness hazards inside auths' own validator — they're interop blockers.
After this issue closes, an auths-emitted icp or rot should serialize/deserialize through keripy.kering.Serder (or equivalent) without modification, with the same SAID computed by both implementations.
Current state
Reference inception event from bash launch-repo/checks/cli/check_e2e_identity_lifecycle.sh:
{
"v": "KERI10JSON00012f_",
"t": "icp",
"d": "EZRCNIzUCUvT1rtLf59o26ZTZPKJPgGX_pJHCJ9h-FRw",
"i": "EZRCNIzUCUvT1rtLf59o26ZTZPKJPgGX_pJHCJ9h-FRw",
"s": "0",
"kt": "1",
"k": ["1AAIA6CVG04Gvoxo7w1BoZBkWUoxt7jW0jslg7zyQ0jcA_wR"],
"nt": "1",
"n": ["EIygB9e6mf-P6QMSw_29sozWVXqWo3crcUKsnwp7XdSI"],
"bt": "0",
"b": [],
"c": [],
"a": [],
"x": "91ngCQXR…04ac11"
}
Four concrete deviations:
1. Signature lives inside the event body as x
KERI signatures are externalized as CESR attachments appended after the event body (see keripy.coring indexed-signature codes -A##, -B## for transferable / non-transferable). Auths puts the issuer's signature in a JSON field named x. A strict verifier will either:
- Reject the unknown
x field, or
- Compute a different SAID than auths because
x participates in the digest (the SAID is computed over the canonical event with d placeholder-filled).
The project already documents the same rule for receipts at crates/auths-keri/src/witness/receipt.rs:1-12:
"Per the spec, the receipt body contains only [v, t, d, i, s]. Signatures are externalized (not in the body)."
This is also flagged for receipts in crates/auths-keri/docs/epics.md:1683. The fix needs to be applied workspace-wide for KEL events.
2. KeriSequence width is u64, spec says u128
Internal type at crates/auths-keri/src/events.rs (search for KeriSequence::new). Spec mandates u128 because some KERI use cases (e.g. high-throughput witness chains) produce sequence counts that don't fit in u64. Documented in crates/auths-keri/docs/epics.md as a known follow-up. Not visible in JSON below 2⁶⁴ but a strictly-typed verifier expecting u128 diverges.
3. Sequence-number serialization may be decimal, not hex
Spec specifies s is a hex string. Inception's s: "0" and rotation's s: "1" look identical in hex and decimal — the first observable case is sequence 16 (should be "10", not "16"). Currently unverified. Search for KeriSequence::value/Display impl in crates/auths-keri/src/events.rs.
4. drt (delegated rotation) validation returns "not yet implemented"
crates/auths-keri/src/validate.rs:221-224:
Event::Drt(_) => {
return Err(ValidationError::Serialization(
"delegated rotation (drt) validation not yet implemented".to_string(),
));
}
A dip (delegated inception) event type is also defined in crates/auths-keri/docs/epics.md (Epic 11) but not implemented. Without drt, no delegated identifier in auths can ever rotate — silent dead-end. The validator must accept and verify a well-formed drt (delegator seal cross-check, per KERI spec §11).
Required changes
Phase 1 — externalize the signature
Phase 2 — sequence width and serialization
Phase 3 — drt validation
Phase 4 — interop conformance test
Acceptance criteria
rg '"x":' crates/auths-keri/src crates/auths-id/src returns zero hits in event-body construction code.
KeriSequence is u128; cargo expand shows no u64 in its definition.
- Sequence 16 round-trips as
"s": "10" (hex).
cargo test -p auths-keri --lib validate_delegated_rotation passes.
- The keripy interop test passes in CI.
crates/auths-keri/docs/spec_compliance_audit.md updated: every previously-flagged item moved from "open" to "resolved" or has a follow-up issue linked.
Out of scope
- Multi-key / weighted-threshold inception (
kt: ["1/2", "1/2", "1/2"]). Spec-legal already; using scalar kt: "1" is also spec-legal. Whether to expose multi-key controllers at the CLI is a separate product question (covered by the kels comparison work in launch-repo/collab_bordumb_rotation.md).
ixn event emission for device link/unlink anchoring. That's the sister issue — see keri-ixn-anchored-attestations.md. Note: this issue's drt work uses ixn events (delegator's KEL must contain a seal), but only on the read path; emitting them is a different scope.
- CESR binary (terse) domain. This issue covers CESR text-domain attachments only. Binary domain is a future optimization.
- TEL (Transaction Event Log) registries / ACDC. Separate epic — useful for credential issuance, not required for KEL spec compliance.
References
KERI spec-compliance: make
icp/rotevents round-trip cleanly through keripy / keria / kerigoProblem
Auths emits KERI-shaped events under
refs/auths/registryatv1/identities/<shard>/<prefix>/events/<seq>.json, but they have several deviations from the KERI IETF draft / Smith whitepaper that prevent strict spec-conformant verifiers (keripy,keria,kerigo) from accepting them. None of these are silent-correctness hazards inside auths' own validator — they're interop blockers.After this issue closes, an auths-emitted
icporrotshould serialize/deserialize throughkeripy.kering.Serder(or equivalent) without modification, with the same SAID computed by both implementations.Current state
Reference inception event from
bash launch-repo/checks/cli/check_e2e_identity_lifecycle.sh:{ "v": "KERI10JSON00012f_", "t": "icp", "d": "EZRCNIzUCUvT1rtLf59o26ZTZPKJPgGX_pJHCJ9h-FRw", "i": "EZRCNIzUCUvT1rtLf59o26ZTZPKJPgGX_pJHCJ9h-FRw", "s": "0", "kt": "1", "k": ["1AAIA6CVG04Gvoxo7w1BoZBkWUoxt7jW0jslg7zyQ0jcA_wR"], "nt": "1", "n": ["EIygB9e6mf-P6QMSw_29sozWVXqWo3crcUKsnwp7XdSI"], "bt": "0", "b": [], "c": [], "a": [], "x": "91ngCQXR…04ac11" }Four concrete deviations:
1. Signature lives inside the event body as
xKERI signatures are externalized as CESR attachments appended after the event body (see
keripy.coringindexed-signature codes-A##,-B##for transferable / non-transferable). Auths puts the issuer's signature in a JSON field namedx. A strict verifier will either:xfield, orxparticipates in the digest (the SAID is computed over the canonical event withdplaceholder-filled).The project already documents the same rule for receipts at
crates/auths-keri/src/witness/receipt.rs:1-12:This is also flagged for receipts in
crates/auths-keri/docs/epics.md:1683. The fix needs to be applied workspace-wide for KEL events.2.
KeriSequencewidth isu64, spec saysu128Internal type at
crates/auths-keri/src/events.rs(search forKeriSequence::new). Spec mandatesu128because some KERI use cases (e.g. high-throughput witness chains) produce sequence counts that don't fit inu64. Documented incrates/auths-keri/docs/epics.mdas a known follow-up. Not visible in JSON below2⁶⁴but a strictly-typed verifier expectingu128diverges.3. Sequence-number serialization may be decimal, not hex
Spec specifies
sis a hex string. Inception'ss: "0"and rotation'ss: "1"look identical in hex and decimal — the first observable case is sequence 16 (should be"10", not"16"). Currently unverified. Search forKeriSequence::value/Displayimpl incrates/auths-keri/src/events.rs.4.
drt(delegated rotation) validation returns "not yet implemented"crates/auths-keri/src/validate.rs:221-224:A
dip(delegated inception) event type is also defined incrates/auths-keri/docs/epics.md(Epic 11) but not implemented. Withoutdrt, no delegated identifier in auths can ever rotate — silent dead-end. The validator must accept and verify a well-formeddrt(delegator seal cross-check, per KERI spec §11).Required changes
Phase 1 — externalize the signature
-A) for single-key inception/rotation; multi-sig indexed group (-A##) when multi-key thresholds are introduced.SignedEvent { event_bytes: Vec<u8>, attachments: Vec<u8> }wrapper inauths-keriand use it everywhere the current(Event, signature)tuple is passed.x); verify this matches whatkeripyproduces for an identical event.validate_event_saidandverify_event_signature(or equivalent incrates/auths-keri/src/validate.rs) to read the externalized attachment.v1/identities/<shard>/<prefix>/events/<seq>.json+events/<seq>.attachments.cesr(or one combined file).crates/auths-keri/src/witness/receipt.rssimilarly — receipts already have the doc comment saying signatures should be externalized; the field shape needs to follow.Phase 2 — sequence width and serialization
KeriSequencetou128. Audit all callers for casts (as u64,try_into::<u64>, etc.); none should remain.Display/Serializeemits hex strings ("a"for sequence 10,"10"for sequence 16). Add a serialization test that asserts hex output for at least sequences 0, 9, 10, 16, 255, 256,u64::MAX + 1.Phase 3 —
drtvalidationvalidate_delegated_rotation(drt, expected_seq, &mut state)per KERI spec §11. Required checks:drt.di(delegator AID) is a known transferable AIDixnevent with a seal whoseiisdrt.iand whosedisdrt.d(cross-KEL seal check; this is the load-bearing constraint)trait DelegatorKelLookup { fn find_seal(delegator_aid: &Prefix, seal: &Said) -> Option<KeriSequence>; }so callers can plug in their KEL store.dip(delegated inception) parallel — same cross-KEL seal pattern.Phase 4 — interop conformance test
icp+rotpair via authspython3 -c "from keri.core import serdering; …") — or, if keripy is too heavy, a fixture file generated by keripy and checked into the repokering.Serder.kvers == "KERI10"Acceptance criteria
rg '"x":' crates/auths-keri/src crates/auths-id/srcreturns zero hits in event-body construction code.KeriSequenceisu128;cargo expandshows nou64in its definition."s": "10"(hex).cargo test -p auths-keri --lib validate_delegated_rotationpasses.crates/auths-keri/docs/spec_compliance_audit.mdupdated: every previously-flagged item moved from "open" to "resolved" or has a follow-up issue linked.Out of scope
kt: ["1/2", "1/2", "1/2"]). Spec-legal already; using scalarkt: "1"is also spec-legal. Whether to expose multi-key controllers at the CLI is a separate product question (covered by the kels comparison work inlaunch-repo/collab_bordumb_rotation.md).ixnevent emission for device link/unlink anchoring. That's the sister issue — seekeri-ixn-anchored-attestations.md. Note: this issue'sdrtwork usesixnevents (delegator's KEL must contain a seal), but only on the read path; emitting them is a different scope.References
crates/auths-keri/docs/spec_compliance_audit.mdcrates/auths-keri/docs/epics.mdxfield deviation:launch-repo/checks/cli/output/e2e_lifecycle.md("Spec deviations" callout under Inception event)keri-ixn-anchored-attestations.md