Aleph
Architecture

Gateway Architecture

WebSocket control plane on port 18790, JSON-RPC 2.0 protocol, multi-channel routing, middleware pipeline, event distribution, and connection lifecycle.

The Gateway is the single entry point for all communication with Aleph. It runs a WebSocket server on ws://127.0.0.1:18790/ws, speaks JSON-RPC 2.0, and routes requests from any connected client -- whether a native macOS app, a Telegram bot, or the terminal CLI -- to the appropriate handler. This page covers the protocol layer, message pipeline, multi-channel routing, and the event system that powers real-time streaming.

For how sessions are managed once a request arrives, see Session Service. For what happens after routing reaches the agent, see Agent Harness.


Gateway Components

┌─────────────────────────────────────────────────────────────────────┐
│                        Gateway Server                               │
│                  ws://127.0.0.1:18790/ws                            │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌──────────────┐     ┌──────────────┐     ┌──────────────┐        │
│  │   Inbound    │     │   Handler    │     │   Outbound   │        │
│  │   Router     │ ──▶ │   Registry   │ ──▶ │   Emitter    │        │
│  │              │     │              │     │              │        │
│  │ • Parse req  │     │ • Route      │     │ • Stream     │        │
│  │ • Validate   │     │ • Execute    │     │ • Events     │        │
│  └──────────────┘     └──────────────┘     └──────────────┘        │
│                                                                     │
│  ┌──────────────┐     ┌──────────────┐     ┌──────────────┐        │
│  │   Session    │     │    Event     │     │  Interface   │        │
│  │   Manager    │     │     Bus      │     │   Registry   │        │
│  │              │     │              │     │              │        │
│  │ • SQLite     │     │ • Pub/Sub    │     │ • Telegram   │        │
│  │ • Compaction │     │ • Topics     │     │ • Discord    │        │
│  │ • History    │     │ • Subscribe  │     │ • iMessage   │        │
│  └──────────────┘     └──────────────┘     └──────────────┘        │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

The Gateway has six core components:

  • Inbound Router -- Parses incoming JSON-RPC frames, validates structure, and dispatches to the correct handler
  • Handler Registry -- Maps method names (e.g., agent.run, session.list) to handler functions
  • Outbound Emitter -- Sends streaming events and final responses back to the client
  • Session Manager -- Persists conversation history in SQLite, handles compaction
  • Event Bus -- Pub/sub system for real-time event distribution with glob-pattern subscriptions
  • Interface Registry -- Tracks active chat integrations (Telegram, Discord, iMessage)

JSON-RPC 2.0 Protocol

All communication uses JSON-RPC 2.0 over WebSocket frames. There are three message types.

Request (Client to Gateway)

{
  "jsonrpc": "2.0",
  "id": "uuid-xxx",
  "method": "agent.run",
  "params": {
    "message": "Hello, what can you do?",
    "session_key": "agent:main:main"
  }
}

The id field is a client-generated UUID. The Gateway returns the same id in the response so the client can match request to response.

Response (Gateway to Client)

{
  "jsonrpc": "2.0",
  "id": "uuid-xxx",
  "result": {
    "run_id": "run-123",
    "status": "running"
  }
}

For agent.run, the initial response confirms the run has started. The actual AI output arrives via streaming events.

Event (Gateway to Client, no id)

{
  "jsonrpc": "2.0",
  "method": "event",
  "params": {
    "topic": "stream.chunk",
    "data": {
      "run_id": "run-123",
      "content": "Hello! I can help you with..."
    }
  }
}

Events are server-initiated notifications (no id field). Clients subscribe to event topics via the events.subscribe method.


RPC Method Reference

Agent Methods

MethodDescriptionKey Parameters
agent.runStart agent executionmessage, session_key, thinking?, model?
agent.statusGet run statusrun_id
agent.cancelCancel a running agentrun_id
agent.abortForce-abort immediatelyrun_id

Session Methods

MethodDescriptionKey Parameters
session.getGet session infosession_key
session.listList all sessionsfilter?
session.historyGet message historysession_key, limit?
session.compactTrigger compressionsession_key
session.deleteDelete a sessionsession_key

Configuration Methods

MethodDescriptionKey Parameters
config.getGet current config--
config.patchPartial update (JSON Merge Patch)patch
config.applyFull config replaceconfig
config.reloadReload from disk--

Event Methods

MethodDescriptionKey Parameters
events.subscribeSubscribe to event topicpattern (glob)
events.unsubscribeUnsubscribepattern
events.listList active subscriptions--

Memory Methods

MethodDescriptionKey Parameters
memory.storeStore a factcontent, metadata?
memory.searchSearch factsquery, limit?
memory.deleteDelete a factfact_id
memory.statsGet memory statistics--

Browser Methods (CDP)

MethodDescriptionKey Parameters
browser.navigateGo to URLurl
browser.clickClick elementselector
browser.typeType textselector, text
browser.screenshotTake screenshotselector?
browser.evaluateRun JavaScriptscript

Other Method Domains

DomainMethods
auth.*connect, pairing.approve, pairing.reject, devices.list
interface.*status, config, login
mcp.*start, stop, list, call
plugins.*install, uninstall, list, enable, disable
skills.*list, install, activate
runs.*list, status, wait, queue
models.*list, config
generation.*image, video
cron.*list, add, remove, run

Message Pipeline

When a WebSocket frame arrives, the Gateway processes it through a layered middleware pipeline:

WebSocket Frame


┌────────────────────────────────┐
│ 1. Frame Parsing               │
│    • Deserialize JSON-RPC      │
│    • Validate structure        │
│    • Extract method + params   │
└────────────────┬───────────────┘


┌────────────────────────────────┐
│ 2. Middleware Pipeline         │
│    • TraceLayer (span/req-id)  │
│    • MetricsLayer (timing)     │
│    • AuthLayer (auth check)    │
│    • RateLimitLayer (quota)    │
│    • ValidateLayer (schema)    │
│    • HandlerService (dispatch) │
└────────────────┬───────────────┘


┌────────────────────────────────┐
│ 3. Handler Dispatch            │
│    • Look up method in registry│
│    • agent.run → ExecutionEngine│
│    • session.* → SessionManager│
│    • config.* → ConfigManager  │
└────────────────┬───────────────┘


┌────────────────────────────────┐
│ 4. Execution                   │
│    • Handler processes request │
│    • May spawn AgentLoop       │
│    • Streams events via EventBus│
└────────────────┬───────────────┘


┌────────────────────────────────┐
│ 5. Response                    │
│    • JSON-RPC result frame     │
│    • Or JSON-RPC error frame   │
└────────────────────────────────┘

The middleware pipeline runs for every JSON-RPC request in this order:

  1. TraceLayer -- Attaches request spans and trace IDs for observability
  2. MetricsLayer -- Records request timing and method-level metrics
  3. AuthLayer -- Validates authentication (skipped when auth.mode = "none")
  4. RateLimitLayer -- Enforces per-identity, per-scope rate limits
  5. ValidateLayer -- Validates request schema before reaching the handler
  6. HandlerService -- Dispatches to the registered handler for execution

Multi-Channel Routing

Aleph supports multiple chat interfaces simultaneously. Each interface (Telegram, Discord, iMessage, etc.) translates its platform-specific messages into the unified JSON-RPC protocol and forwards them to the Gateway.

Interface Trait

Every channel adapter implements the Interface trait:

#[async_trait]
pub trait Interface: Send + Sync {
    fn name(&self) -> &str;
    async fn start(&self) -> Result<()>;
    async fn stop(&self) -> Result<()>;
    async fn send_message(
        &self,
        target: &InterfaceTarget,
        message: &str,
    ) -> Result<()>;
    fn is_running(&self) -> bool;
}

Available Interfaces

InterfaceFeature FlagPlatform
CLIcliAll platforms
TelegramtelegramAll platforms
DiscorddiscordAll platforms
iMessage(built-in)macOS only
WebChatgatewayAll platforms

Session Key Resolution

Each channel produces a unique session key that determines whether conversations are shared or isolated:

Session Key FormatUse Case
agent:main:mainCross-channel shared session (Main)
agent:main:telegram:dm:user123Per-user Telegram DM (DirectMessage)
agent:main:discord:group:guild-idDiscord server channel (Group)
agent:main:cron:daily-summaryCron job / webhook (Task)
subagent:agent:main:translatorSub-agent delegation (Subagent)
agent:main:ephemeral:uuidTemporary, no persistence (Ephemeral)

DM scope can be configured per interface:

pub enum DmScope {
    Main,           // All DMs share the main session
    PerPeer,        // Isolated session per user (default)
    PerChannelPeer, // Isolated per channel + user
}

Event System

The EventBus provides a pub/sub mechanism for real-time event distribution. Clients subscribe using glob patterns.

Event Topics

PatternEvents
stream.*All streaming events
stream.chunkText content chunks
stream.agent_traceStructured loop-originated execution trace
stream.tool_startTool execution started
stream.tool_endTool execution completed
agent.*Agent lifecycle events
agent.startedRun started
agent.completedRun completed
agent.errorRun error
session.*Session events
config.*Configuration change events
system.tickPeriodic heartbeat (uptime, connections, state version)
system.shutdownServer shutdown notification

Subscribing to Events

{
  "jsonrpc": "2.0",
  "id": "sub-1",
  "method": "events.subscribe",
  "params": { "pattern": "stream.*" }
}

After subscribing, the client receives all matching events as server-initiated notifications until it unsubscribes.


Connection Lifecycle

Authentication Flow

Client WebSocket Connect


┌─────────────────────────────────┐
│ require_auth enabled?           │
│   Yes → First frame must be     │
│         "connect" method        │
│   No  → Direct access allowed   │
└─────────────────────────────────┘

    ▼ (if auth required)
┌─────────────────────────────────┐
│ Validate credentials            │
│   • Bearer token                │
│   • Device fingerprint          │
│   • Public key signature        │
└─────────────────────────────────┘


┌─────────────────────────────────┐
│ Grant session token             │
│ Set client role (operator/node) │
└─────────────────────────────────┘

Authentication mode defaults to Token. Set auth.mode = "none" in config to disable authentication entirely. The legacy require_auth field is still accepted for backward compatibility.

Connect Request

When authentication is enabled, the client must send a connect message as the first frame:

{
  "method": "connect",
  "params": {
    "minProtocol": 1,
    "maxProtocol": 1,
    "client": {
      "id": "macos-app",
      "version": "1.0.0",
      "platform": "macos"
    },
    "role": "operator",
    "auth": {
      "token": "bearer_token"
    }
  }
}

OpenAI-Compatible Routes

In addition to the WebSocket JSON-RPC API, the Gateway exposes an OpenAI-compatible HTTP API on the same port (18790). This allows existing clients and tools that speak the OpenAI API format to connect to Aleph without modification.

RouteMethodDescription
/v1/modelsGETList available models
/v1/models/{model_id}GETGet model details
/v1/chat/completionsPOSTChat completions (streaming + non-streaming)
/v1/embeddingsPOSTText embeddings
/v1/responsesPOSTOpenAI Responses API
/v1/healthGETAPI health check

These routes support bearer token authentication (same token as WebSocket auth when auth.mode = "token"). In development mode (auth.mode = "none"), the endpoints are open.


Hot Reload

The Gateway watches ~/.aleph/config.toml for changes and applies them without restarting:

~/.aleph/config.toml modified


┌─────────────────────────────────┐
│ Debounce (500ms)                │
└─────────────────┬───────────────┘


┌─────────────────────────────────┐
│ Parse + validate new config     │
└─────────────────┬───────────────┘


┌─────────────────────────────────┐
│ Apply changes:                  │
│ • Restart affected interfaces   │
│ • Update routing rules          │
│ • Emit config.changed event     │
└─────────────────────────────────┘

Protocol adapter definitions in ~/.aleph/protocols/ are also hot-reloaded with a similar mechanism (see Architecture Overview for details on the protocol adapter system).


HTTP Endpoints

Alongside the WebSocket server, the Gateway also serves HTTP endpoints on the same port:

EndpointPurpose
/wsWebSocket upgrade endpoint
/healthHealth check (returns 200 OK)
/metricsPrometheus-compatible metrics
/Static files for built-in WebChat UI (ControlPlane)
/loginSession-cookie login page
/auth/loginLogin form submission
/auth/logoutLogout (clears session cookie)

When A2A (Agent-to-Agent) is enabled, additional A2A routes are mounted on the same HTTP server.


Interface Configuration Example

{
  "interfaces": {
    "telegram": {
      "token": "BOT_TOKEN",
      "allowFrom": ["+1234567890"],
      "groups": {
        "*": { "requireMention": true }
      }
    },
    "discord": {
      "token": "BOT_TOKEN",
      "guilds": ["guild-id-1"]
    }
  }
}

Each interface has its own authentication and scope configuration. Telegram can restrict which phone numbers are allowed; Discord can restrict which guilds the bot joins.


On this page