@dghila/walther
v0.4.1
Published
Lightweight server-side license verification library for Node.js and Next.js applications.
Downloads
1,906
Readme
walther
walther is a lightweight, strictly server-side licensing library designed to be embedded directly into host applications (such as Next.js backends). It serves as the local enforcer of the licensing system: securely identifying the host machine, activating licenses against double-o, and validating license chains offline.
🔒 Security & Validation Model
walther operates on a Zero Trust model regarding license payloads. It executes a two-phase cryptographic validation sequence completely offline:
Infrastructure Validation (X.509):
- Cryptographically validates the Intermediate Certificate against a hardcoded Public Root CA Certificate.
- Assures the Intermediate Certificate is within its validity dates (
NotBefore/NotAfter). - Checks the Intermediate Certificate against the local Certificate Revocation List (CRL) cache.
Application Validation (JWT):
- Extracts the public key from the validated Intermediate Certificate.
- Validates the license JWT signature using the extracted key.
- Validates expiration (
exp) and active entitlements. - Computes the local hardware fingerprint and checks that it strictly matches the
machine_idbound to the JWT.
🛠️ Technology Stack
- Language: TypeScript
- Runtime: Node.js (specifically targeting server-side environments like Next.js Server Components / API Routes). It cannot run in the browser due to hardware access requirements.
- Cryptography: Node.js native
cryptomodule andjosefor secure JWT parsing and signature verification. - Storage: Local file system cache (
.walther_cache/) for CRLs, Intermediate Certificates, and JWT payloads.
📁 Fingerprinting Engine
walther generates a stable, reproducible machine_id without requiring internet access.
- Identifiers used: Motherboard UUID, primary MAC address, and OS installation ID.
- Privacy: Data is combined and hashed using SHA-256 to generate an opaque
machine_id, preserving privacy while enforcing strict node-locking.
🚀 Public API
import { Walther } from '@dghila/walther';
// Initialize the library (e.g. for Double-O provider with background scheduling)
Walther.init({
provider: "double-o",
config: {
rootCertificate: "-----BEGIN CERTIFICATE-----\n...",
backendUrl: "https://licensing.double-o.com",
cacheDir: "./.walther_cache",
licenseKey: "XXXX-XXXX-XXXX-XXXX", // Optional, enables background JWT auto-renewal
checkIntervalMs: 3600000, // Check every hour (unreferenced timer)
refreshThresholdMs: 86400000, // Preemptively refresh if expiring in < 24 hours
}
});
// Perform initial online activation (binds machine and downloads JWT/certs)
const activated = await Walther.activate("XXXX-XXXX-XXXX-XXXX");
// Verify license status locally & synchronously
if (Walther.isValid()) {
// Retrieve cryptographically validated entitlements
const entitlements = Walther.getEntitlements();
console.log("Active Tier:", entitlements?.tier);
console.log("Expires At:", entitlements?.expiresAt);
}
// Clean up background timers on application teardown
Walther.stopScheduler();🔄 License Check & Refresh Scheduler
walther supports automated background checking and proactive license renewal. This prevents application downtime by renewing the license before it actually expires.
Configuration Properties
Specify these options in the configuration object under the config field when calling Walther.init():
checkIntervalMs(number): Frequency (in milliseconds) at which a background job is run to check license expiration. If specified,waltherwill run a timer using Node.jssetInterval(). The timer is unreferenced (.unref()), meaning it will not prevent the Node.js process from exiting when the main application shuts down.refreshThresholdMs(number): Duration before expiration (in milliseconds) under which the license will attempt a refresh. E.g., setting it to86400000(24 hours) will trigger a refresh attempt if the license is expiring in less than 24 hours.
Operational Modes
- Active Background Scheduling (Timer-based):
- Triggered when
checkIntervalMsis set to a positive number. - Runs periodically in the background.
- For tests or hot-reloading environments, call
Walther.stopScheduler()to clear active background timers.
- Triggered when
- Passive/Lazy Background Trigger:
- Triggered when
refreshThresholdMsis set butcheckIntervalMsis not. - Every time
Walther.isValid()is called, it checks the license expiration time. If the remaining time is below the threshold, it triggers the check-refresh process asynchronously in the background as a floating promise (without blocking the calling code). - Utilizes a concurrency lock to ensure only one refresh runs at a time.
- Triggered when
Provider-Specific Refresh Actions
m-license: Calls/licenses/:key/tokento fetch and cache a newly-extended JWT token.double-o:- If
licenseKeyis provided inDoubleOProviderConfig, it executes a re-activation step (activate(licenseKey)) to fetch and cache a new JWT. - If
licenseKeyis absent, it only runs a refresh of the infrastructure (refreshOnline()) to update the Intermediate Certificate and CRL list.
- If
local(offline): Executes a forced validation reload from the local disk cache (validateOffline(true, true)). This allowswaltherto seamlessly detect new license files written to disk by an external process or sidecar container.
🧪 Development & Testing
Installation
npm installRun Tests
npm testLinting & Formatting
npm run lint
npm run format🗺️ Roadmap (Tamper Resistance)
To counter active code tampering (e.g., modifying JS files to bypass isValid()), the core cryptographic and fingerprinting components of walther will be migrated to Rust via napi-rs post-MVP. This will compile into a native .node binary, providing:
- Memory safety and high performance.
- Direct hardware access without shell child-processes.
- Strong binary obfuscation while maintaining the identical TypeScript API interface.
