@hsuite/smart-engines-sdk 3.0.2 → 3.0.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.2.1 — 2026-05-16
4
+
5
+ **Documentation-only fix.** No runtime behavior change.
6
+
7
+ ### Fixed
8
+ - `DeploymentClient.init` JSDoc and `BaasInitResponse` type docs no longer
9
+ describe the registry as DOCR. The runtime arc routes pushes through a
10
+ per-tenant Harbor project (`hsuite-customers-<appId>`), and the credentials
11
+ returned in `registry.{server, username, password, repository}` are an
12
+ ephemeral project-scoped robot account whose secret is single-use and not
13
+ persisted server-side. README example aligned with the same wording.
14
+
15
+ ## 3.2.0 — undocumented (PR-A → PR-I, 2026-05-12 → 2026-05-13)
16
+
17
+ Smart-app runtime arc surface — added `DeploymentClient` with the full
18
+ init → push → uploadFrontend → deploy flow plus lifecycle (suspend/
19
+ resume/update/rollback/delete) and ownership-gated metrics. See PRs
20
+ #516 (PR-A), #523 (PR-C), and #530 (PR-H) for details.
21
+
22
+ ## 3.1.0 — undocumented (2026-05-12)
23
+
24
+ Added `CustomerSessionClient` for NFT-gated customer→smart-app auth (PR #496).
25
+
26
+ ## 3.0.2 — undocumented (2026-05-12)
27
+
28
+ Dropped `nestjs` re-exports from the root entry to keep tree-shaking clean for
29
+ non-Nest consumers (PR #494).
30
+
3
31
  ## 3.0.0 — 2026-05-11
4
32
 
5
33
  **First publishable release.**
package/README.md CHANGED
@@ -52,14 +52,24 @@ await baas.authenticate({
52
52
  },
53
53
  });
54
54
 
55
- // 4. Register a free_testnet smart-app (no HSUITE deposit on testnet; cluster runs DKG)
56
- const app = await baas.register({
57
- name: 'My Smart App',
55
+ // 4. Initialise a free_testnet smart-app via the four-step deploy flow
56
+ // (init docker push → optional uploadFrontend → deploy). `init` runs
57
+ // the per-entity DKG ceremony on the cluster and returns the DKG entityId
58
+ // as `appId`, plus ephemeral push credentials for the cluster's per-tenant
59
+ // Harbor project (single-use secret — not persisted server-side).
60
+ const init = await baas.deployment.init({
61
+ name: 'my-smart-app',
62
+ port: 3000,
58
63
  services: ['database', 'storage', 'messaging', 'functions'],
59
64
  });
60
65
 
61
- console.log(`Registered smart-app ${app.appId} — status: ${app.status}`);
62
- baas.setAppId(app.appId);
66
+ console.log(`Allocated smart-app ${init.appId}`);
67
+ console.log(`Push to: ${init.registry.server}/${init.registry.repository}:<tag>`);
68
+ baas.setAppId(init.appId);
69
+
70
+ // (out-of-band) docker login + docker push <init.registry.server>/<init.registry.repository>:v1
71
+ // (optional) await baas.deployment.uploadFrontend(init.appId, await fs.readFile('./bundle.tar.gz'));
72
+ // (deploy) await baas.deployment.deploy(init.appId, { tag: 'v1', replicas: 1 });
63
73
  ```
64
74
 
65
75
  **Why XRPL?** Per the Smart Engines V3 architecture, XRPL is canonical for
@@ -166,19 +176,40 @@ await baas.agents.resume(agent.agentId);
166
176
  const events = await baas.agents.getEvents(agent.agentId);
167
177
  ```
168
178
 
169
- ### Deployment
179
+ ### Deployment (runtime orchestration — spec §6.1)
180
+
181
+ The four-step deploy flow: **init → docker push → (optional) uploadFrontend → deploy**.
170
182
 
171
183
  ```ts
172
- const deployment = await baas.deployment.create({
173
- name: 'My Smart App v2',
184
+ // 1. init allocate appId via DKG + receive ephemeral Harbor push credentials
185
+ const init = await baas.deployment.init({
186
+ name: 'my-smart-app',
187
+ port: 3000,
174
188
  services: ['database', 'storage', 'messaging'],
175
- config: { /* app-specific */ },
176
189
  });
177
190
 
191
+ // 2. docker push — out-of-band, using the ephemeral creds returned above
192
+ // docker login -u <init.registry.username> -p <init.registry.password> <init.registry.server>
193
+ // docker push <init.registry.server>/<init.registry.repository>:v1
194
+
195
+ // 3. uploadFrontend — optional SPA tarball (content-addressed, mounted read-only)
196
+ await baas.deployment.uploadFrontend(init.appId, await fs.readFile('./bundle.tar.gz'));
197
+
198
+ // 4. deploy — reconcile to k8s
199
+ const deployed = await baas.deployment.deploy(init.appId, {
200
+ tag: 'v1',
201
+ replicas: 1,
202
+ env: { NODE_ENV: 'production' },
203
+ });
204
+ console.log(`live at ${deployed.url}`);
205
+
206
+ // Lifecycle + listing
178
207
  const apps = await baas.deployment.list();
179
- const info = await baas.deployment.get(appId);
180
- await baas.deployment.suspend(appId);
181
- await baas.deployment.resume(appId);
208
+ const info = await baas.deployment.get(init.appId);
209
+ const status = await baas.deployment.status(init.appId);
210
+ await baas.deployment.suspend(init.appId);
211
+ await baas.deployment.resume(init.appId);
212
+ await baas.deployment.rollback(init.appId, { toTag: 'v0' });
182
213
  ```
183
214
 
184
215
  ---
package/dist/index.d.ts CHANGED
@@ -1055,7 +1055,7 @@ export type HttpClient = {
1055
1055
  get<T = any>(path: string): Promise<T>;
1056
1056
  put<T = any>(path: string, body: unknown): Promise<T>;
1057
1057
  delete<T = any>(path: string): Promise<T>;
1058
- upload<T = any>(path: string, file: Blob | Buffer, filename: string, metadata?: Record<string, string>): Promise<T>;
1058
+ upload<T = any>(path: string, file: Blob | Buffer, filename: string, metadata?: Record<string, string>, fieldName?: string): Promise<T>;
1059
1059
  };
1060
1060
  export type HttpClientConfig = {
1061
1061
  baseUrl: string;
@@ -1197,7 +1197,6 @@ export type EntityCreationResponse = {
1197
1197
  success: boolean;
1198
1198
  entityId: string;
1199
1199
  publicKeys: string[];
1200
- signerIndices: number[];
1201
1200
  threshold: number;
1202
1201
  ceremonyIds: string[];
1203
1202
  };
@@ -1217,7 +1216,6 @@ export type ReshareResponse = {
1217
1216
  export type EntityDetails = {
1218
1217
  success: boolean;
1219
1218
  payload?: unknown;
1220
- signerIndices?: number[];
1221
1219
  validators?: string[];
1222
1220
  publicKeys?: string[];
1223
1221
  error?: string;
@@ -2098,7 +2096,7 @@ declare function xlmToStroops(xlm: string | number): string;
2098
2096
  declare function validateBitcoinAddress(address: string): boolean;
2099
2097
  declare function satoshisToBtc(satoshis: string | number): string;
2100
2098
  declare function btcToSatoshis(btc: string | number): string;
2101
- export type BaasService = "auth" | "database" | "storage" | "functions" | "messaging";
2099
+ export type BaasService = "auth" | "database" | "storage" | "functions" | "messaging" | "customer-session";
2102
2100
  export type BaasSupportedChain = "hedera" | "xrpl" | "polkadot" | "solana";
2103
2101
  export type BaasEndpoints = {
2104
2102
  auth: string;
@@ -2193,24 +2191,6 @@ export type BaasSessionInfo = {
2193
2191
  expiresAt?: number;
2194
2192
  error?: string;
2195
2193
  };
2196
- export type BaasRegisterRequest = {
2197
- name: string;
2198
- services: BaasService[];
2199
- environment?: Record<string, string>;
2200
- limits?: {
2201
- storage?: string;
2202
- functions?: number;
2203
- channels?: number;
2204
- };
2205
- };
2206
- export type BaasRegisterResponse = {
2207
- appId: string;
2208
- name: string;
2209
- status: DeployedAppStatus;
2210
- services: string[];
2211
- endpoints: BaasEndpoints;
2212
- createdAt: string;
2213
- };
2214
2194
  export type BaasDocument = {
2215
2195
  _id: string;
2216
2196
  data: Record<string, unknown>;
@@ -2416,18 +2396,56 @@ export type BaasPublishResult = {
2416
2396
  channel: string;
2417
2397
  timestamp: string;
2418
2398
  };
2419
- export type BaasDeployRequest = {
2399
+ export type BaasInitRequest = {
2420
2400
  name: string;
2401
+ port: number;
2421
2402
  services: BaasService[];
2422
- config?: Record<string, unknown>;
2403
+ limits?: {
2404
+ cpu?: string;
2405
+ memory?: string;
2406
+ };
2423
2407
  };
2424
- export type BaasDeployResult = {
2408
+ export type BaasInitResponse = {
2425
2409
  appId: string;
2426
- name: string;
2427
- status: "active" | "deploying" | "error";
2428
- services: string[];
2429
- endpoints: BaasEndpoints;
2430
- createdAt: string;
2410
+ registry: {
2411
+ server: string;
2412
+ username: string;
2413
+ password: string;
2414
+ repository: string;
2415
+ };
2416
+ };
2417
+ export type BaasDeployRequest = {
2418
+ tag: string;
2419
+ replicas?: number;
2420
+ env?: Record<string, string>;
2421
+ resources?: {
2422
+ cpu?: string;
2423
+ memory?: string;
2424
+ };
2425
+ strategy?: "rolling" | "recreate";
2426
+ };
2427
+ export type BaasDeployResponse = {
2428
+ appId: string;
2429
+ status: string;
2430
+ url: string;
2431
+ };
2432
+ export type BaasUploadFrontendResponse = {
2433
+ bundleSha256: string;
2434
+ bundleSizeBytes: number;
2435
+ };
2436
+ export type BaasRollbackRequest = {
2437
+ toTag: string;
2438
+ };
2439
+ export type BaasRuntimeStatus = {
2440
+ appId: string;
2441
+ state: "PENDING_SUBSCRIPTION" | "ACTIVE" | "SUSPENDED" | "RETIRED";
2442
+ runtime?: {
2443
+ image: string;
2444
+ runtimeState: "NOT_DEPLOYED" | "DEPLOYING" | "RUNNING" | "FAILED" | "DEGRADED";
2445
+ replicas: number;
2446
+ lastReconciledAt?: string;
2447
+ lastError?: string;
2448
+ };
2431
2449
  };
2432
2450
  export type BaasAppListResponse = {
2433
2451
  apps: DeployedAppInfo[];
@@ -2571,7 +2589,11 @@ export declare class MessagingClient {
2571
2589
  export declare class DeploymentClient {
2572
2590
  private readonly http;
2573
2591
  constructor(http: HttpClient);
2574
- create(request: BaasDeployRequest): Promise<BaasDeployResult>;
2592
+ init(request: BaasInitRequest): Promise<BaasInitResponse>;
2593
+ uploadFrontend(appId: string, bundle: Blob | Buffer, filename?: string): Promise<BaasUploadFrontendResponse>;
2594
+ deploy(appId: string, request: BaasDeployRequest): Promise<BaasDeployResponse>;
2595
+ rollback(appId: string, request: BaasRollbackRequest): Promise<BaasDeployResponse>;
2596
+ status(appId: string): Promise<BaasRuntimeStatus>;
2575
2597
  list(): Promise<BaasAppListResponse>;
2576
2598
  get(appId: string): Promise<DeployedAppInfo>;
2577
2599
  update(appId: string, updates: Partial<BaasDeployRequest>): Promise<DeployedAppInfo>;
@@ -2586,7 +2608,11 @@ export declare class DeploymentClient {
2586
2608
  success: boolean;
2587
2609
  status: string;
2588
2610
  }>;
2589
- getStats(): Promise<any>;
2611
+ getStats(): Promise<{
2612
+ totalApps: number;
2613
+ activeApps: number;
2614
+ totalOwners: number;
2615
+ }>;
2590
2616
  }
2591
2617
  export type AgentStatus = "active" | "paused" | "revoked" | "pending";
2592
2618
  export type AgentRegisterRequest = {
@@ -2711,6 +2737,55 @@ export declare class AgentsClient {
2711
2737
  success: boolean;
2712
2738
  }>;
2713
2739
  }
2740
+ export type CustomerSessionChallenge = {
2741
+ challenge: string;
2742
+ };
2743
+ export type CustomerSessionVerifyRequest = {
2744
+ appId: string;
2745
+ chain: string;
2746
+ address: string;
2747
+ publicKey?: string;
2748
+ signature: string;
2749
+ challenge: string;
2750
+ };
2751
+ export type CustomerSessionToken = {
2752
+ token: string;
2753
+ sessionId: string;
2754
+ validatorId: string;
2755
+ expiresAt: string;
2756
+ sessionSecret: string;
2757
+ };
2758
+ export type CustomerSessionInfo = {
2759
+ sessionId: string;
2760
+ appId: string;
2761
+ customerChain: string;
2762
+ customerAddress: string;
2763
+ subscriptionContext?: {
2764
+ nftSerial: number;
2765
+ tier: "free_testnet" | "starter" | "professional" | "enterprise";
2766
+ expiresAt: string;
2767
+ allowedAutomations?: string[];
2768
+ };
2769
+ createdAt: string;
2770
+ expiresAt: string;
2771
+ lastActivityAt: string;
2772
+ };
2773
+ export declare class CustomerSessionClient {
2774
+ private readonly baseUrl;
2775
+ private readonly timeoutMs;
2776
+ constructor(baseUrl: string, timeoutMs?: number);
2777
+ challenge(input: {
2778
+ chain: string;
2779
+ address: string;
2780
+ }): Promise<CustomerSessionChallenge>;
2781
+ verify(req: CustomerSessionVerifyRequest): Promise<CustomerSessionToken>;
2782
+ validate(bearer: string): Promise<CustomerSessionInfo>;
2783
+ end(bearer: string): Promise<{
2784
+ revoked: boolean;
2785
+ sessionId: string;
2786
+ }>;
2787
+ private fetch;
2788
+ }
2714
2789
  export type AuthenticateOptions = {
2715
2790
  chain: BaasSupportedChain;
2716
2791
  walletAddress: string;
@@ -2732,6 +2807,7 @@ export declare class BaasClient {
2732
2807
  readonly messaging: MessagingClient;
2733
2808
  readonly deployment: DeploymentClient;
2734
2809
  readonly agents: AgentsClient;
2810
+ readonly customerSession: CustomerSessionClient;
2735
2811
  constructor(config: BaasClientConfig);
2736
2812
  setAppId(appId: string): void;
2737
2813
  isAuthenticated(): boolean;
@@ -2744,7 +2820,6 @@ export declare class BaasClient {
2744
2820
  authenticate(options: AuthenticateOptions): Promise<BaasAuthResult>;
2745
2821
  validateSession(): Promise<BaasSessionInfo>;
2746
2822
  logout(): Promise<void>;
2747
- register(request: BaasRegisterRequest): Promise<BaasRegisterResponse>;
2748
2823
  private requireAuth;
2749
2824
  private getHeaders;
2750
2825
  private post;
@@ -2776,7 +2851,7 @@ declare namespace stellar {
2776
2851
  export { stroopsToXlm, validateStellarAddress, xlmToStroops };
2777
2852
  }
2778
2853
  declare namespace baas {
2779
- export { AgentBalance, AgentEvent, AgentFundRequest, AgentInfo, AgentOperation, AgentRegisterRequest, AgentRules, AgentRulesValidationResult, AgentStatus, AgentTradeRequest, AgentWithdrawRequest, AgentsClient, AuthenticateOptions, BaasAppListResponse, BaasAuthConfig, BaasAuthResult, BaasChallengeRequest, BaasChallengeResponse, BaasChannelConfig, BaasClient, BaasClientConfig, BaasDeleteResult, BaasDeployRequest, BaasDeployResult, BaasDocument, BaasEndpoints, BaasError, BaasErrorDetails, BaasErrorResponse, BaasFileInfo, BaasFileMetadata, BaasFindResult, BaasFunctionDeployRequest, BaasFunctionDeployResult, BaasFunctionInfo, BaasFunctionLog, BaasFunctionLogOptions, BaasFunctionResources, BaasFunctionResult, BaasFunctionRuntime, BaasHistoryOptions, BaasInsertResult, BaasMerkleProof, BaasMessage, BaasPresenceInfo, BaasPresenceMember, BaasPublishResult, BaasQueryOptions, BaasRegisterRequest, BaasRegisterResponse, BaasService, BaasSessionInfo, BaasStateTransition, BaasStorageUsage, BaasSupportedChain, BaasTriggerType, BaasUpdateResult, BaasUploadResult, BaasVerifyRequest, ChannelSubscription, DatabaseClient, DatabaseStatsResponse, DbComparisonOperator, DbDocument, DbQuery, DeployedApp, DeployedAppInfo, DeployedAppLimits, DeployedAppStatus, DeployedAppUsage, DeploymentClient, DocumentProofResponse, FunctionsClient, MessageHandler, MessagingClient, StateRootResponse, StateTransitionsResponse, StorageClient, validateAgentRules };
2854
+ export { AgentBalance, AgentEvent, AgentFundRequest, AgentInfo, AgentOperation, AgentRegisterRequest, AgentRules, AgentRulesValidationResult, AgentStatus, AgentTradeRequest, AgentWithdrawRequest, AgentsClient, AuthenticateOptions, BaasAppListResponse, BaasAuthConfig, BaasAuthResult, BaasChallengeRequest, BaasChallengeResponse, BaasChannelConfig, BaasClient, BaasClientConfig, BaasDeleteResult, BaasDeployRequest, BaasDeployResponse, BaasDocument, BaasEndpoints, BaasError, BaasErrorDetails, BaasErrorResponse, BaasFileInfo, BaasFileMetadata, BaasFindResult, BaasFunctionDeployRequest, BaasFunctionDeployResult, BaasFunctionInfo, BaasFunctionLog, BaasFunctionLogOptions, BaasFunctionResources, BaasFunctionResult, BaasFunctionRuntime, BaasHistoryOptions, BaasInitRequest, BaasInitResponse, BaasInsertResult, BaasMerkleProof, BaasMessage, BaasPresenceInfo, BaasPresenceMember, BaasPublishResult, BaasQueryOptions, BaasRollbackRequest, BaasRuntimeStatus, BaasService, BaasSessionInfo, BaasStateTransition, BaasStorageUsage, BaasSupportedChain, BaasTriggerType, BaasUpdateResult, BaasUploadFrontendResponse, BaasUploadResult, BaasVerifyRequest, ChannelSubscription, DatabaseClient, DatabaseStatsResponse, DbComparisonOperator, DbDocument, DbQuery, DeployedApp, DeployedAppInfo, DeployedAppLimits, DeployedAppStatus, DeployedAppUsage, DeploymentClient, DocumentProofResponse, FunctionsClient, MessageHandler, MessagingClient, StateRootResponse, StateTransitionsResponse, StorageClient, validateAgentRules };
2780
2855
  }
2781
2856
  declare namespace discovery {
2782
2857
  export { MIRROR_NODE_URLS, MirrorNodeClient, MirrorNodeConfig, MirrorNodeError, TopicMessage, TopicMessagesResponse, ValidatorDiscoveryClient, ValidatorDiscoveryConfig, ValidatorInfo, ValidatorMetadata, ValidatorNetworkEndpoints };
package/dist/index.js CHANGED
@@ -1725,14 +1725,14 @@ function createHttpClient(config) {
1725
1725
  throw new SdkHttpError(`Network error: ${err.message}`, 0, error);
1726
1726
  }
1727
1727
  }
1728
- async function upload(path, file, filename, metadata) {
1728
+ async function upload(path, file, filename, metadata, fieldName = "file") {
1729
1729
  const url = `${config.baseUrl}${path}`;
1730
1730
  const controller = new AbortController();
1731
1731
  const timeoutId = setTimeout(() => controller.abort(), timeout * 2);
1732
1732
  try {
1733
1733
  const formData = new FormData();
1734
1734
  const blob = file instanceof Blob ? file : new Blob([new Uint8Array(file)]);
1735
- formData.append("file", blob, filename);
1735
+ formData.append(fieldName, blob, filename);
1736
1736
  if (metadata) {
1737
1737
  for (const [key, value] of Object.entries(metadata)) {
1738
1738
  formData.append(key, value);
@@ -1776,7 +1776,7 @@ function createHttpClient(config) {
1776
1776
  get: (path) => request("GET", path),
1777
1777
  put: (path, body) => request("PUT", path, body),
1778
1778
  delete: (path) => request("DELETE", path),
1779
- upload,
1779
+ upload: ((path, file, filename, metadata, fieldName) => upload(path, file, filename, metadata, fieldName)),
1780
1780
  setAuthToken
1781
1781
  };
1782
1782
  return client;
@@ -3619,49 +3619,101 @@ var DeploymentClient = class {
3619
3619
  }
3620
3620
  http;
3621
3621
  /**
3622
- * Create (deploy) a new app
3622
+ * Step 1 allocate appId + receive ephemeral push credentials for
3623
+ * the cluster's per-tenant Harbor project.
3624
+ *
3625
+ * Each app gets its own Harbor project (`hsuite-customers-<appId>`)
3626
+ * isolated by Harbor's RBAC. The push robot returned in
3627
+ * `registry.{username, password}` is scoped to that project only —
3628
+ * it cannot read or write any other tenant's images.
3629
+ *
3630
+ * **Single-use secret discipline:** `registry.password` is returned
3631
+ * exactly once and is NOT persisted server-side. Store it locally
3632
+ * for the `docker push` and discard. To rotate, call `init` again
3633
+ * (issues a new robot under the same project).
3634
+ *
3635
+ * Use the credentials to `docker login` + `docker push`, then call
3636
+ * {@link deploy} with the pushed image tag.
3637
+ */
3638
+ async init(request) {
3639
+ return this.http.post("/api/deployment/apps/init", request);
3640
+ }
3641
+ /**
3642
+ * Step 3 (optional) — upload the SPA tarball.
3643
+ *
3644
+ * The tarball is content-addressed (SHA-256) and mounted read-only
3645
+ * into the customer's pod alongside the backend container. Returns
3646
+ * the hash + size so the caller can verify the upload.
3647
+ */
3648
+ async uploadFrontend(appId, bundle, filename = "bundle.tar.gz") {
3649
+ return this.http.upload(
3650
+ `/api/deployment/apps/${encodeURIComponent(appId)}/frontend`,
3651
+ bundle,
3652
+ filename,
3653
+ void 0,
3654
+ "bundle"
3655
+ );
3656
+ }
3657
+ /**
3658
+ * Step 4 — reconcile the runtime to k8s.
3659
+ *
3660
+ * Returns immediately with `status: 'deploying'`. Poll {@link status}
3661
+ * until `runtime.runtimeState === 'RUNNING'` for the URL to be live.
3662
+ */
3663
+ async deploy(appId, request) {
3664
+ return this.http.post(`/api/deployment/apps/${encodeURIComponent(appId)}/deploy`, request);
3665
+ }
3666
+ /**
3667
+ * Roll back to a previously-deployed image tag (must exist in
3668
+ * `runtime.deploymentHistory[]`).
3623
3669
  */
3624
- async create(request) {
3625
- return this.http.post("/api/deployment/apps", request);
3670
+ async rollback(appId, request) {
3671
+ return this.http.post(`/api/deployment/apps/${encodeURIComponent(appId)}/rollback`, request);
3626
3672
  }
3627
3673
  /**
3628
- * List all deployed apps
3674
+ * Live combined lifecycle + runtime status of an app.
3675
+ */
3676
+ async status(appId) {
3677
+ return this.http.get(`/api/deployment/apps/${encodeURIComponent(appId)}/status`);
3678
+ }
3679
+ /**
3680
+ * List all deployed apps for the authenticated developer.
3629
3681
  */
3630
3682
  async list() {
3631
3683
  return this.http.get("/api/deployment/apps");
3632
3684
  }
3633
3685
  /**
3634
- * Get app details
3686
+ * Get app details.
3635
3687
  */
3636
3688
  async get(appId) {
3637
3689
  return this.http.get(`/api/deployment/apps/${encodeURIComponent(appId)}`);
3638
3690
  }
3639
3691
  /**
3640
- * Update app configuration
3692
+ * Update app configuration. Runtime effect lands in PR-H.
3641
3693
  */
3642
3694
  async update(appId, updates) {
3643
3695
  return this.http.put(`/api/deployment/apps/${encodeURIComponent(appId)}`, updates);
3644
3696
  }
3645
3697
  /**
3646
- * Delete an app
3698
+ * Delete an app. Runtime effect (namespace teardown) lands in PR-H.
3647
3699
  */
3648
3700
  async delete(appId) {
3649
3701
  return this.http.delete(`/api/deployment/apps/${encodeURIComponent(appId)}`);
3650
3702
  }
3651
3703
  /**
3652
- * Suspend an app
3704
+ * Suspend an app. Runtime effect (scale to zero) lands in PR-H.
3653
3705
  */
3654
3706
  async suspend(appId) {
3655
3707
  return this.http.post(`/api/deployment/apps/${encodeURIComponent(appId)}/suspend`, {});
3656
3708
  }
3657
3709
  /**
3658
- * Resume a suspended app
3710
+ * Resume a suspended app. Runtime effect (scale back up) lands in PR-H.
3659
3711
  */
3660
3712
  async resume(appId) {
3661
3713
  return this.http.post(`/api/deployment/apps/${encodeURIComponent(appId)}/resume`, {});
3662
3714
  }
3663
3715
  /**
3664
- * Get deployment statistics
3716
+ * Get deployment statistics.
3665
3717
  */
3666
3718
  async getStats() {
3667
3719
  return this.http.get("/api/deployment/stats");
@@ -3791,6 +3843,78 @@ var AgentsClient = class {
3791
3843
  }
3792
3844
  };
3793
3845
 
3846
+ // src/baas/customer-session/index.ts
3847
+ var CustomerSessionClient = class {
3848
+ constructor(baseUrl, timeoutMs = 3e4) {
3849
+ this.baseUrl = baseUrl;
3850
+ this.timeoutMs = timeoutMs;
3851
+ }
3852
+ baseUrl;
3853
+ timeoutMs;
3854
+ /**
3855
+ * Step 1: ask the host to issue a fresh challenge for the customer to sign.
3856
+ */
3857
+ async challenge(input) {
3858
+ return this.fetch("POST", "/api/customer-session/challenge", input);
3859
+ }
3860
+ /**
3861
+ * Step 2: submit the customer's signed challenge. On success returns a
3862
+ * short-lived bearer JWT scoped to {appId, chain, address}.
3863
+ */
3864
+ async verify(req) {
3865
+ return this.fetch("POST", "/api/customer-session/verify", req);
3866
+ }
3867
+ /**
3868
+ * Validate a customer bearer + return the decoded session info. Used by
3869
+ * smart-app backends to authorise incoming customer requests.
3870
+ */
3871
+ async validate(bearer) {
3872
+ return this.fetch("GET", "/api/customer-session/validate", void 0, bearer);
3873
+ }
3874
+ /**
3875
+ * Revoke a customer session. Idempotent.
3876
+ */
3877
+ async end(bearer) {
3878
+ return this.fetch(
3879
+ "POST",
3880
+ "/api/customer-session/end",
3881
+ void 0,
3882
+ bearer
3883
+ );
3884
+ }
3885
+ async fetch(method, path, body, bearer) {
3886
+ const url = `${this.baseUrl}${path}`;
3887
+ const controller = new AbortController();
3888
+ const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
3889
+ try {
3890
+ const headers = { "Content-Type": "application/json" };
3891
+ if (bearer) headers["Authorization"] = `Bearer ${bearer}`;
3892
+ const init = { method, headers, signal: controller.signal };
3893
+ if (body !== void 0) init.body = JSON.stringify(body);
3894
+ const response = await fetch(url, init);
3895
+ clearTimeout(timeoutId);
3896
+ if (!response.ok) {
3897
+ const errBody = await response.json().catch(() => ({}));
3898
+ const err = new Error(
3899
+ errBody.message ?? `customer-session ${path} failed: ${response.status} ${response.statusText}`
3900
+ );
3901
+ err.status = response.status;
3902
+ throw err;
3903
+ }
3904
+ const text = await response.text();
3905
+ if (!text) return void 0;
3906
+ return JSON.parse(text);
3907
+ } catch (error) {
3908
+ clearTimeout(timeoutId);
3909
+ const e = error;
3910
+ if (e.name === "AbortError") {
3911
+ throw new Error(`customer-session ${path} timeout`);
3912
+ }
3913
+ throw error;
3914
+ }
3915
+ }
3916
+ };
3917
+
3794
3918
  // src/baas/client.ts
3795
3919
  var BaasClient = class {
3796
3920
  hostUrl;
@@ -3815,6 +3939,8 @@ var BaasClient = class {
3815
3939
  deployment;
3816
3940
  /** Autonomous smart agent management */
3817
3941
  agents;
3942
+ /** Customer→smart-app session bridge (TokenGate Face B). */
3943
+ customerSession;
3818
3944
  constructor(config) {
3819
3945
  this.allowInsecure = config.allowInsecure ?? false;
3820
3946
  this.hostUrl = validateUrl2(config.hostUrl, this.allowInsecure);
@@ -3834,6 +3960,7 @@ var BaasClient = class {
3834
3960
  this.messaging = new MessagingClient(this.http, getAppId);
3835
3961
  this.deployment = new DeploymentClient(this.http);
3836
3962
  this.agents = new AgentsClient(this.http);
3963
+ this.customerSession = new CustomerSessionClient(baseUrlWithPrefix, this.timeout);
3837
3964
  }
3838
3965
  /** Set the app ID (for newly registered apps) */
3839
3966
  setAppId(appId) {
@@ -3860,7 +3987,7 @@ var BaasClient = class {
3860
3987
  requireAppId() {
3861
3988
  if (!this.appId) {
3862
3989
  throw new BaasError(
3863
- "App ID required. Either provide appId in config or call register() first.",
3990
+ "App ID required. Provide appId in config (e.g. from a prior deployment.init() call).",
3864
3991
  400
3865
3992
  );
3866
3993
  }
@@ -3907,12 +4034,6 @@ var BaasClient = class {
3907
4034
  }
3908
4035
  this.authToken = null;
3909
4036
  }
3910
- // ========== App Registration ==========
3911
- /** Register a new app on the BaaS host */
3912
- async register(request) {
3913
- this.requireAuth();
3914
- return this.post("/api/deployment/apps", request);
3915
- }
3916
4037
  // ========== HTTP Helpers ==========
3917
4038
  requireAuth() {
3918
4039
  if (!this.authToken) {
@@ -4007,6 +4128,7 @@ exports.CapabilityNotEnabledError = CapabilityNotEnabledError;
4007
4128
  exports.CapabilityValidationError = CapabilityValidationError;
4008
4129
  exports.CircuitBreaker = CircuitBreaker;
4009
4130
  exports.CircuitBreakerOpenError = CircuitBreakerOpenError;
4131
+ exports.CustomerSessionClient = CustomerSessionClient;
4010
4132
  exports.DEFAULT_CIRCUIT_BREAKER_CONFIG = DEFAULT_CIRCUIT_BREAKER_CONFIG;
4011
4133
  exports.DEFAULT_RETRY_CONFIG = DEFAULT_RETRY_CONFIG;
4012
4134
  exports.DatabaseClient = DatabaseClient;