@mycadet/gateway 0.12.7 → 0.12.9

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.
Files changed (179) hide show
  1. package/README.md +3 -3
  2. package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/package.json +2 -2
  3. package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/{platform-credential.ts → cloud-credential.ts} +9 -9
  4. package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
  5. package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
  6. package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
  7. package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
  8. package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
  9. package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
  10. package/node_modules/@mycadet/ces-client/package.json +1 -1
  11. package/node_modules/@mycadet/ces-client/src/__tests__/ces-client.test.ts +14 -14
  12. package/node_modules/@mycadet/ces-client/src/rpc-client.ts +1 -1
  13. package/node_modules/@mycadet/environments/src/seeds.ts +6 -6
  14. package/node_modules/@mycadet/environments/src/shell.test.ts +1 -1
  15. package/node_modules/@mycadet/environments/src/types.ts +6 -6
  16. package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/package.json +2 -2
  17. package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/{platform-credential.ts → cloud-credential.ts} +9 -9
  18. package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
  19. package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
  20. package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
  21. package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
  22. package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
  23. package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
  24. package/node_modules/@mycadet/gateway-client/src/binding-strength-contract.ts +5 -5
  25. package/node_modules/@mycadet/gateway-client/src/gateway-ipc-contracts.ts +1 -1
  26. package/node_modules/@mycadet/gateway-client/src/inbound-contract.ts +2 -2
  27. package/node_modules/@mycadet/gateway-client/src/outbound-contract.ts +1 -1
  28. package/node_modules/@mycadet/service-contracts/package.json +2 -2
  29. package/node_modules/@mycadet/service-contracts/src/{platform-credential.ts → cloud-credential.ts} +9 -9
  30. package/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
  31. package/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
  32. package/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
  33. package/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
  34. package/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
  35. package/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
  36. package/openapi.json +17 -17
  37. package/package.json +1 -1
  38. package/scripts/generate-openapi.ts +1 -1
  39. package/src/__tests__/actor-token-revocation.test.ts +0 -1
  40. package/src/__tests__/{backup-worker-platform-mode.test.ts → backup-worker-cloud-mode.test.ts} +4 -4
  41. package/src/__tests__/channel-verification-session-proxy.test.ts +0 -1
  42. package/src/__tests__/{platform-identity.test.ts → cloud-identity.test.ts} +72 -86
  43. package/src/__tests__/config.test.ts +0 -24
  44. package/src/__tests__/contacts-control-plane-proxy.test.ts +0 -1
  45. package/src/__tests__/credential-requests.test.ts +6 -6
  46. package/src/__tests__/{credential-watcher-managed-bootstrap.test.ts → credential-watcher-cloud-bootstrap.test.ts} +1 -1
  47. package/src/__tests__/edge-auth.test.ts +18 -18
  48. package/src/__tests__/edge-guardian-auth.test.ts +10 -10
  49. package/src/__tests__/guardian-init-lockfile.test.ts +6 -8
  50. package/src/__tests__/guardian-pin.test.ts +8 -8
  51. package/src/__tests__/handle-inbound-admission.test.ts +0 -1
  52. package/src/__tests__/handle-inbound-app-context.test.ts +0 -1
  53. package/src/__tests__/handle-inbound-invite-intercept.test.ts +0 -1
  54. package/src/__tests__/handle-inbound-trust-verdict.test.ts +0 -1
  55. package/src/__tests__/inbound-contact-seed.test.ts +1 -1
  56. package/src/__tests__/live-voice-websocket.test.ts +12 -12
  57. package/src/__tests__/load-guards.test.ts +0 -1
  58. package/src/__tests__/migration-teleport-gcs-proxy.test.ts +0 -1
  59. package/src/__tests__/oauth-callback.test.ts +0 -1
  60. package/src/__tests__/remote-feature-flag-sync.test.ts +35 -45
  61. package/src/__tests__/resolve-assistant.test.ts +0 -1
  62. package/src/__tests__/route-schema-guard.test.ts +4 -1
  63. package/src/__tests__/runtime-client.test.ts +1 -2
  64. package/src/__tests__/runtime-health-proxy.test.ts +0 -1
  65. package/src/__tests__/runtime-proxy-auth.test.ts +3 -2
  66. package/src/__tests__/runtime-proxy-device-stamp.test.ts +0 -1
  67. package/src/__tests__/runtime-proxy-error-body.test.ts +0 -1
  68. package/src/__tests__/runtime-proxy.test.ts +0 -1
  69. package/src/__tests__/runtime-stream-test-utils.ts +0 -1
  70. package/src/__tests__/shared-invite-registration.test.ts +15 -32
  71. package/src/__tests__/slack-control-plane-proxy.test.ts +0 -1
  72. package/src/__tests__/slack-display-name.test.ts +0 -1
  73. package/src/__tests__/slack-normalize.test.ts +0 -1
  74. package/src/__tests__/slack-reaction-normalize.test.ts +0 -1
  75. package/src/__tests__/slack-socket-mode-catchup.test.ts +0 -1
  76. package/src/__tests__/slack-socket-mode-group-dm.test.ts +0 -1
  77. package/src/__tests__/slack-socket-mode-liveness.test.ts +0 -1
  78. package/src/__tests__/slack-socket-mode-outbound-root.test.ts +0 -1
  79. package/src/__tests__/slack-socket-mode-thread-tracking.test.ts +0 -1
  80. package/src/__tests__/speech-relay-websocket.test.ts +16 -14
  81. package/src/__tests__/stt-stream-websocket.test.ts +5 -5
  82. package/src/__tests__/telegram-control-plane-proxy.test.ts +0 -1
  83. package/src/__tests__/telegram-webhook-drop-visibility.test.ts +0 -1
  84. package/src/__tests__/telegram-webhook-handler.test.ts +0 -1
  85. package/src/__tests__/telegram-webhook-manager.test.ts +79 -80
  86. package/src/__tests__/twilio-media-websocket.test.ts +0 -1
  87. package/src/__tests__/twilio-webhooks.test.ts +6 -7
  88. package/src/__tests__/whatsapp-download.test.ts +0 -1
  89. package/src/__tests__/whatsapp-webhook.test.ts +0 -1
  90. package/src/attachments/ingest.test.ts +0 -1
  91. package/src/auth/guardian-bootstrap.ts +16 -15
  92. package/src/auth/scopes.ts +1 -1
  93. package/src/auth-fallback-reporter.ts +1 -1
  94. package/src/backup/backup-worker.ts +2 -2
  95. package/src/channels/inbound-event.ts +3 -3
  96. package/src/channels/ingress-inbound-vendors.test.ts +2 -2
  97. package/src/channels/ingress-inbound.ts +1 -1
  98. package/src/channels/ingress-verification.test.ts +2 -2
  99. package/src/channels/ingress-verification.ts +4 -4
  100. package/src/channels/plugin-ingress-approvals.test.ts +1 -1
  101. package/src/channels/plugin-ingress-approvals.ts +1 -1
  102. package/src/channels/plugin-ingress.test.ts +3 -3
  103. package/src/channels/plugin-ingress.ts +3 -3
  104. package/src/channels/room-admission.ts +2 -2
  105. package/src/cli/contacts.ts +3 -1
  106. package/src/cli/index.ts +1 -1
  107. package/src/{platform-identity.ts → cloud-identity.ts} +60 -68
  108. package/src/cloud-url.ts +25 -0
  109. package/src/{platform-user-id.ts → cloud-user-id.ts} +4 -4
  110. package/src/config.ts +2 -4
  111. package/src/credential-reader.ts +2 -2
  112. package/src/db/assistant-db-proxy.ts +1 -1
  113. package/src/discord/forward.ts +1 -1
  114. package/src/email/normalize.ts +4 -4
  115. package/src/email/register-callback.test.ts +27 -34
  116. package/src/email/register-callback.ts +21 -26
  117. package/src/feature-flag-registry.json +5 -5
  118. package/src/feature-flag-remote-store.ts +2 -2
  119. package/src/feature-flag-resolver.ts +5 -5
  120. package/src/http/cadet-signature.ts +1 -1
  121. package/src/http/edge-forwarded-header.ts +1 -1
  122. package/src/http/loopback-guard.ts +1 -1
  123. package/src/http/middleware/auth.ts +22 -22
  124. package/src/http/router.ts +1 -1
  125. package/src/http/routes/auto-approve-thresholds-routes.ts +1 -1
  126. package/src/http/routes/cadet-identity.ts +2 -2
  127. package/src/http/routes/channel-admission-policy-routes.ts +2 -2
  128. package/src/http/routes/channel-ingress-routes.ts +3 -3
  129. package/src/http/routes/channel-ingress.test.ts +13 -2
  130. package/src/http/routes/channel-permission-overrides-routes.ts +3 -3
  131. package/src/http/routes/channel-verification-session-proxy.ts +8 -22
  132. package/src/http/routes/{platform-push-proxy.test.ts → cloud-push-proxy.test.ts} +21 -22
  133. package/src/http/routes/{platform-push-proxy.ts → cloud-push-proxy.ts} +13 -14
  134. package/src/http/routes/contacts-control-plane-proxy.ts +3 -3
  135. package/src/http/routes/contacts-control-plane-routes.ts +5 -5
  136. package/src/http/routes/email-webhook.test.ts +2 -3
  137. package/src/http/routes/email-webhook.ts +4 -4
  138. package/src/http/routes/feature-flags.ts +1 -1
  139. package/src/http/routes/guardian-channel-create.ts +3 -3
  140. package/src/http/routes/guardian-pin.ts +9 -9
  141. package/src/http/routes/inbound-register.ts +2 -2
  142. package/src/http/routes/ingress-domains.test.ts +129 -0
  143. package/src/http/routes/ingress-domains.ts +73 -0
  144. package/src/http/routes/live-voice-websocket.ts +2 -2
  145. package/src/http/routes/log-export.test.ts +0 -1
  146. package/src/http/routes/log-tail.test.ts +0 -1
  147. package/src/http/routes/migration-proxy.ts +1 -1
  148. package/src/http/routes/plugin-webhook-websocket.test.ts +3 -3
  149. package/src/http/routes/plugin-webhook.test.ts +9 -8
  150. package/src/http/routes/plugin-webhook.ts +1 -1
  151. package/src/http/routes/speech-relay-websocket.ts +8 -11
  152. package/src/http/routes/stt-stream-websocket.ts +1 -1
  153. package/src/http/routes/telegram-webhook.test.ts +0 -1
  154. package/src/http/routes/trust-rules-routes.ts +2 -2
  155. package/src/http/routes/twilio-voice-verify-callback.ts +3 -3
  156. package/src/http/routes/twilio-voice-webhook.test.ts +3 -4
  157. package/src/http/routes/twilio-voice-webhook.ts +3 -3
  158. package/src/http/routes/whatsapp-webhook.test.ts +0 -1
  159. package/src/index.ts +57 -56
  160. package/src/ipc/credential-request-handlers.ts +4 -4
  161. package/src/ipc/debug-export-handlers.test.ts +0 -1
  162. package/src/ipc/log-tail-handlers.test.ts +0 -1
  163. package/src/ipc/risk-classification-handlers.test.ts +4 -4
  164. package/src/ipc/risk-classification-handlers.ts +2 -2
  165. package/src/remote-feature-flag-sync.ts +17 -19
  166. package/src/risk/command-registry/commands/assistant.ts +2 -2
  167. package/src/risk/skill-risk-classifier.test.ts +24 -24
  168. package/src/risk/skill-risk-classifier.ts +10 -10
  169. package/src/runtime/client.ts +17 -17
  170. package/src/schema.ts +29 -29
  171. package/src/telegram/send.test.ts +0 -1
  172. package/src/telegram/webhook-manager.ts +33 -38
  173. package/src/tunnel/allowed-paths.test.ts +3 -3
  174. package/src/tunnel/allowed-paths.ts +1 -1
  175. package/src/tunnel/client.test.ts +20 -21
  176. package/src/tunnel/client.ts +16 -17
  177. package/src/verification/shared-invite-registration.ts +22 -17
  178. package/src/version.ts +1 -1
  179. package/src/platform-url.ts +0 -24
@@ -34,13 +34,12 @@ mock.module("../fetch.js", () => ({
34
34
  let testAssistantDb: Database | null = null;
35
35
 
36
36
  mock.module("../db/assistant-db-proxy.js", () => ({
37
-
38
37
  async assistantDbQuery(sql: string, bind?: any[]) {
39
38
  if (!testAssistantDb) throw new Error("test assistant DB not initialized");
40
39
  const stmt = testAssistantDb.prepare(sql);
41
40
  return bind ? stmt.all(...bind) : stmt.all();
42
41
  },
43
-
42
+
44
43
  async assistantDbRun(sql: string, bind?: any[]) {
45
44
  if (!testAssistantDb) throw new Error("test assistant DB not initialized");
46
45
  const stmt = testAssistantDb.prepare(sql);
@@ -159,7 +158,6 @@ function makeConfig(overrides: Partial<GatewayConfig> = {}): GatewayConfig {
159
158
  routingEntries: [],
160
159
  port: 7830,
161
160
  runtimeProxyRequireAuth: true,
162
- shutdownDrainMs: 5000,
163
161
  runtimeTimeoutMs: 30000,
164
162
  runtimeMaxRetries: 2,
165
163
  runtimeInitialBackoffMs: 500,
@@ -738,7 +736,7 @@ describe("guardian/reset-bootstrap", () => {
738
736
  describe("guardian/init bare-metal loopback gating", () => {
739
737
  test("rejects non-loopback clients in bare-metal mode", async () => {
740
738
  delete process.env.GUARDIAN_BOOTSTRAP_SECRET;
741
- delete process.env.IS_PLATFORM;
739
+ delete process.env.IS_CLOUD;
742
740
  const handler = createChannelVerificationSessionProxyHandler(makeConfig());
743
741
  const res = await handler.handleGuardianInit(
744
742
  new Request("http://localhost:7830/v1/guardian/init", {
@@ -756,7 +754,7 @@ describe("guardian/init bare-metal loopback gating", () => {
756
754
 
757
755
  test("allows loopback clients in bare-metal mode", async () => {
758
756
  delete process.env.GUARDIAN_BOOTSTRAP_SECRET;
759
- delete process.env.IS_PLATFORM;
757
+ delete process.env.IS_CLOUD;
760
758
  const handler = createChannelVerificationSessionProxyHandler(makeConfig());
761
759
  const res = await handler.handleGuardianInit(
762
760
  new Request("http://localhost:7830/v1/guardian/init", {
@@ -792,9 +790,9 @@ describe("guardian/init bare-metal loopback gating", () => {
792
790
  expect(body.accessToken).toBeTruthy();
793
791
  });
794
792
 
795
- test("skips loopback check in platform-managed mode (IS_PLATFORM=true)", async () => {
793
+ test("skips loopback check in cloud-managed mode (IS_CLOUD=true)", async () => {
796
794
  delete process.env.GUARDIAN_BOOTSTRAP_SECRET;
797
- process.env.IS_PLATFORM = "true";
795
+ process.env.IS_CLOUD = "true";
798
796
  try {
799
797
  const handler =
800
798
  createChannelVerificationSessionProxyHandler(makeConfig());
@@ -814,7 +812,7 @@ describe("guardian/init bare-metal loopback gating", () => {
814
812
  const body = await res.json();
815
813
  expect(body.accessToken).toBeTruthy();
816
814
  } finally {
817
- delete process.env.IS_PLATFORM;
815
+ delete process.env.IS_CLOUD;
818
816
  }
819
817
  });
820
818
  });
@@ -23,8 +23,8 @@ mock.module("../auth/guardian-bootstrap.js", () => ({
23
23
  let mockReadCredential = mock(
24
24
  async (_key: string): Promise<string | undefined> => TUNNEL_USER_ID,
25
25
  );
26
- mock.module("../platform-user-id.js", () => ({
27
- readStoredPlatformUserId: async () => ({
26
+ mock.module("../cloud-user-id.js", () => ({
27
+ readStoredCloudUserId: async () => ({
28
28
  userId: await mockReadCredential("cadet:platform_user_id"),
29
29
  unreachable: false,
30
30
  }),
@@ -44,7 +44,7 @@ beforeEach(() => {
44
44
  });
45
45
 
46
46
  afterEach(() => {
47
- delete process.env.IS_PLATFORM;
47
+ delete process.env.IS_CLOUD;
48
48
  });
49
49
 
50
50
  /**
@@ -130,7 +130,7 @@ describe("authorizeGuardianStream: the actor token path", () => {
130
130
  });
131
131
 
132
132
  /**
133
- * The managed path, where tunnel validated the browser's token and injected the
133
+ * The cloud path, where tunnel validated the browser's token and injected the
134
134
  * caller. The attestation proves the caller is *a* platform user who traversed
135
135
  * tunnel, not that they are this assistant's guardian, so it is cross-checked
136
136
  * against the stored `platform_user_id`.
@@ -158,9 +158,9 @@ describe("authorizeGuardianStream: the tunnel-attested managed path", () => {
158
158
  tunnelBaseUrl?: string;
159
159
  }) => {
160
160
  if (managed) {
161
- process.env.IS_PLATFORM = "true";
161
+ process.env.IS_CLOUD = "true";
162
162
  } else {
163
- delete process.env.IS_PLATFORM;
163
+ delete process.env.IS_CLOUD;
164
164
  }
165
165
  const headers = new Headers({
166
166
  "x-tunnel-user-id": userId,
@@ -205,7 +205,7 @@ describe("authorizeGuardianStream: the tunnel-attested managed path", () => {
205
205
  expect(res!.status).toBe(403);
206
206
  });
207
207
 
208
- test("answers 503 when the platform user lookup fails", async () => {
208
+ test("answers 503 when the cloud user lookup fails", async () => {
209
209
  mockReadCredential = mock(async () => {
210
210
  throw new Error("credential store down");
211
211
  });
@@ -280,7 +280,7 @@ describe("authorizeGuardianStream: the tunnel-attested managed path", () => {
280
280
  });
281
281
 
282
282
  /**
283
- * The managed path skips the shared gate, so the upgrade header is checked
283
+ * The cloud path skips the shared gate, so the upgrade header is checked
284
284
  * before it: a plain managed request must get 426, not a failed upgrade.
285
285
  */
286
286
  test("answers 426 for a plain request even when tunnel attests the guardian", async () => {
@@ -90,7 +90,6 @@ function makeConfig(overrides: Partial<GatewayConfig> = {}): GatewayConfig {
90
90
  runtimeMaxRetries: 2,
91
91
  runtimeProxyRequireAuth: false,
92
92
  runtimeTimeoutMs: 30000,
93
- shutdownDrainMs: 5000,
94
93
  trustProxy: false,
95
94
  ...overrides,
96
95
  } as GatewayConfig;
@@ -86,7 +86,6 @@ function makeConfig(): GatewayConfig {
86
86
  runtimeMaxRetries: 2,
87
87
  runtimeProxyRequireAuth: false,
88
88
  runtimeTimeoutMs: 30000,
89
- shutdownDrainMs: 5000,
90
89
  trustProxy: false,
91
90
  } as unknown as GatewayConfig;
92
91
  }
@@ -180,7 +180,6 @@ function makeConfig(): GatewayConfig {
180
180
  runtimeMaxRetries: 2,
181
181
  runtimeProxyRequireAuth: false,
182
182
  runtimeTimeoutMs: 30000,
183
- shutdownDrainMs: 5000,
184
183
  trustProxy: false,
185
184
  } as unknown as GatewayConfig;
186
185
  }
@@ -155,7 +155,6 @@ function makeConfig(): GatewayConfig {
155
155
  runtimeMaxRetries: 2,
156
156
  runtimeProxyRequireAuth: false,
157
157
  runtimeTimeoutMs: 30000,
158
- shutdownDrainMs: 5000,
159
158
  trustProxy: false,
160
159
  } as unknown as GatewayConfig;
161
160
  }
@@ -237,7 +237,7 @@ describe("re-seen identity", () => {
237
237
  status: "unverified",
238
238
  });
239
239
 
240
- // The platform profile says "Alice Example", a DM supplies the chat id.
240
+ // The cloud profile says "Alice Example", a DM supplies the chat id.
241
241
  await upsertContactChannel({
242
242
  sourceChannel: "slack",
243
243
  externalUserId: SLACK_USER,
@@ -36,7 +36,7 @@ mock.module("../auth/guardian-bootstrap.js", () => ({
36
36
  findCadetGuardian: () => mockFindCadetGuardian(),
37
37
  }));
38
38
 
39
- // The managed/tunnel path cross-checks the attested caller against the stored
39
+ // The cloud/tunnel path cross-checks the attested caller against the stored
40
40
  // `platform_user_id`. Default the credential to `TUNNEL_USER_ID` so the tunnel
41
41
  // success cases match; negative cases override this mock.
42
42
  let mockReadCredential = mock(
@@ -45,8 +45,8 @@ let mockReadCredential = mock(
45
45
  mock.module("../credential-reader.js", () => ({
46
46
  readCredential: (key: string) => mockReadCredential(key),
47
47
  }));
48
- mock.module("../platform-user-id.js", () => ({
49
- readStoredPlatformUserId: async () => {
48
+ mock.module("../cloud-user-id.js", () => ({
49
+ readStoredCloudUserId: async () => {
50
50
  const userId = await mockReadCredential("cadet:platform_user_id");
51
51
  return { userId, unreachable: false };
52
52
  },
@@ -273,13 +273,13 @@ describe("createLiveVoiceWebsocketHandler — tunnel-attested managed auth", ()
273
273
  const TEST_TOKEN = mintEdgeToken();
274
274
  // TUNNEL_USER_ID is declared at module scope (shared with the credential mock).
275
275
  const TUNNEL_ORG_ID = "22222222-2222-2222-2222-222222222222";
276
- const originalIsPlatform = process.env.IS_PLATFORM;
276
+ const originalIsPlatform = process.env.IS_CLOUD;
277
277
 
278
278
  function setPlatform(on: boolean) {
279
279
  if (on) {
280
- process.env.IS_PLATFORM = "true";
280
+ process.env.IS_CLOUD = "true";
281
281
  } else {
282
- delete process.env.IS_PLATFORM;
282
+ delete process.env.IS_CLOUD;
283
283
  }
284
284
  }
285
285
 
@@ -302,9 +302,9 @@ describe("createLiveVoiceWebsocketHandler — tunnel-attested managed auth", ()
302
302
 
303
303
  afterEach(() => {
304
304
  if (originalIsPlatform === undefined) {
305
- delete process.env.IS_PLATFORM;
305
+ delete process.env.IS_CLOUD;
306
306
  } else {
307
- process.env.IS_PLATFORM = originalIsPlatform;
307
+ process.env.IS_CLOUD = originalIsPlatform;
308
308
  }
309
309
  });
310
310
 
@@ -319,9 +319,9 @@ describe("createLiveVoiceWebsocketHandler — tunnel-attested managed auth", ()
319
319
  });
320
320
 
321
321
  /**
322
- * Tunnel attests a platform user, not an actor principal, so this path has
322
+ * Tunnel attests a cloud user, not an actor principal, so this path has
323
323
  * no principal in the request at all. The binding supplies it, which is
324
- * what lets the managed deployment carry an identity downstream rather than
324
+ * what lets the cloud deployment carry an identity downstream rather than
325
325
  * leaving the daemon to resolve one of its own.
326
326
  */
327
327
  test("managed mode carries the bound guardian's principal on the socket", async () => {
@@ -455,8 +455,8 @@ describe("createLiveVoiceWebsocketHandler — tunnel-attested managed auth", ()
455
455
 
456
456
  /**
457
457
  * A locally hosted assistant reaches the mobile app through a tunnel
458
- * with IS_PLATFORM unset. The attestation arrives through the same bridge
459
- * as on a managed pod, so the tunnel, not the env var, is what admits it.
458
+ * with IS_CLOUD unset. The attestation arrives through the same bridge
459
+ * as on a cloud pod, so the tunnel, not the env var, is what admits it.
460
460
  */
461
461
  test("a locally hosted gateway with a tunnel trusts an attested caller with bridge proof", async () => {
462
462
  setPlatform(false);
@@ -21,7 +21,6 @@ function makeConfig(overrides: Partial<GatewayConfig> = {}): GatewayConfig {
21
21
  routingEntries: [],
22
22
  port: 7830,
23
23
  runtimeProxyRequireAuth: true,
24
- shutdownDrainMs: 5000,
25
24
  runtimeTimeoutMs: 30000,
26
25
  runtimeMaxRetries: 2,
27
26
  runtimeInitialBackoffMs: 500,
@@ -29,7 +29,6 @@ function makeConfig(overrides: Partial<GatewayConfig> = {}): GatewayConfig {
29
29
  routingEntries: [],
30
30
  port: 7830,
31
31
  runtimeProxyRequireAuth: true,
32
- shutdownDrainMs: 5000,
33
32
  runtimeTimeoutMs: 30000,
34
33
  runtimeMaxRetries: 2,
35
34
  runtimeInitialBackoffMs: 500,
@@ -33,7 +33,6 @@ function makeConfig(overrides: Partial<GatewayConfig> = {}): GatewayConfig {
33
33
  routingEntries: [],
34
34
  port: 7830,
35
35
  runtimeProxyRequireAuth: true,
36
- shutdownDrainMs: 5000,
37
36
  runtimeTimeoutMs: 30000,
38
37
  runtimeMaxRetries: 2,
39
38
  runtimeInitialBackoffMs: 500,
@@ -35,9 +35,8 @@ const { readRemoteFeatureFlags, clearRemoteFeatureFlagStoreCache } =
35
35
  await import("../feature-flag-remote-store.js");
36
36
  const { resetFeatureFlagDefaultsCache, _setRegistryCandidateOverrides } =
37
37
  await import("../feature-flag-defaults.js");
38
- const { resetEnvOverridesCache } = await import(
39
- "../feature-flag-env-overrides.js"
40
- );
38
+ const { resetEnvOverridesCache } =
39
+ await import("../feature-flag-env-overrides.js");
41
40
 
42
41
  // ---------------------------------------------------------------------------
43
42
  // Test-local registry with a GA flag (defaultEnabled: true) for the
@@ -110,7 +109,6 @@ function fakeCredentialCache(
110
109
 
111
110
  function defaultCredentials(): Record<string, string> {
112
111
  return {
113
- "credential/cadet/platform_base_url": "https://cloud.mycadet.ai",
114
112
  "credential/cadet/assistant_api_key": "test-api-key",
115
113
  };
116
114
  }
@@ -118,16 +116,16 @@ function defaultCredentials(): Record<string, string> {
118
116
  // ---------------------------------------------------------------------------
119
117
  // Setup / teardown
120
118
  // ---------------------------------------------------------------------------
121
- const savedCadetPlatformUrl = process.env.CADET_PLATFORM_URL;
119
+ const savedCadetCloudUrl = process.env.CADET_CLOUD_URL;
122
120
  const savedAssistantCredential = process.env.ASSISTANT_API_KEY;
123
- const savedDisablePlatform = process.env.CADET_DISABLE_PLATFORM;
121
+ const savedDisablePlatform = process.env.CADET_DISABLE_CLOUD;
124
122
 
125
123
  beforeEach(() => {
126
124
  // Clear env vars that the production code falls back to, so tests remain
127
125
  // deterministic unless they explicitly set them.
128
- delete process.env.CADET_PLATFORM_URL;
126
+ process.env.CADET_CLOUD_URL = "https://test-cloud.mycadet.ai";
129
127
  delete process.env.ASSISTANT_API_KEY;
130
- delete process.env.CADET_DISABLE_PLATFORM;
128
+ delete process.env.CADET_DISABLE_CLOUD;
131
129
  mkdirSync(protectedDir, { recursive: true });
132
130
  // Write the test registry and point resolution at it
133
131
  writeFileSync(testRegistryPath, JSON.stringify(TEST_REGISTRY, null, 2));
@@ -147,9 +145,9 @@ afterEach(() => {
147
145
  process.env[key] = saved;
148
146
  }
149
147
  };
150
- restoreEnv("CADET_PLATFORM_URL", savedCadetPlatformUrl);
148
+ restoreEnv("CADET_CLOUD_URL", savedCadetCloudUrl);
151
149
  restoreEnv("ASSISTANT_API_KEY", savedAssistantCredential);
152
- restoreEnv("CADET_DISABLE_PLATFORM", savedDisablePlatform);
150
+ restoreEnv("CADET_DISABLE_CLOUD", savedDisablePlatform);
153
151
  try {
154
152
  rmSync(protectedDir, { recursive: true, force: true });
155
153
  mkdirSync(protectedDir, { recursive: true });
@@ -168,7 +166,7 @@ afterEach(() => {
168
166
  describe("RemoteFeatureFlagSync", () => {
169
167
  test("skips sync when platform features are disabled", async () => {
170
168
  fetchMock = mock(async () => Response.json({ flags: { ff1: true } }));
171
- process.env.CADET_DISABLE_PLATFORM = "true";
169
+ process.env.CADET_DISABLE_CLOUD = "true";
172
170
 
173
171
  const sync = new RemoteFeatureFlagSync({
174
172
  credentials: fakeCredentialCache(defaultCredentials()),
@@ -179,12 +177,11 @@ describe("RemoteFeatureFlagSync", () => {
179
177
  expect(fetchMock).not.toHaveBeenCalled();
180
178
  });
181
179
 
182
- test("skips sync when no platform URL is available from cache or env", async () => {
180
+ test("skips sync when no platform URL is available from env", async () => {
183
181
  fetchMock = mock(async () => Response.json({ flags: { ff1: true } }));
184
182
 
185
183
  const creds = defaultCredentials();
186
- delete creds["credential/cadet/platform_base_url"];
187
- delete process.env.CADET_PLATFORM_URL;
184
+ delete process.env.CADET_CLOUD_URL;
188
185
 
189
186
  const sync = new RemoteFeatureFlagSync({
190
187
  credentials: fakeCredentialCache(creds),
@@ -196,12 +193,11 @@ describe("RemoteFeatureFlagSync", () => {
196
193
  expect(fetchMock).not.toHaveBeenCalled();
197
194
  });
198
195
 
199
- test("falls back to CADET_PLATFORM_URL env var when platform_base_url is missing", async () => {
196
+ test("uses the CADET_CLOUD_URL env var", async () => {
200
197
  fetchMock = mock(async () => Response.json({ flags: {} }));
201
- process.env.CADET_PLATFORM_URL = "https://env-platform.example.com";
198
+ process.env.CADET_CLOUD_URL = "https://env-platform.example.com";
202
199
 
203
200
  const creds = defaultCredentials();
204
- delete creds["credential/cadet/platform_base_url"];
205
201
 
206
202
  const sync = new RemoteFeatureFlagSync({
207
203
  credentials: fakeCredentialCache(creds),
@@ -235,11 +231,11 @@ describe("RemoteFeatureFlagSync", () => {
235
231
  expect(headers.Accept).toBe("application/json");
236
232
  });
237
233
 
238
- test("syncs when only platformUrl and assistantApiKey are present", async () => {
234
+ test("syncs when only cloudUrl and assistantApiKey are present", async () => {
239
235
  fetchMock = mock(async () => Response.json({ flags: { ff1: true } }));
240
236
 
237
+ process.env.CADET_CLOUD_URL = "https://test-cloud.mycadet.ai";
241
238
  const creds = {
242
- "credential/cadet/platform_base_url": "https://platform.example.com",
243
239
  "credential/cadet/assistant_api_key": "test-api-key",
244
240
  };
245
241
 
@@ -256,9 +252,8 @@ describe("RemoteFeatureFlagSync", () => {
256
252
  fetchMock = mock(async () => Response.json({ flags: { ff1: true } }));
257
253
  process.env.ASSISTANT_API_KEY = "env-key";
258
254
 
259
- const creds = {
260
- "credential/cadet/platform_base_url": "https://platform.example.com",
261
- };
255
+ process.env.CADET_CLOUD_URL = "https://test-cloud.mycadet.ai";
256
+ const creds = {};
262
257
 
263
258
  const sync = new RemoteFeatureFlagSync({
264
259
  credentials: fakeCredentialCache(creds),
@@ -299,8 +294,8 @@ describe("RemoteFeatureFlagSync", () => {
299
294
  // with the NEW api key and overwrite the cached per-assistant flag values
300
295
  // immediately, rather than serving the previous identity's (or registry
301
296
  // default) values until the next ~5-min poll.
297
+ process.env.CADET_CLOUD_URL = "https://test-cloud.mycadet.ai";
302
298
  const cache = fakeCredentialCache({
303
- "credential/cadet/platform_base_url": "https://platform.example.com",
304
299
  "credential/cadet/assistant_api_key": "old-assistant-key",
305
300
  });
306
301
 
@@ -317,8 +312,8 @@ describe("RemoteFeatureFlagSync", () => {
317
312
 
318
313
  // Warm-pool claim: credentials now belong to the newly-assigned assistant,
319
314
  // whose flag evaluation returns self-intro-greeting ON.
315
+ process.env.CADET_CLOUD_URL = "https://test-cloud.mycadet.ai";
320
316
  cache._setValues({
321
- "credential/cadet/platform_base_url": "https://platform.example.com",
322
317
  "credential/cadet/assistant_api_key": "new-assistant-key",
323
318
  });
324
319
  fetchMock = mock(async () =>
@@ -346,8 +341,8 @@ describe("RemoteFeatureFlagSync", () => {
346
341
  // second syncNow() arriving while it is in flight must NOT be dropped by
347
342
  // the re-entrancy guard — it must trigger one more fetch so the final
348
343
  // identity's flags are cached, not the stale ones.
344
+ process.env.CADET_CLOUD_URL = "https://test-cloud.mycadet.ai";
349
345
  const cache = fakeCredentialCache({
350
- "credential/cadet/platform_base_url": "https://platform.example.com",
351
346
  "credential/cadet/assistant_api_key": "old-assistant-key",
352
347
  });
353
348
 
@@ -375,8 +370,8 @@ describe("RemoteFeatureFlagSync", () => {
375
370
 
376
371
  // Warm-pool claim: identity changes, then a second syncNow arrives while
377
372
  // the first fetch is still in flight.
373
+ process.env.CADET_CLOUD_URL = "https://test-cloud.mycadet.ai";
378
374
  cache._setValues({
379
- "credential/cadet/platform_base_url": "https://platform.example.com",
380
375
  "credential/cadet/assistant_api_key": "new-assistant-key",
381
376
  });
382
377
  await sync.syncNow(); // returns immediately, records pendingResync
@@ -491,10 +486,8 @@ describe("RemoteFeatureFlagSync", () => {
491
486
  test("constructs correct URL from platform base URL", async () => {
492
487
  fetchMock = mock(async () => Response.json({ flags: {} }));
493
488
 
494
- const creds = {
495
- ...defaultCredentials(),
496
- "credential/cadet/platform_base_url": "https://platform.example.com",
497
- };
489
+ process.env.CADET_CLOUD_URL = "https://test-cloud.mycadet.ai";
490
+ const creds = { ...defaultCredentials() };
498
491
  const sync = new RemoteFeatureFlagSync({
499
492
  credentials: fakeCredentialCache(creds),
500
493
  });
@@ -504,7 +497,7 @@ describe("RemoteFeatureFlagSync", () => {
504
497
  expect(fetchMock).toHaveBeenCalledTimes(1);
505
498
  const [url] = fetchMock.mock.calls[0];
506
499
  expect(url).toBe(
507
- "https://platform.example.com/v1/feature-flags/assistant-flag-values/",
500
+ "https://test-cloud.mycadet.ai/v1/feature-flags/assistant-flag-values/",
508
501
  );
509
502
  });
510
503
 
@@ -572,10 +565,8 @@ describe("RemoteFeatureFlagSync", () => {
572
565
  test("strips trailing slashes from platform URL", async () => {
573
566
  fetchMock = mock(async () => Response.json({ flags: {} }));
574
567
 
575
- const creds = {
576
- ...defaultCredentials(),
577
- "credential/cadet/platform_base_url": "https://platform.example.com///",
578
- };
568
+ process.env.CADET_CLOUD_URL = "https://test-cloud.mycadet.ai///";
569
+ const creds = { ...defaultCredentials() };
579
570
  const sync = new RemoteFeatureFlagSync({
580
571
  credentials: fakeCredentialCache(creds),
581
572
  });
@@ -585,12 +576,12 @@ describe("RemoteFeatureFlagSync", () => {
585
576
  expect(fetchMock).toHaveBeenCalledTimes(1);
586
577
  const [url] = fetchMock.mock.calls[0];
587
578
  expect(url).toBe(
588
- "https://platform.example.com/v1/feature-flags/assistant-flag-values/",
579
+ "https://test-cloud.mycadet.ai/v1/feature-flags/assistant-flag-values/",
589
580
  );
590
581
  });
591
582
 
592
583
  test("normalizes remote false for GA flags (defaultEnabled: true in registry)", async () => {
593
- // The platform sends false for all flags it knows about (blanket-deny).
584
+ // The cloud sends false for all flags it knows about (blanket-deny).
594
585
  // GA flags (defaultEnabled: true in the registry) should not be disabled
595
586
  // by remote overrides — only local persisted overrides can do that.
596
587
  // Uses the test-local registry which defines test-ga-flag as GA
@@ -725,8 +716,8 @@ describe("RemoteFeatureFlagSync", () => {
725
716
  test("trims whitespace from credential values", async () => {
726
717
  fetchMock = mock(async () => Response.json({ flags: {} }));
727
718
 
719
+ process.env.CADET_CLOUD_URL = " https://test-cloud.mycadet.ai ";
728
720
  const creds = {
729
- "credential/cadet/platform_base_url": " https://platform.example.com ",
730
721
  "credential/cadet/assistant_api_key": " trimmed-key ",
731
722
  };
732
723
  const sync = new RemoteFeatureFlagSync({
@@ -738,7 +729,7 @@ describe("RemoteFeatureFlagSync", () => {
738
729
  expect(fetchMock).toHaveBeenCalledTimes(1);
739
730
  const [url, init] = fetchMock.mock.calls[0];
740
731
  expect(url).toBe(
741
- "https://platform.example.com/v1/feature-flags/assistant-flag-values/",
732
+ "https://test-cloud.mycadet.ai/v1/feature-flags/assistant-flag-values/",
742
733
  );
743
734
  const headers = init?.headers as Record<string, string>;
744
735
  expect(headers.Authorization).toBe("Api-Key trimmed-key");
@@ -905,17 +896,18 @@ describe("RemoteFeatureFlagSync", () => {
905
896
  credentials: creds,
906
897
  initialPollIntervalMs: 50,
907
898
  });
899
+ // Missing platform URL from the start — polling should pause
900
+ delete process.env.CADET_CLOUD_URL;
908
901
  await sync.start();
909
902
 
910
- // No fetch calls — missing platform URL, polling should be paused
911
903
  expect(fetchMock).not.toHaveBeenCalled();
912
904
 
913
905
  // Wait well past the initial poll interval — should still not poll
914
906
  await new Promise((r) => setTimeout(r, 200));
915
907
  expect(fetchMock).not.toHaveBeenCalled();
916
908
 
917
- // Simulate platform URL becoming available
918
- creds._setValues(defaultCredentials());
909
+ // Simulate the cloud URL becoming available
910
+ process.env.CADET_CLOUD_URL = "https://test-cloud.mycadet.ai";
919
911
  creds._invalidate();
920
912
 
921
913
  // Wait for syncNow to complete (async, fire-and-forget from callback)
@@ -949,9 +941,7 @@ describe("RemoteFeatureFlagSync", () => {
949
941
  expect(readRemoteFeatureFlags()).toEqual({ "per-assistant-flag": true });
950
942
 
951
943
  // Simulate transient API key loss (CES hiccup) — platform URL still present
952
- cache._setValues({
953
- "credential/cadet/platform_base_url": "https://cloud.mycadet.ai",
954
- });
944
+ cache._setValues({});
955
945
  fetchMock = mock(async () =>
956
946
  Response.json({ flags: { "per-assistant-flag": false } }),
957
947
  );
@@ -9,7 +9,6 @@ function makeConfig(overrides: Partial<GatewayConfig> = {}): GatewayConfig {
9
9
  routingEntries: [],
10
10
  port: 7830,
11
11
  runtimeProxyRequireAuth: true,
12
- shutdownDrainMs: 5000,
13
12
  runtimeTimeoutMs: 30000,
14
13
  runtimeMaxRetries: 2,
15
14
  runtimeInitialBackoffMs: 500,
@@ -210,7 +210,7 @@ const EXCLUDED_FROM_SCHEMA = new Set([
210
210
  "/v1/remote-web/pairing-requests",
211
211
  "/v1/remote-web/pairing-requests/approve",
212
212
  "/v1/remote-web/pairing-requests/deny",
213
- // Shared-invite redemption, called by the platform on an invitee's behalf;
213
+ // Shared-invite redemption, called by the cloud on an invitee's behalf;
214
214
  // not part of the public gateway API
215
215
  "/v1/shared/invites/redeem",
216
216
  // Loopback-only device management — not part of the public gateway API
@@ -227,6 +227,9 @@ const EXCLUDED_FROM_SCHEMA = new Set([
227
227
  "/v1/speech/stt/stream",
228
228
  "/v2/speech/stt/stream",
229
229
  "/v1/speech/tts/stream",
230
+ // Loopback-only ingress domain recording — the local CLI reports its
231
+ // tunnel's public URL (bearer-authed); not part of the public gateway API
232
+ "/v1/ingress/domains",
230
233
  // Plugin-declared webhooks — which paths exist is decided at runtime by the
231
234
  // installed plugins and the guardian's approvals, so there is no fixed set
232
235
  // to document. The declaration schema is the contract, not this path.
@@ -42,7 +42,6 @@ function makeConfig(overrides: Partial<GatewayConfig> = {}): GatewayConfig {
42
42
  routingEntries: [],
43
43
  port: 7830,
44
44
  runtimeProxyRequireAuth: true,
45
- shutdownDrainMs: 5000,
46
45
  runtimeTimeoutMs: 30000,
47
46
  runtimeMaxRetries: 2,
48
47
  runtimeInitialBackoffMs: 500,
@@ -340,7 +339,7 @@ describe("resolvePublicHttpBaseUrl", () => {
340
339
  expect(result).toBe("https://assistant.example.com");
341
340
  });
342
341
 
343
- test("does not read the platform assistant id when the config URL resolves", async () => {
342
+ test("does not read the cloud assistant id when the config URL resolves", async () => {
344
343
  const platformIdGetter = getId("assistant-123");
345
344
  await resolvePublicHttpBaseUrl(
346
345
  makeConfig({ tunnelBaseUrl: "https://tunnel-dev.mycadet.ai" }),
@@ -26,7 +26,6 @@ function makeConfig(overrides: Partial<GatewayConfig> = {}): GatewayConfig {
26
26
  routingEntries: [],
27
27
  port: 7830,
28
28
  runtimeProxyRequireAuth: true,
29
- shutdownDrainMs: 5000,
30
29
  runtimeTimeoutMs: 30000,
31
30
  runtimeMaxRetries: 2,
32
31
  runtimeInitialBackoffMs: 500,
@@ -45,7 +45,6 @@ function makeConfig(overrides: Partial<GatewayConfig> = {}): GatewayConfig {
45
45
  routingEntries: [],
46
46
  port: 7830,
47
47
  runtimeProxyRequireAuth: true,
48
- shutdownDrainMs: 5000,
49
48
  runtimeTimeoutMs: 30000,
50
49
  runtimeMaxRetries: 2,
51
50
  runtimeInitialBackoffMs: 500,
@@ -412,7 +411,9 @@ describe("runtime proxy OAuth grant path rewrite", () => {
412
411
  },
413
412
  );
414
413
 
415
- expect(url).toBe("http://localhost:7821/v1/oauth/proxy/stripe_link/v1/charges");
414
+ expect(url).toBe(
415
+ "http://localhost:7821/v1/oauth/proxy/stripe_link/v1/charges",
416
+ );
416
417
  });
417
418
 
418
419
  test("does not rewrite a host-absolute path without a proxy grant", async () => {
@@ -161,7 +161,6 @@ function makeConfig(overrides: Partial<GatewayConfig> = {}): GatewayConfig {
161
161
  routingEntries: [],
162
162
  port: 7830,
163
163
  runtimeProxyRequireAuth: true,
164
- shutdownDrainMs: 5000,
165
164
  runtimeTimeoutMs: 30000,
166
165
  runtimeMaxRetries: 2,
167
166
  runtimeInitialBackoffMs: 500,
@@ -75,7 +75,6 @@ function makeConfig(): GatewayConfig {
75
75
  routingEntries: [],
76
76
  port: 7830,
77
77
  runtimeProxyRequireAuth: true,
78
- shutdownDrainMs: 5000,
79
78
  runtimeTimeoutMs: 30000,
80
79
  runtimeMaxRetries: 2,
81
80
  runtimeInitialBackoffMs: 500,
@@ -26,7 +26,6 @@ function makeConfig(overrides: Partial<GatewayConfig> = {}): GatewayConfig {
26
26
  routingEntries: [],
27
27
  port: 7830,
28
28
  runtimeProxyRequireAuth: false,
29
- shutdownDrainMs: 5000,
30
29
  runtimeTimeoutMs: 30000,
31
30
  runtimeMaxRetries: 2,
32
31
  runtimeInitialBackoffMs: 500,
@@ -50,7 +50,6 @@ export function makeConfig(
50
50
  routingEntries: [],
51
51
  port: 7830,
52
52
  runtimeProxyRequireAuth: true,
53
- shutdownDrainMs: 5000,
54
53
  runtimeTimeoutMs: 30000,
55
54
  runtimeMaxRetries: 2,
56
55
  runtimeInitialBackoffMs: 500,