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| Variable | Required | Description |
|---|---|---|
HIVEFORGE_LICENSE_KEY | Yes | JWT license key from HiveForge |
HIVEFORGE_LICENSE_SIGNING_KEY | No | Signing 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():
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
apiUrl | string | No | https://api.hiveforge.dev/api/v1/licenses/phone-home | Phone home endpoint URL |
debug | boolean | No | false (or true in development) | Enable debug logging |
signingKey | string | No | From HIVEFORGE_LICENSE_SIGNING_KEY env | Signing 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:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
licenseKey | string | Yes | -- | JWT license key |
options.debug | boolean | No | false | Enable debug logging |
options.apiUrl | string | No | https://api.hiveforge.dev/api/v1/licenses/phone-home | Phone home URL |
options.signingKey | string | No | '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:
- The JWT is decoded (header.payload.signature)
- Expiration is checked
- Phone home is attempted if required by the license
- If phone home fails, offline mode begins (within grace period)
- API keys are decrypted using the signing key
- 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:
| Name | Type | Description |
|---|---|---|
provider | string | Provider 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:
| Name | Type | Description |
|---|---|---|
feature | keyof FeatureFlags | Feature 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:
| Name | Type | Required | Description |
|---|---|---|---|
metrics | Record<string, unknown> | No | Optional usage metrics to report |
Returns (PhoneHomeResponse):
| Field | Type | Description |
|---|---|---|
status | 'valid' | 'invalid' | 'expired' | 'revoked' | License validation status |
next_phone_home_seconds | number | Seconds until next phone home should occur |
features | FeatureFlags | Updated feature flags from server |
limits | Record<string, number> | Updated limits from server |
expires_at | string | ISO timestamp of license expiration |
commands | Array<{ 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:
- On initialization, the SDK attempts phone home to validate the license
- If phone home fails, the SDK enters offline mode
- The license remains valid for
phone_home.grace_days(typically 30 days) - Once the grace period expires, the license is no longer valid
- 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);