@carchalabs/seal-hwid 0.0.0-stage → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,109 @@
1
+ Carcha Seal SDK License
2
+ Version 1.0, 7 November 2026
3
+
4
+ Copyright (c) 2026 Bugra Omer Aydemir (Carcha Labs) ("we", "us").
5
+ All rights not expressly granted below are reserved.
6
+
7
+ This license covers the software in the carcha-seal-sdk repository and the packages
8
+ built from it (the "SDK"). The source code is published so that you can review it;
9
+ publishing it does not make it open source. Versions of the SDK published before
10
+ 7 November 2026 were released under the MIT License, which still applies to those
11
+ versions.
12
+
13
+ 1. Definitions
14
+
15
+ "Carcha Seal Service" means a license server operated by us, or a license server you
16
+ run under a written agreement with us.
17
+
18
+ "Your Application" means software that you develop and provide to your own customers,
19
+ that uses the SDK to obtain and check licenses from the Carcha Seal Service, and whose
20
+ main purpose is not licensing, license management, copy protection or software
21
+ protection.
22
+
23
+ "End Users" means the people and organizations that receive Your Application from you.
24
+
25
+ 2. What you may do
26
+
27
+ As long as you comply with this license, we grant you a non-exclusive, worldwide,
28
+ royalty-free, non-transferable license, which you may not sublicense except as
29
+ section 2(c) allows, to:
30
+
31
+ (a) use, copy and modify the SDK to integrate Your Application with the Carcha Seal
32
+ Service;
33
+
34
+ (b) read and study the SDK's source code, for example to evaluate the SDK or to review
35
+ its security; and
36
+
37
+ (c) distribute the SDK, unmodified or modified under (a), only as part of Your
38
+ Application, in object code or source code form, and let your End Users use it
39
+ only as part of Your Application.
40
+
41
+ 3. What you may not do
42
+
43
+ You may not, and may not allow anyone else to:
44
+
45
+ (a) sell, license, rent, lend or otherwise distribute the SDK, or any part or modified
46
+ version of it, on its own or as part of a software development kit, library,
47
+ framework or other product for developers;
48
+
49
+ (b) use the SDK, or any part or modified version of it, with a license server or
50
+ service other than the Carcha Seal Service, except a test server you run only to
51
+ test Your Application;
52
+
53
+ (c) use the SDK, or any part, modified version or translation of it, in a product or
54
+ service that substitutes for the Carcha Seal Service or the SDK, or that offers the
55
+ same or substantially similar functionality;
56
+
57
+ (d) reverse engineer, decompile or disassemble the compiled parts of the SDK (such as
58
+ its native libraries), or otherwise try to derive source code we have not
59
+ published, except to the extent applicable law permits it despite this
60
+ restriction;
61
+
62
+ (e) disable, bypass or tamper with the SDK's license checks, signature verification,
63
+ encryption or device binding, or help anyone else do so, in any application that
64
+ uses the Carcha Seal Service, except in Your Application while you test it;
65
+
66
+ (f) remove or change this license or any copyright or other notice in the SDK; or
67
+
68
+ (g) use our names, logos or trademarks, including "Carcha Seal" and "Carcha Labs",
69
+ except to state truthfully that Your Application uses Carcha Seal.
70
+
71
+ 4. Your End Users
72
+
73
+ Your terms for Your Application must not give your End Users more rights in the SDK
74
+ than to use it as part of Your Application. You are responsible for your End Users'
75
+ use of the SDK as part of Your Application.
76
+
77
+ 5. Contributions
78
+
79
+ If you send us changes or suggestions for the SDK, you grant us a perpetual,
80
+ irrevocable, worldwide, royalty-free license to use, modify and distribute them for
81
+ any purpose.
82
+
83
+ 6. Termination
84
+
85
+ Your rights under this license end automatically if you breach it. If you end the
86
+ breach within 30 days after we notify you of it, your rights are restored, unless you
87
+ have breached this license before. When your rights end, you must stop using and
88
+ distributing the SDK; your End Users may keep using copies of Your Application they
89
+ received before then. Sections 3, 5, 7 and 8 continue to apply after termination.
90
+
91
+ 7. No warranty
92
+
93
+ To the extent the law allows, the SDK is provided "as is", without warranty of any
94
+ kind, express or implied, including warranties of merchantability, fitness for a
95
+ particular purpose and non-infringement. If you have a separate written agreement with
96
+ us for the Carcha Seal Service, its terms on warranty and liability apply to the SDK
97
+ instead of sections 7 and 8.
98
+
99
+ 8. Limitation of liability
100
+
101
+ To the extent the law allows, we are not liable for any damages arising from the SDK
102
+ or this license. Nothing in this license limits or excludes liability that cannot be
103
+ limited or excluded under applicable law, including liability for intent or gross
104
+ negligence, for death or personal injury, or under product liability law.
105
+
106
+ 9. New versions of this license
107
+
108
+ We may publish new versions of this license. Each version of the SDK stays under the
109
+ license it was published with.
package/README.md CHANGED
@@ -1,3 +1,52 @@
1
- # Temporary Holding Version
1
+ # @carchalabs/seal-hwid
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Node-API layer over the `seal_hwid` native core (`../hwid`), for Node.js and Electron. One `.node` file per platform; Node-API is ABI-stable, and napi-rs looks up the Node-API functions in the host process at load time, so the same file loads in Node and in Electron.
4
+
5
+ ```js
6
+ const { collect, version } = require('@carchalabs/seal-hwid');
7
+ const { vm, components, notes } = collect('your-product-key');
8
+ // components: [{ kind: 'uuid' | 'board' | 'disk' | 'mac' | 'os' | 'cpu' | 'gpu', hash: '<64 hex>' }]
9
+ ```
10
+
11
+ Only kinds, hashes, the VM flag and diagnostic notes reach JavaScript, never raw serials. If no native module exists for the platform, `require` throws (`code: 'HWID_UNAVAILABLE'`) instead of falling back.
12
+
13
+ ## Lease verification
14
+
15
+ The module also verifies Carcha Seal leases (`../lease`, the `seal_lease` crate) with the same rules, order and failure reasons as `verifyLease` in `@carchalabs/seal-core`. The Ed25519 public keys it trusts are compiled in, so a lease signed with another key cannot be made to pass by changing a file or a setting.
16
+
17
+ ```js
18
+ const { verifyLease, leaseKeyIds } = require('@carchalabs/seal-hwid');
19
+ verifyLease(token, { product, fingerprintHash, now, graceSeconds }); // { ok, reason?, kid? }
20
+ leaseKeyIds(); // ids of the compiled-in keys (keyId in @carchalabs/seal-core)
21
+ decryptFeature(token, { product, fingerprintHash, now }, licenseKey, 'pro', data); // { ok, data?, reason? }
22
+ ```
23
+
24
+ `devicePublicKey('licensekit:<product>')` returns this machine's X25519 public key (base64url), derived from its hardware; the SDK sends it so the server wraps feature keys for this machine only, and `decryptFeature` derives the matching secret itself (it never reaches JavaScript). A test build with `--features test-device-secret` (CI only) takes the secret as a sixth argument so the shared vectors can run through this layer.
25
+
26
+ `decryptFeature` verifies the lease first, then unwraps the feature's content key from the lease (claim `fk`) for this license key and device and decrypts data encrypted for the feature; the key never reaches JavaScript.
27
+
28
+ The published build trusts the hosted service's current and next keys (`../quickstart/public-keys`). A self-hosted server's operator builds with their own: `SEAL_LEASE_PUBLIC_KEYS=<base64 SPKI>,<base64 SPKI> npm run build` (the PEM bodies, comma-separated). The Electron SDK uses this verifier next to its JavaScript one when the compiled-in keys include every key the app was given; a different answer from the two is treated as a modified license.
29
+
30
+ Both verifiers are tested against the same vectors: `../lease/tests/vectors.json`, leases signed by @carchalabs/seal-core with a throwaway key and the result core gives for each (`node ../lease/tests/make-vectors.js` regenerates them). The Rust crate (`cargo test` in `../lease`), core's own tests and this module (a build that trusts the vectors' key, in CI) must all give those results.
31
+
32
+ ## Packages
33
+
34
+ Published as napi-rs lays out prebuilt modules: the main package holds the loader, and each target is its own package listed as an exact-version optional dependency, with `os`/`cpu` (and `libc`) so npm installs only the one for the machine. Nothing is compiled or downloaded at install time.
35
+
36
+ | Target | Package | Built on |
37
+ | --- | --- | --- |
38
+ | macOS arm64 | `@carchalabs/seal-hwid-darwin-arm64` | `macos-latest` |
39
+ | macOS x64 | `@carchalabs/seal-hwid-darwin-x64` | `macos-15-intel` (GitHub's Intel macOS runners end in fall 2027) |
40
+ | Windows x64 | `@carchalabs/seal-hwid-win32-x64-msvc` | `windows-latest` |
41
+ | Linux x64 (glibc) | `@carchalabs/seal-hwid-linux-x64-gnu` | `ubuntu-22.04` |
42
+
43
+ Other platforms (Linux musl or arm64, Windows arm64) have no module yet: `require` throws `HWID_UNAVAILABLE`. CI (`.github/workflows/hwid-node.yml`) builds each target on its own runner, runs the tests there, installs the main and platform package from tarballs the way users get them, and uploads the platform package.
44
+
45
+ In Electron the `.node` file must sit outside the app's asar archive (Electron loads native modules with `process.dlopen`); electron-builder unpacks native modules automatically.
46
+
47
+ ```
48
+ npm run build # cargo build --release, then seal-hwid.<target>.node next to index.js (loaded first)
49
+ npm test # Node tests
50
+ node scripts/build.js --npm # also copies it into npm/<target>/ (CI)
51
+ npx electron test/electron-check.js # loads it in Electron (with electron installed)
52
+ ```
package/index.d.ts ADDED
@@ -0,0 +1,72 @@
1
+ export type ComponentKind = 'uuid' | 'board' | 'disk' | 'mac' | 'os' | 'cpu' | 'gpu';
2
+
3
+ export interface Component {
4
+ kind: ComponentKind;
5
+ /** Lowercase hex HMAC-SHA256 of the component, keyed by the product. */
6
+ hash: string;
7
+ }
8
+
9
+ export interface Collected {
10
+ /** Version of the result shape. */
11
+ format: number;
12
+ /** True when a hypervisor was detected (best effort). */
13
+ vm: boolean;
14
+ components: Component[];
15
+ /** Why kinds are missing: the failing call and its error code, never a value. */
16
+ notes: string[];
17
+ }
18
+
19
+ /** Reads this machine's components and hashes each with the product key. */
20
+ export function collect(key: string | Buffer): Collected;
21
+
22
+ export interface LeaseCheck {
23
+ product: string;
24
+ fingerprintHash: string;
25
+ /** Seconds since the Unix epoch; default: now. */
26
+ now?: number;
27
+ graceSeconds?: number;
28
+ leewaySeconds?: number;
29
+ issuer?: string;
30
+ }
31
+
32
+ export interface LeaseVerdict {
33
+ ok: boolean;
34
+ /** The failure reason, as @carchalabs/seal-core names it; absent when ok. */
35
+ reason?: string;
36
+ /** Id of the key that signed the lease; present when ok. */
37
+ kid?: string;
38
+ }
39
+
40
+ /**
41
+ * Verifies a lease with the public keys compiled into this build and the same rules,
42
+ * order and reasons as verifyLease in @carchalabs/seal-core.
43
+ */
44
+ export function verifyLease(token: string, check: LeaseCheck): LeaseVerdict;
45
+
46
+ export interface FeatureDecrypt {
47
+ ok: boolean;
48
+ /** The plaintext; present when ok. */
49
+ data?: Buffer;
50
+ /** A lease failure reason, 'not_licensed' or 'decrypt_failed'; absent when ok. */
51
+ reason?: string;
52
+ }
53
+
54
+ /**
55
+ * Verifies the lease (compiled-in keys), unwraps the feature's content key for this license key and
56
+ * device, and decrypts data encrypted for the feature (encryptForFeature in @carchalabs/seal-core). The
57
+ * content key never reaches JavaScript.
58
+ */
59
+ export function decryptFeature(token: string, check: LeaseCheck, licenseKey: string, feature: string, data: Buffer): FeatureDecrypt;
60
+
61
+ /** Ids of the lease signing keys this build trusts (keyId in @carchalabs/seal-core). */
62
+ export function leaseKeyIds(): string[];
63
+
64
+ /**
65
+ * This machine's X25519 device public key (base64url) for a product key, derived from its system UUID,
66
+ * motherboard serial, system disk serial and OS install id; null when it reports none of them. The
67
+ * secret half never leaves the module. It changes when one of those parts changes.
68
+ */
69
+ export function devicePublicKey(key: string | Buffer): string | null;
70
+
71
+ /** Version of the native core, e.g. "0.1.0". */
72
+ export function version(): string;
package/index.js ADDED
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+ // Loads the prebuilt addon for this platform: a local build next to this file (development),
3
+ // otherwise the platform package npm installed (@carchalabs/seal-hwid-<target>, one of
4
+ // the optionalDependencies). Fails loudly when there is none: the SDK then sends no components
5
+ // rather than a weaker fingerprint.
6
+ const path = require('node:path');
7
+ const { existsSync } = require('node:fs');
8
+ const { target } = require('./target');
9
+
10
+ function unavailable(detail) {
11
+ const err = new Error(`@carchalabs/seal-hwid: no native module for ${process.platform}-${process.arch}: ${detail}`);
12
+ err.code = 'HWID_UNAVAILABLE';
13
+ return err;
14
+ }
15
+
16
+ const name = target();
17
+ if (!name) throw unavailable('this platform has no prebuilt module');
18
+ const local = path.join(__dirname, `seal-hwid.${name}.node`);
19
+ let addon;
20
+ try {
21
+ addon = existsSync(local) ? require(local) : require(`@carchalabs/seal-hwid-${name}`);
22
+ } catch (e) {
23
+ throw unavailable(e.message);
24
+ }
25
+
26
+ module.exports = { collect: addon.collect, devicePublicKey: addon.devicePublicKey, verifyLease: addon.verifyLease, leaseKeyIds: addon.leaseKeyIds, decryptFeature: addon.decryptFeature, version: addon.version };
package/package.json CHANGED
@@ -1,6 +1,39 @@
1
1
  {
2
2
  "name": "@carchalabs/seal-hwid",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.4.0",
4
+ "description": "Hardware component hashes for Carcha Seal (formerly LicenseKit) machine binding (Node.js and Electron).",
5
+ "keywords": ["hardware-id", "machine-id", "licensing", "electron", "carcha-seal"],
6
+ "main": "index.js",
7
+ "types": "index.d.ts",
8
+ "files": [
9
+ "index.js",
10
+ "index.d.ts",
11
+ "target.js"
12
+ ],
13
+ "scripts": {
14
+ "build": "node scripts/build.js",
15
+ "test": "node --test test/*.test.js"
16
+ },
17
+ "engines": {
18
+ "node": ">=22.13"
19
+ },
20
+ "license": "SEE LICENSE IN LICENSE",
21
+ "optionalDependencies": {
22
+ "@carchalabs/seal-hwid-darwin-arm64": "0.4.0",
23
+ "@carchalabs/seal-hwid-darwin-x64": "0.4.0",
24
+ "@carchalabs/seal-hwid-win32-x64-msvc": "0.4.0",
25
+ "@carchalabs/seal-hwid-linux-x64-gnu": "0.4.0"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/carcha-labs/carcha-seal-sdk.git",
30
+ "directory": "packages/hwid-node"
31
+ },
32
+ "bugs": {
33
+ "url": "https://github.com/carcha-labs/carcha-seal-sdk/issues"
34
+ },
35
+ "homepage": "https://seal.carchalabs.com/docs/",
36
+ "publishConfig": {
37
+ "access": "public"
38
+ }
39
+ }
package/target.js ADDED
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+ // The napi-rs target name for this machine (the platform package suffix), or null when
3
+ // no prebuilt module exists for it. Shared by index.js (loading) and scripts/build.js.
4
+ function isMusl() {
5
+ // glibc reports its version in the process report; musl does not.
6
+ const report = typeof process.report?.getReport === 'function' ? process.report.getReport() : null;
7
+ return !report?.header?.glibcVersionRuntime;
8
+ }
9
+
10
+ function target(platform = process.platform, arch = process.arch, musl = platform === 'linux' && isMusl()) {
11
+ if (platform === 'darwin' && (arch === 'arm64' || arch === 'x64')) return `darwin-${arch}`;
12
+ if (platform === 'win32' && arch === 'x64') return 'win32-x64-msvc';
13
+ if (platform === 'linux' && arch === 'x64' && !musl) return 'linux-x64-gnu';
14
+ return null;
15
+ }
16
+
17
+ module.exports = { target, TARGETS: ['darwin-arm64', 'darwin-x64', 'win32-x64-msvc', 'linux-x64-gnu'] };