Loading vLEI.wiki
Fetching knowledge base...
Fetching knowledge base...
This comprehensive explanation has been generated from 2 GitHub source documents. All source documents are searchable here.
Last updated: September 21, 2025
This content is meant to be consumed by AI agents via MCP. Click here to get the MCP configuration.
Note: In rare cases it may contain LLM hallucinations.
For authoritative documentation, please consult the official GLEIF vLEI trainings and the ToIP Glossary.
In KERI/ACDC systems, 'iss' is an abbreviation for 'verifiable credential issuance' - the cryptographically secured process of creating and delivering ACDC credentials from an issuer to a holder using IPEX protocol flows.
The term iss serves as a standardized abbreviation within the KERI ecosystem representing verifiable credential issuance - the complete cryptographic process of creating, signing, and delivering Authentic Chained Data Container (ACDC) credentials from an issuer's Autonomic Identifier (AID) to a holder's AID through the Issuance and Presentation Exchange (IPEX) protocol.
iss := vc_issue | verifiable_credential_issuance
where:
vc_issue ≡ ACDC_creation ∘ cryptographic_signing ∘ IPEX_delivery
ACDC_creation := schema_validation ∘ SAID_generation ∘ field_population
cryptographic_signing := Ed25519_signature(issuer_private_key, ACDC_digest)
IPEX_delivery := grant_message ∘ admit_response ∘ TEL_anchoring
The issuance process operates on ACDC credentials with the following canonical structure:
{
"v": "ACDC10JSON000197_",
"d": "EBdXt3gIXOf2BBWNHdSXCJnFJL5OuQPyM5K0neuniccM",
"i": "EAoTNZH3UfSVPzhzS6b5CMZAoTNZH3UfSVPzhzS6b5CM",
"ri": "EGUPiCVO73M9worPwR3PfThAtC0AJnH5ZgwsXf6TzbVK",
"s": "ENPXp1vQzRF6JwIuS-mp2U8Uf1MoADoP_GqQ62VsDZWY",
"a": {
"d": "ELvaU6Z-i0d8JJR2nmwyYAZAoTNZH3UfSVPzhzS6b5CM",
"i": "EKxy2sgzfplyr-tgwIxS19f2OchFHtLwPWD3v4oYimBx",
"dt": "2023-08-19T17:30:00.000000+00:00",
"LEI": "5493001KJTIIGC8Y1R17"
}
}
Key fields in issuance context:
i: Issuer AID (cryptographically bound to issuer's KEL)ri: Registry identifier (SAID of credential registry TEL)s: Schema SAID (cryptographic commitment to credential structure)a.i: Issuee AID (optional, creates targeted vs untargeted distinction)Schema Resolution Race Conditions
TEL State Synchronization
IPEX Message Ordering
Batch Credential Issuance
# Issue multiple credentials in single KEL interaction
def batch_issue_credentials(issuer, registry, credentials_data):
tel_events = []
acdcs = []
for data in credentials_data:
acdc = build_acdc(issuer, registry, data)
tel_event = create_tel_event(acdc)
tel_events.append(tel_event)
acdcs.append(acdc)
# Single KEL anchoring for all credentials
anchor_batch_to_kel(issuer, tel_events)
return acdcs
Connection Pooling for Witness Communication
import aiohttp
class WitnessPool:
def __init__(self, witness_urls):
self.session = aiohttp.ClientSession(
connector=aiohttp.TCPConnector(
limit=100,
limit_per_host=10,
keepalive_timeout=30
)
)
self.witness_urls = witness_urls
Private Key Protection
Credential Validation
def validate_credential_before_issuance(acdc, schema):
# Validate schema compliance
jsonschema.validate(acdc['a'], schema)
# Validate SAID integrity
calculated_said = generate_said(acdc)
assert acdc['d'] == calculated_said
# Validate issuer authorization
assert is_authorized_issuer(acdc['i'], schema['$id'])
# Validate timestamp freshness
dt = datetime.fromisoformat(acdc['a']['dt'])
assert abs((datetime.now(timezone.utc) - dt).total_seconds()) < 300
Every credential issuance creates a corresponding TEL event with state transition:
NULL → issued
TEL Event Structure:
{
"v": "KERI10JSON00011c_",
"t": "iss", // Issuance event type
"d": "ELvaU6Z-i0d8JJR2nmwyYAZAoTNZH3UfSVPzhzS6b5CM",
"i": "EAoTNZH3UfSVPzhzS6b5CMZAoTNZH3UfSVPzhzS6b5CM",
"s": "0",
"ri": "EGUPiCVO73M9worPwR3PfThAtC0AJnH5ZgwsXf6TzbVK",
"ra": {
"d": "EBdXt3gIXOf2BBWNHdSXCJnFJL5OuQPyM5K0neuniccM"
}
}
The complete issuance workflow follows this message sequence:
Issuer Holder
| |
|--- IPEX Grant --------->|
| (credential offer) |
| |
|<-- IPEX Admit ----------|
| (acceptance) |
| |
|--- TEL Anchor -------->|
| (registry update) |
IPEX Grant Message Structure:
{
"v": "KERI10JSON000260_",
"t": "exn",
"d": "ELvaU6Z-i0d8JJR2nmwyYAZAoTNZH3UfSVPzhzS6b5CM",
"i": "EAoTNZH3UfSVPzhzS6b5CMZAoTNZH3UfSVPzhzS6b5CM",
"p": "",
"dt": "2023-08-19T17:30:00.000000+00:00",
"r": "/ipex/grant",
"q": {},
"a": {
"m": "",
"i": "EKxy2sgzfplyr-tgwIxS19f2OchFHtLwPWD3v4oYimBx",
"acdc": "<ACDC_CREDENTIAL_OBJECT>"
}
}
All credential issuance MUST use Ed25519 signatures with CESR encoding:
Signature Generation:
1. ACDC_bytes = CESR_encode(ACDC_object)
2. digest = Blake3-256(ACDC_bytes)
3. signature = Ed25519_sign(issuer_private_key, digest)
4. CESR_signature = CESR_encode(signature, "0B") // Ed25519 signature code
Signature Verification:
1. CESR_decode(signature) → raw_signature
2. Ed25519_verify(issuer_public_key, digest, raw_signature) → boolean
Every ACDC requires cryptographic self-addressing:
def generate_SAID(acdc_dict):
# Remove existing 'd' field for calculation
temp_dict = acdc_dict.copy()
temp_dict['d'] = ''
# Serialize with consistent ordering
serialized = json.dumps(temp_dict, separators=(',', ':'), sort_keys=True)
# Generate Blake3-256 digest
digest = blake3.blake3(serialized.encode('utf-8')).digest()
# CESR encode with Blake3-256 code 'E'
said = 'E' + base64.urlsafe_b64encode(digest).decode('ascii').rstrip('=')
return said
TEL events are cryptographically anchored to the issuer's KEL through Event Source Seals:
{
"s": "3", // KEL sequence number
"d": "ELvaU6Z-i0d8JJR2nmwyYAZAoTNZH3UfSVPzhzS6b5CM" // TEL event digest
}
The KEL interaction event includes this seal in its anchors section, creating cryptographic binding between credential issuance and issuer's key state.
The reference implementation in KERIpy provides the Credentialer class:
class Credentialer:
def __init__(self, hby, rgy, verifier):
self.hby = hby # Habery (keystore)
self.rgy = rgy # Registry
self.verifier = verifier
def issue(self, issuer, registry, schema, data, recipient=None):
# 1. Validate schema compliance
self._validate_schema(schema, data)
# 2. Generate ACDC structure
acdc = self._build_acdc(issuer, registry, schema, data, recipient)
# 3. Generate SAID
acdc['d'] = self._generate_said(acdc)
# 4. Create TEL issuance event
tel_event = self._create_tel_event(acdc, registry)
# 5. Anchor to KEL
self._anchor_to_kel(issuer, tel_event)
# 6. Sign ACDC
signature = self._sign_acdc(issuer, acdc)
return acdc, signature
The KERIA agent exposes RESTful endpoints for credential issuance:
POST /identifiers/{aid}/credentials
Content-Type: application/json
{
"registry": "EGUPiCVO73M9worPwR3PfThAtC0AJnH5ZgwsXf6TzbVK",
"schema": "ENPXp1vQzRF6JwIuS-mp2U8Uf1MoADoP_GqQ62VsDZWY",
"recipient": "EKxy2sgzfplyr-tgwIxS19f2OchFHtLwPWD3v4oYimBx",
"data": {
"LEI": "5493001KJTIIGC8Y1R17",
"legalName": "Example Corporation"
}
}
Response: 202 Accepted
{
"d": "EBdXt3gIXOf2BBWNHdSXCJnFJL5OuQPyM5K0neuniccM",
"credential": { /* ACDC object */ },
"signature": "0BDKrANtOpUKuC9AZhxzfCc991iuG1bWmIeHDjk2PTwmvVdwNAg2XgXqeqBBps_6jdEu1IR3GrfyLRnyB7E0D5Cw"
}
interface CredentialIssuanceArgs {
registryId: string;
schemaId: string;
recipient?: string;
data: Record<string, any>;
timestamp?: string;
}
class SignifyClient {
async issueCredential(aid: string, args: CredentialIssuanceArgs): Promise<Credential> {
const response = await this.client.post(
`/identifiers/${aid}/credentials`,
{
registry: args.registryId,
schema: args.schemaId,
recipient: args.recipient,
data: args.data,
dt: args.timestamp || new Date().toISOString()
}
);
return new Credential(response.data.credential, response.data.signature);
}
}
Credential issuance requires prior schema OOBI resolution:
Schema OOBI: http://schema-server:7723/oobi/ENPXp1vQzRF6JwIuS-mp2U8Uf1MoADoP_GqQ62VsDZWY
Resolution Process:
1. HTTP GET to schema OOBI URL
2. Retrieve JSON Schema definition
3. Validate schema SAID matches expected value
4. Cache schema for credential validation
Credential registries must be created before issuance:
# Registry inception
registry_event = {
"v": "KERI10JSON00011c_",
"t": "vcp", # Registry inception
"d": "EGUPiCVO73M9worPwR3PfThAtC0AJnH5ZgwsXf6TzbVK",
"i": "EGUPiCVO73M9worPwR3PfThAtC0AJnH5ZgwsXf6TzbVK",
"s": "0",
"c": ["NB"], # No backers configuration
"bt": "0", # Backer threshold
"b": [] # Backer list (empty for NB config)
}
Issuance events require witness receipts for availability:
Witness Receipt Structure:
{
"v": "KERI10JSON000091_",
"t": "rct",
"d": "ELvaU6Z-i0d8JJR2nmwyYAZAoTNZH3UfSVPzhzS6b5CM",
"i": "BWzwEHHzq7K0gzQPYGGwTmuupUhPx5_yZ-Wk1x4ejhcc"
}
Signature: 0BDKrANtOpUKuC9AZhxzfCc991iuG1bWmIeHDjk2PTwmvVdwNAg2XgXqeqBBps_6jdEu1IR3GrfyLRnyB7E0D5Cw
Typical ACDC Memory Usage:
- Base ACDC structure: ~500 bytes
- Attribute data: variable (100-2000 bytes typical)
- CESR signature: 88 bytes
- TEL event: ~300 bytes
- Total per credential: ~1-3 KB
IPEX Grant Message Size:
- Message envelope: ~400 bytes
- Embedded ACDC: ~1-3 KB
- CESR attachments: ~200 bytes
- Total network payload: ~1.5-4 KB per issuance
class SchemaValidationError(Exception):
def __init__(self, schema_id, validation_errors):
self.schema_id = schema_id
self.errors = validation_errors
super().__init__(f"Schema {schema_id} validation failed: {validation_errors}")
def validate_against_schema(data, schema):
try:
jsonschema.validate(data, schema)
except jsonschema.ValidationError as e:
raise SchemaValidationError(schema['$id'], [e.message])
def handle_registry_conflict(registry_id, credential_id):
# Check if credential already exists in registry
existing = registry.get_credential_state(credential_id)
if existing == 'issued':
raise CredentialAlreadyIssuedError(credential_id)
elif existing == 'revoked':
raise CredentialRevokedError(credential_id)
# Proceed with issuance
return True
async def wait_for_witness_receipts(event_digest, timeout=30):
start_time = time.time()
required_receipts = calculate_witness_threshold()
received_receipts = 0
while received_receipts < required_receipts:
if time.time() - start_time > timeout:
raise WitnessTimeoutError(f"Only {received_receipts}/{required_receipts} receipts received")
await asyncio.sleep(0.1)
received_receipts = count_receipts_for_event(event_digest)
return True
def ensure_key_state_consistency(issuer_aid, issuance_timestamp):
current_key_state = get_key_state_at_time(issuer_aid, issuance_timestamp)
if current_key_state != get_current_key_state(issuer_aid):
# Key rotation occurred during issuance
# Use historical key state for signature verification
return current_key_state
return get_current_key_state(issuer_aid)
draft-ssmith-keri-core-01draft-ssmith-acdc-02draft-ssmith-ipex-01draft-ssmith-cesr-03draft-pfeairheller-ptel-00All ACDC schemas MUST comply with JSON Schema Draft 2020-12:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "ENPXp1vQzRF6JwIuS-mp2U8Uf1MoADoP_GqQ62VsDZWY",
"title": "Legal Entity vLEI Credential",
"type": "object",
"properties": {
"LEI": {
"type": "string",
"pattern": "^[0-9A-Z]{18}[0-9]{2}$"
},
"legalName": {
"type": "string",
"maxLength": 500
}
},
"required": ["LEI", "legalName"]
}
For vLEI ecosystem credentials, issuance MUST follow:
apiVersion: apps/v1
kind: Deployment
metadata:
name: keria-issuer
spec:
replicas: 3
selector:
matchLabels:
app: keria-issuer
template:
spec:
containers:
- name: keria
image: gleif/keria:latest
env:
- name: KERI_WITNESS_URLS
value: "http://witness1:5642,http://witness2:5643,http://witness3:5644"
- name: KERI_DB_PATH
value: "/data/keri"
volumeMounts:
- name: keri-data
mountPath: /data
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
import prometheus_client
# Metrics for credential issuance
CREDENTIAL_ISSUANCE_COUNTER = prometheus_client.Counter(
'keri_credentials_issued_total',
'Total number of credentials issued',
['issuer_aid', 'schema_type', 'status']
)
CREDENTIAL_ISSUANCE_DURATION = prometheus_client.Histogram(
'keri_credential_issuance_duration_seconds',
'Time spent issuing credentials',
['issuer_aid', 'schema_type']
)
def issue_credential_with_metrics(issuer, schema, data):
start_time = time.time()
try:
credential = issue_credential(issuer, schema, data)
CREDENTIAL_ISSUANCE_COUNTER.labels(
issuer_aid=issuer,
schema_type=schema,
status='success'
).inc()
return credential
except Exception as e:
CREDENTIAL_ISSUANCE_COUNTER.labels(
issuer_aid=issuer,
schema_type=schema,
status='error'
).inc()
raise
finally:
duration = time.time() - start_time
CREDENTIAL_ISSUANCE_DURATION.labels(
issuer_aid=issuer,
schema_type=schema
).observe(duration)
# Sharded credential storage
class ShardedCredentialStore:
def __init__(self, shard_count=16):
self.shard_count = shard_count
self.shards = [LMDBStore(f"credentials_shard_{i}") for i in range(shard_count)]
def get_shard(self, credential_id):
shard_index = int(credential_id[-2:], 16) % self.shard_count
return self.shards[shard_index]
def store_credential(self, credential_id, credential_data):
shard = self.get_shard(credential_id)
return shard.put(credential_id, credential_data)
#!/bin/bash
# KERI credential backup script
BACKUP_DIR="/backup/keri/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
# Backup KEL databases
cp -r /data/keri/db/kels "$BACKUP_DIR/"
# Backup credential registries
cp -r /data/keri/db/regs "$BACKUP_DIR/"
# Backup credential store
cp -r /data/keri/db/creds "$BACKUP_DIR/"
# Create integrity manifest
find "$BACKUP_DIR" -type f -exec sha256sum {} \; > "$BACKUP_DIR/integrity.sha256"
# Compress backup
tar -czf "$BACKUP_DIR.tar.gz" -C "$(dirname $BACKUP_DIR)" "$(basename $BACKUP_DIR)"
echo "Backup completed: $BACKUP_DIR.tar.gz"
Registry Access Control
Unit Testing Credential Issuance
import pytest
from unittest.mock import Mock, patch
class TestCredentialIssuance:
def test_acdc_structure_validation(self):
issuer = 'EAoTNZH3UfSVPzhzS6b5CMZAoTNZH3UfSVPzhzS6b5CM'
registry = 'EGUPiCVO73M9worPwR3PfThAtC0AJnH5ZgwsXf6TzbVK'
acdc = build_acdc(issuer, registry, test_data)
assert acdc['v'].startswith('ACDC10JSON')
assert acdc['i'] == issuer
assert acdc['ri'] == registry
assert 'd' in acdc
assert 'a' in acdc
@patch('keri.core.eventing.Kevery')
def test_tel_anchoring(self, mock_kevery):
tel_event = create_tel_event(test_acdc)
anchor_to_kel(issuer_aid, tel_event)
mock_kevery.assert_called_once()
# Verify KEL interaction event created
Integration Testing with Test Networks
#!/bin/bash
# Start test witness network
kli witness demo &
WITNESS_PID=$!
# Start test schema server
vlei-server -s ./test-schemas -p 7723 &
SCHEMA_PID=$!
# Run credential issuance tests
python -m pytest tests/test_credential_issuance.py -v
# Cleanup
kill $WITNESS_PID $SCHEMA_PID
Load Testing Considerations