@olane/o-leader
v0.8.16
Published
oLane leader node used as the root node for a network
Readme
o-leader {#o-leader}
The root coordinator for self-organizing Olane OS agent networks.
TL;DR: o-leader enables multi-agent systems to discover and coordinate autonomously. Agents self-register their capabilities, discover other agents via a built-in registry, and form dynamic networks without manual configuration. Perfect for scaling from single-agent to distributed multi-agent architectures.
Overview {#overview}
o-leader is the root coordinator node for Olane OS agent networks. It provides centralized discovery and coordination for distributed agents, enabling self-organizing systems without manual infrastructure configuration.
When to use o-leader {#when-to-use}
Use o-leader when:
- Building multi-agent systems requiring coordination
- Creating discoverable agent networks (agents find each other autonomously)
- Implementing hierarchical agent architectures (departments, teams, domains)
- Scaling from single-agent to multi-agent systems
Core capabilities {#core-capabilities}
| Capability | Description | |------------|-------------| | Network Coordination | Entry point for agents joining the network | | Agent Discovery | Built-in registry for finding agents by capability | | Network Intelligence | Automatic indexing and mapping of agent capabilities | | Fault Tolerance | Automatic failover and recovery coordination |
Quick Start {#quick-start}
Prerequisites {#prerequisites}
- Node.js 20.x or higher
- Basic understanding of Olane OS concepts
@olane/o-coreinstalled
Installation {#installation}
pnpm install @olane/o-leaderBasic Leader Node Setup {#basic-setup}
import { oLeaderNode } from '@olane/o-leader';
import { oAddress } from '@olane/o-core';
// Create a leader node for your agent network
const leader = new oLeaderNode({
networkName: 'my-agent-network',
address: oAddress.leader(), // o://leader
});
// Start the leader (initializes registry and coordination services)
await leader.start();
console.log('Leader node running at o://leader');
console.log('Registry service available at o://registry');What this does:
- Creates a leader node at
o://leader - Initializes the built-in registry at
o://registry - Opens the network for agents to join
Next Steps {#next-steps}
- Join Agents - Connect agents to your network
- Discover Agents - Search for agents by capability
- Custom Validation - Implement access control
Core Concepts {#core-concepts}
Leader Node Architecture {#leader-architecture}
The leader node is the root coordinator of an Olane OS agent network. Unlike traditional orchestrators that require pre-defined workflows, o-leader enables emergent coordination where agents self-organize.
Key Responsibilities {#leader-responsibilities}
- Network Entry Point: First contact for agents joining the network
- Registry Management: Maintains live directory of agents and capabilities
- Discovery Coordination: Helps agents find each other via
o://addressing - Network Intelligence: Tracks and indexes agent capabilities across the network
Leader vs Traditional Orchestration {#leader-vs-traditional}
| Traditional Orchestrators | o-leader Node | |--------------------------|---------------| | Pre-defined workflows | Emergent workflows | | Centralized control | Distributed coordination | | Manual scaling | Self-organizing | | Fixed topology | Dynamic hierarchy |
The Registry Service {#registry-service}
The registry is the discovery mechanism for your agent network - a dynamic directory where agents register their capabilities and find other agents.
Registry Operations {#registry-operations}
commit: Register an agent and its capabilitiessearch: Find agents by address, protocol, or capabilityfind_all: List all registered agentsremove: Deregister an agent from the network
Example: Finding Agents by Capability {#registry-example}
import { oAddress } from '@olane/o-core';
// Search for agents with specific capabilities
const agents = await leader.use(new oAddress('o://registry'), {
method: 'search',
params: {
protocols: ['payment-processing', 'tax-calculation']
}
});
if (agents.result.success) {
console.log(`Found ${agents.result.data.length} agents with payment capabilities`);
// Use the discovered agents in your workflow
}Network Joining Flow {#joining-flow}
When an agent joins the network, the following process occurs:
- Agent sends join request to
o://leader - Leader validates the request (customizable)
- Leader updates parent-child relationships
- Agent is registered in the registry
- Agent receives network configuration
- Agent can now discover and communicate with other agents
Join Request Structure {#join-request-structure}
// Example join request parameters
{
caller: 'o://company/finance/analyst', // Agent's address
parent: 'o://company/finance', // Parent in hierarchy
transports: ['webrtc', 'websocket'] // Supported transports
}Network Indexing {#network-indexing}
Network indexing periodically crawls all registered agents to build a comprehensive map of network capabilities, enabling intelligent routing and discovery.
Why Indexing Matters {#indexing-benefits}
- Capability Discovery: Agents can find specialized tools across the network
- Intelligent Routing: Requests automatically routed to capable agents
- Network Health: Identify disconnected or unresponsive agents
- Knowledge Mapping: Build network-wide capability graph
Integration Guide {#integration-guide}
Setting Up Your Leader Node {#setup-leader}
Basic Configuration {#basic-configuration}
import { oLeaderNode } from '@olane/o-leader';
import { oAddress, NodeType } from '@olane/o-core';
import { RegistryMemoryTool } from '@olane/o-leader';
// Create leader with basic configuration
const leader = new oLeaderNode({
networkName: 'production-agents',
address: oAddress.leader(), // o://leader
type: NodeType.LEADER,
});
// Start the leader node (initializes registry and network services)
await leader.start();
console.log('Leader node ready to accept agent connections');Configuration Options {#configuration-options}
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| networkName | string | Identifier for your agent network | Required |
| address | oAddress | Network address | o://leader |
| type | NodeType | Node type | NodeType.LEADER |
| methods | object | Custom methods exposed by the leader | {} |
| registry | RegistryTool | Registry implementation (memory or persistent) | RegistryMemoryTool |
Joining Agents to Network {#joining-agents-to-network}
From an Agent Node {#agent-join-example}
import { oNode } from '@olane/o-node';
import { oAddress } from '@olane/o-core';
// Create an agent node
const agent = new oNode({
address: new oAddress('o://company/sales/analyst'),
parent: new oAddress('o://company/sales'),
protocols: ['lead-qualification', 'deal-analysis']
});
// Join the network via leader
await agent.use(oAddress.leader(), {
method: 'join_network',
params: {
caller: agent.address.toString(),
parent: 'o://company/sales',
transports: ['webrtc']
}
});
console.log('Agent successfully joined network');
// Agent is now discoverable via registryJoin Request Validation {#join-request-validation}
Customize validation logic for network access control:
import { oLeaderNode } from '@olane/o-leader';
import { oRequest } from '@olane/o-core';
class CustomLeader extends oLeaderNode {
// Override to implement custom validation
async validateJoinRequest(request: oRequest): Promise<boolean> {
const { caller, parent } = request.params;
// Example: Check parent authorization
if (!this.isAuthorizedParent(parent)) {
throw new Error('Unauthorized parent address');
}
// Example: Verify security requirements
if (!this.meetsSecurityRequirements(caller)) {
throw new Error('Agent does not meet security requirements');
}
return true;
}
private isAuthorizedParent(parent: string): boolean {
// Your authorization logic
return parent.startsWith('o://company/');
}
private meetsSecurityRequirements(caller: string): boolean {
// Your security validation logic
return true;
}
}Using the Registry {#using-the-registry}
Registering Agent Capabilities {#registering-capabilities}
import { oAddress } from '@olane/o-core';
// Commit agent to registry with its capabilities
await leader.use(new oAddress('o://registry'), {
method: 'commit',
params: {
peerId: 'QmXxxx...', // libp2p peer ID
address: 'o://company/finance/analyst',
staticAddress: 'analyst-prod-01', // Stable reference
protocols: [ // Agent capabilities
'financial-analysis',
'report-generation',
'data-visualization'
],
transports: ['webrtc', 'websocket'] // Supported transports
}
});
console.log('Agent registered in registry');Searching for Agents {#searching-agents}
// Find agents by capability
const analysts = await leader.use(new oAddress('o://registry'), {
method: 'search',
params: {
protocols: ['financial-analysis']
}
});
console.log(`Found ${analysts.result.data.length} financial analysts`);
// Find agent by address
const agent = await leader.use(new oAddress('o://registry'), {
method: 'search',
params: {
address: 'o://company/finance/analyst'
}
});
console.log('Found agent:', agent.result.data[0]);
// Find by static address (for stable references)
const stable = await leader.use(new oAddress('o://registry'), {
method: 'search',
params: {
staticAddress: 'analyst-prod-01'
}
});
console.log('Found stable agent reference');Listing All Agents {#listing-agents}
// Get all registered agents in the network
const allAgents = await leader.use(new oAddress('o://registry'), {
method: 'find_all',
params: {}
});
console.log(`Network has ${allAgents.result.data.length} active agents`);
allAgents.result.data.forEach(agent => {
console.log(`- ${agent.address} (${agent.protocols.join(', ')})`);
});Network Indexing {#network-indexing-usage}
Triggering Network Index {#trigger-index}
import { oAddress } from '@olane/o-core';
// Trigger a full network index
await leader.use(oAddress.leader(), {
method: 'index_network',
params: {}
});
console.log('Network indexing complete');
// This crawls all registered agents and indexes their capabilitiesTip: Run indexing on a schedule (e.g., every 5-10 minutes) or trigger it after significant network changes.
Custom Indexing Logic {#custom-indexing}
Extend the leader to implement custom indexing:
import { oLeaderNode } from '@olane/o-leader';
import { oRequest, oAddress, RestrictedAddresses } from '@olane/o-core';
class CustomLeader extends oLeaderNode {
// Override the default indexing logic
async _tool_index_network(request: oRequest): Promise<any> {
// Get all registered nodes
const nodes = await this.use(
new oAddress(RestrictedAddresses.REGISTRY),
{ method: 'find_all', params: {} }
);
console.log(`Indexing ${nodes.result.data.length} nodes`);
// Custom indexing per node
for (const node of nodes.result.data) {
const capabilities = await this.indexNodeCapabilities(node);
await this.storeCapabilities(node.address, capabilities);
}
return {
message: 'Network indexed with custom logic!',
nodesIndexed: nodes.result.data.length
};
}
private async indexNodeCapabilities(node: any) {
// Your custom capability indexing logic
return { /* capabilities */ };
}
private async storeCapabilities(address: string, capabilities: any) {
// Store in your custom storage
}
}Advanced Topics {#advanced-topics}
Custom Registry Implementations {#custom-registry}
The default RegistryMemoryTool stores registrations in memory (great for development but not persistent). For production, implement a persistent registry backed by a database.
Persistent Registry Example {#persistent-registry-example}
import { RegistryTool } from '@olane/o-leader';
import { oRequest } from '@olane/o-core';
class PostgresRegistryTool extends RegistryTool {
constructor(config: oNodeToolConfig, private db: Database) {
super(config);
}
async _tool_commit(request: oRequest): Promise<any> {
const params = request.params as oRegistrationParams;
await this.db.query(`
INSERT INTO agent_registry (peer_id, address, protocols, transports)
VALUES ($1, $2, $3, $4)
ON CONFLICT (peer_id) DO UPDATE SET
address = EXCLUDED.address,
protocols = EXCLUDED.protocols,
transports = EXCLUDED.transports,
updated_at = NOW()
`, [params.peerId, params.address, params.protocols, params.transports]);
// Return raw data - base class wraps it in { success, data, error }
return { peerId: params.peerId, address: params.address };
}
async _tool_search(request: oRequest): Promise<any> {
const params = request.params as oRegistrySearchParams;
// Implement database search logic
}
// Implement other methods...
}Using Custom Registry {#using-custom-registry}
import { oLeaderNode } from '@olane/o-leader';
// Initialize your database connection
const db = new Database(dbConfig);
// Create custom registry instance
const registry = new PostgresRegistryTool(config, db);
// Use custom registry in leader
const leader = new oLeaderNode({
networkName: 'production-network',
registry: registry // Use persistent registry instead of memory
});
await leader.start();
console.log('Leader using persistent registry');Production Recommendation: Always use a persistent registry (PostgreSQL, Redis, MongoDB) for production networks.
Multi-Leader Networks {#multi-leader-networks}
When to Use Multiple Leaders {#when-multi-leader}
Use multiple leaders when you need:
- Geographic Distribution: Leader per region for low latency
- Organizational Boundaries: Leader per department or team
- Fault Tolerance: Backup leaders for high availability
- Scale: Distribute coordination load across leaders
Federation Pattern {#federation-pattern}
// Primary leader
const primaryLeader = new oLeaderNode({
networkName: 'global-network',
address: new oAddress('o://leader/primary')
});
// Regional leaders
const usLeader = new oLeaderNode({
networkName: 'us-network',
address: new oAddress('o://leader/us'),
parent: 'o://leader/primary'
});
const euLeader = new oLeaderNode({
networkName: 'eu-network',
address: new oAddress('o://leader/eu'),
parent: 'o://leader/primary'
});
// Each regional leader manages its own registry
// but can query primary leader for global discoveryPattern: Regional leaders handle local coordination while primary leader maintains global visibility.
Security and Access Control {#security-access-control}
Network Access Policies {#access-policies}
class SecureLeader extends oLeaderNode {
private allowedNetworks = new Set(['o://company/*']);
private requiresAuth = true;
async validateJoinRequest(request: oRequest): Promise<boolean> {
const { caller, authToken } = request.params;
// Verify authentication
if (this.requiresAuth && !this.verifyToken(authToken)) {
throw new Error('Authentication required');
}
// Check network authorization
const isAllowed = Array.from(this.allowedNetworks).some(pattern =>
this.matchesPattern(caller, pattern)
);
if (!isAllowed) {
throw new Error(`Address ${caller} not authorized for this network`);
}
// Rate limiting
if (this.exceedsJoinRateLimit(request.metadata.peerId)) {
throw new Error('Join rate limit exceeded');
}
return true;
}
}Registry Access Control {#registry-access-control}
import { RegistryTool } from '@olane/o-leader';
import { oRequest } from '@olane/o-core';
class ProtectedRegistry extends RegistryTool {
// Override search to add permission checks
async _tool_search(request: oRequest): Promise<any> {
// Verify requesting agent has permission
const requester = request.metadata.caller;
if (!this.canSearch(requester)) {
throw new Error('Insufficient permissions for registry search');
}
// Filter results based on permissions
const results = await this.performSearch(request.params);
return this.filterByPermissions(results, requester);
}
private canSearch(caller: string): boolean {
// Your permission logic
return caller.startsWith('o://company/');
}
private filterByPermissions(results: any[], caller: string): any[] {
// Filter results based on caller permissions
return results.filter(agent => this.hasAccessTo(caller, agent));
}
private hasAccessTo(caller: string, agent: any): boolean {
// Your access control logic
return true;
}
}Network Health Monitoring {#network-health}
Health Check Implementation {#health-check-implementation}
class MonitoredLeader extends oLeaderNode {
async getNetworkHealth(): Promise<NetworkHealth> {
const allAgents = await this.use(
new oAddress('o://registry'),
{ method: 'find_all', params: {} }
);
const agents = allAgents.result.data;
const health = {
totalAgents: agents.length,
activeAgents: 0,
unhealthyAgents: [],
timestamp: Date.now()
};
// Check each agent
for (const agent of agents) {
const isHealthy = await this.checkAgentHealth(agent);
if (isHealthy) {
health.activeAgents++;
} else {
health.unhealthyAgents.push(agent.address);
}
}
return health;
}
}Performance Optimization {#performance-optimization}
Registry Caching {#registry-caching}
class CachedRegistry extends RegistryTool {
private cache = new Map<string, CacheEntry>();
private cacheTTL = 60000; // 1 minute
async _tool_search(request: oRequest): Promise<any> {
const cacheKey = this.getCacheKey(request.params);
const cached = this.cache.get(cacheKey);
if (cached && !this.isExpired(cached)) {
return cached.data;
}
const results = await this.performSearch(request.params);
this.cache.set(cacheKey, {
data: results,
timestamp: Date.now()
});
return results;
}
}Protocol Indexing Optimization
class OptimizedRegistry extends RegistryMemoryTool {
// Maintain protocol -> agents mapping for O(1) lookups
private protocolIndex = new Map<string, Set<string>>();
async _tool_commit(request: oRequest): Promise<any> {
const result = await super._tool_commit(request);
// Update protocol index
const { peerId, protocols } = request.params;
protocols.forEach(protocol => {
if (!this.protocolIndex.has(protocol)) {
this.protocolIndex.set(protocol, new Set());
}
this.protocolIndex.get(protocol)!.add(peerId);
});
return result;
}
async _tool_search(request: oRequest): Promise<any> {
const { protocols } = request.params;
if (protocols) {
// Fast lookup using index
const peerIds = this.findByProtocolIndex(protocols);
return peerIds.map(id => this.registry.get(id));
}
return super._tool_search(request);
}
}Best Practices {#best-practices}
Network Design Patterns {#network-design-patterns}
1. Hierarchical Organization {#hierarchical-organization}
Structure your network to mirror your business domains:
o://leader # Root coordinator
└── o://company # Company root
├── o://company/finance # Finance department
│ ├── o://company/finance/analyst
│ └── o://company/finance/reporting
└── o://company/engineering # Engineering department
├── o://company/engineering/backend
└── o://company/engineering/frontend2. Capability-Based Discovery {#capability-based-discovery}
Register agents with clear, specific capabilities:
// ✅ Good: Specific capabilities
protocols: ['payment-processing', 'stripe-api', 'refund-handling']
// ❌ Avoid: Generic capabilities
protocols: ['payments', 'api', 'processing']3. Static Addresses for Stability {#static-addresses}
Use static addresses for production agents that need stable references:
// Register agent with both dynamic and static addresses
{
address: 'o://company/finance/analyst', // Dynamic (changes with restarts)
staticAddress: 'analyst-prod-01' // Stable (consistent reference)
}Why? Static addresses allow reliable references even when agent instances change.
Operational Guidelines {#operational-guidelines}
Registry Maintenance {#registry-maintenance}
- TTL Strategy: Implement time-to-live for registry entries to auto-cleanup stale agents
- Health Checks: Periodically verify registered agents are still responsive
- Cleanup: Remove inactive agents to keep registry performant
Network Indexing {#indexing-guidelines}
- Scheduled Indexing: Run network indexing on a schedule (e.g., every 5 minutes)
- Event-Driven: Trigger indexing when significant network changes occur
- Incremental: For large networks, implement incremental indexing
Monitoring and Observability {#monitoring-observability}
// Log key network events
leader.on('agent:joined', (agent) => {
logger.info('Agent joined network', {
address: agent.address,
capabilities: agent.protocols,
timestamp: Date.now()
});
});
leader.on('agent:removed', (agent) => {
logger.info('Agent removed from network', {
address: agent.address,
timestamp: Date.now()
});
});
// Metrics collection
const metrics = {
totalAgents: await registry.count(),
joinRate: calculateJoinRate(),
searchLatency: measureSearchLatency(),
networkHealth: await leader.getNetworkHealth()
};Security Best Practices {#security-best-practices}
Follow these security guidelines for production networks:
- Validate Join Requests - Always implement custom
validateJoinRequest()logic with business rules - Use Authentication - Require authentication tokens for all join requests
- Rate Limiting - Implement rate limiting on join requests to prevent abuse
- Permission-Based Access - Filter registry results based on caller permissions
- Monitor Patterns - Watch for unusual patterns (rapid joins, suspicious addresses)
- Encrypted Transports - Prefer WebRTC over WebSocket for sensitive data
Troubleshooting {#troubleshooting}
Common Issues {#common-issues}
Agents Can't Join Network {#agents-cant-join}
Symptoms: Join requests fail or timeout Solutions:
- Verify leader is running and accessible
- Check network connectivity between agent and leader
- Review
validateJoinRequestlogic for rejections - Ensure parent address exists in hierarchy
Registry Search Returns No Results {#registry-no-results}
Symptoms: Search returns empty array when agents should exist
Solutions:
- Verify agents committed to registry successfully
- Check search parameters match registered protocols/addresses
- Inspect registry state:
await registry.find_all() - Look for typos in protocol names or addresses
Network Indexing Fails {#indexing-fails}
Symptoms: Index operation throws errors or never completes
Solutions:
- Check for agents that are unresponsive or disconnected
- Implement timeout logic for indexing individual agents
- Add error handling to continue indexing after failures
- Monitor agent count vs indexed count
Memory Issues with Large Networks {#memory-issues}
Symptoms: Registry memory grows unbounded
Solutions:
- Implement persistent registry instead of in-memory
- Add TTL for registry entries
- Implement cleanup for disconnected agents
- Consider registry sharding for very large networks
Debugging Tips {#debugging-tips}
Enable Debug Logging {#debug-logging}
# Enable debug output for o-leader and o-protocol
DEBUG=o-protocol:*,o-leader:* node your-app.jsInspect Registry State {#inspect-registry}
const allAgents = await leader.use(
new oAddress('o://registry'),
{ method: 'find_all', params: {} }
);
if (allAgents.result.success) {
console.log('Registry state:', JSON.stringify(allAgents.result.data, null, 2));
} else {
console.error('Failed to fetch registry:', allAgents.result.error);
}Monitor Join Requests
class DebugLeader extends oLeaderNode {
async _tool_join_network(request: oRequest): Promise<any> {
console.log('Join request received:', {
caller: request.params.caller,
parent: request.params.parent,
transports: request.params.transports,
timestamp: Date.now()
});
try {
return await super._tool_join_network(request);
} catch (error) {
console.error('Join request failed:', error);
throw error;
}
}
}API Reference {#api-reference}
Response Structure {#response-structure}
All use() calls return responses following the standard Olane response wrapping pattern:
const response = await leader.use(new oAddress('o://registry'), {
method: 'find_all',
params: {}
});
// Response structure:
// {
// jsonrpc: "2.0",
// id: "request-id",
// result: {
// success: boolean, // Whether the operation succeeded
// data: any, // The returned data (on success)
// error?: string // Error details (on failure)
// }
// }
// Always check success before accessing data
if (response.result.success) {
const agents = response.result.data;
console.log(`Found ${agents.length} agents`);
} else {
console.error('Error:', response.result.error);
}Important: Access data via
response.result.data, notresponse.resultdirectly.
oLeaderNode {#oleadernode}
The main class for creating a leader node.
Constructor {#oleadernode-constructor}
constructor(config: oNodeToolConfig)Parameters:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| config.networkName | string | Yes | Network identifier |
| config.address | oAddress | No | Leader node address (default: o://leader) |
| config.type | NodeType | No | Must be NodeType.LEADER |
| config.methods | object | No | Custom methods to expose |
Methods {#oleadernode-methods}
validateJoinRequest(request: oRequest): Promise<boolean> {#validate-join}
Override to implement custom join validation logic.
Parameters:
request(oRequest): Join request containing caller, parent, transports
Returns: Promise<boolean> - Resolves to true if valid, throws error if invalid
Example:
async validateJoinRequest(request: oRequest): Promise<boolean> {
if (!isAuthorized(request.params.caller)) {
throw new Error('Unauthorized');
}
return true;
}_tool_join_network(request: oRequest): Promise<any> {#tool-join-network}
Processes agent join requests.
Request Parameters:
caller(string): Address of joining agentparent(string): Parent address in hierarchytransports(string[]): Available transport protocols
Returns: Promise<any> - Success message
_tool_index_network(request: oRequest): Promise<any> {#tool-index-network}
Indexes all registered agents in the network.
Returns: Promise<any> - Index completion status
_tool_save_plan(request: oRequest): Promise<any> {#tool-save-plan}
Saves network coordination plans.
Request Parameters:
plan(object): Plan to save
Returns: Promise<any> - Success indicator
RegistryTool {#registrytool}
Abstract base class for registry implementations.
Abstract Methods {#registrytool-methods}
_tool_commit(request: oRequest): Promise<ToolResult> {#registry-commit}
Register an agent in the registry.
Request Parameters:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| peerId | string | Yes | Agent's peer ID |
| address | string | No | Agent's o:// address |
| staticAddress | string | No | Stable reference address |
| protocols | string[] | No | Capabilities/protocols |
| transports | string[] | No | Transport protocols |
Returns: Promise<ToolResult> - Success indicator
_tool_search(request: oRequest): Promise<ToolResult> {#registry-search}
Search for agents matching criteria.
Request Parameters:
address(string, optional): Search by addressstaticAddress(string, optional): Search by static addressprotocols(string[], optional): Search by protocols
Returns: Promise<ToolResult> - Array of matching agents
_tool_find_all(request: oRequest): Promise<ToolResult> {#registry-find-all}
List all registered agents.
Returns: Promise<ToolResult> - Array of all registered agents
_tool_remove(request: oRequest): Promise<ToolResult> {#registry-remove}
Remove an agent from registry.
Request Parameters:
peerId(string): Peer ID of agent to remove
Returns: Promise<ToolResult> - Success indicator
RegistryMemoryTool {#registrymemorytool}
Concrete implementation of RegistryTool using in-memory storage.
Usage:
import { RegistryMemoryTool } from '@olane/o-leader';
const registry = new RegistryMemoryTool(config);Characteristics:
- Uses Map data structures for fast lookups
- Protocol indexing for efficient searches
- Not persistent across restarts (use for development only)
Warning: Use a persistent registry (PostgreSQL, Redis) for production networks.
Examples {#examples}
Example 1: Basic Multi-Agent Network {#example-basic-network}
// examples/basic-network.ts
import { oLeaderNode, RegistryMemoryTool } from '@olane/o-leader';
import { oNode } from '@olane/o-node';
import { oAddress } from '@olane/o-core';
async function main() {
// 1. Start leader
const leader = new oLeaderNode({
networkName: 'my-agents',
});
await leader.start();
// 2. Create specialized agents
const analyst = new oNode({
address: 'o://agents/financial-analyst',
protocols: ['financial-analysis', 'report-generation']
});
const dataCollector = new oNode({
address: 'o://agents/data-collector',
protocols: ['data-fetching', 'api-integration']
});
// 3. Join agents to network
await analyst.use(oAddress.leader(), {
method: 'join_network',
params: {
caller: 'o://agents/financial-analyst',
parent: 'o://agents',
transports: ['webrtc']
}
});
await dataCollector.use(oAddress.leader(), {
method: 'join_network',
params: {
caller: 'o://agents/data-collector',
parent: 'o://agents',
transports: ['webrtc']
}
});
// 4. Use registry to find agents
const agents = await leader.use(new oAddress('o://registry'), {
method: 'find_all',
params: {}
});
console.log(`Network has ${agents.result.data.length} agents`);
}Example 2: Secure Network with Validation {#example-secure-network}
// examples/secure-network.ts
import { oLeaderNode } from '@olane/o-leader';
import { oRequest } from '@olane/o-core';
class SecureLeader extends oLeaderNode {
private authorizedDomains = ['o://company', 'o://partners'];
async validateJoinRequest(request: oRequest): Promise<boolean> {
const { caller, authToken } = request.params;
// Verify auth token
if (!this.verifyAuthToken(authToken)) {
throw new Error('Invalid authentication token');
}
// Check domain authorization
const isAuthorized = this.authorizedDomains.some(domain =>
caller.startsWith(domain)
);
if (!isAuthorized) {
throw new Error(`Domain not authorized: ${caller}`);
}
this.logger.info(`Agent ${caller} authorized to join`);
return true;
}
private verifyAuthToken(token: string): boolean {
// Your token verification logic
return token === 'valid-secret';
}
}
const leader = new SecureLeader({
networkName: 'secure-network'
});
await leader.start();Example 3: Capability Discovery {#example-capability-discovery}
// examples/capability-discovery.ts
import { oAddress } from '@olane/o-core';
async function findAnalysisAgents(leader: oLeaderNode) {
// Search for agents with specific capabilities
const result = await leader.use(new oAddress('o://registry'), {
method: 'search',
params: {
protocols: ['financial-analysis', 'data-visualization']
}
});
const agents = result.result.data;
console.log(`Found ${agents.length} agents with analysis capabilities`);
// Connect to first available agent
if (agents.length > 0) {
const targetAgent = new oAddress(agents[0].address);
const analysis = await leader.use(targetAgent, {
method: 'analyze_portfolio',
params: { portfolio: 'tech-stocks' }
});
return analysis;
}
}Example 4: Network Health Dashboard {#example-health-dashboard}
// examples/health-dashboard.ts
import { oLeaderNode, RegistryMemoryTool } from '@olane/o-leader';
class HealthMonitorLeader extends oLeaderNode {
async getNetworkDashboard() {
const allAgents = await this.use(
new oAddress('o://registry'),
{ method: 'find_all', params: {} }
);
const agents = allAgents.result.data;
const dashboard = {
totalAgents: agents.length,
byCapability: this.groupByCapability(agents),
byDomain: this.groupByDomain(agents),
health: await this.checkAllAgentsHealth(agents)
};
return dashboard;
}
private groupByCapability(agents: any[]) {
const groups = new Map<string, number>();
agents.forEach(agent => {
agent.protocols.forEach(protocol => {
groups.set(protocol, (groups.get(protocol) || 0) + 1);
});
});
return Object.fromEntries(groups);
}
private groupByDomain(agents: any[]) {
const groups = new Map<string, number>();
agents.forEach(agent => {
const domain = agent.address.split('/').slice(0, 3).join('/');
groups.set(domain, (groups.get(domain) || 0) + 1);
});
return Object.fromEntries(groups);
}
private async checkAllAgentsHealth(agents: any[]) {
// Implementation for health checks
return { healthy: agents.length, unhealthy: 0 };
}
}Migration Guide {#migration-guide}
From Manual Coordination to o-leader {#migration-manual-to-leader}
Before: Manual Agent Management {#before-manual}
// Manually tracking agents
const agents = {
'analyst-1': { address: '...', capabilities: [...] },
'collector-1': { address: '...', capabilities: [...] }
};
// Manual discovery
function findAgent(capability) {
for (const [id, agent] of Object.entries(agents)) {
if (agent.capabilities.includes(capability)) {
return agent;
}
}
}After: With o-leader {#after-with-leader}
// Automatic registration and discovery
const leader = new oLeaderNode({ networkName: 'agents' });
// Agents self-register
await agent.use(oAddress.leader(), {
method: 'join_network',
params: { caller: agent.address, parent, transports }
});
// Automatic discovery via registry
const agents = await leader.use(new oAddress('o://registry'), {
method: 'search',
params: { protocols: ['analysis'] }
});From Other Frameworks {#migration-from-frameworks}
From LangGraph {#migration-from-langgraph}
LangGraph requires explicit graph definitions. With o-leader, agents discover each other:
// LangGraph: Pre-defined graph
const graph = new StateGraph({
nodes: ['analyst', 'collector', 'reporter'],
edges: [['analyst', 'reporter'], ['collector', 'reporter']]
});
// o-leader: Emergent connections
const leader = new oLeaderNode({ networkName: 'agents' });
// Agents discover each other via registry
// No pre-defined connections neededFrom CrewAI {#migration-from-crewai}
CrewAI uses explicit crew definitions. o-leader enables dynamic crews:
// CrewAI: Static crew
const crew = new Crew({
agents: [analyst, researcher, writer],
tasks: [research_task, analysis_task, writing_task]
});
// o-leader: Dynamic discovery
const agents = await leader.use(new oAddress('o://registry'), {
method: 'search',
params: { protocols: ['research', 'analysis', 'writing'] }
});
// Agents can form dynamic collaborationsFAQ {#faq}
General Questions {#faq-general}
Do I need a leader node for every Olane OS network? Yes, every network needs at least one leader node. It serves as the entry point and coordination hub for the network.
Can I have multiple leader nodes? Yes, for large or distributed networks, you can implement a federation pattern with regional leaders. See Multi-Leader Networks.
Is the registry required? The registry is built into the leader node and is essential for agent discovery. You cannot run a leader without a registry.
What happens if the leader node goes down? Existing agent connections remain active, but new agents cannot join. Implement leader failover for high availability.
Technical Questions {#faq-technical}
How does registry search performance scale? In-memory registry is O(n) for searches. For large networks (1000+ agents), implement indexed or database-backed registries.
Can I customize the registry implementation?
Yes, extend RegistryTool to implement custom storage backends (PostgreSQL, Redis, MongoDB, etc.). See Custom Registry Implementations.
What transports does the leader support? The leader supports all libp2p transports: WebRTC, WebSocket, TCP, QUIC, etc.
How do I handle registry cleanup? Implement TTL logic in your registry and periodic cleanup of disconnected agents. See Registry Maintenance.
Operational Questions {#faq-operational}
How often should I run network indexing? Depends on network dynamics:
- Stable networks: Every 5-10 minutes
- Dynamic networks: Event-driven indexing
- Large networks: Incremental indexing
What's the recommended registry backend for production? For production, use a persistent registry (PostgreSQL, Redis, MongoDB) instead of in-memory. See Persistent Registry Example.
How do I monitor leader health? Implement health check endpoints and monitoring using your observability tools. See Network Health Monitoring.
Can agents join from different networks? Yes, as long as they can reach the leader node and pass validation.
Support and Resources {#support-resources}
Documentation {#documentation-links}
- Olane OS Overview - Learn about Olane OS architecture
- o-core - Core protocol and addressing
- o-node - Build agent nodes
- o-lane - Add intelligence to nodes
Examples {#example-projects}
- Basic Network Setup - Simple multi-agent network
- Secure Network - Custom validation and access control
- Multi-Region Federation - Distributed leader pattern
- Health Monitoring - Network observability
Community {#community}
- GitHub Issues - Report bugs or request features
- Discussions - Ask questions and share ideas
- Discord - Join our community
Commercial Support {#commercial-support}
For enterprise support, custom implementations, or consulting:
- Email: [email protected]
- Website: olane.io/enterprise
Contributing {#contributing}
We welcome contributions! See CONTRIBUTING.md for guidelines.
Areas for Contribution {#contribution-areas}
- Registry Backends: PostgreSQL, Redis, MongoDB implementations
- Performance: Optimization and scaling improvements
- Security: Enhanced validation and access control
- Documentation: Guides, tutorials, and examples
- Examples: Real-world use cases and patterns
License {#license}
ISC License - see LICENSE for details.
