@subnetter/core
Core CIDR allocation engine, configuration loading, and output utilities. This is the main package for programmatic use.
Subnetter provides a programmatic API that allows you to use its functionality in your own Node.js applications. This overview documents the available packages, key components, and usage patterns.
Subnetter is organized as a monorepo with multiple packages:
@subnetter/core
Core CIDR allocation engine, configuration loading, and output utilities. This is the main package for programmatic use.
@subnetter/cli
Command-line interface for generate, validate, analyze, and export operations.
@subnetter/netbox
NetBox IPAM integration for exporting allocations to NetBox.
@subnetter/cidr-utils
Low-level CIDR utilities for validation, parsing, and calculation.
# Core package (for programmatic use)npm install @subnetter/core
# NetBox integrationnpm install @subnetter/netbox
# CIDR utilities onlynpm install @subnetter/cidr-utilsimport { loadConfig, CidrAllocator, writeAllocationsToCsv} from '@subnetter/core';
async function main() { // Load and validate configuration const config = await loadConfig('./config.json');
// Generate allocations const allocator = new CidrAllocator(config); const allocations = allocator.generateAllocations();
// Write to CSV await writeAllocationsToCsv(allocations, './allocations.csv');
console.log(`Generated ${allocations.length} subnet allocations`);}
main();The core package provides the main functionality for CIDR allocation.
| Export | Type | Description |
|---|---|---|
CidrAllocator | Class | Main class for hierarchical CIDR allocation |
loadConfig | Function | Load and validate configuration files (JSON/YAML) |
validateConfig | Function | Validate configuration objects programmatically |
writeAllocationsToCsv | Function | Write allocations to CSV file |
filterAllocationsByProvider | Function | Filter allocations by cloud provider |
validateNoOverlappingCidrs | Function | Validate allocations for CIDR overlaps |
import type { Config, // Main configuration interface Allocation, // Output allocation record Account, // Account configuration CloudConfig, // Cloud-specific configuration SubnetTypesMap, // Subnet type definitions RawConfig, // Raw config before normalization} from '@subnetter/core';import { loadConfig } from '@subnetter/core';
// Load from JSON or YAML fileconst config = await loadConfig('./config.json');const yamlConfig = await loadConfig('./config.yaml');import { validateConfig } from '@subnetter/core';
const config = validateConfig({ baseCidr: '10.0.0.0/8', accounts: [{ name: 'production', clouds: { aws: { regions: ['us-east-1', 'us-west-2'] } } }], subnetTypes: { public: 26, private: 24, database: 27 }});import { CidrAllocator } from '@subnetter/core';
const allocator = new CidrAllocator(config);const allocations = allocator.generateAllocations();
// Each allocation contains:allocations.forEach(alloc => { console.log({ account: alloc.accountName, provider: alloc.cloudProvider, region: alloc.regionName, az: alloc.availabilityZone, subnet: alloc.subnetCidr, role: alloc.subnetRole, usableIps: alloc.usableIps });});Subnetter provides a comprehensive error hierarchy:
import { SubnetterError, // Base error class ConfigurationError, // Configuration validation errors AllocationError, // CIDR allocation failures IOError, // File I/O errors ValidationError, // General validation errors CloudProviderError, // Cloud provider specific errors ErrorCode // Error code enum} from '@subnetter/core';
try { const config = await loadConfig('./config.json'); const allocator = new CidrAllocator(config); const allocations = allocator.generateAllocations();} catch (error) { if (error instanceof ConfigurationError) { console.error(`Config error [${error.code}]: ${error.message}`); console.log('Help:', error.getHelpText()); } else if (error instanceof AllocationError) { console.error('Allocation failed:', error.message); }}import { createLogger, configureLogger, LogLevel, parseLogLevel} from '@subnetter/core';
// Configure global loggingconfigureLogger({ level: LogLevel.DEBUG, useColor: true, timestamps: true});
// Create a named loggerconst logger = createLogger('MyApp');logger.info('Starting allocation...');logger.debug('Config loaded:', config);The core package re-exports common CIDR utilities:
import { isValidIpv4Cidr, calculateUsableIps, doCidrsOverlap, subdivideIpv4Cidr, calculateOptimalPrefixLength, calculateRequiredPrefixLength, ContiguousAllocator, HierarchicalAllocator} from '@subnetter/core';
// Validate CIDR formatisValidIpv4Cidr('10.0.0.0/24'); // true
// Calculate usable IPscalculateUsableIps('10.0.0.0/24'); // 254
// Check for overlapdoCidrsOverlap('10.0.0.0/24', '10.0.0.128/25'); // true
// Subdivide a CIDRsubdivideIpv4Cidr('10.0.0.0/24', 26);// ['10.0.0.0/26', '10.0.0.64/26', '10.0.0.128/26', '10.0.0.192/26']The NetBox package provides integration with NetBox IPAM.
| Export | Type | Description |
|---|---|---|
NetBoxClient | Class | REST API client for NetBox |
NetBoxExporter | Class | Export allocations to NetBox |
NetBoxApiError | Class | API error with status code |
import { NetBoxClient } from '@subnetter/netbox';
const client = new NetBoxClient({ url: 'https://netbox.example.com', token: process.env.NETBOX_TOKEN!, timeout: 30000, // optional});
// Test connectionconst connected = await client.testConnection();
// List prefixesconst prefixes = await client.prefixes.listAll();
// Create a prefixconst prefix = await client.prefixes.create({ prefix: '10.1.0.0/16', status: 'reserved', description: 'Production VPC',});
// Other resources: sites, tenants, roles, tags, locations, aggregatesimport { CidrAllocator, loadConfig } from '@subnetter/core';import { NetBoxClient, NetBoxExporter } from '@subnetter/netbox';
// Generate allocationsconst config = await loadConfig('./config.json');const allocator = new CidrAllocator(config);const allocations = allocator.generateAllocations();
// Create client and exporterconst client = new NetBoxClient({ url: 'https://netbox.example.com', token: process.env.NETBOX_TOKEN!,});
const exporter = new NetBoxExporter(client);
// Dry run firstconst preview = await exporter.export(allocations, { dryRun: true, baseCidr: config.baseCidr,});
console.log(`Would create: ${preview.summary.created}`);console.log(`Would update: ${preview.summary.updated}`);
// Apply changesconst result = await exporter.export(allocations, { dryRun: false, baseCidr: config.baseCidr, status: 'reserved', // Prefix status prune: false, // Delete orphaned prefixes});Subnetter maps cloud concepts to NetBox objects:
| Subnetter | NetBox | Description |
|---|---|---|
| Base CIDR | Aggregate | Top-level IP block |
| Cloud Provider | Site Group | AWS, Azure, GCP |
| Cloud Region | Site | us-east-1, eastus |
| Availability Zone | Location | us-east-1a |
| Account | Tenant | Ownership boundary |
| Subnet Type | Role | public, private |
| Subnet CIDR | Prefix | Actual allocation |
Low-level CIDR utilities for validation, parsing, and calculation.
import { ipv4ToNumber, numberToIpv4, createIpAddress, getNetworkAddress, getBroadcastAddress, calculateSubnetMask} from '@subnetter/cidr-utils';
// Convert between formatsconst num = ipv4ToNumber('192.168.1.1'); // 3232235777const ip = numberToIpv4(3232235777); // '192.168.1.1'
// Create IP address objectconst ipObj = createIpAddress('192.168.1.1');console.log(ipObj.asNumber); // 3232235777console.log(ipObj.asString); // '192.168.1.1'console.log(ipObj.octets); // [192, 168, 1, 1]
// Network calculationsgetNetworkAddress('10.0.0.15/24'); // IpAddress for '10.0.0.0'getBroadcastAddress('10.0.0.0/24'); // IpAddress for '10.0.0.255'calculateSubnetMask(24); // 4294967040 (0xFFFFFF00)import { isValidIpv4Cidr, validateIpv4Cidr, CidrError} from '@subnetter/cidr-utils';
// Simple validationisValidIpv4Cidr('10.0.0.0/24'); // trueisValidIpv4Cidr('10.0.0.0/33'); // false
// Validation with error detailstry { validateIpv4Cidr('10.0.0.0/33');} catch (error) { if (error instanceof CidrError) { console.error(error.message); // 'Invalid prefix length...' console.error(error.type); // 'INVALID_PREFIX_LENGTH' }}import { parseIpv4Cidr, normalizeCidr} from '@subnetter/cidr-utils';
// Parse CIDR into componentsconst parsed = parseIpv4Cidr('10.0.0.0/24');console.log(parsed.address); // '10.0.0.0'console.log(parsed.prefixLength); // 24
// Normalize CIDR to network addressnormalizeCidr('10.0.0.15/24'); // '10.0.0.0/24'import { calculateSubnetInfo, getCidrRange, checkCidrOverlap, subdivideCidr, calculateSupernet} from '@subnetter/cidr-utils';
// Get subnet informationconst info = calculateSubnetInfo('10.0.0.0/24');console.log(info.totalIps); // 256console.log(info.usableIps); // 254
// Get IP rangeconst range = getCidrRange('10.0.0.0/24');console.log(range.start.asString); // '10.0.0.0'console.log(range.end.asString); // '10.0.0.255'
// Check for overlapcheckCidrOverlap('10.0.0.0/24', '10.0.0.128/25'); // true
// Subdivide a CIDR (by prefix delta)const result = subdivideCidr('10.0.0.0/24', 2); // Add 2 bitsconsole.log(result.subnets); // ['10.0.0.0/26', '10.0.0.64/26', ...]console.log(result.bitsAdded); // 2
// Calculate supernetcalculateSupernet('10.0.1.0/24', 8); // '10.0.0.0/16'All packages are written in TypeScript and provide full type definitions:
import type { Config, Allocation, Account, CloudConfig} from '@subnetter/core';
import type { NetBoxConfig, Prefix, ExportResult} from '@subnetter/netbox';
import type { IpAddress, SubnetInfo, IpRange} from '@subnetter/cidr-utils';import { loadConfig, CidrAllocator, writeAllocationsToCsv, filterAllocationsByProvider, validateNoOverlappingCidrs, SubnetterError, ConfigurationError} from '@subnetter/core';
async function main() { try { // Load configuration const config = await loadConfig('./config.json'); console.log(`Loaded config with ${config.accounts.length} accounts`);
// Generate allocations const allocator = new CidrAllocator(config); const allocations = allocator.generateAllocations(); console.log(`Generated ${allocations.length} allocations`);
// Validate for overlaps const validation = validateNoOverlappingCidrs(allocations); if (!validation.valid) { console.error(`Found ${validation.overlaps.length} overlaps!`); process.exit(1); }
// Filter by provider if needed const awsOnly = filterAllocationsByProvider(allocations, 'aws'); console.log(`AWS allocations: ${awsOnly.length}`);
// Write to CSV await writeAllocationsToCsv(allocations, './allocations.csv'); console.log('Written to allocations.csv');
} catch (error) { if (error instanceof ConfigurationError) { console.error(`Configuration error: ${error.message}`); console.log('Hint:', error.getHelpText()); } else if (error instanceof SubnetterError) { console.error(`Error [${error.code}]: ${error.message}`); } else { console.error('Unexpected error:', error); } process.exit(1); }}
main();