@trap_stevo/lockline
v0.0.11
Published
The ultimate solution for secure, scalable, and tamper-resistant license key validation. Combine flexible validation strategies, encrypted / pluggable storage, smart fallback resilience, and real-time revocation control to lock down your software — and un
Downloads
28
Maintainers
Keywords
Readme
🔐 @trap_stevo/lockline
The ultimate solution for secure, scalable, and tamper-resistant license key validation.
Combine flexible validation strategies, encrypted / pluggable storage, smart fallback resilience, and real-time revocation control to lock down your software — and unlock next-gen licensing workflows.
🚀 Features
- 🔎 Pluggable Validators – Register and chain custom validation strategies
- 🧠 Smart Fallbacks – Automatically retry with secondary validators if primary fails
- 📦 Flexible Storage Engine – Support for encrypted file-based or custom async storage
- 🔒 Tamper-Resistant Sealing – Enrich keys with metadata and protect against injection
- 🔁 Offline Tolerance – Optional trust logic when all validators are unreachable
- 🧹 Automatic Cleanup – Purge or audit expired keys with configurable callbacks
- 🧰 Provisioning & Revocation – Issue, inspect, or revoke license keys in real-time
⚙️ Storage Setup
Custom Storage
You can configure a custom storage backend with setStorage.
lock.setStorage({
get : async (key) => { /* retrieve */ },
set : async (key, value) => { /* store */ },
delete : async (key) => { /* remove */ },
keys : async () => { /* list keys */ }
}, {
strict : false
});Custom Callback Methods
| Key | Type | Description |
|-----------|-------------------------------|----------------------------------------------|
| get | (key) => Promise<value> | Retrieve a value by key |
| set | (key, value) => Promise<void> | Store a key-value pair |
| delete | (key) => Promise<void> | Delete a specific key |
| keys | () => Promise<string[]> | Retrieve all stored keys |
setStorage Options
| Key | Type | Description | Default |
|-----------|-----------|-----------------------------------------------|---------|
| strict | boolean | Throw an error if found a missing a method | false |
🔐 Built-in Lockbox Shortcut
Use the built-in encrypted file storage engine with:
lock.setStorage({ path : "./lockbox" });Full Lockbox Usage
lock.setStorage({
path : "./lockbox",
namespace : "my-service",
encrypt : true,
offset : "optional-entropy",
lockprintOptions : {
filename : ".device-id",
printDNA : 64
}
});Equivalent to:
lock.setStorage(Lockline.lockbox("./lockbox", {
namespace : "my-service",
encrypt : true,
offset : "optional-entropy",
lockprintOptions : {
filename : ".device-id",
printDNA : 64
}
}));Lockbox Options
| Key | Type | Description | Default |
|-------------------|------------|-----------------------------------------------------------------------------|---------------------------|
| path | string | Root path for the lockbox storage | process.cwd()/lockbox |
| namespace | string | Subfolder within the lockbox path | "default" |
| encrypt | boolean | Enable lockbox encryption | true |
| offset | string | Optional entropy used in secret derivation | "" |
| lockprint | string | Provide a fixed fingerprint (skips auto generation) | null |
| lockprintOptions| object | Options when generating a fingerprint if lockprint is not provided | {} |
lockprintOptions Fields
| Key | Type | Description | Default |
|-------------|----------|--------------------------------------------------------|-----------------|
| filename | string | File used to persist fingerprint | .fingerprint |
| printDNA | number | Number of random bytes used for fingerprint generation | 32 |
🧠 Validator Management
| Method | Description |
|--------------------------------|----------------------------------------------|
| registerValidator(name, fn) | Register a custom validator by name |
| setDefaultValidator(name) | Set the fallback validator if none defined |
Validator Function Signature
async function ({ key, input }) {
return {
valid : true,
reason : "Optional message",
raw : { any : "metadata" }
};
}🔐 License Key Workflow
| Method | Description | Async |
|-------------------------|---------------------------------------------------------------|--------|
| provisionKey(key, config) | Temporarily register a key for validation (in-memory) | ❌ |
| validate(key, options) | Run validator chain against a key | ✅ |
| sealKey(key, config, options)| Validate and persist enriched metadata to storage | ✅ |
| inspectKey(key) | View current stored key metadata | ✅ |
| revokeKey(key) | Delete key from storage | ✅ |
🔎 Validation Options
| Option | Type | Description |
|------------------------|-----------|--------------------------------------------------------------------------|
| recordAssumedValidation | boolean | Stores the assume validation metadata to the key |
| assumePreviouslyValid | boolean | Trust stored key if validators are unreachable |
| fallbackIfOffline | boolean | Allow fallback if offline (requires assumePreviouslyValid) |
| autoRemoveExpired | boolean | Automatically remove key if expired |
| storeInvalid | boolean | Store even if result is invalid |
| metaFilter | Function| Custom function to filter extracted metadata |
🔏 Key Configuration
| Config Key | Type | Description |
|------------------|------------------------|---------------------------------------------------------------|
| validators | Array<{ name, input, fallback?, tag? }> | Chain of validator strategies |
| validate | Function(results) | Custom reducer for validator results (default = AND) |
| shortCircuit | boolean | Exit chain early if one validator fails |
| meta | Object | Custom metadata (expiresAt, license tier, etc) |
🧹 Key Lifecycle
| Method | Description | Async |
|------------------------|------------------------------------------------------------------|--------|
| scanKeys({ filter }) | Returns keys by filter: "all", "valid", "expired" | ✅ |
| auditExpiredKeys() | Returns list of expired keys with metadata | ✅ |
| purgeExpiredKeys(cb) | Deletes expired keys, optionally invoking callback per key | ✅ |
🧭 License Scope Management
| Method | Description | Async |
|-----------------------------------|-----------------------------------------------------------------------------|--------|
| loadRecentLicenseKeys(groupBy?)| Loads most recently updated valid key per group and stores by scope | ✅ |
| setCurrentLicenseKey(scope, key)| Manually set the active license key for a specific scope | ❌ |
| getCurrentLicenseKey(scope?) | Retrieve the currently active license key for a given scope (default = "default") | ❌ |
| touchLicenseKey(key) | Update a license key's lastUsedAt metadata to mark recent access | ✅ |
loadRecentLicenseKeys(groupBy?)
Scans all stored keys and selects the most recently updated valid key per logical group.
Groups are defined by the optional groupBy(meta) function (default returns "default").
Example
await lock.loadRecentLicenseKeys(meta => meta.module || "default");
const key = lock.getCurrentLicenseKey("analytics");setCurrentLicenseKey(scope, key)
Manually assign a license key to a specific logical scope.
lock.setCurrentLicenseKey("videoEditor", "ACCESS-VID-123");getCurrentLicenseKey(scope?)
Returns the current license key for the given scope. Defaults to "default".
const key = lock.getCurrentLicenseKey(); // returns key for "default"touchLicenseKey(key)
Updates the lastUsedAt timestamp in storage for the license key.
await lock.touchLicenseKey("ACCESS-XYZ");Use this in protected methods to track key usage and improve recent-key prioritization.
🛡️ License Protection Wrapper
| Method | Description |
|-------------------------------|------------------------------------------------------------------|
| withLicenseProtection(fn, scope?, options?) | Wrap a function to enforce license validation per scope |
Options Object
| Key | Type | Description |
|--------------------|------------|-----------------------------------------------------------------------------|
| onMissingKey | Function | Called if no license key found for the given scope |
| onInvalidKey | Function | Called if the license invalid or expired |
| onSuccess | Function | Called if the license valid and access granted |
| onLicenseBlocked | Function | Called if access blocked; return fallback |
Example
lock.setStorage({ path : "./lockbox" });
await lock.loadRecentLicenseKeys();
const protectedExport = lock.withLicenseProtection(() => {
return { success : true, data : "Exported data" };
}, "analytics", {
onLicenseBlocked : () => ({ error : "License required to access this feature." }),
onSuccess : () => console.log("🔓 License valid"),
onInvalidKey : () => console.warn("❌ Invalid license"),
onMissingKey : () => console.warn("⚠️ No license key loaded")
});
await protectedExport(); // Only runs if valid license valid📦 Installation
npm install @trap_stevo/lockline🛠️ Constructor
const Lockline = require("@trap_stevo/lockline");
const lock = new Lockline();💡 Example Usage
Set storage and default validator
lock.setStorage({ path : "./lockbox" });
/*
If you want explicit control ~
lock.setStorage(lock.lockbox("./lockbox", {
namespace : "keys",
namespace : "keys",
encrypt : true,
offset : 7,
lockprint : "CUSTOM-LOCKPRINT-1234"
}));
*/Register a validator
lock.registerValidator("isAccessKey", async ({ key }) => {
return {
valid : key.startsWith("ACCESS-"),
reason : "Must start with ACCESS-",
raw : { region : "EU" }
};
});Set default validator
lock.setDefaultValidator("isAccessKey");Provision and validate
lock.provisionKey("ACCESS-123", {
validators : [{ name : "isAccessKey" }],
meta : { expiresAt : Date.now() + 1000 * 60 * 60 }
});
const result = await lock.validate("ACCESS-123");
console.log(result.valid); // trueSeal to storage
await lock.sealKey("ACCESS-123", {
validators : [{ name : "isAccessKey" }],
meta : { licenseTier : "pro", expiresAt : Date.now() + 7 * 86400000 }
});Purge expired
await lock.purgeExpiredKeys((key, meta) => {
console.log(`Deleted expired key: ${key}`);
});📜 License
See License in LICENSE.md
🔐 Lock Down. Unlock Possibilities.
Lockline gives you the tools to craft smart, trusted, resilient licensing workflows that adapt to your software’s ecosystem — from CLI tools to full SaaS systems. Empower validation logic that evolves with you.
