devlock-client
v1.1.1
Published
DevLock Frontend SDK for React, Next.js, and Vanilla JS. Integrate license validation, remote kill-switch, feature flags, and domain locking into your web applications securely.
Maintainers
Readme
devlock-client
Frontend SDK for DevLock — License enforcement, remote management, and developer protection for web applications.
🌐 Website & Dashboard: devlock.tashanto.com · 🖥️ Backend SDK: devlock-sdk
Fail-safe by design. If DevLock's servers are ever unreachable, this SDK never throws and never blocks your app — your website keeps running smoothly. See Fail-open behavior.
Features
- 🔐 License Validation — Verify licenses with HMAC-signed requests
- 🚀 Real-Time Updates — WebSocket-powered kill-switch, maintenance mode, feature flags
- 🌐 Domain Locking — Prevent unauthorized redistribution
- 🏴 Feature Flags — Toggle features instantly across all deployments
- 📡 Offline Support — Cryptographically signed offline tokens with grace period
- 🛡️ Tamper Detection — Detect SDK manipulation attempts
- 💧 Watermark Injection — Show watermark for invalid licenses
- 🔔 Remote Notifications — Push messages to deployed apps
- ⚡ Kill Switch — Instantly disable applications remotely
- 🔧 Maintenance Mode — Show maintenance messages without redeploying
Framework Support
| Framework | Import Path |
|-----------|-------------|
| Vanilla JS/TS | devlock-client |
| React | devlock-client/react |
| Next.js | devlock-client/next |
| Vue 3 | devlock-client/vue |
Installation
npm install devlock-client
# or
yarn add devlock-client
# or
pnpm add devlock-clientSetup & Dashboard
Before using the SDK, you need to create a project and obtain your keys from the DevLock Dashboard:
- Go to DevLock Dashboard.
- Sign in and navigate to the Projects section.
- Click Create Project and fill in your details.
- Copy your Project Public Key (
pk_live_...). This is safe to expose in your frontend. - (Optional) Generate a License Key for your users from the Licenses page.
Recommended .env setup (Vite)
VITE_DEVLOCK_PROJECT_KEY=pk_live_your_public_key_here
VITE_DEVLOCK_LICENSE_KEY=DLCK-XXXX-XXXX-XXXX-XXXXFor Next.js:
NEXT_PUBLIC_DEVLOCK_PROJECT_KEY=pk_live_your_public_key_here
NEXT_PUBLIC_DEVLOCK_LICENSE_KEY=DLCK-XXXX-XXXX-XXXX-XXXXQuick Start
Vanilla TypeScript / JavaScript
import { DevLock } from 'devlock-client';
const devlock = new DevLock({
projectKey: 'pk_live_your_project_key',
licenseKey: 'DLCK-XXXX-XXXX-XXXX-XXXX',
on: {
onReady: (state) => console.log('DevLock ready:', state),
onKillSwitch: (reason) => showBlockedUI(reason),
onMaintenanceMode: (config) => showMaintenance(config.message),
onLicenseSuspended: (reason) => showSuspendedUI(reason),
onFeatureToggle: (flag, enabled) => updateFeature(flag, enabled),
onNotification: (notif) => showToast(notif.message),
},
});
await devlock.init();
// Check feature flags
if (devlock.isFeatureEnabled('premium-export')) {
showPremiumFeature();
}
// Check license status
if (devlock.isLicenseValid()) {
// Full access
}
// Track custom events
devlock.track('export_clicked', { format: 'pdf' });React
import { DevLockProvider, useDevLock, useFeatureFlag } from 'devlock-client/react';
// Wrap your app in main.tsx or App.tsx
function App() {
return (
<DevLockProvider
config={{
projectKey: import.meta.env.VITE_DEVLOCK_PROJECT_KEY,
licenseKey: import.meta.env.VITE_DEVLOCK_LICENSE_KEY,
}}
>
<MyApp />
</DevLockProvider>
);
}
// Use in components
function Dashboard() {
const { state, isReady } = useDevLock();
const isPremium = useFeatureFlag('premium');
if (state.maintenance.enabled) {
return <MaintenancePage message={state.maintenance.message} />;
}
if (state.killSwitch.enabled) {
return <BlockedPage reason={state.killSwitch.reason} />;
}
return (
<div>
<h1>Dashboard</h1>
{isPremium && <PremiumWidget />}
</div>
);
}Next.js
// app/providers.tsx
'use client';
import { DevLockProvider } from 'devlock-client/next';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<DevLockProvider
config={{
projectKey: process.env.NEXT_PUBLIC_DEVLOCK_PROJECT_KEY!,
licenseKey: process.env.NEXT_PUBLIC_DEVLOCK_LICENSE_KEY,
}}
>
{children}
</DevLockProvider>
);
}Vue 3
import { createApp } from 'vue';
import { DevLockPlugin } from 'devlock-client/vue';
const app = createApp(App);
app.use(DevLockPlugin, {
projectKey: 'pk_live_your_project_key',
licenseKey: 'DLCK-XXXX-XXXX-XXXX-XXXX',
});
// In components
import { useDevLock, useFeatureFlag } from 'devlock-client/vue';
const { state, isReady } = useDevLock();
const isPremium = useFeatureFlag('premium');Configuration
const devlock = new DevLock({
// Required
projectKey: 'pk_live_xxx', // Public key from Dashboard
// Optional
licenseKey: 'DLCK-XXXX-XXXX-XXXX-XXXX', // User or app license key
apiUrl: 'https://dl-api.tashanto.com', // Custom API URL (don't set unless self-hosting)
wsUrl: 'wss://dl-ws.tashanto.com', // Custom WebSocket URL
environment: 'production', // 'production' | 'staging' | 'development'
debug: false, // Enable console logging (useful in development)
offlineGraceHours: 72, // Hours to allow offline operation
heartbeatInterval: 30000, // Heartbeat interval (ms)
tamperDetection: true, // Enable tamper detection
watermark: false, // Show watermark when license invalid
watermarkText: 'UNLICENSED', // Custom watermark text
failBehavior: 'open', // 'open' (default) | 'closed'
});API Reference
DevLock Class
| Method | Returns | Description |
|--------|---------|-------------|
| init() | Promise<DevLockState> | Initialize SDK, validate license, connect WebSocket |
| isFeatureEnabled(flag) | boolean | Check if a feature flag is enabled |
| isLicenseValid() | boolean | Check if license is valid |
| isMaintenanceMode() | boolean | Check if maintenance mode is active |
| isKillSwitchActive() | boolean | Check if kill switch is active |
| getState() | DevLockState | Get current SDK state |
| getLicense() | LicenseInfo | Get license information |
| getConfig(key, default) | T | Get remote config value |
| track(event, metadata) | void | Track a telemetry event |
| revalidate() | Promise<DevLockState> | Force re-validation |
| on(event, handler) | () => void | Subscribe to events (returns unsubscribe) |
| destroy() | void | Cleanup all resources |
Events
| Event | Payload | Description |
|-------|---------|-------------|
| ready | DevLockState | SDK initialized |
| license:valid | LicenseInfo | License validated successfully |
| license:invalid | string | License validation failed |
| license:suspended | string | License suspended (reason) |
| maintenance:enabled | MaintenanceConfig | Maintenance mode activated |
| maintenance:disabled | — | Maintenance mode deactivated |
| killswitch:activated | { reason } | Kill switch activated |
| killswitch:deactivated | — | Kill switch deactivated |
| feature:toggled | flag, enabled | Feature flag changed |
| notification:push | Notification | New notification received |
| connection:change | boolean | WebSocket connection state |
| tamper:detected | string | Tampering detected |
Offline Support
The SDK automatically caches validation results and supports offline operation:
- On successful validation, response is cached locally
- If network is unavailable, cached result is used
- Grace period (default 72h) determines how long offline is allowed
- After grace period expires, license is considered invalid
- On reconnection, SDK automatically revalidates
Fail-open behavior
🔒 The safety guarantee
Your website locks ONLY when you explicitly lock it from the DevLock dashboard. A DevLock outage, a network/API error, a request timeout, a malformed response, or the SDK itself failing will never lock, block, or crash your site. Errors always resolve to unlocked. A lock is applied only when the server explicitly reports
enabled: truefor a kill-switch or maintenance state that you turned on.
DevLock is a safety layer for your app — it must never become a single point of
failure. If DevLock's servers are unreachable and there is no usable cache, the SDK
does not throw and does not block rendering. init() resolves in a permissive
offline state and your application keeps running exactly as normal.
Concretely, the SDK guarantees:
init()never rejects whenfailBehavioris'open'(the default).- Every server response is parsed defensively — missing or malformed fields can never crash your app and can never flip the app into a locked state.
- A kill-switch / maintenance lock requires an explicit
enabled === truefrom the server. Anything else (undefined, garbage, HTTP error, unreachable) is treated as unlocked.
const devlock = new DevLock({
projectKey: 'pk_live_xxx',
// failBehavior: 'open', // default — never break the host app
// failBehavior: 'closed', // opt in to hard-failing if you *want* to block
});
await devlock.init(); // never rejects when failBehavior is 'open'Enforcement signals (kill-switch, suspension, maintenance) are still delivered and cached — once received they persist across reloads, so an offline period cannot be used to bypass a lock that was already issued.
Local Development & Testing
When you integrate the SDK into a local project (e.g., http://localhost:5173), you can test it directly against your production DevLock account.
- No URL changes needed: Do not set
apiUrlorwsUrllocally. The SDK automatically connects to the production DevLock servers. - Domain Lock: If you have enabled Domain Lock in your DevLock Dashboard, add
localhostor127.0.0.1to the Allowed Domains list. An empty list allows all domains by default. - Enable debug mode during development to see logs in the browser console:
config={{ projectKey: '...', debug: true }} - CORS: The DevLock API server (
dl-api.tashanto.com) accepts requests from any origin by default. If you are self-hosting DevLock, ensure your server allows your frontend domain.
Links
License
MIT
Built by Md Tanvir Ahamed Shanto · devlock.tashanto.com
