@moonbase.sh/licensing 3.0.0 → 3.1.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/dist/index.d.cts CHANGED
@@ -33,11 +33,30 @@ declare const FINGERPRINT_VERSION = 2;
33
33
  declare const MAX_VALUE_LENGTH = 128;
34
34
  /**
35
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.
36
+ * `deviceName` is the opt-in, deliberately weaker host-name fallback; `scoped` is
37
+ * an id that is stable only within one app scope (iOS/Android, where the platform
38
+ * exposes nothing an unrelated app can read). Each is stamped distinctly so a
39
+ * server can tell them apart.
38
40
  */
39
- type DeviceIdSource = 'identity' | 'deviceName';
40
- type PlatformTag = 'mac' | 'windows' | 'android' | 'linux' | 'bsd' | 'unknown';
41
+ type DeviceIdSource = 'identity' | 'deviceName' | 'scoped';
42
+ /** The tags this version defines, as a detached frozen map (see {@link IDENTIFYING_PARAM_NAMES}). */
43
+ declare const DEVICE_ID_SOURCE_TAGS: Readonly<Record<DeviceIdSource, string>>;
44
+ type PlatformTag = 'mac' | 'ios' | 'windows' | 'android' | 'linux' | 'bsd' | 'unknown';
45
+ /**
46
+ * The source a successful identity read earns on this platform.
47
+ *
48
+ * Scoped platforms expose no identifier an unrelated app can read, so anything
49
+ * built from their parameters is scoped to the app and must be stamped `mbd2s_`.
50
+ * Stamping it `mbd2_` would tell a server the id is a hardware fingerprint
51
+ * comparable across every app on the device, which is exactly what it is not:
52
+ * the server would then be entitled to correlate ids the spec forbids
53
+ * correlating, and a diagnostic would offer remedies that cannot apply.
54
+ *
55
+ * Derived from the platform rather than chosen by the caller, so a host that
56
+ * bridges `identifierForVendor` or `androidId` through a custom
57
+ * {@link DeviceIdentityReader} cannot accidentally mislabel it.
58
+ */
59
+ declare function identitySource(platform: PlatformTag): DeviceIdSource;
41
60
  type FingerprintParam = readonly [name: string, value: string];
42
61
  interface DeviceIdentity {
43
62
  /** Ordered identity params. Empty values are dropped by the material builder. */
@@ -53,7 +72,10 @@ interface DeviceIdentityReader {
53
72
  interface DeviceIdStamp {
54
73
  /** Fingerprint spec version that produced the digest. */
55
74
  version: number;
56
- source: DeviceIdSource;
75
+ /** The literal source tag: `''`, `'n'`, `'s'`, or one a newer SDK introduced. */
76
+ sourceTag: string;
77
+ /** What {@link sourceTag} means, or `null` when this SDK does not define that tag. */
78
+ source: DeviceIdSource | null;
57
79
  /** The 64-char lowercase-hex SHA-256. */
58
80
  digest: string;
59
81
  }
@@ -69,8 +91,23 @@ interface DeviceIdStamp {
69
91
  * decodings disagree about is discarded either way.
70
92
  */
71
93
  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;
94
+ /**
95
+ * Map a Node.js `process.platform` value onto a canonical platform tag, or pass
96
+ * through a value that is already one.
97
+ *
98
+ * The passthrough is what makes the mobile platforms reachable at all. Node does
99
+ * not run on iOS, so `process.platform` is never `'ios'` and no amount of mapping
100
+ * can produce that tag — yet a host embedding Node can bridge
101
+ * `identifierForVendor` from platform API. Without this, such a host has no way
102
+ * to say which platform it is on: the value would fall through to `'unknown'`,
103
+ * and the material would carry `platform=unknown`, so the digest would not match
104
+ * what a conforming iOS SDK computes on the same device, and the id would be
105
+ * stamped `mbd2_` rather than `mbd2s_`. Both wrong, and both silent.
106
+ *
107
+ * `'android'` and `'linux'` belong to both vocabularies and map to themselves, so
108
+ * the two spellings cannot disagree.
109
+ */
110
+ declare function platformTag(platform?: NodeJS.Platform | PlatformTag): PlatformTag;
74
111
  /**
75
112
  * The identifying parameter names, as a detached frozen list.
76
113
  *
@@ -107,9 +144,12 @@ declare function stampDeviceId(digest: string, source?: DeviceIdSource): string;
107
144
  declare function fingerprintDeviceId(material: string, source?: DeviceIdSource): string;
108
145
  /**
109
146
  * 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
147
+ * stamp at all (a legacy id, or one from a custom resolver). Lets a validator tell
111
148
  * "this license belongs to another machine" apart from "this license was bound
112
149
  * by an older fingerprint version".
150
+ *
151
+ * An id whose *tag* is unrecognised still parses, with `source` null: it came from
152
+ * a newer SDK, and reporting it as unparseable would be worse than saying so.
113
153
  */
114
154
  declare function parseDeviceIdStamp(deviceId: string): DeviceIdStamp | null;
115
155
  /** Extract `IOPlatformUUID` from `ioreg` output: hyphens stripped, uppercased (spec: macOS `ioPlatformUuid`). */
@@ -136,7 +176,7 @@ declare function selectMachineId(...sources: string[]): string;
136
176
  */
137
177
  declare function parseSmbiosParams(smbiosData: Buffer): FingerprintParam[];
138
178
  /** The real, platform-dispatching identity reader used by {@link MoonbaseDeviceIdResolver}. */
139
- declare function defaultDeviceIdentityReader(platform?: NodeJS.Platform): DeviceIdentityReader;
179
+ declare function defaultDeviceIdentityReader(platform?: NodeJS.Platform | PlatformTag): DeviceIdentityReader;
140
180
 
141
181
  interface IDeviceIdResolver {
142
182
  resolveDeviceName: () => Promise<string>;
@@ -181,8 +221,16 @@ interface IMigratingDeviceIdResolver extends IDeviceIdResolver {
181
221
  interface MoonbaseDeviceIdResolverOptions {
182
222
  /** Overrides the identity source. Primarily for testing. */
183
223
  reader?: DeviceIdentityReader;
184
- /** Overrides the detected platform. Primarily for testing. */
185
- platform?: NodeJS.Platform;
224
+ /**
225
+ * Overrides the detected platform. Accepts a Node.js `process.platform` value
226
+ * or a canonical {@link PlatformTag}.
227
+ *
228
+ * The tag spelling exists for hosts that bridge a platform Node does not run
229
+ * on: pass `'ios'` together with a {@link reader} that supplies
230
+ * `identifierForVendor`. Without it the platform would resolve to `'unknown'`,
231
+ * which changes the material and so the device id.
232
+ */
233
+ platform?: NodeJS.Platform | PlatformTag;
186
234
  /**
187
235
  * What to do when no hardware identity is readable. `'none'` (the default)
188
236
  * throws {@link InsufficientDeviceIdentityError}; `'deviceName'` falls back to
@@ -190,6 +238,10 @@ interface MoonbaseDeviceIdResolverOptions {
190
238
  *
191
239
  * The fallback is opt-in because a host name is user-renameable, frequently
192
240
  * duplicated across imaged machines, and regenerated on every container start.
241
+ *
242
+ * It is **ignored on iOS and Android**, which throw regardless: there the host
243
+ * name is identical on every device, so the fallback would give a whole install
244
+ * base one id rather than merely a weak one.
193
245
  */
194
246
  fallback?: 'none' | 'deviceName';
195
247
  }
@@ -575,14 +627,15 @@ declare class MoonbaseError extends Error {
575
627
  * either would hand a whole class of machines the *same* device id, and a license
576
628
  * bound to it would validate on all of them.
577
629
  *
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.
630
+ * Reachable on platforms with no identity parameters this package can read (BSD
631
+ * and anything unknown, which the spec leaves undefined, plus Android, whose
632
+ * `androidId` needs Android-framework API a Node.js process cannot call); when
633
+ * every source fails a sandboxed process that cannot spawn `ioreg`, a container
634
+ * with no DMI, a blocked PowerShell; and on machines whose per-device identifiers
635
+ * are simply absent, such as a Linux install with no `machine-id` or a VM whose
636
+ * SMBIOS carries an unset UUID and a blank baseboard serial. Enable the host-name
637
+ * fallback (`new MoonbaseDeviceIdResolver({ fallback: 'deviceName' })`) to accept
638
+ * a deliberately weaker id on those machines.
586
639
  */
587
640
  declare class InsufficientDeviceIdentityError extends MoonbaseError {
588
641
  readonly platform: string;
@@ -631,4 +684,4 @@ declare class MoonbaseLicensing {
631
684
  readRawLicense(license: Buffer): Promise<License>;
632
685
  }
633
686
 
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 };
687
+ export { ActivationMethod, type ActivationRequestResponse, DEVICE_ID_SOURCE_TAGS, 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, identitySource, parseDeviceIdStamp, parseIoregPlatformUuid, parseSmbiosParams, platformTag, selectMachineId, stampDeviceId };
package/dist/index.d.ts CHANGED
@@ -33,11 +33,30 @@ declare const FINGERPRINT_VERSION = 2;
33
33
  declare const MAX_VALUE_LENGTH = 128;
34
34
  /**
35
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.
36
+ * `deviceName` is the opt-in, deliberately weaker host-name fallback; `scoped` is
37
+ * an id that is stable only within one app scope (iOS/Android, where the platform
38
+ * exposes nothing an unrelated app can read). Each is stamped distinctly so a
39
+ * server can tell them apart.
38
40
  */
39
- type DeviceIdSource = 'identity' | 'deviceName';
40
- type PlatformTag = 'mac' | 'windows' | 'android' | 'linux' | 'bsd' | 'unknown';
41
+ type DeviceIdSource = 'identity' | 'deviceName' | 'scoped';
42
+ /** The tags this version defines, as a detached frozen map (see {@link IDENTIFYING_PARAM_NAMES}). */
43
+ declare const DEVICE_ID_SOURCE_TAGS: Readonly<Record<DeviceIdSource, string>>;
44
+ type PlatformTag = 'mac' | 'ios' | 'windows' | 'android' | 'linux' | 'bsd' | 'unknown';
45
+ /**
46
+ * The source a successful identity read earns on this platform.
47
+ *
48
+ * Scoped platforms expose no identifier an unrelated app can read, so anything
49
+ * built from their parameters is scoped to the app and must be stamped `mbd2s_`.
50
+ * Stamping it `mbd2_` would tell a server the id is a hardware fingerprint
51
+ * comparable across every app on the device, which is exactly what it is not:
52
+ * the server would then be entitled to correlate ids the spec forbids
53
+ * correlating, and a diagnostic would offer remedies that cannot apply.
54
+ *
55
+ * Derived from the platform rather than chosen by the caller, so a host that
56
+ * bridges `identifierForVendor` or `androidId` through a custom
57
+ * {@link DeviceIdentityReader} cannot accidentally mislabel it.
58
+ */
59
+ declare function identitySource(platform: PlatformTag): DeviceIdSource;
41
60
  type FingerprintParam = readonly [name: string, value: string];
42
61
  interface DeviceIdentity {
43
62
  /** Ordered identity params. Empty values are dropped by the material builder. */
@@ -53,7 +72,10 @@ interface DeviceIdentityReader {
53
72
  interface DeviceIdStamp {
54
73
  /** Fingerprint spec version that produced the digest. */
55
74
  version: number;
56
- source: DeviceIdSource;
75
+ /** The literal source tag: `''`, `'n'`, `'s'`, or one a newer SDK introduced. */
76
+ sourceTag: string;
77
+ /** What {@link sourceTag} means, or `null` when this SDK does not define that tag. */
78
+ source: DeviceIdSource | null;
57
79
  /** The 64-char lowercase-hex SHA-256. */
58
80
  digest: string;
59
81
  }
@@ -69,8 +91,23 @@ interface DeviceIdStamp {
69
91
  * decodings disagree about is discarded either way.
70
92
  */
71
93
  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;
94
+ /**
95
+ * Map a Node.js `process.platform` value onto a canonical platform tag, or pass
96
+ * through a value that is already one.
97
+ *
98
+ * The passthrough is what makes the mobile platforms reachable at all. Node does
99
+ * not run on iOS, so `process.platform` is never `'ios'` and no amount of mapping
100
+ * can produce that tag — yet a host embedding Node can bridge
101
+ * `identifierForVendor` from platform API. Without this, such a host has no way
102
+ * to say which platform it is on: the value would fall through to `'unknown'`,
103
+ * and the material would carry `platform=unknown`, so the digest would not match
104
+ * what a conforming iOS SDK computes on the same device, and the id would be
105
+ * stamped `mbd2_` rather than `mbd2s_`. Both wrong, and both silent.
106
+ *
107
+ * `'android'` and `'linux'` belong to both vocabularies and map to themselves, so
108
+ * the two spellings cannot disagree.
109
+ */
110
+ declare function platformTag(platform?: NodeJS.Platform | PlatformTag): PlatformTag;
74
111
  /**
75
112
  * The identifying parameter names, as a detached frozen list.
76
113
  *
@@ -107,9 +144,12 @@ declare function stampDeviceId(digest: string, source?: DeviceIdSource): string;
107
144
  declare function fingerprintDeviceId(material: string, source?: DeviceIdSource): string;
108
145
  /**
109
146
  * 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
147
+ * stamp at all (a legacy id, or one from a custom resolver). Lets a validator tell
111
148
  * "this license belongs to another machine" apart from "this license was bound
112
149
  * by an older fingerprint version".
150
+ *
151
+ * An id whose *tag* is unrecognised still parses, with `source` null: it came from
152
+ * a newer SDK, and reporting it as unparseable would be worse than saying so.
113
153
  */
114
154
  declare function parseDeviceIdStamp(deviceId: string): DeviceIdStamp | null;
115
155
  /** Extract `IOPlatformUUID` from `ioreg` output: hyphens stripped, uppercased (spec: macOS `ioPlatformUuid`). */
@@ -136,7 +176,7 @@ declare function selectMachineId(...sources: string[]): string;
136
176
  */
137
177
  declare function parseSmbiosParams(smbiosData: Buffer): FingerprintParam[];
138
178
  /** The real, platform-dispatching identity reader used by {@link MoonbaseDeviceIdResolver}. */
139
- declare function defaultDeviceIdentityReader(platform?: NodeJS.Platform): DeviceIdentityReader;
179
+ declare function defaultDeviceIdentityReader(platform?: NodeJS.Platform | PlatformTag): DeviceIdentityReader;
140
180
 
141
181
  interface IDeviceIdResolver {
142
182
  resolveDeviceName: () => Promise<string>;
@@ -181,8 +221,16 @@ interface IMigratingDeviceIdResolver extends IDeviceIdResolver {
181
221
  interface MoonbaseDeviceIdResolverOptions {
182
222
  /** Overrides the identity source. Primarily for testing. */
183
223
  reader?: DeviceIdentityReader;
184
- /** Overrides the detected platform. Primarily for testing. */
185
- platform?: NodeJS.Platform;
224
+ /**
225
+ * Overrides the detected platform. Accepts a Node.js `process.platform` value
226
+ * or a canonical {@link PlatformTag}.
227
+ *
228
+ * The tag spelling exists for hosts that bridge a platform Node does not run
229
+ * on: pass `'ios'` together with a {@link reader} that supplies
230
+ * `identifierForVendor`. Without it the platform would resolve to `'unknown'`,
231
+ * which changes the material and so the device id.
232
+ */
233
+ platform?: NodeJS.Platform | PlatformTag;
186
234
  /**
187
235
  * What to do when no hardware identity is readable. `'none'` (the default)
188
236
  * throws {@link InsufficientDeviceIdentityError}; `'deviceName'` falls back to
@@ -190,6 +238,10 @@ interface MoonbaseDeviceIdResolverOptions {
190
238
  *
191
239
  * The fallback is opt-in because a host name is user-renameable, frequently
192
240
  * duplicated across imaged machines, and regenerated on every container start.
241
+ *
242
+ * It is **ignored on iOS and Android**, which throw regardless: there the host
243
+ * name is identical on every device, so the fallback would give a whole install
244
+ * base one id rather than merely a weak one.
193
245
  */
194
246
  fallback?: 'none' | 'deviceName';
195
247
  }
@@ -575,14 +627,15 @@ declare class MoonbaseError extends Error {
575
627
  * either would hand a whole class of machines the *same* device id, and a license
576
628
  * bound to it would validate on all of them.
577
629
  *
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.
630
+ * Reachable on platforms with no identity parameters this package can read (BSD
631
+ * and anything unknown, which the spec leaves undefined, plus Android, whose
632
+ * `androidId` needs Android-framework API a Node.js process cannot call); when
633
+ * every source fails a sandboxed process that cannot spawn `ioreg`, a container
634
+ * with no DMI, a blocked PowerShell; and on machines whose per-device identifiers
635
+ * are simply absent, such as a Linux install with no `machine-id` or a VM whose
636
+ * SMBIOS carries an unset UUID and a blank baseboard serial. Enable the host-name
637
+ * fallback (`new MoonbaseDeviceIdResolver({ fallback: 'deviceName' })`) to accept
638
+ * a deliberately weaker id on those machines.
586
639
  */
587
640
  declare class InsufficientDeviceIdentityError extends MoonbaseError {
588
641
  readonly platform: string;
@@ -631,4 +684,4 @@ declare class MoonbaseLicensing {
631
684
  readRawLicense(license: Buffer): Promise<License>;
632
685
  }
633
686
 
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 };
687
+ export { ActivationMethod, type ActivationRequestResponse, DEVICE_ID_SOURCE_TAGS, 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, identitySource, parseDeviceIdStamp, parseIoregPlatformUuid, parseSmbiosParams, platformTag, selectMachineId, stampDeviceId };
package/dist/index.js CHANGED
@@ -222,7 +222,20 @@ var FINGERPRINT_VERSION = 2;
222
222
  var MAX_VALUE_LENGTH = 128;
223
223
  var PRINTABLE_ASCII_MIN = 32;
224
224
  var PRINTABLE_ASCII_MAX = 126;
225
- var STAMP_PATTERN = /^mbd(\d+)(n?)_([0-9a-f]{64})$/;
225
+ var SOURCE_TAGS = {
226
+ identity: "",
227
+ deviceName: "n",
228
+ scoped: "s"
229
+ };
230
+ var DEVICE_ID_SOURCE_TAGS = Object.freeze({ ...SOURCE_TAGS });
231
+ var SOURCE_BY_TAG = new Map(
232
+ Object.entries(SOURCE_TAGS).map(([source, tag]) => [tag, source])
233
+ );
234
+ var SCOPED_PLATFORMS = /* @__PURE__ */ new Set(["ios", "android"]);
235
+ function identitySource(platform) {
236
+ return SCOPED_PLATFORMS.has(platform) ? "scoped" : "identity";
237
+ }
238
+ var STAMP_PATTERN = /^mbd(\d+)([a-z]*)_([0-9a-f]{64})$/;
226
239
  function canonicalizeValue(value) {
227
240
  var _a;
228
241
  let printable = "";
@@ -233,6 +246,12 @@ function canonicalizeValue(value) {
233
246
  }
234
247
  return printable.slice(0, MAX_VALUE_LENGTH).replace(/^ +| +$/g, "");
235
248
  }
249
+ var PLATFORM_TAGS = /* @__PURE__ */ new Set(
250
+ ["mac", "ios", "windows", "android", "linux", "bsd", "unknown"]
251
+ );
252
+ function isPlatformTag(value) {
253
+ return PLATFORM_TAGS.has(value);
254
+ }
236
255
  function platformTag(platform = process.platform) {
237
256
  switch (platform) {
238
257
  case "darwin":
@@ -248,7 +267,7 @@ function platformTag(platform = process.platform) {
248
267
  case "netbsd":
249
268
  return "bsd";
250
269
  default:
251
- return "unknown";
270
+ return isPlatformTag(platform) ? platform : "unknown";
252
271
  }
253
272
  }
254
273
  var IDENTIFYING_PARAMS = /* @__PURE__ */ new Set([
@@ -256,6 +275,8 @@ var IDENTIFYING_PARAMS = /* @__PURE__ */ new Set([
256
275
  "machineId",
257
276
  "systemUuid",
258
277
  "baseboardSerialNumber",
278
+ "identifierForVendor",
279
+ "androidId",
259
280
  "deviceName"
260
281
  ]);
261
282
  var IDENTIFYING_PARAM_NAMES = Object.freeze([...IDENTIFYING_PARAMS]);
@@ -280,9 +301,35 @@ var NOT_PROGRAMMED_VALUES = /* @__PURE__ */ new Set([
280
301
  // deployed from such an image reads it, so it is the opposite of an identifier.
281
302
  "uninitialized"
282
303
  ]);
304
+ var IDENTITY_CONSTRAINTS = {
305
+ androidId: {
306
+ // Reading the *static field* `Settings.Secure.ANDROID_ID` instead of calling
307
+ // `getString` with it yields the key name "android_id", identical on every
308
+ // device, so a single activation would unlock a whole Android install base.
309
+ // That string is not hex, so this stops it reaching the material.
310
+ //
311
+ // The bound is 1,16 and not 16 because AOSP before 8.0 generated the value
312
+ // with `Long.toHexString`, which drops leading zeros — a strict 16 would
313
+ // reject legitimate ids on roughly one in sixteen pre-Oreo devices.
314
+ format: /^[0-9a-f]{1,16}$/,
315
+ // A real ANDROID_ID shared by a large batch of 2010-era devices whose
316
+ // `ro.serialno` was unset, seeding the generator identically on every unit.
317
+ // Valid hex, so the format rule cannot catch it.
318
+ rejected: /* @__PURE__ */ new Set(["9774d56d682e549c"])
319
+ }
320
+ };
283
321
  function isNotProgrammed(value) {
284
322
  return NOT_PROGRAMMED_VALUES.has(value.toLowerCase()) || /^0+$/.test(value) || /^f+$/i.test(value);
285
323
  }
324
+ function isUsableIdentity(name, value) {
325
+ var _a, _b, _c;
326
+ if (isNotProgrammed(value))
327
+ return false;
328
+ const constraint = IDENTITY_CONSTRAINTS[name];
329
+ if (!constraint)
330
+ return true;
331
+ return ((_b = (_a = constraint.format) == null ? void 0 : _a.test(value)) != null ? _b : true) && !((_c = constraint.rejected) == null ? void 0 : _c.has(value.toLowerCase()));
332
+ }
286
333
  function canonicalizeParams(params) {
287
334
  const kept = [];
288
335
  const seen = /* @__PURE__ */ new Set();
@@ -290,7 +337,7 @@ function canonicalizeParams(params) {
290
337
  const value = canonicalizeValue(rawValue);
291
338
  if (value.length === 0)
292
339
  continue;
293
- if (IDENTIFYING_PARAMS.has(name) && isNotProgrammed(value))
340
+ if (IDENTIFYING_PARAMS.has(name) && !isUsableIdentity(name, value))
294
341
  continue;
295
342
  if (seen.has(name))
296
343
  throw new Error(`Duplicate fingerprint parameter name: ${name}`);
@@ -303,6 +350,12 @@ function buildFingerprintMaterial(platform, params) {
303
350
  const kept = canonicalizeParams(params);
304
351
  if (kept.length === 0)
305
352
  throw new InsufficientDeviceIdentityError(platform, "no identity parameter could be read");
353
+ if (SCOPED_PLATFORMS.has(platform) && kept.some(([name]) => name === "deviceName")) {
354
+ throw new InsufficientDeviceIdentityError(
355
+ platform,
356
+ "the host-name fallback is not available on this platform, where the host name is the same on every device"
357
+ );
358
+ }
306
359
  if (!kept.some(([name]) => IDENTIFYING_PARAMS.has(name))) {
307
360
  throw new InsufficientDeviceIdentityError(
308
361
  platform,
@@ -318,18 +371,20 @@ function fingerprintDigest(material) {
318
371
  return createHash("sha256").update(material, "utf8").digest("hex");
319
372
  }
320
373
  function stampDeviceId(digest, source = "identity") {
321
- return `mbd${FINGERPRINT_VERSION}${source === "deviceName" ? "n" : ""}_${digest}`;
374
+ return `mbd${FINGERPRINT_VERSION}${SOURCE_TAGS[source]}_${digest}`;
322
375
  }
323
376
  function fingerprintDeviceId(material, source = "identity") {
324
377
  return stampDeviceId(fingerprintDigest(material), source);
325
378
  }
326
379
  function parseDeviceIdStamp(deviceId) {
380
+ var _a;
327
381
  const match = STAMP_PATTERN.exec(deviceId);
328
382
  if (!match)
329
383
  return null;
330
384
  return {
331
385
  version: Number(match[1]),
332
- source: match[2] === "n" ? "deviceName" : "identity",
386
+ sourceTag: match[2],
387
+ source: (_a = SOURCE_BY_TAG.get(match[2])) != null ? _a : null,
333
388
  digest: match[3]
334
389
  };
335
390
  }
@@ -458,18 +513,23 @@ function readWindowsIdentity() {
458
513
  return { params: parseSmbiosParams(readWindowsSmbios()), deviceName: os.hostname() };
459
514
  }
460
515
  function defaultDeviceIdentityReader(platform = process.platform) {
516
+ const tag = platformTag(platform);
461
517
  return {
462
518
  read() {
463
- switch (platform) {
464
- case "darwin":
519
+ switch (tag) {
520
+ case "mac":
465
521
  return readMacIdentity();
466
522
  case "linux":
467
523
  return readLinuxIdentity();
468
- case "win32":
524
+ case "windows":
469
525
  return readWindowsIdentity();
470
- // android, bsd and anything else define no identity params: the spec
471
- // has no stable hardware source for them, so they resolve to an
526
+ // Everything else yields no identity params here, so it resolves to an
472
527
  // insufficient-identity error unless the deviceName fallback is enabled.
528
+ // For bsd and unknown that matches the spec, which defines none. Android
529
+ // is different: the spec *does* define `androidId`, but it comes from
530
+ // `Settings.Secure.getString`, which is Android-framework API that a
531
+ // Node.js process cannot reach. Collecting it is the C++/.NET SDKs' job.
532
+ // A host that can bridge it should supply its own DeviceIdentityReader.
473
533
  default:
474
534
  return { params: [], deviceName: os.hostname() };
475
535
  }
@@ -521,7 +581,7 @@ var MoonbaseDeviceIdResolver = class {
521
581
  computeDescription() {
522
582
  const { params, deviceName } = this.readIdentity();
523
583
  try {
524
- return this.describe(params, "identity");
584
+ return this.describe(params, identitySource(this.platform));
525
585
  } catch (err) {
526
586
  if (this.fallback !== "deviceName" || !(err instanceof InsufficientDeviceIdentityError))
527
587
  throw err;
@@ -768,25 +828,48 @@ var LicenseValidator = class {
768
828
  };
769
829
  function deviceMismatchError(expected, bound) {
770
830
  const detail = "This license is not for this device";
771
- const versionNote = describeVersionDifference(expected, bound);
831
+ const stampNote = describeStampDifference(expected, bound);
772
832
  return new MoonbaseError(
773
833
  "License is for another device",
774
- versionNote ? `${detail}. ${versionNote}` : detail,
834
+ stampNote ? `${detail}. ${stampNote}` : detail,
775
835
  "LicenseDeviceMismatch" /* LicenseDeviceMismatch */
776
836
  );
777
837
  }
778
- function describeVersionDifference(expected, bound) {
838
+ function describeStampDifference(expected, bound) {
779
839
  const expectedStamp = parseDeviceIdStamp(expected);
780
840
  if (!expectedStamp)
781
841
  return null;
782
842
  const boundStamp = parseDeviceIdStamp(bound);
783
- if (boundStamp && boundStamp.version === expectedStamp.version)
784
- return null;
785
- if (boundStamp && boundStamp.version > expectedStamp.version) {
786
- return `The binding was created by device fingerprint v${boundStamp.version}, which is newer than the v${expectedStamp.version} this SDK computes \u2014 update the SDK rather than re-activating, which would rebind the device to the older algorithm.`;
843
+ if (!boundStamp || boundStamp.version !== expectedStamp.version)
844
+ return describeVersionDifference(expectedStamp, boundStamp);
845
+ if (boundStamp.sourceTag !== expectedStamp.sourceTag)
846
+ return describeSourceDifference(expectedStamp, boundStamp);
847
+ return null;
848
+ }
849
+ function describeVersionDifference(expected, bound) {
850
+ if (bound && bound.version > expected.version) {
851
+ return `The binding was created by device fingerprint v${bound.version}, which is newer than the v${expected.version} this SDK computes \u2014 update the SDK rather than re-activating, which would rebind the device to the older algorithm.`;
852
+ }
853
+ const boundVersion = bound ? `device fingerprint v${bound.version}` : "an SDK predating versioned device fingerprints";
854
+ return `The binding was created by ${boundVersion}, while this SDK computes v${expected.version}, so this may instead be the same machine bound under the older algorithm \u2014 re-activate to find out, or configure a MigratingDeviceIdResolver to keep accepting the previous id.`;
855
+ }
856
+ function describeSourceDifference(expected, bound) {
857
+ if (bound.source === "scoped") {
858
+ return "The binding uses an app-scoped device identity, which cannot be compared with the id this SDK computes \u2014 not even on the same device. Re-activate here to bind this build.";
859
+ }
860
+ if (expected.source === "scoped") {
861
+ return "This SDK computes an app-scoped device identity, which cannot be compared with the one the binding carries \u2014 not even on the same device. Re-activate here to bind this app.";
862
+ }
863
+ if (bound.source === null) {
864
+ return `The binding carries the device identity tag "${bound.sourceTag}", which this SDK does not recognise \u2014 it was created by a newer Moonbase SDK, so update rather than re-activating.`;
865
+ }
866
+ if (expected.source === null) {
867
+ return `The id this SDK computes carries the device identity tag "${expected.sourceTag}", which the spec does not define \u2014 it came from a custom device id resolver, so check that resolver rather than the binding.`;
868
+ }
869
+ if (bound.source === "deviceName") {
870
+ return "The binding was created from the host-name fallback, while this SDK reads hardware identity, so this may instead be the same machine bound while no hardware identity could be read \u2014 re-activate to find out.";
787
871
  }
788
- const boundVersion = boundStamp ? `device fingerprint v${boundStamp.version}` : "an SDK predating versioned device fingerprints";
789
- return `The binding was created by ${boundVersion}, while this SDK computes v${expectedStamp.version}, so this may instead be the same machine bound under the older algorithm \u2014 re-activate to find out, or configure a MigratingDeviceIdResolver to keep accepting the previous id.`;
872
+ return "The binding was created from hardware identity, while this SDK has fallen back to the host name \u2014 check why hardware identity cannot be read here rather than re-activating, which would rebind the device to the weaker id.";
790
873
  }
791
874
 
792
875
  // src/index.ts
@@ -833,6 +916,7 @@ var MoonbaseLicensing = class {
833
916
  };
834
917
  export {
835
918
  ActivationMethod,
919
+ DEVICE_ID_SOURCE_TAGS,
836
920
  ErrorType,
837
921
  FINGERPRINT_PREFIX,
838
922
  FINGERPRINT_VERSION,
@@ -854,6 +938,7 @@ export {
854
938
  defaultDeviceIdentityReader,
855
939
  fingerprintDeviceId,
856
940
  fingerprintDigest,
941
+ identitySource,
857
942
  parseDeviceIdStamp,
858
943
  parseIoregPlatformUuid,
859
944
  parseSmbiosParams,