Transport Layer
ANCP messages travel over a pluggable transport layer — the same envelope works across SignalR, HTTP, SSE, and EdgeStream pub/sub without modification.
Transport Abstraction
A core design goal of ANCP is that message producers and consumers are isolated from the transport mechanism. The ITransport interface in EdgeStream defines the contract that all transports implement:
interface ITransport {
readonly id: string;
readonly type: 'signalr' | 'http-polling' | 'sse' | 'websocket';
readonly status: 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';
connect(): Promise<void>;
disconnect(): Promise<void>;
send(data: string | ArrayBuffer): Promise<void>;
onMessage(handler: (raw: string | ArrayBuffer) => void): () => void;
onStatusChange(handler: (status: TransportStatus) => void): () => void;
onError(handler: (error: Error) => void): () => void;
}
The ANCP router serializes the envelope to JSON (or another protocol format), hands it to the active transport's send() method, and receives raw bytes from onMessage() — it never knows or cares which physical transport is being used.
Available Transports
SignalR (Primary)
SignalR is the default and recommended transport for ANCP in BizFirstGO. It provides full-duplex WebSocket communication with automatic fallback to HTTP long-polling when WebSockets are unavailable. The ASP.NET Core SignalR hub acts as the ANCP message broker for browser clients and server-side node hosts.
| Property | Value |
|---|---|
| Protocol | WebSocket (primary), HTTP long-polling (fallback) |
| Direction | Full-duplex (send and receive) |
| Authentication | JWT bearer token on connection handshake |
| Auto-reconnect | Yes — exponential backoff with configurable max attempts |
| Group routing | SignalR Groups map to ANCP topic subscriptions |
| Best for | Browser clients, real-time UI updates, low-latency node communication |
Connection Flow
Connect to Hub
Client creates a HubConnection with the SignalR hub URL and a JWT access token factory.
Join Server Scope
EdgeStreamHubAdapter.joinServer() places the client in a SignalR group scoped to its tenant and server ID.
Subscribe to Topics
Client subscribes to ANCP topics; each subscription maps to a SignalR group join (JoinGroup).
Send / Receive Messages
Outgoing ANCP envelopes are sent via invoke('SendMessage', envelope). Incoming messages arrive via ReceiveMessage hub method.
Auto-Reconnect on Disconnect
On network interruption, the adapter re-connects, re-joins groups, and re-subscribes automatically. No ANCP messages are lost in the reconnect buffer.
HTTP (Synchronous)
For server-to-server communication where real-time push is not required, ANCP messages can be sent as HTTP POST requests. This is common for inter-service calls within the backend, webhook delivery, and integrations with external APIs.
| Property | Value |
|---|---|
| Protocol | HTTPS (TLS 1.2+) |
| Direction | Request/response (not full-duplex) |
| Authentication | Bearer token or API key in Authorization header |
| Content-Type | application/json |
| Endpoint pattern | POST /api/ancp/messages |
| Best for | Server-to-server Commands and Queries, webhook ingestion |
POST /api/ancp/messages HTTP/1.1
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
{
"id": "cmd-http-001",
"type": "Command",
"source": "service://tenant-acme/payroll-service",
"destination": "node://tenant-acme/flow-55/notify",
"tenantId": "tenant-acme",
"timestamp": "2026-05-25T09:10:00.000Z",
"protocolVersion": "1.0",
"payload": { "action": "sendPayslip", "employeeId": "E-100" }
}
SSE (Server-Sent Events)
SSE provides a one-way server-to-client push channel. It is a good fit for browser clients that only need to receive ANCP Events (notifications, status updates) without sending messages back. SSE connections survive HTTP/1.1 proxies that would block WebSocket upgrades.
| Property | Value |
|---|---|
| Protocol | HTTP/1.1 (keeps connection alive) |
| Direction | Server → Client only |
| Authentication | Bearer token as query parameter or cookie |
| Reconnect | Browser auto-reconnects using Last-Event-ID header |
| Best for | Read-only dashboards, notification feeds, status indicators |
EdgeStream Pub/Sub
EdgeStream's topic-based pub/sub is the transport for ANCP Events that need to fan out to multiple subscribers. The EdgeStream subscription manager handles wildcard matching, subscriber lifecycle, and message replay from the LRU cache. This is the preferred transport for Event-type ANCP messages.
| Property | Value |
|---|---|
| Pattern | Publish/Subscribe with wildcard topic matching |
| Fan-out | One message delivered to all active subscribers matching the topic |
| Message cache | LRU cache per server (configurable size) |
| Replay | New subscribers can receive recent cached messages on subscribe |
| Best for | ANCP Events, live UI updates, workflow status broadcasts |
Choosing the Right Transport
| Scenario | Recommended Transport | Reason |
|---|---|---|
| Browser real-time updates | SignalR | Full-duplex, auto-reconnect, JWT auth |
| Server-to-server Command | HTTP | Simple, synchronous, no persistent connection needed |
| Read-only notification feed | SSE | One-way push, works behind more proxies |
| Event fan-out to many subscribers | EdgeStream pub/sub | Wildcard routing, fan-out, LRU cache |
| High-volume workflow events | SignalR + EdgeStream | SignalR carries raw messages; EdgeStream routes to subscribers |
Transport Configuration
// TypeScript — configuring a SignalR transport
const transportConfig: TransportConfig = {
type: 'signalr',
url: 'https://api.bizfirst.io/hub',
reconnect: {
maxAttempts: 10, // 0 = infinite
initialDelayMs: 1000, // 1 second before first retry
maxDelayMs: 30000, // cap at 30 seconds
backoffMultiplier: 2.0 // exponential backoff
},
timeoutMs: 10000,
accessToken: getJwtToken() // called on each reconnect
};
Node and agent code that produces or consumes ANCP messages does not reference a transport directly. It calls send() and subscribe() on the EdgeStream facade. The framework selects the appropriate transport based on configuration. To switch transports, update the configuration — no node code changes.
When SignalR cannot establish a WebSocket (corporate proxy, old browser), it automatically falls back to HTTP long-polling. This increases latency (typically 1–3 seconds) but preserves message delivery. Expect higher server load in long-polling mode on large deployments.