style
KERI Coding Style Guide
Overview
The KERI coding style is a Domain-Specific Linguistic Programming (DSLP) approach that creates a 1:1 mapping between the KERI specification terminology and code structure. This style makes code self-documenting and enables implementers in other languages to translate patterns while maintaining conceptual fidelity.
Core Philosophy: Every naming convention reinforces KERI/CESR concepts. The code reads like the KERI spec itself.
Quick Reference
Five Primary Patterns
- Modules: Use gerunds with
-ingsuffix (e.g.,coring.py,eventing.py,signing.py) - Classes: Use agent nouns with
-ersuffix (e.g.,Verfer,Diger,Siger,Salter) - Code Tables: Use frozen dataclasses with
-Dexsuffix (e.g.,DigDex,PreDex,NumDex) - Transformations: Use verbs with
-ifysuffix (e.g.,sizeify(),versify(),saidify()) - Data Structures: Use namedtuples with
-agesuffix (e.g.,Versionage,Smellage)
CESR Abbreviations
qb64 # Qualified base64 string
qb64b # Qualified base64 bytes
qb2 # Qualified base2 (binary)
hs # Hard size (code size in sextets)
ss # Soft size (variable part)
fs # Full size (total)
cs # Code size (hs + ss)
ked # Key Event Dict
raw # Raw unqualified bytes
When to Apply This Style
Always use for:
- KERI protocol implementations (Python, Rust, TypeScript)
- Modules interfacing with keripy, keria, or signify-ts
- Cryptographic primitive wrappers
- CESR encoding/decoding implementations
Never use for:
- Generic Python utilities unrelated to KERI
- Public APIs targeting non-KERI developers
- CLI tools for end users (unless internal KERI operations)
Module Organization
File Naming Pattern
Use gerunds (present participles) indicating action or capability:
# Core operations
coring.py # Core cryptographic material classes
eventing.py # Event processing and validation
signing.py # Signature operations
indexing.py # Index-based operations
# Specialized operations
serdering.py # Serialization/deserialization
parsing.py # Parsing operations
routing.py # Message routing
# Database operations
basing.py # Database foundation
dbing.py # Database operations
escrowing.py # Escrow management
# Application operations
agenting.py # Agent lifecycle
habbing.py # Habitat (key environment) management
keeping.py # Key storage operations
oobiing.py # Out-Of-Band-Introduction operations
Rules:
- Always gerund form:
event→eventing.py, notevent.py - Single word preferred, compounds acceptable:
oobiing.py - Never:
utils.py,helpers.py,common.py(use gerund equivalents)
Module Structure Template
# -*- encoding: utf-8 -*-
"""
keri.core.digesting module
[Brief description of module purpose]
"""
import [standard libraries]
from [keri imports]
from ..kering import [exceptions]
from ..help.helping import [utilities]
# Constants and code tables
@dataclass(frozen=True)
class SomeCodex:
"""Code table docstring"""
pass
SomeDex = SomeCodex()
# Classes
class SomeClass(Matter):
"""Class docstring"""
pass
# Functions
def someify(arg):
"""Transformation function"""
pass
Class Design
Base Class: Matter
All cryptographic material inherits from Matter:
class Matter:
"""Base for all qualified cryptographic material"""
def __init__(self, raw=None, qb64b=None, qb64=None, qb2=None, code=None):
"""Accept material in any qualified or unqualified form"""
pass
@property
def raw(self):
"""Raw unqualified bytes"""
return self._raw
@property
def code(self):
"""Derivation code"""
return self._code
@property
def qb64(self):
"""Qualified base64 string"""
return self._qb64
Agent Noun Classes
Name classes as "one who does X":
class Verfer(Matter): # One who verifies (public key)
class Diger(Matter): # One who digests (hash)
class Siger(Indexer): # One who signs (indexed signature)
class Salter(Matter): # One who salts (seed/salt)
class Prefixer(Matter): # One who prefixes (identifier)
class Seqner(Matter): # One who sequences (sequence number)
class Dater(Matter): # One who dates (timestamp)
class Saider(Matter): # One who creates SAIDs
class Noncer(Diger): # One who nonces (nonce/UUID)
class Bexter(Matter): # One who works with bytes/hex
class Texter(Matter): # One who works with text
class Pather(Matter): # One who handles paths
class Labeler(Matter): # One who labels
class Tagger(Matter): # One who tags
Naming Rules:
- Derive from base verb:
verify→Verfer,digest→Diger - Drop 'e' before -er where natural:
Cigar(notCigarer) - Irregular forms acceptable:
Signer(notSignor)
Class Template
class Diger(Matter):
"""
Diger class for cryptographic digests
Inherits from Matter for qualified encoding
"""
# Override code tables
Hards = {c: hs for c in DigDex}
Sizes = {...}
def __init__(self, raw=None, ser=None, code=DigDex.Blake3_256, **kwa):
"""
Initialize with raw digest or serialization to digest
Parameters:
raw: pre-computed digest bytes
ser: serialization to digest
code: algorithm code from DigDex
"""
if ser is not None:
raw = self._digest(ser, code)
super().__init__(raw=raw, code=code, **kwa)
def verify(self, ser):
"""Verify digest matches serialization"""
return self.raw == self._digest(ser, self.code)
Code Tables (Codex Pattern)
Use frozen dataclasses with singleton instances:
@dataclass(frozen=True)
class DigestCodex:
"""Digest algorithm derivation codes"""
Blake3_256: str = 'E'
Blake2b_256: str = 'F'
SHA3_256: str = 'H'
SHA2_256: str = 'I'
def __iter__(self):
return iter(astuple(self))
DigDex = DigestCodex() # Create singleton
@dataclass(frozen=True)
class PrefixCodex:
"""Identifier prefix derivation codes"""
Ed25519N: str = 'B' # Non-transferable
Ed25519: str = 'D' # Transferable
X25519: str = 'C' # Encryption key
def __iter__(self):
return iter(astuple(self))
PreDex = PrefixCodex()
Rules:
- Class name:
<Purpose>Codex - Instance name:
<Purpose>Dex - Always frozen dataclass
- Include
__iter__for iteration - Field names in PascalCase:
Blake3_256
Transformation Functions
Use -ify suffix for transformations:
def sizeify(ked, kind=None, version=Version):
"""Compute and update size in version string"""
raw = dumps(ked, kind)
size = len(raw)
ked["v"] = versify(kind=kind, size=size, version=version)
return raw, size
def versify(proto=Protocols.keri, pvrsn=Version, kind=Kinds.json, size=0):
"""Create version string from components"""
return f"{proto}{pvrsn.major:x}{pvrsn.minor:x}{kind}{size:06x}_"
def deversify(vs):
"""Parse version string into components"""
match = Rever.match(vs)
# ... parse and return components
def saidify(ked, label=Saids.d, code=DigDex.Blake3_256):
"""Create Self-Addressing IDentifier for dict"""
# ... compute SAID digest that includes itself
def klasify(sers, klases):
"""Convert CESR serializations to class instances"""
return [klas(qb64=ser) for ser, klas in zip(sers, klases)]
def dictify(dataclass_instance):
"""Convert dataclass to dict"""
return asdict(dataclass_instance)
Rules:
- Transformation:
<base>ify - Inverse:
de<base>ify - Document transformation clearly
- Return transformed value
Data Structures
Use namedtuples with -age suffix:
Versionage = namedtuple("Versionage", "major minor")
Version = Versionage(major=1, minor=0)
Kindage = namedtuple("Kindage", "json mgpk cbor cesr")
Kinds = Kindage(json='JSON', mgpk='MGPK', cbor='CBOR', cesr='CESR')
Protocolage = namedtuple("Protocolage", "keri acdc")
Protocols = Protocolage(keri="KERI", acdc="ACDC")
Smellage = namedtuple("Smellage", "proto pvrsn kind size gvrsn")
Digestage = namedtuple("Digestage", "klas size length")
Rules:
- Type:
<Concept>age - Instance: Plural or singular based on semantics
- Field names: lowercase, abbreviated appropriately
Helper Functions
Use creative descriptive names:
def smell(raw):
"""Sniff/detect version string in bytes"""
match = Rever.search(raw)
return rematch(match) if match else None
def nabSextets(qb2, num):
"""Nab (grab) num sextets from qb2"""
return qb2[:sceil(num * 3 / 4)]
def sceil(r):
"""Symmetric ceiling (away from zero)"""
return int(r) + isign(r - int(r))
def isign(i):
"""Integer sign function"""
return (1 if i > 0 else -1 if i < 0 else 0)
def simple(n):
"""Simple majority threshold"""
return min(max(0, n), (max(0, n) // 2) + 1)
def ample(n, f=None, weak=True):
"""Byzantine fault tolerant (ample) majority"""
# ... compute BFT threshold
Variable Naming
CESR Abbreviations
qb64 # Qualified base64 string (str)
qb64b # Qualified base64 bytes (bytes)
qb2 # Qualified base2/binary (bytes)
hs # Hard size (code size in sextets)
ss # Soft size (variable size in sextets)
xs # Extra size (padding in sextets)
fs # Full size (total in sextets)
ls # Lead size (leading bytes)
cs # Code size (hs + ss)
ps # Pad size (for alignment)
bhs # Byte hard size
bcs # Byte code size
bfs # Byte full size
Key Event Dict Fields
v # Version string
t # Type (ilk)
d # Digest (SAID)
i # Identifier prefix
s # Sequence number
p # Prior event digest
kt # Key threshold
k # Keys (current)
nt # Next threshold
n # Next key digests
bt # Witness threshold
b # Witness list (backers)
Common Terms
ked # Key Event Dict
ser # Serialization
dig # Digest
pre # Prefix
sn # Sequence number
ilk # Event type/kind
knd # Serialization kind
Advanced Patterns
See reference files for detailed patterns:
- naming_conventions.md - Complete naming reference with all patterns and rules
- patterns.md - Matter class pattern, extraction methods, cryptographic primitives, KEDs
- examples.md - Complete module examples, class hierarchies, event processing
Style Checklist
When writing KERI-style code, verify:
- Module name uses
-ingsuffix - Classes use agent noun
-ersuffix - Code tables are frozen dataclasses with
-Dexsuffix - Transformations use
-ifysuffix - Namedtuples use
-agesuffix - CESR abbreviations used consistently (
qb64,hs,ss, etc.) - Material classes inherit from
Matterwith proper extraction methods - Helper functions have creative descriptive names
- Docstrings clearly explain purpose and parameters
- Code reads like the KERI specification
Anti-Patterns to Avoid
Don't:
# Generic utility names
utils.py # Use helping.py or specific gerund
helpers.py # Use helping.py
common.py # Use specific gerund
# Non-agent noun classes
class Digest(Matter) # Use Diger
class Verifier(Matter) # Use Verfer
class Signature(Matter) # Use Siger
# Generic variable names
data = ... # Use ked, ser, raw, etc.
result = ... # Use specific name
temp = ... # Use descriptive name
Do:
helping.py # Helper utilities
class Diger(Matter) # One who digests
class Verfer(Matter) # One who verifies
class Siger(Indexer) # One who signs
ked = {...} # Key Event Dict
ser = b'...' # Serialization
raw = b'...' # Raw bytes