Skip to main content

What is Discovery?

Discovery is the system that automatically makes functions and triggers available across your entire backend application stack without manual configuration. When a worker connects and registers capabilities, they become immediately available to all other parts of the system.
Discovery eliminates the need for service registries, API gateways, or configuration files. Everything is registered dynamically over the WebSocket protocol.

How Discovery Works

The discovery process follows this flow:
  1. Worker connects to the engine via WebSocket
  2. Worker registers its functions and trigger types
  3. Engine tracks all registrations in thread-safe registries
  4. Functions become callable by any other worker or module
  5. Worker disconnects, all its registrations are cleaned up

Worker Registration

When a worker first connects, the engine assigns it a unique ID and tracks it in the WorkerRegistry:

Worker Lifecycle

1

Connection

Worker establishes WebSocket connection to engine (default port 49134)
2

Worker ID Assignment

Engine generates a UUID and sends WorkerRegistered message:
3

Capability Registration

Worker registers its functions and trigger types:
4

Ready for Work

Worker is now fully integrated and can:
  • Execute functions registered by other workers
  • Have its own functions called by others
  • Register triggers that invoke any function

Function Discovery

Functions are stored in the FunctionsRegistry, a concurrent hash map that allows lock-free lookups:

Function Registration Protocol

External functions are special: they’re stored in external_function_ids and invoke HTTP endpoints instead of local handlers. The engine manages the HTTP lifecycle transparently.

Trigger Discovery

Triggers have a two-tier discovery system:

1. Trigger Type Registration

Workers first declare what trigger types they support:
This gets stored in the TriggerRegistry:

2. Trigger Instance Registration

Once a trigger type exists, any worker can register trigger instances:
If you register a trigger before its type exists, the trigger is stored but not activated. It activates automatically when the trigger type is registered later.

Cross-Runtime Discovery

Discovery works seamlessly across different runtimes. A Python worker can call functions registered by a Node.js worker, and vice versa:

Automatic Cleanup

When a worker disconnects, the engine automatically cleans up all its registrations:
This automatic cleanup ensures that your system stays consistent even when workers crash or disconnect unexpectedly.

Service Discovery

In addition to functions and triggers, workers can register services:
Services are logical groupings that help organize functions but don’t affect routing.

Discovery at Scale

The discovery system is designed for high concurrency:
  • Lock-free reads: Uses DashMap for concurrent hash map access
  • Async message passing: Workers communicate via async channels
  • Non-blocking registration: Functions become available immediately
  • Distributed tracing: All discovery operations are traced via OpenTelemetry

Performance Characteristics

Function Lookup

O(1) hash map lookup with zero lock contention

Registration

Instant - no coordination or consensus required

Worker Cleanup

Async - cleanup runs in background without blocking

Cross-worker Calls

Single WebSocket hop - direct routing via engine

Discovery Events

You can listen for discovery events to react to changes:

Health and Introspection

You can query the discovery state at runtime:

Best Practices

Use namespaced IDs

Prefix functions with service name: users.create, orders.process

Register trigger types early

Register trigger types on worker startup before any triggers

Handle connection loss

Implement reconnection logic with exponential backoff

Monitor registrations

Track worker connection/disconnection events for observability

Next Steps

Architecture

Learn how the engine coordinates discovery across workers

Functions

Deep dive into function registration and invocation