@qnsp/tenant-sdk 0.3.0 → 0.3.2

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.js CHANGED
@@ -1,31 +1,168 @@
1
1
  import { performance } from "node:perf_hooks";
2
+ import { activateSdk } from "@qnsp/sdk-activation";
2
3
  import { createTenantClientTelemetry, isTenantClientTelemetry } from "./observability.js";
4
+ import { SDK_PACKAGE_VERSION } from "./sdk-package-version.js";
3
5
  import { validateUUID } from "./validation.js";
6
+ /**
7
+ * @qnsp/tenant-sdk
8
+ *
9
+ * TypeScript SDK client for the QNSP tenant-service API.
10
+ * Provides a high-level interface for tenant lifecycle and subscription management.
11
+ */
12
+ /** Default QNSP cloud API base URL. Get a free API key at https://cloud.qnsp.cuilabs.io/signup */
13
+ export const DEFAULT_BASE_URL = "https://api.qnsp.cuilabs.io";
4
14
  /**
5
15
  * Default algorithms per crypto policy tier.
16
+ * These match the definitions in packages/security/src/crypto-policy.ts
17
+ * and determine which algorithms appear in the portal's Generate Key dropdown.
18
+ *
19
+ * default: All supported PQC algorithms (NIST-finalized + candidates via liboqs)
20
+ * strict: NIST-finalized/selected at higher security levels
21
+ * maximum: Highest-security NIST-finalized only
22
+ * government: FIPS-finalized only (no draft standards)
6
23
  */
7
24
  export const CRYPTO_POLICY_ALGORITHMS = {
8
25
  default: {
9
- kemAlgorithms: ["kyber-512", "kyber-768", "kyber-1024"],
10
- signatureAlgorithms: ["dilithium-2", "dilithium-3", "dilithium-5"],
26
+ kemAlgorithms: [
27
+ // FIPS 203 — ML-KEM (NIST finalized)
28
+ "kyber-512",
29
+ "kyber-768",
30
+ "kyber-1024",
31
+ // HQC (NIST selected March 2025)
32
+ "hqc-128",
33
+ "hqc-192",
34
+ "hqc-256",
35
+ // BIKE (NIST Round 4 candidate)
36
+ "bike-l1",
37
+ "bike-l3",
38
+ "bike-l5",
39
+ // Classic McEliece (ISO standard)
40
+ "mceliece-348864",
41
+ "mceliece-460896",
42
+ "mceliece-6688128",
43
+ "mceliece-6960119",
44
+ "mceliece-8192128",
45
+ // FrodoKEM (ISO standard)
46
+ "frodokem-640-aes",
47
+ "frodokem-640-shake",
48
+ "frodokem-976-aes",
49
+ "frodokem-976-shake",
50
+ "frodokem-1344-aes",
51
+ "frodokem-1344-shake",
52
+ // NTRU (lattice-based)
53
+ "ntru-hps-2048-509",
54
+ "ntru-hps-2048-677",
55
+ "ntru-hps-4096-821",
56
+ "ntru-hps-4096-1229",
57
+ "ntru-hrss-701",
58
+ "ntru-hrss-1373",
59
+ // NTRU-Prime
60
+ "sntrup761",
61
+ ],
62
+ signatureAlgorithms: [
63
+ // FIPS 204 — ML-DSA (NIST finalized)
64
+ "dilithium-2",
65
+ "dilithium-3",
66
+ "dilithium-5",
67
+ // FIPS 205 — SLH-DSA (NIST finalized, SHA-2 variants)
68
+ "sphincs-sha2-128f-simple",
69
+ "sphincs-sha2-128s-simple",
70
+ "sphincs-sha2-192f-simple",
71
+ "sphincs-sha2-192s-simple",
72
+ "sphincs-sha2-256f-simple",
73
+ "sphincs-sha2-256s-simple",
74
+ // FIPS 205 — SLH-DSA (NIST finalized, SHAKE variants)
75
+ "sphincs-shake-128f-simple",
76
+ "sphincs-shake-128s-simple",
77
+ "sphincs-shake-192f-simple",
78
+ "sphincs-shake-192s-simple",
79
+ "sphincs-shake-256f-simple",
80
+ "sphincs-shake-256s-simple",
81
+ // FN-DSA / Falcon (FIPS 206 draft)
82
+ "falcon-512",
83
+ "falcon-1024",
84
+ // MAYO (NIST Additional Signatures Round 2)
85
+ "mayo-1",
86
+ "mayo-2",
87
+ "mayo-3",
88
+ "mayo-5",
89
+ // CROSS (NIST Additional Signatures Round 2)
90
+ "cross-rsdp-128-balanced",
91
+ "cross-rsdp-128-fast",
92
+ "cross-rsdp-128-small",
93
+ "cross-rsdp-192-balanced",
94
+ "cross-rsdp-192-fast",
95
+ "cross-rsdp-192-small",
96
+ "cross-rsdp-256-balanced",
97
+ "cross-rsdp-256-fast",
98
+ "cross-rsdp-256-small",
99
+ "cross-rsdpg-128-balanced",
100
+ "cross-rsdpg-128-fast",
101
+ "cross-rsdpg-128-small",
102
+ "cross-rsdpg-192-balanced",
103
+ "cross-rsdpg-192-fast",
104
+ "cross-rsdpg-192-small",
105
+ "cross-rsdpg-256-balanced",
106
+ "cross-rsdpg-256-fast",
107
+ "cross-rsdpg-256-small",
108
+ // UOV (NIST Additional Signatures Round 2)
109
+ "ov-Is",
110
+ "ov-Ip",
111
+ "ov-III",
112
+ "ov-V",
113
+ "ov-Is-pkc",
114
+ "ov-Ip-pkc",
115
+ "ov-III-pkc",
116
+ "ov-V-pkc",
117
+ "ov-Is-pkc-skc",
118
+ "ov-Ip-pkc-skc",
119
+ "ov-III-pkc-skc",
120
+ "ov-V-pkc-skc",
121
+ // SNOVA (NIST Additional Signatures Round 2)
122
+ "snova-24-5-4",
123
+ "snova-24-5-4-shake",
124
+ "snova-24-5-4-esk",
125
+ "snova-24-5-4-shake-esk",
126
+ "snova-25-8-3",
127
+ "snova-37-17-2",
128
+ "snova-37-8-4",
129
+ "snova-24-5-5",
130
+ "snova-56-25-2",
131
+ "snova-49-11-3",
132
+ "snova-60-10-4",
133
+ "snova-29-6-5",
134
+ ],
11
135
  defaultKemAlgorithm: "kyber-768",
12
136
  defaultSignatureAlgorithm: "dilithium-3",
13
137
  },
14
138
  strict: {
15
- kemAlgorithms: ["kyber-768", "kyber-1024"],
16
- signatureAlgorithms: ["dilithium-3", "dilithium-5", "falcon-1024"],
139
+ kemAlgorithms: ["kyber-768", "kyber-1024", "hqc-192", "hqc-256"],
140
+ signatureAlgorithms: [
141
+ "dilithium-3",
142
+ "dilithium-5",
143
+ "falcon-1024",
144
+ "sphincs-sha2-256f-simple",
145
+ "sphincs-sha2-256s-simple",
146
+ "sphincs-shake-256f-simple",
147
+ "sphincs-shake-256s-simple",
148
+ ],
17
149
  defaultKemAlgorithm: "kyber-768",
18
150
  defaultSignatureAlgorithm: "dilithium-3",
19
151
  },
20
152
  maximum: {
21
- kemAlgorithms: ["kyber-1024"],
22
- signatureAlgorithms: ["dilithium-5", "falcon-1024", "sphincs-shake-256f-simple"],
153
+ kemAlgorithms: ["kyber-1024", "hqc-256"],
154
+ signatureAlgorithms: [
155
+ "dilithium-5",
156
+ "falcon-1024",
157
+ "sphincs-sha2-256f-simple",
158
+ "sphincs-shake-256f-simple",
159
+ ],
23
160
  defaultKemAlgorithm: "kyber-1024",
24
161
  defaultSignatureAlgorithm: "dilithium-5",
25
162
  },
26
163
  government: {
27
164
  kemAlgorithms: ["kyber-1024"],
28
- signatureAlgorithms: ["dilithium-5", "sphincs-shake-256f-simple"],
165
+ signatureAlgorithms: ["dilithium-5", "sphincs-sha2-256f-simple", "sphincs-shake-256f-simple"],
29
166
  defaultKemAlgorithm: "kyber-1024",
30
167
  defaultSignatureAlgorithm: "dilithium-5",
31
168
  },
@@ -158,19 +295,41 @@ export class TenantClient {
158
295
  config;
159
296
  telemetry;
160
297
  targetService;
298
+ activationPromise = null;
299
+ activationConfig;
300
+ async ensureActivated() {
301
+ if (!this.activationPromise) {
302
+ this.activationPromise = activateSdk(this.activationConfig).then(() => undefined);
303
+ }
304
+ await this.activationPromise;
305
+ }
161
306
  constructor(config) {
162
307
  if (!config.apiKey || config.apiKey.trim().length === 0) {
163
308
  throw new Error("QNSP Tenant SDK: apiKey is required. " +
164
309
  "Get your free API key at https://cloud.qnsp.cuilabs.io/signup — " +
165
- "no credit card required (FREE tier: 5 GB storage, 2,000 API calls/month). " +
310
+ "no credit card required (FREE tier: 10 GB storage, 50,000 API calls/month). " +
166
311
  "Docs: https://docs.qnsp.cuilabs.io/sdk/tenant-sdk");
167
312
  }
168
- const baseUrl = config.baseUrl.replace(/\/$/, "");
169
- // Enforce HTTPS in production (allow HTTP only for localhost in development)
313
+ const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
314
+ // Enforce HTTPS in production (allow HTTP for localhost in development and
315
+ // for internal service-mesh hostnames — e.g. *.internal — which are on a
316
+ // private VPC network and do not require TLS termination at the transport layer).
170
317
  if (!baseUrl.startsWith("https://")) {
171
318
  const isLocalhost = baseUrl.startsWith("http://localhost") || baseUrl.startsWith("http://127.0.0.1");
319
+ let isInternalService = false;
320
+ try {
321
+ const parsed = new URL(baseUrl);
322
+ isInternalService =
323
+ parsed.protocol === "http:" &&
324
+ (parsed.hostname.endsWith(".internal") ||
325
+ parsed.hostname === "localhost" ||
326
+ parsed.hostname === "127.0.0.1");
327
+ }
328
+ catch {
329
+ // ignore; invalid URL will be caught later by fetch
330
+ }
172
331
  const isDevelopment = process.env["NODE_ENV"] === "development" || process.env["NODE_ENV"] === "test";
173
- if (!isLocalhost || !isDevelopment) {
332
+ if ((!isLocalhost || !isDevelopment) && !isInternalService) {
174
333
  throw new Error("baseUrl must use HTTPS in production. HTTP is only allowed for localhost in development.");
175
334
  }
176
335
  }
@@ -192,6 +351,12 @@ export class TenantClient {
192
351
  catch {
193
352
  this.targetService = "tenant-service";
194
353
  }
354
+ this.activationConfig = {
355
+ apiKey: config.apiKey,
356
+ sdkId: "tenant-sdk",
357
+ sdkVersion: SDK_PACKAGE_VERSION,
358
+ platformUrl: baseUrl,
359
+ };
195
360
  }
196
361
  async request(method, path, options) {
197
362
  return this.requestWithRetry(method, path, options, 0);
@@ -296,6 +461,7 @@ export class TenantClient {
296
461
  * Requires PQC-signed security envelope and optional signature.
297
462
  */
298
463
  async createTenant(request) {
464
+ await this.ensureActivated();
299
465
  // Validation is handled by the service, but we validate format here for early feedback
300
466
  return this.request("POST", "/tenant/v1/tenants", {
301
467
  body: {
@@ -319,6 +485,7 @@ export class TenantClient {
319
485
  */
320
486
  async updateTenant(id, request) {
321
487
  validateUUID(id, "id");
488
+ await this.ensureActivated();
322
489
  return this.request("PATCH", `/tenant/v1/tenants/${id}`, {
323
490
  body: {
324
491
  ...(request.plan !== undefined ? { plan: request.plan } : {}),
@@ -338,6 +505,7 @@ export class TenantClient {
338
505
  */
339
506
  async getTenant(id) {
340
507
  validateUUID(id, "id");
508
+ await this.ensureActivated();
341
509
  return this.request("GET", `/tenant/v1/tenants/${id}`, {
342
510
  operation: "getTenant",
343
511
  });
@@ -347,6 +515,7 @@ export class TenantClient {
347
515
  * Returns a list of tenants and an optional next cursor for pagination.
348
516
  */
349
517
  async listTenants(options) {
518
+ await this.ensureActivated();
350
519
  const params = new URLSearchParams();
351
520
  if (options?.limit !== undefined) {
352
521
  params.set("limit", String(options.limit));
@@ -367,6 +536,7 @@ export class TenantClient {
367
536
  */
368
537
  async getTenantCryptoPolicy(tenantId) {
369
538
  validateUUID(tenantId, "tenantId");
539
+ await this.ensureActivated();
370
540
  return this.request("GET", `/tenant/v1/tenants/${tenantId}/crypto-policy`, {
371
541
  operation: "getTenantCryptoPolicy",
372
542
  });
@@ -377,6 +547,7 @@ export class TenantClient {
377
547
  */
378
548
  async getTenantCryptoPolicyV1(tenantId) {
379
549
  validateUUID(tenantId, "tenantId");
550
+ await this.ensureActivated();
380
551
  return this.request("GET", `/tenant/v1/tenants/${tenantId}/crypto-policy-v1`, {
381
552
  operation: "getTenantCryptoPolicyV1",
382
553
  });
@@ -386,6 +557,7 @@ export class TenantClient {
386
557
  */
387
558
  async listTenantCryptoPolicyV1History(tenantId, options) {
388
559
  validateUUID(tenantId, "tenantId");
560
+ await this.ensureActivated();
389
561
  const params = new URLSearchParams();
390
562
  if (options?.limit !== undefined) {
391
563
  params.set("limit", String(options.limit));
@@ -404,6 +576,7 @@ export class TenantClient {
404
576
  */
405
577
  async upsertTenantCryptoPolicy(tenantId, policy) {
406
578
  validateUUID(tenantId, "tenantId");
579
+ await this.ensureActivated();
407
580
  return this.request("PUT", `/tenant/v1/tenants/${tenantId}/crypto-policy`, {
408
581
  body: {
409
582
  policyTier: policy.policyTier,
@@ -426,6 +599,7 @@ export class TenantClient {
426
599
  */
427
600
  async updateTenantCryptoPolicyV1(tenantId, policy, etag) {
428
601
  validateUUID(tenantId, "tenantId");
602
+ await this.ensureActivated();
429
603
  if (!etag) {
430
604
  throw new Error("etag is required for updateTenantCryptoPolicyV1");
431
605
  }
@@ -442,6 +616,7 @@ export class TenantClient {
442
616
  */
443
617
  async enableTier0Legacy(tenantId, input, etag) {
444
618
  validateUUID(tenantId, "tenantId");
619
+ await this.ensureActivated();
445
620
  if (!etag) {
446
621
  throw new Error("etag is required for enableTier0Legacy");
447
622
  }
@@ -458,6 +633,7 @@ export class TenantClient {
458
633
  */
459
634
  async disableTier0Legacy(tenantId, etag) {
460
635
  validateUUID(tenantId, "tenantId");
636
+ await this.ensureActivated();
461
637
  if (!etag) {
462
638
  throw new Error("etag is required for disableTier0Legacy");
463
639
  }
@@ -474,6 +650,7 @@ export class TenantClient {
474
650
  */
475
651
  async enableTier4Experimental(tenantId, input, etag) {
476
652
  validateUUID(tenantId, "tenantId");
653
+ await this.ensureActivated();
477
654
  if (!etag) {
478
655
  throw new Error("etag is required for enableTier4Experimental");
479
656
  }
@@ -490,6 +667,7 @@ export class TenantClient {
490
667
  */
491
668
  async rollbackTenantCryptoPolicyV1(tenantId, input, etag) {
492
669
  validateUUID(tenantId, "tenantId");
670
+ await this.ensureActivated();
493
671
  if (!etag) {
494
672
  throw new Error("etag is required for rollbackTenantCryptoPolicyV1");
495
673
  }
@@ -512,6 +690,8 @@ export class TenantClient {
512
690
  * Convenience method that fetches the policy and returns the allowed algorithms.
513
691
  */
514
692
  async getAllowedKemAlgorithms(tenantId) {
693
+ validateUUID(tenantId, "tenantId");
694
+ await this.ensureActivated();
515
695
  const policy = await this.getTenantCryptoPolicy(tenantId);
516
696
  if (policy.customAllowedKemAlgorithms && policy.customAllowedKemAlgorithms.length > 0) {
517
697
  return policy.customAllowedKemAlgorithms;
@@ -523,6 +703,8 @@ export class TenantClient {
523
703
  * Convenience method that fetches the policy and returns the allowed algorithms.
524
704
  */
525
705
  async getAllowedSignatureAlgorithms(tenantId) {
706
+ validateUUID(tenantId, "tenantId");
707
+ await this.ensureActivated();
526
708
  const policy = await this.getTenantCryptoPolicy(tenantId);
527
709
  if (policy.customAllowedSignatureAlgorithms &&
528
710
  policy.customAllowedSignatureAlgorithms.length > 0) {
@@ -534,6 +716,8 @@ export class TenantClient {
534
716
  * Get the default KEM algorithm for a tenant based on their crypto policy tier.
535
717
  */
536
718
  async getDefaultKemAlgorithm(tenantId) {
719
+ validateUUID(tenantId, "tenantId");
720
+ await this.ensureActivated();
537
721
  const policy = await this.getTenantCryptoPolicy(tenantId);
538
722
  return CRYPTO_POLICY_ALGORITHMS[policy.policyTier].defaultKemAlgorithm;
539
723
  }
@@ -541,9 +725,250 @@ export class TenantClient {
541
725
  * Get the default signature algorithm for a tenant based on their crypto policy tier.
542
726
  */
543
727
  async getDefaultSignatureAlgorithm(tenantId) {
728
+ validateUUID(tenantId, "tenantId");
729
+ await this.ensureActivated();
544
730
  const policy = await this.getTenantCryptoPolicy(tenantId);
545
731
  return CRYPTO_POLICY_ALGORITHMS[policy.policyTier].defaultSignatureAlgorithm;
546
732
  }
733
+ /**
734
+ * Record a health snapshot for a tenant.
735
+ */
736
+ async recordHealthSnapshot(request) {
737
+ validateUUID(request.tenantId, "tenantId");
738
+ await this.ensureActivated();
739
+ return this.request("POST", `/tenant/v1/tenants/${request.tenantId}/health/snapshots`, {
740
+ body: { metrics: request.metrics },
741
+ operation: "recordHealthSnapshot",
742
+ });
743
+ }
744
+ /**
745
+ * Get the current health status for a tenant.
746
+ */
747
+ async getCurrentHealth(tenantId) {
748
+ validateUUID(tenantId, "tenantId");
749
+ await this.ensureActivated();
750
+ return this.request("GET", `/tenant/v1/tenants/${tenantId}/health`, {
751
+ operation: "getCurrentHealth",
752
+ });
753
+ }
754
+ /**
755
+ * Get health trends for a tenant over time.
756
+ */
757
+ async getHealthTrends(request) {
758
+ validateUUID(request.tenantId, "tenantId");
759
+ await this.ensureActivated();
760
+ const params = new URLSearchParams();
761
+ if (request.since !== undefined)
762
+ params.set("since", request.since);
763
+ if (request.until !== undefined)
764
+ params.set("until", request.until);
765
+ if (request.granularity !== undefined)
766
+ params.set("granularity", request.granularity);
767
+ const queryString = params.toString();
768
+ const path = queryString
769
+ ? `/tenant/v1/tenants/${request.tenantId}/health/trends?${queryString}`
770
+ : `/tenant/v1/tenants/${request.tenantId}/health/trends`;
771
+ return this.request("GET", path, {
772
+ operation: "getHealthTrends",
773
+ });
774
+ }
775
+ /**
776
+ * Create a health alert for a tenant.
777
+ */
778
+ async createHealthAlert(request) {
779
+ validateUUID(request.tenantId, "tenantId");
780
+ await this.ensureActivated();
781
+ return this.request("POST", `/tenant/v1/tenants/${request.tenantId}/health/alerts`, {
782
+ body: {
783
+ severity: request.severity,
784
+ title: request.title,
785
+ description: request.description,
786
+ ...(request.metric !== undefined ? { metric: request.metric } : {}),
787
+ ...(request.threshold !== undefined ? { threshold: request.threshold } : {}),
788
+ ...(request.currentValue !== undefined ? { currentValue: request.currentValue } : {}),
789
+ },
790
+ operation: "createHealthAlert",
791
+ });
792
+ }
793
+ /**
794
+ * Acknowledge a health alert.
795
+ */
796
+ async acknowledgeAlert(request) {
797
+ validateUUID(request.alertId, "alertId");
798
+ await this.ensureActivated();
799
+ return this.request("POST", `/tenant/v1/health/alerts/${request.alertId}/acknowledge`, {
800
+ body: {
801
+ acknowledgedBy: request.acknowledgedBy,
802
+ ...(request.note !== undefined ? { note: request.note } : {}),
803
+ },
804
+ operation: "acknowledgeAlert",
805
+ });
806
+ }
807
+ /**
808
+ * Record quota usage for a tenant.
809
+ */
810
+ async recordQuotaUsage(request) {
811
+ validateUUID(request.tenantId, "tenantId");
812
+ await this.ensureActivated();
813
+ return this.request("POST", `/tenant/v1/tenants/${request.tenantId}/quotas/usage`, {
814
+ body: {
815
+ quotaName: request.quotaName,
816
+ usage: request.usage,
817
+ ...(request.timestamp !== undefined ? { timestamp: request.timestamp } : {}),
818
+ },
819
+ operation: "recordQuotaUsage",
820
+ });
821
+ }
822
+ /**
823
+ * Get current quota usage for a tenant.
824
+ */
825
+ async getCurrentQuotas(tenantId) {
826
+ validateUUID(tenantId, "tenantId");
827
+ await this.ensureActivated();
828
+ return this.request("GET", `/tenant/v1/tenants/${tenantId}/quotas`, {
829
+ operation: "getCurrentQuotas",
830
+ });
831
+ }
832
+ /**
833
+ * Get quota forecast for a tenant.
834
+ */
835
+ async getForecast(request) {
836
+ validateUUID(request.tenantId, "tenantId");
837
+ await this.ensureActivated();
838
+ const params = new URLSearchParams();
839
+ if (request.quotaName !== undefined)
840
+ params.set("quotaName", request.quotaName);
841
+ if (request.horizonDays !== undefined)
842
+ params.set("horizonDays", String(request.horizonDays));
843
+ const queryString = params.toString();
844
+ const path = queryString
845
+ ? `/tenant/v1/tenants/${request.tenantId}/quotas/forecast?${queryString}`
846
+ : `/tenant/v1/tenants/${request.tenantId}/quotas/forecast`;
847
+ return this.request("GET", path, {
848
+ operation: "getForecast",
849
+ });
850
+ }
851
+ /**
852
+ * Get quota suggestions based on usage patterns.
853
+ */
854
+ async getQuotaSuggestions(tenantId) {
855
+ validateUUID(tenantId, "tenantId");
856
+ await this.ensureActivated();
857
+ return this.request("GET", `/tenant/v1/tenants/${tenantId}/quotas/suggestions`, {
858
+ operation: "getQuotaSuggestions",
859
+ });
860
+ }
861
+ /**
862
+ * Create an onboarding workflow template.
863
+ */
864
+ async createWorkflowTemplate(request) {
865
+ await this.ensureActivated();
866
+ return this.request("POST", "/tenant/v1/onboarding/templates", {
867
+ body: request,
868
+ operation: "createWorkflowTemplate",
869
+ });
870
+ }
871
+ /**
872
+ * Start onboarding for a tenant.
873
+ */
874
+ async startOnboarding(request) {
875
+ validateUUID(request.tenantId, "tenantId");
876
+ await this.ensureActivated();
877
+ return this.request("POST", `/tenant/v1/tenants/${request.tenantId}/onboarding/start`, {
878
+ body: {
879
+ ...(request.templateId !== undefined ? { templateId: request.templateId } : {}),
880
+ ...(request.metadata !== undefined ? { metadata: request.metadata } : {}),
881
+ },
882
+ operation: "startOnboarding",
883
+ });
884
+ }
885
+ /**
886
+ * Get onboarding status for a tenant.
887
+ */
888
+ async getOnboardingStatus(tenantId) {
889
+ validateUUID(tenantId, "tenantId");
890
+ await this.ensureActivated();
891
+ return this.request("GET", `/tenant/v1/tenants/${tenantId}/onboarding/status`, {
892
+ operation: "getOnboardingStatus",
893
+ });
894
+ }
895
+ /**
896
+ * Get onboarding statistics.
897
+ */
898
+ async getOnboardingStats(options) {
899
+ await this.ensureActivated();
900
+ const params = new URLSearchParams();
901
+ if (options?.since !== undefined)
902
+ params.set("since", options.since);
903
+ if (options?.until !== undefined)
904
+ params.set("until", options.until);
905
+ const queryString = params.toString();
906
+ const path = queryString
907
+ ? `/tenant/v1/onboarding/stats?${queryString}`
908
+ : "/tenant/v1/onboarding/stats";
909
+ return this.request("GET", path, {
910
+ operation: "getOnboardingStats",
911
+ });
912
+ }
913
+ /**
914
+ * Create an isolation policy for a tenant.
915
+ */
916
+ async createIsolationPolicy(request) {
917
+ validateUUID(request.tenantId, "tenantId");
918
+ await this.ensureActivated();
919
+ return this.request("POST", `/tenant/v1/tenants/${request.tenantId}/isolation/policies`, {
920
+ body: {
921
+ name: request.name,
922
+ ...(request.description !== undefined ? { description: request.description } : {}),
923
+ level: request.level,
924
+ ...(request.rules !== undefined ? { rules: request.rules } : {}),
925
+ ...(request.enforcementMode !== undefined
926
+ ? { enforcementMode: request.enforcementMode }
927
+ : {}),
928
+ },
929
+ operation: "createIsolationPolicy",
930
+ });
931
+ }
932
+ /**
933
+ * Run an isolation audit for a tenant.
934
+ */
935
+ async runIsolationAudit(request) {
936
+ validateUUID(request.tenantId, "tenantId");
937
+ await this.ensureActivated();
938
+ return this.request("POST", `/tenant/v1/tenants/${request.tenantId}/isolation/audit`, {
939
+ body: {
940
+ ...(request.policyIds !== undefined ? { policyIds: request.policyIds } : {}),
941
+ ...(request.categories !== undefined ? { categories: request.categories } : {}),
942
+ ...(request.depth !== undefined ? { depth: request.depth } : {}),
943
+ },
944
+ operation: "runIsolationAudit",
945
+ });
946
+ }
947
+ /**
948
+ * Get isolation findings for a tenant.
949
+ */
950
+ async getIsolationFindings(request) {
951
+ validateUUID(request.tenantId, "tenantId");
952
+ await this.ensureActivated();
953
+ const params = new URLSearchParams();
954
+ if (request.runId !== undefined)
955
+ params.set("runId", request.runId);
956
+ if (request.severity !== undefined)
957
+ params.set("severity", request.severity);
958
+ if (request.category !== undefined)
959
+ params.set("category", request.category);
960
+ if (request.limit !== undefined)
961
+ params.set("limit", String(request.limit));
962
+ if (request.cursor !== undefined)
963
+ params.set("cursor", request.cursor);
964
+ const queryString = params.toString();
965
+ const path = queryString
966
+ ? `/tenant/v1/tenants/${request.tenantId}/isolation/findings?${queryString}`
967
+ : `/tenant/v1/tenants/${request.tenantId}/isolation/findings`;
968
+ return this.request("GET", path, {
969
+ operation: "getIsolationFindings",
970
+ });
971
+ }
547
972
  }
548
973
  export * from "./observability.js";
549
974
  export * from "./validation.js";