Skip to content

SGNL-ai/set-transmitter-js

Repository files navigation

@sgnl-ai/set-transmitter

CI codecov npm version Known Vulnerabilities License TypeScript

HTTP transmission library for Security Event Tokens (SET) with CAEP/SSF support. Zero runtime dependencies, built on native fetch API.

Built by SGNL.ai as part of our commitment to advancing continuous access evaluation and the shared signals framework.

Features

  • 🚀 Zero runtime dependencies - Uses native fetch API
  • 🔄 Smart retry logic - Exponential backoff with jitter
  • 🎯 Full TypeScript support - Written in TypeScript with complete type definitions
  • Lightweight - Minimal bundle size
  • 🛡️ Comprehensive error handling - Detailed error types and messages
  • 📦 ESM and CommonJS - Dual module support
  • 🔐 CAEP/SSF compliant - Follows RFC 8417 standards

Installation

npm install @sgnl-ai/set-transmitter

Basic Usage

import { transmitSET } from '@sgnl-ai/set-transmitter';

// Transmit a Security Event Token
const result = await transmitSET(
  'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIn0.signature',
  'https://receiver.example.com/events',
  {
    authToken: 'Bearer xyz123',
  }
);

if (result.status === 'success') {
  console.log('Event transmitted successfully:', result.body);
} else {
  console.error('Transmission failed:', result.error);
}

Advanced Usage

With All Options

import { transmitSET } from '@sgnl-ai/set-transmitter';

const result = await transmitSET(jwt, url, {
  // Authentication
  authToken: 'Bearer token', // or just 'token' - Bearer prefix will be added

  // Custom headers
  headers: {
    'User-Agent': 'MyApp/2.0',
    'X-Request-ID': 'req-123',
  },

  // Timeout in milliseconds (default: 30000)
  timeout: 10000,

  // Retry configuration
  retry: {
    maxAttempts: 5,                              // Maximum retry attempts
    retryableStatuses: [429, 502, 503, 504],    // Which HTTP codes to retry
    backoffMs: 2000,                            // Initial backoff in milliseconds
    maxBackoffMs: 30000,                        // Maximum backoff
    backoffMultiplier: 2,                       // Exponential backoff multiplier
  },

  // Response handling
  parseResponse: true,                          // Auto-parse JSON responses
  validateStatus: (status) => status < 400,     // Custom success validation
});

Creating a Reusable Transmitter

import { createTransmitter } from '@sgnl-ai/set-transmitter';

// Create a transmitter with default options
const transmitter = createTransmitter({
  authToken: 'Bearer default-token',
  headers: {
    'User-Agent': 'MyApp/2.0',
  },
  retry: {
    maxAttempts: 5,
    backoffMs: 2000,
  },
});

// Use the transmitter (options can be overridden per call)
const result = await transmitter(jwt, url, {
  headers: {
    'X-Request-ID': 'specific-request-id',
  },
});

Error Handling

import { transmitSET, ValidationError, TimeoutError, NetworkError } from '@sgnl-ai/set-transmitter';

try {
  const result = await transmitSET(jwt, url, options);
  
  if (result.status === 'failed') {
    if (result.retryable) {
      console.log('Request failed but is retryable:', result.error);
    } else {
      console.log('Request failed and is not retryable:', result.error);
    }
    
    // Handle specific status codes
    switch (result.statusCode) {
      case 400:
        console.error('Bad request:', result.body);
        break;
      case 401:
        console.error('Unauthorized - check auth token');
        break;
      case 429:
        console.error('Rate limited:', result.headers['retry-after']);
        break;
    }
  }
} catch (error) {
  if (error instanceof ValidationError) {
    console.error('Invalid input:', error.message);
  } else if (error instanceof TimeoutError) {
    console.error('Request timed out:', error.message);
  } else if (error instanceof NetworkError) {
    console.error('Network error:', error.message);
  }
}

Integration with @sgnl-ai/secevent

import { createBuilder } from '@sgnl-ai/secevent';
import { transmitSET, EventTypes } from '@sgnl-ai/set-transmitter';
import { createPrivateKey } from 'crypto';

// Build the SET
const builder = createBuilder()
  .withIssuer('https://issuer.example.com')
  .withAudience('https://receiver.example.com')
  .withEvent(EventTypes.SESSION_REVOKED, {
    subject: {
      format: 'email',
      email: '[email protected]',
    },
    initiating_entity: 'admin',
    reason_admin: 'Security policy violation',
    event_timestamp: Math.floor(Date.now() / 1000),
  });

// Sign it
const privateKey = createPrivateKey(privateKeyPem);
const { jwt } = await builder.sign({
  key: privateKey,
  kid: 'key-id',
  alg: 'RS256',
});

// Transmit it
const result = await transmitSET(jwt, 'https://receiver.example.com/events', {
  authToken: process.env.AUTH_TOKEN,
});

API Reference

transmitSET(jwt, url, options?)

Main function to transmit a Security Event Token.

Parameters:

  • jwt (string): The signed JWT string
  • url (string): The destination endpoint URL
  • options (TransmitOptions): Optional configuration object

Returns: Promise<TransmitResult>

createTransmitter(defaultOptions?)

Creates a reusable transmitter function with default options.

Parameters:

  • defaultOptions (TransmitOptions): Default options for all transmissions

Returns: Function with signature (jwt, url, options?) => Promise<TransmitResult>

isValidSET(jwt)

Helper function to validate JWT format (basic check).

Parameters:

  • jwt (string): The JWT string to validate

Returns: boolean

EventTypes

Constants for standard CAEP event types:

  • SESSION_REVOKED
  • TOKEN_CLAIMS_CHANGE
  • CREDENTIAL_CHANGE
  • ASSURANCE_LEVEL_CHANGE
  • DEVICE_COMPLIANCE_CHANGE

Types

TransmitOptions

interface TransmitOptions {
  authToken?: string;
  headers?: Record<string, string>;
  timeout?: number;
  retry?: RetryConfig;
  parseResponse?: boolean;
  validateStatus?: (status: number) => boolean;
}

TransmitResult

interface TransmitResult {
  status: 'success' | 'failed';
  statusCode: number;
  body: string | Record<string, unknown>;
  headers: Record<string, string>;
  error?: string;
  retryable?: boolean;
}

RetryConfig

interface RetryConfig {
  maxAttempts?: number;
  retryableStatuses?: number[];
  backoffMs?: number;
  maxBackoffMs?: number;
  backoffMultiplier?: number;
}

Retry Logic

The library implements intelligent retry logic with:

  • Exponential backoff with jitter - Prevents thundering herd problem
  • Retry-After header support - Respects server-specified retry delays
  • Configurable retry conditions - Customize which status codes trigger retries
  • Network error handling - Automatically retries on network failures

Default retryable status codes: 429, 502, 503, 504

Requirements

  • Node.js >= 18.0.0 (for native fetch support)
  • TypeScript >= 5.0 (for TypeScript projects)

Migration from Inline Transmission

If you're currently using inline transmission code in your CAEP actions:

Before:

// Manual transmission with fetch
const response = await fetch(url, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/secevent+jwt',
    'Authorization': `Bearer ${token}`,
  },
  body: jwt,
});

if (!response.ok) {
  // Manual error handling
}

After:

import { transmitSET } from '@sgnl-ai/set-transmitter';

const result = await transmitSET(jwt, url, {
  authToken: token,
});

if (result.status === 'failed') {
  // Automatic retry logic and error handling included
}

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues and questions, please use the GitHub issues page.

About

No description or website provided.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published