@kerits/core
Guides

Validate a KEL

Verify SAID integrity, structural fields, key-chain continuity, delegation approval, and full chain validity.

KERI's security model requires verifiers to independently validate every event in a KEL before trusting any key state derived from it. There is no trusted third party: validity is a property of the event log itself, derivable from cryptographic rules alone.

@kerits/core exposes granular validation helpers, pure predicates for attachment verification, and a high-level validateKelChain function that runs all checks in sequence.

Validation model assumptions

Witness receipt signature verification assumes the non-transferable basic identifier model: the witness identifier prefix directly encodes the current verification key. This is the KERI default for witnesses.

If transferable witnesses or non-basic identifier types are used, receipt verification must be replaced with witness key state resolution. This assumption applies to both the verifyWitnessReceipt predicate and the fully-witnessed validation mode.

Validation modes

validateKelChain supports two active validation modes via the options.mode parameter:

ModeDefaultDescription
structuralYesSAID integrity, required fields, prior-event linkage, controller signatures, signing threshold, key-chain continuity, delegation VRC verification, AID derivation
fully-witnessedNoEverything in structural plus witness receipt threshold and witness receipt signature verification

A strict mode is reserved in the type system for future use but is not currently implemented.

What validateKelChain checks

In structural mode (default):

  1. SAID integrity — recomputes each event's SAID and compares to d field
  2. AID derivation — for icp/dip events, verifies i === d (only after SAID passes)
  3. Required fields — all mandatory fields present for the event type
  4. Prior-event linkagep field matches previous event's d
  5. Controller signature verification — Ed25519 signatures over canonical event bytes
  6. Signing threshold — valid signatures satisfy kt (simple numeric or weighted multi-clause)
  7. Key-chain continuity — rotation keys hash to previous establishment event's n commitments
  8. Delegation VRC verification — parent signatures verified against parent's kt threshold (supports multi-sig delegators)

In fully-witnessed mode, additionally:

  1. Witness receipt signature verification — each receipt's Ed25519 signature verified using the witness AID prefix as verification key (non-transferable basic model)
  2. Witness receipt threshold — verified receipt count satisfies bt

What validateKelChain does NOT check

Delegation anchoringvalidateKelChain verifies VRC signatures from the delegator, but does NOT verify that the delegator's KEL contains an event with a seal anchoring the delegated event's SAID. This requires traversing the parent KEL, which is an SDK-level orchestration concern.

Use the exported predicates eventContainsAnchorForSaid and isDelegationAnchor as building blocks for SDK-level anchor verification. See the Predicates reference below.

Check SAID integrity

validateEventSaid recomputes an event's SAID from its body and compares it to the d field.

validate-said.ts
import { validateEventSaid } from '@kerits/core';
import type { KELEvent } from '@kerits/core';

const result = validateEventSaid(event);
if (!result.valid) {
  console.error('SAID mismatch', { expected: result.expected, actual: result.actual });
}

Check required fields

validateRequiredFields verifies that every field mandated by the event type is present.

validate-fields.ts
import { validateRequiredFields, isValidKeriEvent } from '@kerits/core';

if (!isValidKeriEvent(rawData)) {
  throw new Error('Not a valid KERI event');
}

const result = validateRequiredFields(rawData);
if (!result.valid) {
  console.error('Missing fields:', result.missing);
}

Required fields by event type:

EventRequired fields
icpv t d i s kt k nt n bt b c a
rotv t d i s p kt k nt n bt br ba c a
ixnv t d i s p a
dipv t d i s kt k nt n bt b c a di
drtv t d i s p kt k nt n bt br ba c a

Validate key-chain continuity

validateKeyChain checks that a rotation event's current keys satisfy the previous establishment event's next-key commitments.

validate-key-chain.ts
import { validateKeyChain } from '@kerits/core';

const result = validateKeyChain(rotEvent, previousEstablishment);
if (!result.valid) {
  console.error('Key chain broken', {
    expected: result.expectedDigests,
    actual: result.actualDigests,
  });
}

Full chain validation

validate-chain.ts
import { validateKelChain } from '@kerits/core';
import type { CESREvent } from '@kerits/core';

const events: CESREvent[] = [
  { event: icpEvent, attachments: [{ kind: 'sig', form: 'indexed', keyIndex: 0, sig: 'AAsig...' }], enc: 'JSON' },
  { event: rotEvent, attachments: [{ kind: 'sig', form: 'indexed', keyIndex: 0, sig: 'ABsig...' }], enc: 'JSON' },
];

const result = validateKelChain(events);
if (!result.valid) {
  const err = result.firstError;
  console.error(`Validation failed at event ${err?.eventIndex}: [${err?.code}] ${err?.message}`);
}

Fully-witnessed mode

fully-witnessed.ts
const result = validateKelChain(events, { mode: 'fully-witnessed' });

Delegated KELs

delegated.ts
const result = validateKelChain(delegatedEvents, { parentKel: parentEvents });

Incremental validation

incremental.ts
const result = validateKelChain(events, { startIndex: events.length - 3 });

Predicates reference

Pure predicates for attachment and anchor verification. These are independently importable from @kerits/core.

verifyWitnessReceipt

Verify a witness receipt signature against an event's canonical bytes. Assumes the non-transferable basic witness model.

verify-receipt.ts
import { verifyWitnessReceipt } from '@kerits/core';

const valid = verifyWitnessReceipt(
  { by: witnessAid, sig: receiptSignature },
  event,
);

verifyVrcAgainstThreshold

Aggregate VRC verifier: validates attachment structure, verifies each signature, and evaluates the delegator's signing threshold. Returns a discriminated union with typed failure reasons.

verify-vrc.ts
import { verifyVrcAgainstThreshold } from '@kerits/core';
import type { VrcVerificationResult } from '@kerits/core';

const result: VrcVerificationResult = verifyVrcAgainstThreshold(
  vrcAttachments,
  childEvent,
  { k: parentKeys, kt: parentThreshold },
);

if (!result.passed) {
  // result.reason: 'cid-mismatch' | 'signature-invalid' | 'key-index-out-of-range' | 'threshold-not-met'
  console.error(result.reason, result.validKeyIndices);
}

eventContainsAnchorForSaid

SAID-only convenience check: does any entry in the event's a[] array have a d field matching the given SAID? Not a full seal matcher — matches SAID only.

anchor-check.ts
import { eventContainsAnchorForSaid } from '@kerits/core';

const hasAnchor = eventContainsAnchorForSaid(parentIxnEvent, delegatedEventSaid);

isDelegationAnchor

Checks that a parent event can carry delegation seals (ixn, rot, drt) and contains a matching SAID anchor. This is a convenience pre-check — complete delegation anchor verification also requires finding the sealing event in the parent KEL and verifying it in its own KEL context.

delegation-anchor.ts
import { isDelegationAnchor } from '@kerits/core';

for (const parentEvent of parentKel) {
  if (isDelegationAnchor(parentEvent.event, delegatedEventSaid)) {
    console.log('Found delegation anchor at', parentEvent.event.s);
  }
}

Validation error codes

CodeModeMeaning
SAID_MISMATCHalld field does not match recomputed SAID
AID_DERIVATION_INVALIDallicp/dip event has i !== d
MISSING_REQUIRED_FIELDallA mandatory field is absent
PREVIOUS_EVENT_MISMATCHallp does not equal the prior event's d
NEXT_KEY_MISMATCHallRotation keys do not satisfy prior n commitments
SIGNATURE_INVALIDallA controller signature failed verification
THRESHOLD_NOT_METallValid signatures do not meet the signing threshold
AID_INCONSISTENTallEvent i field differs from KEL's established AID
SEQUENCE_INVALIDallEvent sequence number is out of order
FIRST_EVENT_NOT_INCEPTIONallFirst event is not icp or dip
NON_TRANSFERABLE_VIOLATIONallNon-transferable AID has events after inception
CONFIG_TRAIT_VIOLATIONallEstablishment-only AID has interaction event
CONFIG_TRAIT_REMOVEDallRotation removes an inception config trait
DUPLICATE_KEYSallDuplicate entries in signing key list
DUPLICATE_NEXT_DIGESTSallDuplicate entries in next key digest list
WITNESS_THRESHOLD_UNSATISFIABLEallbt exceeds witness count
WITNESS_DELTA_INVALIDallMalformed witness add/remove in rotation
MISSING_PARENT_KELallDelegated event but no parent KEL supplied
PARENT_SIGNATURE_INVALIDallVRC signature or CID mismatch
VRC_KEY_INDEX_INVALIDallVRC keyIndex out of range for parent key list
PARENT_THRESHOLD_NOT_METallValid VRC signatures don't meet delegator's kt
WITNESS_RECEIPT_SIGNATURE_INVALIDfully-witnessedA witness receipt signature failed verification
WITNESS_RECEIPT_THRESHOLD_NOT_METfully-witnessedVerified receipt count below bt

On this page