Portal Community

1. Envelope (IEnvelope)

Every message in EdgeStream is wrapped in an IEnvelope. The envelope is the universal container that all hooks, subscribers, and pipelines work with — regardless of transport or protocol.

export interface IEnvelope<TBody = unknown> {
  meta: EnvelopeMeta;          // id, transport, topic, timestamps
  direction: 'incoming' | 'outgoing';
  raw: string | ArrayBuffer;   // original bytes as received/to-send
  body: TBody;                 // parsed, typed message body
  headers?: Record<string, string>;
  attributes?: Record<string, unknown>; // set by hooks during pipeline
}

EnvelopeMeta

export interface EnvelopeMeta {
  id: string;                   // UUID for this envelope
  serverId: string;             // which server received this
  transport: 'signalr' | 'http-polling' | 'sse' | 'websocket';
  protocol: 'json' | 'didcomm-v2' | 'cloudevents' | 'custom';
  receivedAt: Date;
  topic?: string;               // routing key, e.g. 'workflow.started'
  correlationId?: string;       // link related messages
  source?: string;              // originating system
  type?: string;                // message type discriminator
}
Typed Body Use the generic parameter to get type safety in subscribers: IEnvelope<WorkflowStartedEvent>. The body property is then typed as WorkflowStartedEvent.

2. Hook

A hook is a composable, single-responsibility message processor. Hooks are ordered by priority and execute sequentially within a pipeline stage. Any hook can abort processing or pause the pipeline waiting for user input.

// Implementing a custom hook
import { BaseHook, IPipelineContext, HookResult } from 'edge-stream-js';

export class TenantValidationHook extends BaseHook {
  readonly name = 'TenantValidationHook';
  readonly priority = 50; // runs after decrypt (90) but before normalization (110)

  async execute(context: IPipelineContext): Promise<HookResult> {
    const tenantId = (context.body as any)?.tenantId;
    if (!tenantId) {
      context.abort('Missing tenantId in message body');
      return { continue: false };
    }
    context.metadata.validatedTenantId = tenantId;
    return { continue: true };
  }
}

Built-in Hook Priority Reference

HookPriorityStage
HookActivityLogger5Both
DecryptHook90Incoming
VerifySignatureHook95Incoming
ValidationHook100Incoming
NormalizationHook110Incoming
PublishToSubscribersHookTerminalIncoming

3. Subscriber

A subscriber is a callback function registered against a topic pattern. When a message envelope is delivered, all matching subscribers are invoked concurrently. Errors in one subscriber do not affect others.

// Subscribing to a topic
const subscription = edgeStream.subscribe(
  'bas',                              // server ID
  'workflow.execution.*',             // topic pattern (wildcard)
  (envelope: IEnvelope<WorkflowEvent>) => {
    console.log('Event:', envelope.body.eventType);
    console.log('Workflow:', envelope.meta.correlationId);
  }
);

// Cleanup when done
subscription.unsubscribe();

4. Server

A server represents a single logical connection to a backend message hub. Each server has its own transport, pipeline, and subscription list. Most applications use a single 'bas' server. Multi-server patterns are used when you need separate pipelines for different message domains.

// Registering a server
edgeStream.registerServer({
  id: 'bas',
  type: 'bas',
  name: 'BizFirst Application Server',
  url: 'https://app.bizfirst.com/edge-stream-hub',
  transportConfig: {
    type: 'signalr',
    url: 'https://app.bizfirst.com/edge-stream-hub',
    accessToken: authToken,
    reconnect: {
      maxAttempts: 0,        // infinite
      initialDelayMs: 1000,
      maxDelayMs: 30000,
      backoffMultiplier: 2
    }
  }
});

5. Transport

A transport is the network layer. It knows how to connect to a remote endpoint and exchange raw bytes. Transports are swappable — the rest of the pipeline never knows which transport is in use.

TransportBest ForDirection
SignalRBrowser apps with auto-reconnect and group routingFull duplex
WebSocketPerformance-critical paths, server-to-serverFull duplex
SSEServer push only, HTTP/1.1 firewallsServer → Client
HTTP PollingStrict firewall environments with no WebSocketEmulated duplex

6. Topic

Topics are dot-delimited string identifiers that route messages to subscribers. EdgeStream supports exact matches and single-level wildcards (.*).

// Topic pattern rules
'workflow.started'          // exact match
'workflow.*'                // matches: workflow.started, workflow.completed, workflow.failed
                            // does NOT match: workflow.execution.started (multi-level)
'process.engine.node.*'     // matches: process.engine.node.started

// Valid pattern characters: alphanumeric, dots, underscores, hyphens
// Wildcard: only allowed at end as .*
// Invalid: 'workflow.*.events', 'workflow..started'
Wildcard Scope The .* wildcard matches exactly one additional segment. workflow.* does not match workflow.execution.started. For deeper matching, subscribe to the explicit topic or use a more specific wildcard pattern.