@moonbase.sh/licensing 0.0.0-next-20260729150401
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/FINGERPRINT_SPEC.md +414 -0
- package/README.md +243 -0
- package/dist/index.cjs +925 -0
- package/dist/index.d.cts +634 -0
- package/dist/index.d.ts +634 -0
- package/dist/index.js +863 -0
- package/fingerprint-vectors.json +744 -0
- package/package.json +40 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Reference implementation of the Moonbase device fingerprint spec (v2). The
|
|
6
|
+
* authoritative, language-neutral definition lives in FINGERPRINT_SPEC.md, and
|
|
7
|
+
* the machine-readable conformance suite in fingerprint-vectors.json. Any SDK
|
|
8
|
+
* that conforms computes the same id on a given machine. Keep all three in
|
|
9
|
+
* lockstep; when they disagree, the vectors decide.
|
|
10
|
+
*
|
|
11
|
+
* A device id is a stamped SHA-256 of a deterministic "material" string built
|
|
12
|
+
* from stable native hardware identifiers:
|
|
13
|
+
*
|
|
14
|
+
* mbd2_<lowercase-hex sha256 of the material>
|
|
15
|
+
*
|
|
16
|
+
* Material layout (lines joined with "\n", NO trailing newline):
|
|
17
|
+
*
|
|
18
|
+
* moonbase:fingerprint:v2
|
|
19
|
+
* platform=<tag>
|
|
20
|
+
* <name>=<value>
|
|
21
|
+
* ...
|
|
22
|
+
*
|
|
23
|
+
* Every value is canonicalized (NFC, non-printable-ASCII dropped, capped,
|
|
24
|
+
* space-trimmed); empty values are skipped; duplicate parameter names are
|
|
25
|
+
* rejected; and a material that does not identify the individual machine — no
|
|
26
|
+
* parameters at all, or only model-level ones — is an error rather than a digest,
|
|
27
|
+
* so a machine or a whole product line can never share one id.
|
|
28
|
+
*/
|
|
29
|
+
declare const FINGERPRINT_PREFIX = "moonbase:fingerprint:v2";
|
|
30
|
+
/** Spec version. Always equal to the digit in {@link FINGERPRINT_PREFIX} and in the stamp. */
|
|
31
|
+
declare const FINGERPRINT_VERSION = 2;
|
|
32
|
+
/** Longest permitted canonical value, in characters. */
|
|
33
|
+
declare const MAX_VALUE_LENGTH = 128;
|
|
34
|
+
/**
|
|
35
|
+
* What the material was built from. `identity` is the real hardware fingerprint;
|
|
36
|
+
* `deviceName` is the opt-in, deliberately weaker host-name fallback, stamped
|
|
37
|
+
* distinctly so a server can tell the two apart.
|
|
38
|
+
*/
|
|
39
|
+
type DeviceIdSource = 'identity' | 'deviceName';
|
|
40
|
+
type PlatformTag = 'mac' | 'windows' | 'android' | 'linux' | 'bsd' | 'unknown';
|
|
41
|
+
type FingerprintParam = readonly [name: string, value: string];
|
|
42
|
+
interface DeviceIdentity {
|
|
43
|
+
/** Ordered identity params. Empty values are dropped by the material builder. */
|
|
44
|
+
params: FingerprintParam[];
|
|
45
|
+
/** Human-readable device name. Never part of the default material. */
|
|
46
|
+
deviceName: string;
|
|
47
|
+
}
|
|
48
|
+
/** Reads raw device identity for the current platform. Injectable for testing. */
|
|
49
|
+
interface DeviceIdentityReader {
|
|
50
|
+
read: () => DeviceIdentity;
|
|
51
|
+
}
|
|
52
|
+
/** A device id stamp broken into its parts (see {@link parseDeviceIdStamp}). */
|
|
53
|
+
interface DeviceIdStamp {
|
|
54
|
+
/** Fingerprint spec version that produced the digest. */
|
|
55
|
+
version: number;
|
|
56
|
+
source: DeviceIdSource;
|
|
57
|
+
/** The 64-char lowercase-hex SHA-256. */
|
|
58
|
+
digest: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Canonicalize a raw value read from the system (see FINGERPRINT_SPEC.md,
|
|
62
|
+
* "Canonicalizing values"): NFC-normalize, drop every character outside
|
|
63
|
+
* printable ASCII, cap the length, then trim spaces from both ends.
|
|
64
|
+
*
|
|
65
|
+
* Dropping non-printables is what makes the algorithm implementable
|
|
66
|
+
* byte-identically in any language: it removes the newline-injection ambiguity
|
|
67
|
+
* from the material grammar, and it makes the decoding choice for raw firmware
|
|
68
|
+
* strings (latin1 vs. UTF-8 vs. raw bytes) immaterial, since every byte those
|
|
69
|
+
* decodings disagree about is discarded either way.
|
|
70
|
+
*/
|
|
71
|
+
declare function canonicalizeValue(value: string): string;
|
|
72
|
+
/** Map a Node.js `process.platform` value onto a canonical platform tag. */
|
|
73
|
+
declare function platformTag(platform?: NodeJS.Platform): PlatformTag;
|
|
74
|
+
/**
|
|
75
|
+
* The identifying parameter names, as a detached frozen list.
|
|
76
|
+
*
|
|
77
|
+
* Deliberately not the `Set` this module checks against. `ReadonlySet` is erased
|
|
78
|
+
* at compile time, so exporting the live set would let any JavaScript consumer —
|
|
79
|
+
* or any dependency — call `.add('sysVendor')` and turn model-level values into
|
|
80
|
+
* accepted identity for the whole process, silently undoing the collision
|
|
81
|
+
* protection below.
|
|
82
|
+
*/
|
|
83
|
+
declare const IDENTIFYING_PARAM_NAMES: readonly string[];
|
|
84
|
+
/**
|
|
85
|
+
* Canonicalize every value and drop the ones that end up empty, plus any
|
|
86
|
+
* identifying parameter holding an unprogrammed placeholder. Order is preserved.
|
|
87
|
+
* Exported so a caller can see which parameters actually contributed without
|
|
88
|
+
* rebuilding the material.
|
|
89
|
+
*
|
|
90
|
+
* @throws when two surviving parameters share a name — the material grammar has
|
|
91
|
+
* no way to express that, so it is a collection bug rather than a machine state.
|
|
92
|
+
*/
|
|
93
|
+
declare function canonicalizeParams(params: ReadonlyArray<FingerprintParam>): FingerprintParam[];
|
|
94
|
+
/**
|
|
95
|
+
* Assemble the canonical fingerprint material string. Pure and deterministic —
|
|
96
|
+
* exported so consumers can verify cross-SDK parity against the shipped vectors.
|
|
97
|
+
*
|
|
98
|
+
* @throws {InsufficientDeviceIdentityError} when no parameter survives canonicalization, or when
|
|
99
|
+
* none of the survivors identifies the individual machine.
|
|
100
|
+
*/
|
|
101
|
+
declare function buildFingerprintMaterial(platform: PlatformTag, params: ReadonlyArray<FingerprintParam>): string;
|
|
102
|
+
/** Hash fingerprint material into a bare digest: 64-char lowercase hex SHA-256. */
|
|
103
|
+
declare function fingerprintDigest(material: string): string;
|
|
104
|
+
/** Prefix a digest with its version and source, producing the wire-form device id. */
|
|
105
|
+
declare function stampDeviceId(digest: string, source?: DeviceIdSource): string;
|
|
106
|
+
/** Hash material and stamp it: the full device id as sent to Moonbase and stored in `sig`. */
|
|
107
|
+
declare function fingerprintDeviceId(material: string, source?: DeviceIdSource): string;
|
|
108
|
+
/**
|
|
109
|
+
* Split a stamped device id into its parts, or `null` if it is not a Moonbase
|
|
110
|
+
* stamp (a legacy id, or one from a custom resolver). Lets a validator tell
|
|
111
|
+
* "this license belongs to another machine" apart from "this license was bound
|
|
112
|
+
* by an older fingerprint version".
|
|
113
|
+
*/
|
|
114
|
+
declare function parseDeviceIdStamp(deviceId: string): DeviceIdStamp | null;
|
|
115
|
+
/** Extract `IOPlatformUUID` from `ioreg` output: hyphens stripped, uppercased (spec: macOS `ioPlatformUuid`). */
|
|
116
|
+
declare function parseIoregPlatformUuid(ioregOutput: string): string;
|
|
117
|
+
/**
|
|
118
|
+
* Pick the first source holding a real machine id, or `''`.
|
|
119
|
+
*
|
|
120
|
+
* Each candidate is *validated*, not merely checked for being non-empty:
|
|
121
|
+
* `machine-id(5)` defines the file as exactly 32 lowercase hex digits, and
|
|
122
|
+
* legitimately holds the literal marker `uninitialized` in an initrd or a golden
|
|
123
|
+
* image awaiting first boot. Every machine deployed from such an image reads that
|
|
124
|
+
* same marker, so accepting it would hand them all one device id — and would also
|
|
125
|
+
* stop the fall-through to a D-Bus id that may be perfectly valid.
|
|
126
|
+
*/
|
|
127
|
+
declare function selectMachineId(...sources: string[]): string;
|
|
128
|
+
/**
|
|
129
|
+
* Extract the ordered identity params from a raw SMBIOS structure table: the
|
|
130
|
+
* **first** type-1 (System) and **first** type-2 (Baseboard) structures only.
|
|
131
|
+
*
|
|
132
|
+
* Type 4 (Processor) is deliberately not collected — its values are model-class
|
|
133
|
+
* rather than per-machine, and the number of type-4 structures tracks the CPU
|
|
134
|
+
* socket / vCPU count, so collecting them would change the device id whenever a
|
|
135
|
+
* VM is resized.
|
|
136
|
+
*/
|
|
137
|
+
declare function parseSmbiosParams(smbiosData: Buffer): FingerprintParam[];
|
|
138
|
+
/** The real, platform-dispatching identity reader used by {@link MoonbaseDeviceIdResolver}. */
|
|
139
|
+
declare function defaultDeviceIdentityReader(platform?: NodeJS.Platform): DeviceIdentityReader;
|
|
140
|
+
|
|
141
|
+
interface IDeviceIdResolver {
|
|
142
|
+
resolveDeviceName: () => Promise<string>;
|
|
143
|
+
resolveDeviceId: () => Promise<string>;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* A device id plus the provenance needed to reason about it — safe to log, show
|
|
147
|
+
* in an about box, or attach to a support ticket.
|
|
148
|
+
*
|
|
149
|
+
* Parameter *names* only, deliberately. Their values are hardware serial numbers,
|
|
150
|
+
* and a hash of one is no safer to publish: an unsalted digest is a stable global
|
|
151
|
+
* correlator for the machine, and low-entropy values like host names or
|
|
152
|
+
* sequential serials fall to a dictionary. `machine-id(5)` is explicit that the
|
|
153
|
+
* Linux machine id is confidential and must only ever be exposed through an
|
|
154
|
+
* application-specific *keyed* hash. Which parameters contributed is the useful
|
|
155
|
+
* diagnostic anyway; what they read is not.
|
|
156
|
+
*/
|
|
157
|
+
interface DeviceIdDescription {
|
|
158
|
+
deviceId: string;
|
|
159
|
+
/** Fingerprint spec version that produced it. */
|
|
160
|
+
version: number;
|
|
161
|
+
platform: PlatformTag;
|
|
162
|
+
source: DeviceIdSource;
|
|
163
|
+
/** Names of the identity parameters that went into the material, in order. */
|
|
164
|
+
paramNames: string[];
|
|
165
|
+
}
|
|
166
|
+
/** A resolver that can explain how it arrived at its device id. */
|
|
167
|
+
interface IDescribableDeviceIdResolver extends IDeviceIdResolver {
|
|
168
|
+
describeDevice: () => Promise<DeviceIdDescription>;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* A resolver that also recognises device ids this machine used to have.
|
|
172
|
+
*
|
|
173
|
+
* Deliberately separate from {@link IDeviceIdResolver.resolveDeviceId}, which
|
|
174
|
+
* stays single-valued: what a device binds on activation and what a validator
|
|
175
|
+
* accepts are different questions, and conflating them is what forces the
|
|
176
|
+
* all-or-nothing migration choice.
|
|
177
|
+
*/
|
|
178
|
+
interface IMigratingDeviceIdResolver extends IDeviceIdResolver {
|
|
179
|
+
acceptsDeviceId: (deviceId: string) => Promise<boolean>;
|
|
180
|
+
}
|
|
181
|
+
interface MoonbaseDeviceIdResolverOptions {
|
|
182
|
+
/** Overrides the identity source. Primarily for testing. */
|
|
183
|
+
reader?: DeviceIdentityReader;
|
|
184
|
+
/** Overrides the detected platform. Primarily for testing. */
|
|
185
|
+
platform?: NodeJS.Platform;
|
|
186
|
+
/**
|
|
187
|
+
* What to do when no hardware identity is readable. `'none'` (the default)
|
|
188
|
+
* throws {@link InsufficientDeviceIdentityError}; `'deviceName'` falls back to
|
|
189
|
+
* hashing the host name, producing a deliberately weaker id stamped `mbd2n_`.
|
|
190
|
+
*
|
|
191
|
+
* The fallback is opt-in because a host name is user-renameable, frequently
|
|
192
|
+
* duplicated across imaged machines, and regenerated on every container start.
|
|
193
|
+
*/
|
|
194
|
+
fallback?: 'none' | 'deviceName';
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Default resolver. Implements the Moonbase device fingerprint spec (v2; see
|
|
198
|
+
* FINGERPRINT_SPEC.md), building the `moonbase:fingerprint:v2` material from
|
|
199
|
+
* native hardware identifiers (SMBIOS on Windows, `IOPlatformUUID` on macOS,
|
|
200
|
+
* `machine-id` + DMI on Linux) and stamping its SHA-256 as `mbd2_<hex>`. Every
|
|
201
|
+
* Moonbase SDK that implements the spec produces the same id on a given machine.
|
|
202
|
+
*
|
|
203
|
+
* The result is memoized: reading identity can mean spawning a subprocess, and
|
|
204
|
+
* both activation and every validation ask for it.
|
|
205
|
+
*/
|
|
206
|
+
declare class MoonbaseDeviceIdResolver implements IDescribableDeviceIdResolver {
|
|
207
|
+
private readonly reader;
|
|
208
|
+
private readonly platform;
|
|
209
|
+
private readonly fallback;
|
|
210
|
+
private identity?;
|
|
211
|
+
private described?;
|
|
212
|
+
constructor(options?: MoonbaseDeviceIdResolverOptions);
|
|
213
|
+
resolveDeviceName(): Promise<string>;
|
|
214
|
+
resolveDeviceId(): Promise<string>;
|
|
215
|
+
/**
|
|
216
|
+
* A fresh copy each call. The device binding is what activation sends and what
|
|
217
|
+
* validation compares, so handing out the resolver's own object would let a
|
|
218
|
+
* consumer that edits a diagnostic — or logs it through something that
|
|
219
|
+
* normalizes in place — silently change the id every later call returns.
|
|
220
|
+
*/
|
|
221
|
+
describeDevice(): Promise<DeviceIdDescription>;
|
|
222
|
+
private compute;
|
|
223
|
+
/**
|
|
224
|
+
* Read identity at most once. Both halves of an activation request ask for it —
|
|
225
|
+
* the name and then the id — and a read can mean spawning `ioreg` or
|
|
226
|
+
* PowerShell, so reading per call would double the cost of every request and
|
|
227
|
+
* let the name and the id come from two different reads of the machine.
|
|
228
|
+
*/
|
|
229
|
+
private readIdentity;
|
|
230
|
+
private computeDescription;
|
|
231
|
+
private describe;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Binds the current fingerprint but keeps recognising ids this device was bound
|
|
235
|
+
* to before — the migration path off an older algorithm without a flag day.
|
|
236
|
+
*
|
|
237
|
+
* `resolveDeviceId` always returns the *current* resolver's id, so every new
|
|
238
|
+
* activation binds the current algorithm; the historical resolvers are consulted
|
|
239
|
+
* only when a validator is deciding whether to accept an already-issued license.
|
|
240
|
+
* A fleet therefore migrates as licenses are naturally re-activated, instead of
|
|
241
|
+
* every device re-activating at once — which would burn a second activation seat
|
|
242
|
+
* per device and reset device-scoped trials.
|
|
243
|
+
*
|
|
244
|
+
* ```ts
|
|
245
|
+
* new MigratingDeviceIdResolver(new MoonbaseDeviceIdResolver(), new LegacyDeviceIdResolver())
|
|
246
|
+
* ```
|
|
247
|
+
*
|
|
248
|
+
* Historical ids are computed lazily — only on a mismatch — and then memoized,
|
|
249
|
+
* so the cost is never paid on the happy path. A historical resolver that throws
|
|
250
|
+
* is simply skipped.
|
|
251
|
+
*
|
|
252
|
+
* Every accepted id is recomputed from the machine's own hardware on each call.
|
|
253
|
+
* Nothing is read from disk, so widening what a validator accepts does not widen
|
|
254
|
+
* what an attacker can assert.
|
|
255
|
+
*/
|
|
256
|
+
declare class MigratingDeviceIdResolver implements IMigratingDeviceIdResolver {
|
|
257
|
+
private readonly current;
|
|
258
|
+
private readonly previous;
|
|
259
|
+
private previousIds?;
|
|
260
|
+
/** Forwarded so the current resolver stays describable through this wrapper. */
|
|
261
|
+
describeDevice?: () => Promise<DeviceIdDescription>;
|
|
262
|
+
constructor(current: IDeviceIdResolver, ...previous: IDeviceIdResolver[]);
|
|
263
|
+
resolveDeviceName(): Promise<string>;
|
|
264
|
+
resolveDeviceId(): Promise<string>;
|
|
265
|
+
acceptsDeviceId(deviceId: string): Promise<boolean>;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* @deprecated The previous default resolver. Its device id is a base64 SHA-256 of
|
|
269
|
+
* normalized `systeminformation` fields and does **not** implement the
|
|
270
|
+
* `moonbase:fingerprint:v2` spec (FINGERPRINT_SPEC.md). Kept so deployments can keep
|
|
271
|
+
* validating licenses that were bound under the old id during migration; prefer
|
|
272
|
+
* {@link MoonbaseDeviceIdResolver}.
|
|
273
|
+
*/
|
|
274
|
+
declare class LegacyDeviceIdResolver implements IDeviceIdResolver {
|
|
275
|
+
resolveDeviceName(): Promise<string>;
|
|
276
|
+
resolveDeviceId(): Promise<string>;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
declare const activationRequestResponseSchema: z.ZodObject<{
|
|
280
|
+
id: z.ZodString;
|
|
281
|
+
request: z.ZodString;
|
|
282
|
+
browser: z.ZodString;
|
|
283
|
+
}, "strip", z.ZodTypeAny, {
|
|
284
|
+
id: string;
|
|
285
|
+
request: string;
|
|
286
|
+
browser: string;
|
|
287
|
+
}, {
|
|
288
|
+
id: string;
|
|
289
|
+
request: string;
|
|
290
|
+
browser: string;
|
|
291
|
+
}>;
|
|
292
|
+
declare const productSchema: z.ZodObject<{
|
|
293
|
+
id: z.ZodString;
|
|
294
|
+
name: z.ZodString;
|
|
295
|
+
currentReleaseVersion: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
296
|
+
properties: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
297
|
+
}, "strip", z.ZodTypeAny, {
|
|
298
|
+
id: string;
|
|
299
|
+
name: string;
|
|
300
|
+
currentReleaseVersion?: string | null | undefined;
|
|
301
|
+
properties?: Record<string, unknown> | null | undefined;
|
|
302
|
+
}, {
|
|
303
|
+
id: string;
|
|
304
|
+
name: string;
|
|
305
|
+
currentReleaseVersion?: string | null | undefined;
|
|
306
|
+
properties?: Record<string, unknown> | null | undefined;
|
|
307
|
+
}>;
|
|
308
|
+
declare const userSchema: z.ZodObject<{
|
|
309
|
+
id: z.ZodString;
|
|
310
|
+
name: z.ZodString;
|
|
311
|
+
email: z.ZodString;
|
|
312
|
+
properties: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
313
|
+
}, "strip", z.ZodTypeAny, {
|
|
314
|
+
id: string;
|
|
315
|
+
name: string;
|
|
316
|
+
email: string;
|
|
317
|
+
properties?: Record<string, unknown> | null | undefined;
|
|
318
|
+
}, {
|
|
319
|
+
id: string;
|
|
320
|
+
name: string;
|
|
321
|
+
email: string;
|
|
322
|
+
properties?: Record<string, unknown> | null | undefined;
|
|
323
|
+
}>;
|
|
324
|
+
declare const licenseSchema: z.ZodObject<{
|
|
325
|
+
id: z.ZodString;
|
|
326
|
+
trial: z.ZodBoolean;
|
|
327
|
+
activationMethod: z.ZodNativeEnum<typeof ActivationMethod>;
|
|
328
|
+
product: z.ZodObject<{
|
|
329
|
+
id: z.ZodString;
|
|
330
|
+
name: z.ZodString;
|
|
331
|
+
currentReleaseVersion: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
332
|
+
properties: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
333
|
+
}, "strip", z.ZodTypeAny, {
|
|
334
|
+
id: string;
|
|
335
|
+
name: string;
|
|
336
|
+
currentReleaseVersion?: string | null | undefined;
|
|
337
|
+
properties?: Record<string, unknown> | null | undefined;
|
|
338
|
+
}, {
|
|
339
|
+
id: string;
|
|
340
|
+
name: string;
|
|
341
|
+
currentReleaseVersion?: string | null | undefined;
|
|
342
|
+
properties?: Record<string, unknown> | null | undefined;
|
|
343
|
+
}>;
|
|
344
|
+
ownedSubProductIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
345
|
+
subscriptionId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
346
|
+
issuedAt: z.ZodDate;
|
|
347
|
+
issuedTo: z.ZodObject<{
|
|
348
|
+
id: z.ZodString;
|
|
349
|
+
name: z.ZodString;
|
|
350
|
+
email: z.ZodString;
|
|
351
|
+
properties: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
352
|
+
}, "strip", z.ZodTypeAny, {
|
|
353
|
+
id: string;
|
|
354
|
+
name: string;
|
|
355
|
+
email: string;
|
|
356
|
+
properties?: Record<string, unknown> | null | undefined;
|
|
357
|
+
}, {
|
|
358
|
+
id: string;
|
|
359
|
+
name: string;
|
|
360
|
+
email: string;
|
|
361
|
+
properties?: Record<string, unknown> | null | undefined;
|
|
362
|
+
}>;
|
|
363
|
+
expiresAt: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
|
|
364
|
+
validatedAt: z.ZodDate;
|
|
365
|
+
properties: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
366
|
+
token: z.ZodString;
|
|
367
|
+
}, "strip", z.ZodTypeAny, {
|
|
368
|
+
id: string;
|
|
369
|
+
trial: boolean;
|
|
370
|
+
activationMethod: ActivationMethod;
|
|
371
|
+
product: {
|
|
372
|
+
id: string;
|
|
373
|
+
name: string;
|
|
374
|
+
currentReleaseVersion?: string | null | undefined;
|
|
375
|
+
properties?: Record<string, unknown> | null | undefined;
|
|
376
|
+
};
|
|
377
|
+
ownedSubProductIds: string[];
|
|
378
|
+
issuedAt: Date;
|
|
379
|
+
issuedTo: {
|
|
380
|
+
id: string;
|
|
381
|
+
name: string;
|
|
382
|
+
email: string;
|
|
383
|
+
properties?: Record<string, unknown> | null | undefined;
|
|
384
|
+
};
|
|
385
|
+
validatedAt: Date;
|
|
386
|
+
token: string;
|
|
387
|
+
properties?: Record<string, unknown> | null | undefined;
|
|
388
|
+
subscriptionId?: string | null | undefined;
|
|
389
|
+
expiresAt?: Date | null | undefined;
|
|
390
|
+
}, {
|
|
391
|
+
id: string;
|
|
392
|
+
trial: boolean;
|
|
393
|
+
activationMethod: ActivationMethod;
|
|
394
|
+
product: {
|
|
395
|
+
id: string;
|
|
396
|
+
name: string;
|
|
397
|
+
currentReleaseVersion?: string | null | undefined;
|
|
398
|
+
properties?: Record<string, unknown> | null | undefined;
|
|
399
|
+
};
|
|
400
|
+
issuedAt: Date;
|
|
401
|
+
issuedTo: {
|
|
402
|
+
id: string;
|
|
403
|
+
name: string;
|
|
404
|
+
email: string;
|
|
405
|
+
properties?: Record<string, unknown> | null | undefined;
|
|
406
|
+
};
|
|
407
|
+
validatedAt: Date;
|
|
408
|
+
token: string;
|
|
409
|
+
properties?: Record<string, unknown> | null | undefined;
|
|
410
|
+
ownedSubProductIds?: string[] | undefined;
|
|
411
|
+
subscriptionId?: string | null | undefined;
|
|
412
|
+
expiresAt?: Date | null | undefined;
|
|
413
|
+
}>;
|
|
414
|
+
|
|
415
|
+
declare enum ActivationMethod {
|
|
416
|
+
Online = "Online",
|
|
417
|
+
Offline = "Offline"
|
|
418
|
+
}
|
|
419
|
+
type Metadata = Record<string, string>;
|
|
420
|
+
type Platform = 'Windows' | 'Linux' | 'Mac';
|
|
421
|
+
type License = z.infer<typeof licenseSchema>;
|
|
422
|
+
type Product = z.infer<typeof productSchema>;
|
|
423
|
+
type User = z.infer<typeof userSchema>;
|
|
424
|
+
interface DeviceToken {
|
|
425
|
+
id: string;
|
|
426
|
+
name: string;
|
|
427
|
+
productId: string;
|
|
428
|
+
format: 'JWT';
|
|
429
|
+
}
|
|
430
|
+
type ActivationRequestResponse = z.infer<typeof activationRequestResponseSchema>;
|
|
431
|
+
|
|
432
|
+
interface ILicenseClient {
|
|
433
|
+
/**
|
|
434
|
+
* Create a new request to have this product activated, which
|
|
435
|
+
* means the user will be able to open a browser to respond
|
|
436
|
+
* to the activation request.
|
|
437
|
+
* This can be used as an alternative way to authenticate
|
|
438
|
+
* license fulfillment without having to take in user credentials
|
|
439
|
+
* in the app. After requesting activation, open a browser with
|
|
440
|
+
* the returned browser URL, and start polling {@link getRequestedActivation}
|
|
441
|
+
* for a completed request with license.
|
|
442
|
+
*
|
|
443
|
+
* @returns Details about the requested activation with url to open in the browser
|
|
444
|
+
*/
|
|
445
|
+
requestActivation: () => Promise<ActivationRequestResponse>;
|
|
446
|
+
/**
|
|
447
|
+
* Takes in a activation request created through {@link requestActivation}
|
|
448
|
+
* and checks if the request has been fulfilled yet.
|
|
449
|
+
* The returned license will be either a trial or a full license
|
|
450
|
+
* depending on what the user chooses in the browser session.
|
|
451
|
+
*
|
|
452
|
+
* @param request The activation request to poll
|
|
453
|
+
* @returns A license if the request has been fulfilled, else null
|
|
454
|
+
*/
|
|
455
|
+
getRequestedActivation: (request: ActivationRequestResponse) => Promise<License | null>;
|
|
456
|
+
/**
|
|
457
|
+
* Requests a trial license for the product.
|
|
458
|
+
* This can only be done once per product per device, and skips
|
|
459
|
+
* the browser activation process. This currently requires that
|
|
460
|
+
* the product allows anonymous trials, since there is no authentication.
|
|
461
|
+
*
|
|
462
|
+
* @returns The allocated trial license, throws if the trial license cannot be allocated (e.g. user already had a trial) or if the request failed
|
|
463
|
+
*/
|
|
464
|
+
requestTrial: () => Promise<License>;
|
|
465
|
+
/**
|
|
466
|
+
* Checks if the given license is still valid, not revoked
|
|
467
|
+
* and returns an updated license if still active.
|
|
468
|
+
* This method is a good way to ensure the user still has
|
|
469
|
+
* rights to use the product, and should be called regularly
|
|
470
|
+
* unless activated offline.
|
|
471
|
+
*
|
|
472
|
+
* @param license The license to validate
|
|
473
|
+
* @returns An updated license
|
|
474
|
+
*/
|
|
475
|
+
validateLicense: (license: License) => Promise<License>;
|
|
476
|
+
/**
|
|
477
|
+
* Contrary to {@linkvalidateLicense }, this method takes
|
|
478
|
+
* in raw bytes of the license, which can be useful if you're
|
|
479
|
+
* reading the license from a file for offline activations.
|
|
480
|
+
*
|
|
481
|
+
* @param license The license to validate
|
|
482
|
+
* @returns An updated license
|
|
483
|
+
*/
|
|
484
|
+
validateRawLicense: (rawLicense: Buffer) => Promise<License>;
|
|
485
|
+
/**
|
|
486
|
+
* Given a license, this method will try to revoke the license activation,
|
|
487
|
+
* freeing up a seat for other devices to activate the license.
|
|
488
|
+
* This will not work for offline-activated devices.
|
|
489
|
+
* @param license The license to revoke
|
|
490
|
+
*/
|
|
491
|
+
revokeLicense: (license: License) => Promise<void>;
|
|
492
|
+
}
|
|
493
|
+
declare class LicenseClient implements ILicenseClient {
|
|
494
|
+
private readonly configuration;
|
|
495
|
+
private readonly deviceIdResolver;
|
|
496
|
+
private readonly licenseValidator;
|
|
497
|
+
constructor(configuration: MoonbaseConfiguration, deviceIdResolver: IDeviceIdResolver, licenseValidator: ILicenseValidator);
|
|
498
|
+
requestActivation(): Promise<ActivationRequestResponse>;
|
|
499
|
+
getRequestedActivation(request: ActivationRequestResponse): Promise<License | null>;
|
|
500
|
+
requestTrial(): Promise<License>;
|
|
501
|
+
validateLicense(license: License): Promise<License>;
|
|
502
|
+
validateRawLicense(rawLicense: Buffer): Promise<License>;
|
|
503
|
+
revokeLicense(license: License): Promise<void>;
|
|
504
|
+
private handleLicenseResponse;
|
|
505
|
+
private buildQueryString;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
interface ILicenseStore {
|
|
509
|
+
loadLocalLicense: () => Promise<License | null>;
|
|
510
|
+
storeLocalLicense: (license: License) => Promise<void>;
|
|
511
|
+
deleteLocalLicense: () => Promise<void>;
|
|
512
|
+
}
|
|
513
|
+
declare class InMemoryLicenseStore implements ILicenseStore {
|
|
514
|
+
private license?;
|
|
515
|
+
loadLocalLicense(): Promise<License | null>;
|
|
516
|
+
storeLocalLicense(license: License): Promise<void>;
|
|
517
|
+
deleteLocalLicense(): Promise<void>;
|
|
518
|
+
}
|
|
519
|
+
declare class FileLicenseStore implements ILicenseStore {
|
|
520
|
+
private readonly options?;
|
|
521
|
+
constructor(options?: {
|
|
522
|
+
dir?: string;
|
|
523
|
+
licenseFileName?: string;
|
|
524
|
+
} | undefined);
|
|
525
|
+
loadLocalLicense(): Promise<License | null>;
|
|
526
|
+
storeLocalLicense(license: License): Promise<void>;
|
|
527
|
+
deleteLocalLicense(): Promise<void>;
|
|
528
|
+
private get path();
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
interface ILicenseValidator {
|
|
532
|
+
validateLicense: (token: string) => Promise<License>;
|
|
533
|
+
}
|
|
534
|
+
declare class LicenseValidator implements ILicenseValidator {
|
|
535
|
+
private readonly configuration;
|
|
536
|
+
private readonly deviceIdResolver;
|
|
537
|
+
constructor(configuration: MoonbaseConfiguration, deviceIdResolver: IDeviceIdResolver);
|
|
538
|
+
validateLicense(token: string): Promise<License>;
|
|
539
|
+
/**
|
|
540
|
+
* Give a {@link IMigratingDeviceIdResolver} the chance to vouch for a device id
|
|
541
|
+
* this machine used to have. Only consulted after the fast path fails, so
|
|
542
|
+
* apps that have not opted into a migration pay nothing.
|
|
543
|
+
*/
|
|
544
|
+
private acceptsHistoricalDeviceId;
|
|
545
|
+
private parseLicenseToken;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
declare enum ErrorType {
|
|
549
|
+
None = "None",
|
|
550
|
+
ApiError = "ApiError",
|
|
551
|
+
NoEligibleLicense = "NoEligibleLicense",
|
|
552
|
+
LicenseInvalid = "LicenseInvalid",
|
|
553
|
+
LicenseRevoked = "LicenseRevoked",
|
|
554
|
+
LicenseActivationRevoked = "LicenseActivationRevoked",
|
|
555
|
+
LicenseExpired = "LicenseExpired",
|
|
556
|
+
/** The license is valid but bound to a different device, or to an older fingerprint version. */
|
|
557
|
+
LicenseDeviceMismatch = "LicenseDeviceMismatch",
|
|
558
|
+
/** No stable hardware identifier could be read, so no device id can be computed. */
|
|
559
|
+
DeviceIdentityUnavailable = "DeviceIdentityUnavailable"
|
|
560
|
+
}
|
|
561
|
+
declare class MoonbaseError extends Error {
|
|
562
|
+
readonly title: string;
|
|
563
|
+
readonly detail: string | undefined;
|
|
564
|
+
readonly type?: ErrorType | undefined;
|
|
565
|
+
readonly inner?: Error | undefined;
|
|
566
|
+
constructor(title: string, detail: string | undefined, type?: ErrorType | undefined, inner?: Error | undefined);
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Thrown when the device fingerprint has nothing machine-specific to hash —
|
|
570
|
+
* either no parameter could be read at all, or the only ones that could are
|
|
571
|
+
* model-level (vendor, product and board names, shared by every unit of a
|
|
572
|
+
* product line).
|
|
573
|
+
*
|
|
574
|
+
* The spec deliberately makes both an error rather than hashing what is there:
|
|
575
|
+
* either would hand a whole class of machines the *same* device id, and a license
|
|
576
|
+
* bound to it would validate on all of them.
|
|
577
|
+
*
|
|
578
|
+
* Reachable on platforms with no defined identity parameters (Android, BSD,
|
|
579
|
+
* anything unknown); when every source fails — a sandboxed process that cannot
|
|
580
|
+
* spawn `ioreg`, a container with no DMI, a blocked PowerShell; and on machines
|
|
581
|
+
* whose per-device identifiers are simply absent, such as a Linux install with no
|
|
582
|
+
* `machine-id` or a VM whose SMBIOS carries an unset UUID and a blank baseboard
|
|
583
|
+
* serial. Enable the host-name fallback
|
|
584
|
+
* (`new MoonbaseDeviceIdResolver({ fallback: 'deviceName' })`) to accept a
|
|
585
|
+
* deliberately weaker id on those machines.
|
|
586
|
+
*/
|
|
587
|
+
declare class InsufficientDeviceIdentityError extends MoonbaseError {
|
|
588
|
+
readonly platform: string;
|
|
589
|
+
constructor(platform: string, reason?: string);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
interface MoonbaseConfiguration {
|
|
593
|
+
endpoint: string;
|
|
594
|
+
publicKey: string;
|
|
595
|
+
productId: string;
|
|
596
|
+
accountId?: string;
|
|
597
|
+
/**
|
|
598
|
+
* Optional analytics key/value pairs sent with every activation,
|
|
599
|
+
* trial request, and license validation. Recorded on the activation
|
|
600
|
+
* server-side and surfaced in LicenseActivatedEvent /
|
|
601
|
+
* LicenseValidatedEvent webhooks. Strings only; empty values are
|
|
602
|
+
* dropped on the wire. The object is re-read on every call, so
|
|
603
|
+
* mutating it (e.g. to rotate a sessionId) takes effect on the
|
|
604
|
+
* next request.
|
|
605
|
+
*/
|
|
606
|
+
metadata?: Metadata;
|
|
607
|
+
/**
|
|
608
|
+
* Reported as ?platform=... on activation, trial, and validation
|
|
609
|
+
* requests. Defaults to auto-detection from `process.platform`
|
|
610
|
+
* (`darwin` → `Mac`, `win32` → `Windows`, `linux` → `Linux`). Set
|
|
611
|
+
* explicitly to override, or pass `null` to suppress.
|
|
612
|
+
*/
|
|
613
|
+
platform?: Platform | null;
|
|
614
|
+
/**
|
|
615
|
+
* Reported as ?appVersion=... (semver expected by backend). No
|
|
616
|
+
* reliable auto-detection in Node, so callers must set this
|
|
617
|
+
* explicitly to opt in.
|
|
618
|
+
*/
|
|
619
|
+
appVersion?: string;
|
|
620
|
+
licenseStore?: ILicenseStore;
|
|
621
|
+
deviceIdResolver?: IDeviceIdResolver;
|
|
622
|
+
}
|
|
623
|
+
declare class MoonbaseLicensing {
|
|
624
|
+
private readonly configuration;
|
|
625
|
+
readonly store: ILicenseStore;
|
|
626
|
+
readonly deviceIdResolver: IDeviceIdResolver;
|
|
627
|
+
readonly client: ILicenseClient;
|
|
628
|
+
readonly validator: ILicenseValidator;
|
|
629
|
+
constructor(configuration: MoonbaseConfiguration);
|
|
630
|
+
generateDeviceToken(): Promise<Buffer>;
|
|
631
|
+
readRawLicense(license: Buffer): Promise<License>;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
export { ActivationMethod, type ActivationRequestResponse, type DeviceIdDescription, type DeviceIdSource, type DeviceIdStamp, type DeviceIdentity, type DeviceIdentityReader, type DeviceToken, ErrorType, FINGERPRINT_PREFIX, FINGERPRINT_VERSION, FileLicenseStore, type FingerprintParam, IDENTIFYING_PARAM_NAMES, type IDescribableDeviceIdResolver, type IDeviceIdResolver, type ILicenseClient, type ILicenseStore, type ILicenseValidator, type IMigratingDeviceIdResolver, InMemoryLicenseStore, InsufficientDeviceIdentityError, LegacyDeviceIdResolver, type License, LicenseClient, LicenseValidator, MAX_VALUE_LENGTH, type Metadata, MigratingDeviceIdResolver, type MoonbaseConfiguration, MoonbaseDeviceIdResolver, type MoonbaseDeviceIdResolverOptions, MoonbaseError, MoonbaseLicensing, type Platform, type PlatformTag, type Product, type User, buildFingerprintMaterial, canonicalizeParams, canonicalizeValue, defaultDeviceIdentityReader, fingerprintDeviceId, fingerprintDigest, parseDeviceIdStamp, parseIoregPlatformUuid, parseSmbiosParams, platformTag, selectMachineId, stampDeviceId };
|