Portal Community

Priority Range: 300+

Post-pipeline hooks use priorities of 300 and above. They see the final, fully-processed envelope and have access to all metadata accumulated during pre-pipeline and processing stages.

Audit Hook Pattern

export class MessageAuditHook extends BaseHook {
  readonly name = 'MessageAuditHook';
  readonly priority = 300;

  constructor(private auditStore: AuditStore) { super(); }

  async execute(context: IPipelineContext): Promise<HookResult> {
    // Non-blocking audit — fire and forget
    this.auditStore.record({
      messageId: context.envelope.meta.id,
      topic: context.envelope.meta.topic,
      serverId: context.envelope.meta.serverId,
      userId: context.metadata.userId as string,
      tenantId: context.metadata.tenantId as string,
      receivedAt: context.envelope.meta.receivedAt.toISOString(),
      bodyHash: await this.hashBody(context.body),
    }).catch(err =>
      console.warn('[MessageAuditHook] Audit write failed (non-critical):', err)
    );

    return { continue: true }; // always continue — audit is non-blocking
  }

  private async hashBody(body: unknown): Promise<string> {
    const json = JSON.stringify(body);
    const buffer = new TextEncoder().encode(json);
    const hash = await crypto.subtle.digest('SHA-256', buffer);
    return Array.from(new Uint8Array(hash))
      .map(b => b.toString(16).padStart(2, '0'))
      .join('');
  }
}

Prometheus Metrics Hook

export class PrometheusMetricsHook extends BaseHook {
  readonly name = 'PrometheusMetricsHook';
  readonly priority = 310;

  async execute(context: IPipelineContext): Promise<HookResult> {
    const labels = {
      topic: (context.envelope.meta.topic || 'unknown').split('.')[0],
      transport: context.envelope.meta.transport,
      server: context.envelope.meta.serverId,
    };

    metrics.counter('edge_stream_messages_total', labels).inc();
    metrics.histogram('edge_stream_message_body_bytes', labels)
      .observe(JSON.stringify(context.body).length);

    return { continue: true };
  }
}
Don't Block Delivery Post-pipeline hooks run synchronously before subscriber delivery. Use fire-and-forget (non-awaited promises) for I/O operations that should not delay subscribers. Always catch errors internally so a failed side-effect does not drop the message.