Configuring KERIpy Components, KERIA Agents, and Well-known URIs
How do you configure, publish, discover, and connect KERI components? Whether witnesses, verifiers, KERIA deployments, mailboxes, or general KERI controllers, configuration touches the topics of node publication, discovery, and connection, data discovery, and service endpoints. Node discovery and connection occurs with out of band identifiers (OOBIs). Data discovery of ACDC schemas and contents also utilize with OOBIs. Service endpoint configurations combine location scheme records and endpoint role authorizations. This article explains these concepts, KERI configuration mechanisms, and how to use them for real world, production configurations.
Disclaimer: This article assumes some basic familiarity with KERI concepts including identifiers (AIDs), witnesses, OOBIs, and the difference between KERIpy (the KERI implementation) and KERIA (the agent service). You should be able to follow along if you have some experience with KERIpy, the KLI, or KERIA and SignifyTS. If you are new to KERI and KERIA then go to the vLEI Trainings repository and work through those trainings to have a proper introduction and then return to this configuration guide.
Introduction: The Configuration Challenge
Configuring KERIpy controllers and KERIA agents occurs at multiple levels:
Node Configuration
- Controller Configuration (KERIpy) - How your local identifier finds witnesses and peers, and which ports it listens on, if any.
- This is also used as the basis for verifier configuration in components like sally or the vLEI Verifier.
- Witness Configuration (KERIpy) - The endpoints witnesses listen on
- Agency Configuration (KERIA) - Global settings for the KERIA service
- Agent Configuration (KERIA) - Per-agent settings within KERIA
- Mailbox Configuration (KERIpy) - Where receipts and messages are delivered to
Content Hosting 6. ACDC Schema Hosting (KERIpy) - Hosting ACDC schemas for resolution by the OOBI URL resolution process. 7. ACDC Content Hosting (KERIpy) - Hosting ACDC content for resolution by the OOBI URL resolution process.
Well Known Discovery Configuration 8. Well-known URIs for Hosted Components - /.well-known/keri/oobi/ general discovery endpoints for KERI OOBIs, ACDC schemas and witness infrastructure
Let's explore each, starting with file-based configuration and progressing to runtime endpoint role authorization.
Part 1: KERIpy Controller Configuration
Controller configuration applies to components like verifiers, such as the sally verifier service that runs its own direct-mode listeners, or the KERIA service that listens on three ports.
You specify a configuration file with the kli init command like so:
and in your config directory you would need to hav the following directory tree:
config-dir\
- keri\
- cf\
- demo-witness-oobis.json
The Configer Class
At the heart of KERIpy configuration is the Configer class in keri/app/configing.py. It's a specialized file handler supporting four serialization formats:
"""
Habitat Config File
Supports four serializations: HJSON, JSON, MGPK (MsgPack), and CBOR
The serialization is determined by the file extension (.json, .mgpk, .cbor)
"""
=
# Creates file at path like: ~/.keri/cf/main/conf.json
Configuration files follow this structure:
Example: Sally verifier - configuring listening endpoints
The "sally" property of the JSON object corresponds to the --alias argument to kli incept. The curls section of the sally object tells the verifier which port to listen on.
Located at your config_dir/keri/cf/sally.json you would find the following JSON. And the sally.json at the end of the path is really the <alias>.json where alias corresponds to the --alias argument to kli incept.
The iurls section is for witness OOBIs. The durls section is for data OOBIs, usually ACDC schemas or ACDC components. This example config does not have wurls for well knowns.
Example: KERIA agent server - configuring listening endpoints
Similar to the Sally verifier, the keria property in the config corresponds to the value passed to the --name argument to keria start which defaults to keria.
Again, you see the curls section for configuring the HTTP port used for listening to CESR transmissions to an agent. It has iurls and then KERIA-specific configuration for async loop repeat times, called "tocks."
Configuration URL Types:
curls(Controller URLs): URLs + ports on which to set up either a TCP or HTTP port listener - where this controller can be reachediurls(Introduction URLs): OOBI URLs for witnesses, watchers, mailboxes, and any KERI controller to resolve at startupdurls(Data URLs): OOBI URLs for static data like ACDC schemas or credentialswurls(Well Known URLs): OOBI URLs specifically for well known components
Part 2: Witness Configuration
Witnesses are non-transferable identifiers that provide receipt services. Their configuration differs from controllers:
Witness Endpoint Configuration
Witnesses need two types of configuration:
- Controller endpoints - Where the witness accepts events
- Location schemes - The URLs where the witness is reachable
In this case the wan configuration property corresponds to the --alias property passed to the kli incept or kli witness start command. A witness listens on both a TCP and an HTTP port, as shown in the curls section below.
Configuring Witness Endpoints with KLI
After starting a witness with kli witness demo, configure its endpoint roles and location:
Add witness endpoint role:
Add location schemes for both TCP and HTTP:
# TCP location
# HTTP location
Witness Discovery
Controllers discover witnesses through:
- Configuration
iurls: Pre-configured witness OOBIs - OOBI resolution: Resolving witness OOBIs at runtime
- Well-known endpoints: Using
.well-known/keri/oobi/discovery
The witnesses must have their endpoint role authorizations configured so the controller can find them.
Part 3: Mailbox Configuration
Mailboxes are where witnesses and other agents deliver messages. Configuration involves:
Agent Endpoint Roles
An agent acts as a mailbox by having the mailbox endpoint role authorized:
# Configure agent with multiple roles
=
# Controller role
# Location
# Agent role (for controller to reach agent)
# Mailbox role (for witnesses to deliver messages)
# Controller processes too
Adding Endpoint Roles with KLI
The KLI provides commands to add endpoint roles without writing Python code:
Add a controller endpoint role:
Add a witness endpoint role:
Add an agent/mailbox endpoint role:
Add a location scheme for an endpoint:
Querying Endpoints with KLI
Query configured endpoints for an identifier:
Output shows all endpoint roles and their locations:
Querying Endpoints Programmatically
Controllers can query their configured endpoints:
=
# Returns:
# {
# 'agent': {
# 'EBErgFZ...': {'http': 'http://127.0.0.1:6666'}
# },
# 'controller': {
# 'EGadHcy...': {'http': 'http://127.0.0.1:7777'}
# },
# 'mailbox': {
# 'EBErgFZ...': {'http': 'http://127.0.0.1:6666'}
# },
# 'witness': {
# 'BN8t3n1...': {'http': 'http://127.0.0.1:8888'}
# }
# }
Part 4: KERIA Agency Configuration
KERIA introduces a two-level configuration model: agency-level and agent-level.
Agency Configuration: KERIAServerConfig
The agency configuration is defined in keria/app/agenting.py:
"""Agency-wide configuration"""
# HTTP ports
: = 3901
: | None = 3902
: = 3903
# Master controller
: =
: =
: = None
: =
: = None
# TLS
: = None
: = None
: = None
# Logging
: =
: = None
: = False
# Agency settings
: = True
: = 86400
# Global OOBIs applied to all agents
: =
: =
: =
Key Agency Configuration Fields:
- Ports: Three HTTP servers (admin, boot, main)
- Master controller: Agency's own identifier credentials
- Global OOBIs: Applied to every agent created
curls: Controller service endpoint OOBIsiurls: Introduction OOBIs for witnesses/watchers/mailboxesdurls: Data OOBIs for schemas/credentials
Creating an Agency
=
=
Agency Configuration File
Agencies can also load from a config file:
=
=
AS shown earlier, the config file follows the same structure as KERIpy controller configs:
Part 5: KERIA Agent Configuration
Each agent in KERIA gets its own configuration derived from the agency configuration.
Agent Configuration Loading
When creating an agent, the agency loads configuration:
"""
Loads configuration for an agent by:
1. Reading agency configuration
2. Extracting agency subsection
3. Renaming it to agent-specific name
4. Merging with environment variable OOBIs
"""
=
=
# Rename agency section to agent section
= f
=
=
del
=
# Set up curls for agent (controller-specific)
=
# Set up iurls/durls (shared across all agents)
=
=
# Merge with environment variables
= +
= +
= +
return
Important Distinction:
config[habName]["curls"]: Controller-specific endpoints (unique per agent)config["iurls"]: Shared introduction OOBIs (witnesses, watchers, mailboxes)config["durls"]: Shared data OOBIs (schemas, credentials)
Agent Configuration Writing
The agency writes agent-specific config files:
"""
Writes agent configuration as modified copy of agency config
"""
=
=
return
This creates a config file at ~/.keri/cf/{caid}.json containing:
Part 6: Endpoint Role Authorization at Runtime
Beyond file-based configuration, KERI uses endpoint role authorization messages for dynamic configuration.
Endpoint Role Authorization Messages
These are KERI events that authorize specific endpoints for specific roles:
# Controller endpoint
=
# Location scheme
=
Available Roles:
=
Available Schemes:
=
Adding Endpoint Roles with KLI
Instead of writing Python code, use KLI commands to add endpoint roles and locations:
Add a controller endpoint role:
Add the corresponding location:
Add multiple roles for the same endpoint:
# Agent role
# Mailbox role
# Location (shared by both roles)
Processing Endpoint Messages Programmatically
These messages are parsed into the database:
=
# Verify stored in database
=
assert # Role is authorized
=
assert == # Location stored
OOBI Resolution vs File Configuration
The key distinction:
File Configuration (curls, iurls, durls):
- Static configuration loaded at startup
- Processed during
Habery.makeHab()orreconfigure() - Stored in config file permanently
OOBI Resolution:
- Dynamic configuration at runtime
- Processed via
hab.db.oobis.pin()and OOBI resolution - Results in same endpoint role authorizations
Example:
# File-based
=
= # Processes curls
# Equivalent OOBI resolution
=
=
# ... OOBI resolver processes and creates endpoint records
Both methods result in the same database records:
hab.db.ends: Endpoint role authorizationshab.db.locs: Location schemes
Part 7: Answering the Original Questions
Now we can answer the Discord questions:
Q: "How to configure a controller/key store with iurls/curls like with kli init --file?"
Answer: Three methods exist:
Method 1: File-based initialization
# Create config file
=
=
# Initialize Habery with config
=
=
Method 2: Programmatic configuration
# Create Habery
=
# Create config
=
# Create hab (processes config)
=
Method 3: OOBI resolution
# Create hab
=
# Resolve OOBIs at runtime
=
=
# OOBI resolver processes in background
Method 4: KLI commands for endpoint roles
# Add controller endpoint role
# Add location
# Resolve witness OOBI
Q: "Is there a good example of running the controller server?"
Answer: KERIA provides the production pattern:
# Configure agency
=
# Create agency
=
# Setup HTTP servers and doers
=
# Run with Doist
=
For simpler use cases without HTTP servers:
# Just create the agency
=
# Create agents
=
# Run agency
=
Q: "No way for the controller to provide durls/iurls except resolving OOBIs?"
Answer: This is partially correct but requires clarification:
In KERIpy: Controllers can provide curls, iurls, and durls through:
- Configuration file (shown above)
- OOBI resolution at runtime
- Endpoint role authorization messages
In KERIA: The agency configuration provides global iurls and durls that are inherited by all agents. However:
- Limitation: Currently no API endpoint to add agent-specific
curls/iurls/durlsafter agent creation - Workaround: Resolve OOBIs through the
/oobisendpoint or KLI:
Via KERIA API:
{
}
Via KLI:
Both methods trigger OOBI resolution which creates the same endpoint records as file-based configuration.
Improvement Opportunity: As Daniel noted, adding an API to modify agent configuration would be valuable:
# Proposed API (not yet implemented)
{
}
Part 8: Well-Known OOBI Discovery
Well-known URIs provide standardized discovery endpoints for KERI resources following RFC 8615. GLEIF publishes production witness, AID, and schema OOBIs at https://gleif-it.github.io/.well-known/keri/oobi/.
Structure
The well-known directory follows a hierarchical structure:
/.well-known/
├── index.json # Main discovery index
├── schema.json # JSON Schema for validation
└── keri/oobi/
├── index.json # OOBI catalog (AIDs, witnesses, schemas)
├── schema.json # OOBI index schema
└── {identifier}/ # Per-identifier directories
└── index.json # Resource content
Discovery Protocol
Step 1: Discover available resources
Returns:
Step 2: Resolve specific identifier
# Get AID witness URLs
Returns KERI rpy message with witness URLs:
Resource Types
Well-known endpoints serve three resource types:
| Type | Prefix | Content | Content-Type |
|---|---|---|---|
| AID | E | KERI rpy message with witness URLs | application/cesr |
| Witness | B | Witness KEL (icp + rpy messages) | application/cesr |
| Schema | E | ACDC JSON Schema | application/cesr |
Configuration with wurls
Use well-known URIs in configuration files:
The wurls field is processed by Habery.reconfigure() similar to iurls and durls.
Implementation
The vLEI project includes a reference implementation in src/vlei/app/well_known.py:
=
=
The handler:
- Loads
index.jsonto categorize identifiers - Routes requests based on identifier type (AID/witness/schema)
- Returns appropriate content with correct content-type headers
Sample data from GLEIF's production well-known directory is available in vLEI/samples/oobis/.well-known/ including 3 AIDs, 10 witnesses, and 8 vLEI credential schemas.
Benefits
- Standardized Discovery: RFC 8615 compliant path structure
- No Authentication: Public discovery endpoints
- Complete Resource Sets: AIDs return all witness URLs in single response
- Schema Resolution: Direct access to ACDC schemas without OOBI resolution
- Production Ready: GLEIF operates this at scale for vLEI ecosystem
Part 9: Configuration Best Practices
For KERIpy Controllers
Use configuration files for static topology
- Witnesses that rarely change
- Well-known schema OOBIs
- Persistent peer relationships
Use OOBI resolution for dynamic discovery
- New witnesses discovered at runtime
- Peer identifiers learned through introductions
- Ephemeral connections
Use endpoint role authorization for services
- Your own controller endpoints
- Agent mailbox endpoints
- Service availability changes
For KERIA Agencies
Set global
iurlsin agency config- Standard witnesses all agents use
- Common watcher pools
- Shared schema registries
Keep
curlsagent-specific- Each agent may have unique controller endpoints
- Avoid global
curlsunless all agents share endpoints
Use configuration files for production
- Easier to manage than environment variables
- Version controlled configuration
- Clear separation of concerns
Provide OOBI resolution API for runtime changes
- New witnesses can be added without restart
- Dynamic peer discovery
- Schema updates
Conclusion
KERI configuration operates at multiple levels:
- Controller Configuration: File-based
curls,iurls,durlsor OOBI resolution - Witness Configuration: Endpoint role authorization for witness services
- Mailbox Configuration: Endpoint role authorization for agent/mailbox services
- Agency Configuration: Global KERIA service settings and OOBIs
- Agent Configuration: Per-controller settings derived from agency config
- Well-Known Discovery: RFC 8615 compliant
/.well-known/keri/oobi/endpoints for standardized resource discovery
Understanding these levels clarifies the apparent confusion about "no way to provide iurls except OOBIs." The truth is:
- KERIpy: Multiple methods exist (file, OOBI, endpoint messages, well-known URIs)
- KERIA: Agency provides global
iurls, agents inherit them, runtime updates via OOBI resolution
Both approaches ultimately create the same database records: endpoint role authorizations (db.ends) and location schemes (db.locs). The choice between file-based configuration, OOBI resolution, and well-known discovery depends on your use case: static infrastructure, dynamic topology, or public discovery.
For production deployments, use:
- Well-known URIs for public discovery of organizational infrastructure
- Agency config file for stable, shared witnesses and watchers
- OOBI resolution API for dynamic peer discovery and new witnesses
- Endpoint role messages for your own service availability
This provides the flexibility to handle static infrastructure, dynamic peer-to-peer discovery, and standardized public resource publication.