TypeScript SDK
Enterprise

Enterprise

For self-hosted enterprise deployments, HiveForge provides an EnterpriseLicenseManager that uses a JWT license key instead of deployment credentials. This mode gives you direct access to decrypted API keys (OpenAI, Stripe, etc.) and supports offline operation with configurable grace periods.

Enterprise mode is for self-hosted deployments only. If you are using HiveForge's hosted deployment platform, use the standard HiveForgeClient instead.

Environment Variables

HIVEFORGE_LICENSE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJoaXZlZm9yZ2UuZGV2Iiwic3ViIjoib3JnXzEyMzQ1Njc4OTAiLCJjb21wYW55IjoiQWNtZSBDb3JwIiwidHlwZSI6ImVudGVycHJpc2UifQ.signature
HIVEFORGE_LICENSE_SIGNING_KEY=your-production-signing-key
VariableRequiredDescription
HIVEFORGE_LICENSE_KEYYesJWT license key from HiveForge
HIVEFORGE_LICENSE_SIGNING_KEYNoSigning key for API key decryption (must match server-side key)

Initialization

From Environment Variables

import { createEnterpriseLicenseManagerFromEnv } from '@producthacker/hiveforge-sdk';
 
const license = createEnterpriseLicenseManagerFromEnv({
  debug: process.env.NODE_ENV === 'development',
});
 
// Initialize: decodes JWT, performs phone home, decrypts API keys
await license.initialize();

Options for createEnterpriseLicenseManagerFromEnv():

ParameterTypeRequiredDefaultDescription
apiUrlstringNohttps://api.hiveforge.dev/api/v1/licenses/phone-homePhone home endpoint URL
debugbooleanNofalse (or true in development)Enable debug logging
signingKeystringNoFrom HIVEFORGE_LICENSE_SIGNING_KEY envSigning key for API key decryption

Manual Construction

import { EnterpriseLicenseManager } from '@producthacker/hiveforge-sdk';
 
const license = new EnterpriseLicenseManager(
  'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', // license key
  {
    debug: true,
    signingKey: 'your-production-signing-key',
  }
);
 
await license.initialize();

Constructor parameters:

ParameterTypeRequiredDefaultDescription
licenseKeystringYes--JWT license key
options.debugbooleanNofalseEnable debug logging
options.apiUrlstringNohttps://api.hiveforge.dev/api/v1/licenses/phone-homePhone home URL
options.signingKeystringNo'change-me-in-production'Signing key for decryption
⚠️

The signingKey must match the HIVEFORGE_LICENSE_SIGNING_KEY configured on the HiveForge server that issued the license. Using the default value in production is insecure.

Methods


initialize()

Decode the license JWT, perform phone home validation, and decrypt embedded API keys.

try {
  await license.initialize();
  console.log(`License valid for: ${license.getCompanyName()}`);
  console.log(`Expires: ${license.getExpirationDate()?.toISOString()}`);
} catch (error) {
  // Possible errors:
  // - "Invalid license key format"
  // - "License has expired"
  // - "License phone home failed and grace period expired."
  console.error('License initialization failed:', error.message);
}

Throws: Error if the license is invalid, expired, or phone home fails with no remaining grace period.

During initialization:

  1. The JWT is decoded (header.payload.signature)
  2. Expiration is checked
  3. Phone home is attempted if required by the license
  4. If phone home fails, offline mode begins (within grace period)
  5. API keys are decrypted using the signing key
  6. Background phone home is scheduled

shutdown()

Stop background phone home timer and clean up resources.

license.shutdown();

getApiKey(provider)

Get a specific decrypted API key by provider name.

const openaiKey = license.getApiKey('openai');
const stripeKey = license.getApiKey('stripe');
 
if (openaiKey) {
  // Use OpenAI SDK directly
  const { OpenAI } = await import('openai');
  const openai = new OpenAI({ apiKey: openaiKey });
 
  const response = await openai.chat.completions.create({
    model: 'gpt-4o-mini',
    messages: [{ role: 'user', content: 'Hello!' }],
  });
}

Parameters:

NameTypeDescription
providerstringProvider name (e.g., 'openai', 'stripe', 'sendgrid')

Returns: string | null -- the decrypted API key, or null if not found.


getApiKeys()

Get all decrypted API keys as a key-value map.

const keys = license.getApiKeys();
 
if (keys) {
  console.log('Available providers:', Object.keys(keys));
  // ['openai', 'stripe', 'sendgrid', ...]
}

Returns: Record<string, string> | null


isEnabled(feature)

Check if a feature flag is enabled in the license.

if (license.isEnabled('ai_enabled')) {
  // Use AI features
}
 
if (license.isEnabled('billing_enabled')) {
  // Use billing features
}

Parameters:

NameTypeDescription
featurekeyof FeatureFlagsFeature flag to check

Returns: boolean


getFeatures()

Get the full feature flags object from the license.

const features = license.getFeatures();
 
if (features) {
  console.log('AI enabled:', features.ai_enabled);
  console.log('AI monthly limit:', features.ai_monthly_limit);
  console.log('Support level:', features.support_level);
  console.log('Self-hosted:', features.self_hosted);
}

Returns: FeatureFlags | null


getLimits()

Get license-defined limits (e.g., max users, max projects).

const limits = license.getLimits();
 
console.log('Max users:', limits.max_users);
console.log('Max projects:', limits.max_projects);

Returns: Record<string, number>


isValid()

Check if the license is currently valid -- not expired and within grace period if offline.

if (!license.isValid()) {
  console.error('License is no longer valid. Please renew.');
  process.exit(1);
}

Returns: boolean


getExpirationDate()

Get the license expiration date.

const expires = license.getExpirationDate();
if (expires) {
  const daysRemaining = Math.ceil((expires.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
  console.log(`License expires in ${daysRemaining} days`);
}

Returns: Date | null


getCompanyName()

Get the company name from the license payload.

const company = license.getCompanyName();
console.log(`Licensed to: ${company}`);
// "Licensed to: Acme Corp"

Returns: string | null


phoneHome(metrics?)

Manually trigger a phone home to validate the license with the HiveForge server.

try {
  const response = await license.phoneHome({
    active_users: 42,
    api_calls_today: 1500,
  });
 
  console.log('License status:', response.status);
  console.log('Next phone home in:', response.next_phone_home_seconds, 'seconds');
  console.log('Expires at:', response.expires_at);
} catch (error) {
  console.error('Phone home failed:', error.message);
}

Parameters:

NameTypeRequiredDescription
metricsRecord<string, unknown>NoOptional usage metrics to report

Returns (PhoneHomeResponse):

FieldTypeDescription
status'valid' | 'invalid' | 'expired' | 'revoked'License validation status
next_phone_home_secondsnumberSeconds until next phone home should occur
featuresFeatureFlagsUpdated feature flags from server
limitsRecord<string, number>Updated limits from server
expires_atstringISO timestamp of license expiration
commandsArray<{ type, data }>Server-issued commands (e.g., force update)

License JWT Structure

The license key is a JWT with this payload structure:

interface EnterpriseLicensePayload {
  iss: string;           // Issuer ("hiveforge.dev")
  sub: string;           // Organization ID
  aud: string;           // Audience
  iat: number;           // Issued at (Unix timestamp)
  exp: number;           // Expiration (Unix timestamp)
  jti: string;           // License ID (UUID)
  type: string;          // License type (e.g., "enterprise")
  company: string;       // Company name
  features: FeatureFlags & { self_hosted?: boolean };
  limits: Record<string, number>;
  phone_home: {
    required: boolean;         // Whether phone home is mandatory
    interval_hours: number;    // Hours between phone home checks
    grace_days: number;        // Days allowed offline before lockout
  };
  api_keys: {
    encrypted: string;   // Base64 AES-GCM encrypted API keys
    iv: string;          // Base64 initialization vector
  };
}

Offline Operation

Enterprise licenses support offline operation with a configurable grace period:

  1. On initialization, the SDK attempts phone home to validate the license
  2. If phone home fails, the SDK enters offline mode
  3. The license remains valid for phone_home.grace_days (typically 30 days)
  4. Once the grace period expires, the license is no longer valid
  5. When connectivity is restored, the next phone home clears the offline state
const license = createEnterpriseLicenseManagerFromEnv();
 
try {
  await license.initialize();
} catch (error) {
  if (error.message.includes('grace period expired')) {
    // No internet for too long -- must reconnect
    console.error('Please connect to the internet to validate your license.');
  }
}
 
// Check if currently offline
if (!license.isValid()) {
  console.warn('License validation issue -- check connectivity');
}

Full Example: Self-Hosted Application

import { createEnterpriseLicenseManagerFromEnv } from '@producthacker/hiveforge-sdk';
import OpenAI from 'openai';
import Stripe from 'stripe';
 
async function main() {
  // Initialize license
  const license = createEnterpriseLicenseManagerFromEnv();
  await license.initialize();
 
  console.log(`Licensed to: ${license.getCompanyName()}`);
  console.log(`Expires: ${license.getExpirationDate()?.toISOString()}`);
  console.log(`Valid: ${license.isValid()}`);
 
  // Set up OpenAI with decrypted key
  let openai: OpenAI | null = null;
  if (license.isEnabled('ai_enabled')) {
    const openaiKey = license.getApiKey('openai');
    if (openaiKey) {
      openai = new OpenAI({ apiKey: openaiKey });
    }
  }
 
  // Set up Stripe with decrypted key
  let stripe: Stripe | null = null;
  if (license.isEnabled('billing_enabled')) {
    const stripeKey = license.getApiKey('stripe');
    if (stripeKey) {
      stripe = new Stripe(stripeKey, { apiVersion: '2024-12-18.acacia' });
    }
  }
 
  // Use services directly (no proxy needed)
  if (openai) {
    const response = await openai.chat.completions.create({
      model: 'gpt-4o-mini',
      messages: [{ role: 'user', content: 'Hello from self-hosted mode!' }],
    });
    console.log(response.choices[0].message.content);
  }
 
  // Check limits
  const limits = license.getLimits();
  console.log('Max users:', limits.max_users);
 
  // Graceful shutdown
  process.on('SIGTERM', () => {
    license.shutdown();
    process.exit(0);
  });
}
 
main().catch(console.error);