@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
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Loopback-guarded recording of a tunnel's public URL.
3
+ *
4
+ * POST /v1/ingress/domains — the CLI (its ngrok agent, today) tells the
5
+ * gateway the public URL its tunnel published; the gateway writes
6
+ * `ingress.publicBaseUrl` + `publicBaseUrlManagedBy: "tunnel"` in its own
7
+ * config via the same machinery its own tunnel uses, so both publishers
8
+ * record through one path and the gateway's existing loss handling clears
9
+ * it uniformly. Write-only: reads keep flowing through the config the
10
+ * gateway already serves. While we ship one bundle the gateway still hosts
11
+ * the ngrok-related logic; once the bundle splits, the gateway manages its
12
+ * own tunnel (mycadet.ai vs <assistant>.mycadet.bot) and this route is what
13
+ * the CLI-side publishers migrate onto.
14
+ */
15
+
16
+ import { z } from "zod";
17
+
18
+ import { normalizeHttpPublicBaseUrlWithoutTrailingSlash } from "@mycadet/service-contracts/ingress";
19
+
20
+ import type { ConfigFileCache } from "../../config-file-cache.js";
21
+ import { writeManagedPublicBaseUrl } from "../../tunnel/client.js";
22
+ import {
23
+ enforceLoopbackOnly,
24
+ errorResponse,
25
+ rejectBrowserOrigin,
26
+ } from "../loopback-guard.js";
27
+
28
+ const SaveIngressDomainRequestSchema = z.object({
29
+ publicBaseUrl: z.string(),
30
+ });
31
+
32
+ export async function handleSaveIngressDomain(
33
+ req: Request,
34
+ clientIp: string,
35
+ configFile: ConfigFileCache,
36
+ ): Promise<Response> {
37
+ if (req.method !== "POST") {
38
+ return new Response("method not allowed", {
39
+ status: 405,
40
+ headers: { Allow: "POST" },
41
+ });
42
+ }
43
+
44
+ const guardError = enforceLoopbackOnly(req, clientIp, "ingress-domains");
45
+ if (guardError) return guardError;
46
+ const originError = rejectBrowserOrigin(req, clientIp, "ingress-domains");
47
+ if (originError) return originError;
48
+
49
+ let body: unknown;
50
+ try {
51
+ body = await req.json();
52
+ } catch {
53
+ body = {};
54
+ }
55
+ const parsed = SaveIngressDomainRequestSchema.safeParse(body);
56
+ if (!parsed.success) {
57
+ return errorResponse("BAD_REQUEST", "publicBaseUrl is required", 400);
58
+ }
59
+ const publicUrl = normalizeHttpPublicBaseUrlWithoutTrailingSlash(
60
+ parsed.data.publicBaseUrl,
61
+ );
62
+ if (!publicUrl) {
63
+ return errorResponse(
64
+ "BAD_REQUEST",
65
+ "publicBaseUrl must be an absolute http(s) URL",
66
+ 400,
67
+ );
68
+ }
69
+
70
+ // No-op (no write) when the URL is already recorded as tunnel-managed.
71
+ await writeManagedPublicBaseUrl(publicUrl, configFile);
72
+ return Response.json({ ok: true });
73
+ }
@@ -119,7 +119,7 @@ async function checkLiveVoiceAuth(
119
119
  // the caller is *a* platform user who traversed tunnel, not that they
120
120
  // are THIS assistant's guardian, so cross-check the tunnel user id
121
121
  // against the bound platform user id (the same guardian check the
122
- // edge-auth middleware applies to guardian routes under the managed
122
+ // edge-auth middleware applies to guardian routes under the cloud
123
123
  // bypass). Without it, any tunnel-authorized org user reaching this
124
124
  // assistant would be stamped guardian downstream.
125
125
  const guardianError = await requireManagedGuardian(
@@ -127,7 +127,7 @@ async function checkLiveVoiceAuth(
127
127
  log,
128
128
  );
129
129
  if (guardianError) return { response: guardianError };
130
- // Tunnel attests a platform user, not an actor principal, so the
130
+ // Tunnel attests a cloud user, not an actor principal, so the
131
131
  // principal comes from the gateway's own binding rather than from
132
132
  // anything the request carried.
133
133
  const guardian = await findCadetGuardian();
@@ -112,7 +112,6 @@ const baseConfig: GatewayConfig = {
112
112
  runtimeMaxRetries: 2,
113
113
  runtimeProxyRequireAuth: true,
114
114
  runtimeTimeoutMs: 30000,
115
- shutdownDrainMs: 5000,
116
115
  trustProxy: false,
117
116
  };
118
117
 
@@ -79,7 +79,6 @@ function makeConfig(dir: string | undefined): GatewayConfig {
79
79
  runtimeMaxRetries: 2,
80
80
  runtimeProxyRequireAuth: true,
81
81
  runtimeTimeoutMs: 30000,
82
- shutdownDrainMs: 5000,
83
82
  trustProxy: false,
84
83
  } as GatewayConfig;
85
84
  }
@@ -452,7 +452,7 @@ async function handleSyncBytesImport(
452
452
  *
453
453
  * Returns the job's current status on 200, or 404 if the jobId is unknown
454
454
  * (never issued, or pruned after the TTL). The response shape deliberately
455
- * mirrors the platform's existing `/v1/migrations/import/:jobId/status/`
455
+ * mirrors the cloud's existing `/v1/migrations/import/:jobId/status/`
456
456
  * endpoint so `PlatformMigrationClient.pollImportStatus` works against
457
457
  * either surface with no changes.
458
458
  */
@@ -176,7 +176,7 @@ describe("upgrade gate", () => {
176
176
  });
177
177
 
178
178
  it("upgrades a cadet-signed declaration nobody approved", async () => {
179
- // Only a caller holding the platform secret can open it, and the user
179
+ // Only a caller holding the cloud secret can open it, and the user
180
180
  // extended that trust when they connected their account.
181
181
  const handle = makeHandler({
182
182
  resolve: () =>
@@ -203,7 +203,7 @@ describe("upgrade gate", () => {
203
203
  expect(upgrades).toHaveLength(1);
204
204
  });
205
205
 
206
- it("still demands the platform signature on an unapproved cadet route", async () => {
206
+ it("still demands the cloud signature on an unapproved cadet route", async () => {
207
207
  // Skipping approval did not skip authentication.
208
208
  const handle = makeHandler({
209
209
  resolve: () =>
@@ -378,7 +378,7 @@ describe("handshake signature", () => {
378
378
  expect(upgrades).toEqual([]);
379
379
  });
380
380
 
381
- it("verifies a cadet-signed route against the platform secret", async () => {
381
+ it("verifies a cadet-signed route against the cloud secret", async () => {
382
382
  const handle = makeHandler({
383
383
  resolve: () => approvedWith([{ ...ROUTE, signer: "cadet" as const }]),
384
384
  });
@@ -335,7 +335,7 @@ describe("the gate", () => {
335
335
  });
336
336
 
337
337
  it("serves a cadet-signed declaration nobody approved", async () => {
338
- // Only a caller holding the platform secret can reach it, and the user
338
+ // Only a caller holding the cloud secret can reach it, and the user
339
339
  // extended that trust when they connected their account.
340
340
  const { calls, fetchImpl } = recordingFetch();
341
341
  const handle = createPluginWebhookHandler({
@@ -364,7 +364,7 @@ describe("the gate", () => {
364
364
  expect(calls).toHaveLength(1);
365
365
  });
366
366
 
367
- it("still demands the platform signature on an unapproved cadet route", async () => {
367
+ it("still demands the cloud signature on an unapproved cadet route", async () => {
368
368
  // Skipping approval did not skip authentication.
369
369
  const { calls, fetchImpl } = recordingFetch();
370
370
  const handle = createPluginWebhookHandler({
@@ -605,7 +605,7 @@ describe("signature verification", () => {
605
605
  expect(calls).toEqual([]);
606
606
  });
607
607
 
608
- it("verifies a cadet-signed route against the platform secret", async () => {
608
+ it("verifies a cadet-signed route against the cloud secret", async () => {
609
609
  const { calls, fetchImpl } = recordingFetch();
610
610
  const handle = createPluginWebhookHandler({
611
611
  config: CONFIG,
@@ -645,7 +645,7 @@ describe("signature verification", () => {
645
645
  expect(calls).toEqual([]);
646
646
  });
647
647
 
648
- it("does not accept the platform secret on a plugin-signed route", async () => {
648
+ it("does not accept the cloud secret on a plugin-signed route", async () => {
649
649
  const { calls, fetchImpl } = recordingFetch();
650
650
  const handle = createPluginWebhookHandler({
651
651
  config: CONFIG,
@@ -826,7 +826,7 @@ describe("declared verification", () => {
826
826
  expect(calls).toEqual([]);
827
827
  });
828
828
 
829
- it("stops accepting the platform scheme once a descriptor is declared", async () => {
829
+ it("stops accepting the cloud scheme once a descriptor is declared", async () => {
830
830
  // Otherwise a declared route would have two ways in, and the weaker one
831
831
  // would decide.
832
832
  const { calls, fetchImpl } = recordingFetch();
@@ -1807,7 +1807,7 @@ describe("HMAC URL and form-encoded inbound", () => {
1807
1807
  expect(gateCalls.events).toEqual([]);
1808
1808
  });
1809
1809
 
1810
- it("verifies against the platform-injected URL when the raw URL differs", async () => {
1810
+ it("verifies against the cloud-injected URL when the raw URL differs", async () => {
1811
1811
  const { calls, fetchImpl } = recordingFetch();
1812
1812
  const handle = createPluginWebhookHandler({
1813
1813
  config: CONFIG,
@@ -1816,10 +1816,11 @@ describe("HMAC URL and form-encoded inbound", () => {
1816
1816
  fetchImpl,
1817
1817
  });
1818
1818
 
1819
- // Twilio signed the platform callback URL; the gateway sees localhost.
1819
+ // Twilio signed the cloud callback URL; the gateway sees localhost.
1820
1820
  // The proxy injects the original URL it handed the vendor, and the
1821
1821
  // signature covers that spelling rather than the one on the wire here.
1822
- const injected = "https://platform.example.test/v1/gateway/callbacks/cb-1";
1822
+ const injected =
1823
+ "https://test-cloud.mycadet.ai.test/v1/gateway/callbacks/cb-1";
1823
1824
  const params: Record<string, string> = {
1824
1825
  MessageSid: "SM9004",
1825
1826
  From: "+15555550101",
@@ -76,7 +76,7 @@ const log = getLogger("plugin-webhook");
76
76
  /**
77
77
  * Credential holding the secret that signs this route.
78
78
  *
79
- * `cadet` routes verify against the platform's own webhook secret — the
79
+ * `cadet` routes verify against the cloud's own webhook secret — the
80
80
  * same credential the email webhook uses — so a plugin that only ever hears
81
81
  * from Cadet needs no secret of its own. Everything else verifies against
82
82
  * the plugin's, stored under its own service name.
@@ -30,7 +30,7 @@ import type { GatewayConfig } from "../../config.js";
30
30
  import type { CredentialCache } from "../../credential-cache.js";
31
31
  import { credentialKey } from "../../credential-key.js";
32
32
  import { getLogger } from "../../logger.js";
33
- import { getPlatformBaseUrl } from "../../platform-url.js";
33
+ import { getCloudBaseUrl } from "../../cloud-url.js";
34
34
  import { TUNNEL_FORWARDED_HEADER } from "../../tunnel/bridge-utils.js";
35
35
  import { requestHasTunnelBridgeAuth } from "../../tunnel/bridge-auth.js";
36
36
 
@@ -172,18 +172,15 @@ export function tunnelOriginForPlatformHost(host: string): string | null {
172
172
  * assistant's key is presented to its own environment's tunnel rather than
173
173
  * prod (which would reject it with `invalid_key`).
174
174
  */
175
- async function resolveTunnelBaseUrl(
176
- config: GatewayConfig,
177
- deps: SpeechRelayDeps,
178
- ): Promise<string> {
175
+ async function resolveTunnelBaseUrl(config: GatewayConfig): Promise<string> {
179
176
  if (config.tunnelBaseUrl) {
180
177
  return config.tunnelBaseUrl;
181
178
  }
182
- const platformBaseUrl = await getPlatformBaseUrl(deps.credentials);
183
- if (platformBaseUrl) {
179
+ const cloudBaseUrl = getCloudBaseUrl();
180
+ if (cloudBaseUrl) {
184
181
  try {
185
182
  const derived = tunnelOriginForPlatformHost(
186
- new URL(platformBaseUrl).hostname,
183
+ new URL(cloudBaseUrl).hostname,
187
184
  );
188
185
  if (derived) {
189
186
  return derived;
@@ -320,7 +317,7 @@ export function createSpeechRelayUpgradeHandler(
320
317
  }
321
318
  }
322
319
  const { wsUrl, httpUrl } = tunnelSpeechUrls(
323
- await resolveTunnelBaseUrl(config, deps),
320
+ await resolveTunnelBaseUrl(config),
324
321
  operation,
325
322
  params,
326
323
  version,
@@ -334,7 +331,7 @@ export function createSpeechRelayUpgradeHandler(
334
331
  return jsonError(
335
332
  401,
336
333
  "missing_platform_connection",
337
- "no Cadet assistant API key is stored — connect the platform account",
334
+ "no Cadet assistant API key is stored — connect the cloud account",
338
335
  );
339
336
  }
340
337
  const rejection = await probeTunnel(
@@ -408,7 +405,7 @@ export function getSpeechRelayWebsocketHandlers() {
408
405
  sendRelayError(
409
406
  ws,
410
407
  "missing_platform_connection",
411
- "no Cadet assistant API key is stored — connect the platform account",
408
+ "no Cadet assistant API key is stored — connect the cloud account",
412
409
  );
413
410
  ws.close(1011, "missing_platform_connection");
414
411
  return;
@@ -9,7 +9,7 @@
9
9
  * the binding, and dictation takes any valid actor, which is why that pin is a
10
10
  * route opt-in rather than part of the shared gate.
11
11
  *
12
- * A managed assistant is reached through tunnel, which validates the browser's
12
+ * A cloud assistant is reached through tunnel, which validates the browser's
13
13
  * own token and attests the caller to the bridge; there is no edge JWT on that
14
14
  * path to validate. So a tunnel-attested caller with the bridge proof is
15
15
  * admitted here the way live voice and watch admit one, minus their guardian
@@ -72,7 +72,6 @@ const baseConfig: GatewayConfig = {
72
72
  runtimeMaxRetries: 2,
73
73
  runtimeProxyRequireAuth: true,
74
74
  runtimeTimeoutMs: 30000,
75
- shutdownDrainMs: 5000,
76
75
  trustProxy: false,
77
76
  };
78
77
 
@@ -47,7 +47,7 @@ const CreateTrustRuleRequestSchema = z.object({
47
47
  .literal("everywhere")
48
48
  .optional()
49
49
  .describe(
50
- 'Compatibility field. Trust rules apply workspace-wide: the engine matches on (tool, pattern) only, so a narrower scope cannot be honored and any value other than "everywhere" is rejected rather than stored broader than the consent it records.',
50
+ 'Compatibility field. Trust rules apply workspace-wide: the engine matches on tool and pattern only, so a narrower scope cannot be honored and any value other than "everywhere" is rejected rather than stored broader than the consent it records.',
51
51
  ),
52
52
  });
53
53
 
@@ -116,7 +116,7 @@ export const ROUTES: GatewayRouteDefinition[] = [
116
116
  queryParameters: [
117
117
  {
118
118
  name: "origin",
119
- description: "Filter by origin (default | user_defined)",
119
+ description: "Filter by origin: default or user_defined",
120
120
  },
121
121
  { name: "tool", description: "Filter by tool name" },
122
122
  {
@@ -14,7 +14,7 @@
14
14
 
15
15
  import type { GatewayConfig } from "../../config.js";
16
16
  import { getLogger } from "../../logger.js";
17
- import { resolvePlatformAssistantIdOrUndefined } from "../../platform-identity.js";
17
+ import { resolveCloudAssistantIdOrUndefined } from "../../cloud-identity.js";
18
18
  import {
19
19
  CircuitBreakerOpenError,
20
20
  forwardTwilioVoiceWebhook,
@@ -239,7 +239,7 @@ async function forwardToAssistant(
239
239
  caches?: TwilioValidationCaches,
240
240
  ): Promise<Response> {
241
241
  try {
242
- const platformAssistantId = await resolvePlatformAssistantIdOrUndefined();
242
+ const cloudAssistantId = await resolveCloudAssistantIdOrUndefined();
243
243
  const runtimeResponse = await forwardTwilioVoiceWebhook(
244
244
  config,
245
245
  params,
@@ -247,7 +247,7 @@ async function forwardToAssistant(
247
247
  resolvePublicBaseWssUrl(
248
248
  config,
249
249
  caches?.configFile,
250
- platformAssistantId,
250
+ cloudAssistantId,
251
251
  validatedPublicUrl,
252
252
  ),
253
253
  );
@@ -112,7 +112,6 @@ const baseConfig: GatewayConfig = {
112
112
  runtimeMaxRetries: 2,
113
113
  runtimeProxyRequireAuth: true,
114
114
  runtimeTimeoutMs: 30000,
115
- shutdownDrainMs: 5000,
116
115
  trustProxy: false,
117
116
  };
118
117
 
@@ -392,7 +391,7 @@ describe("resolvePublicBaseWssUrl", () => {
392
391
  expect(resolvePublicBaseWssUrl(baseConfig)).toBeUndefined();
393
392
  });
394
393
 
395
- test("uses tunnelBaseUrl + platformAssistantId when both are present", () => {
394
+ test("uses tunnelBaseUrl + cloudAssistantId when both are present", () => {
396
395
  const config = {
397
396
  ...baseConfig,
398
397
  tunnelBaseUrl: "https://tunnel-dev.mycadet.ai",
@@ -428,7 +427,7 @@ describe("resolvePublicBaseWssUrl", () => {
428
427
  );
429
428
  });
430
429
 
431
- test("falls back to configFile publicBaseUrl when platformAssistantId is missing", () => {
430
+ test("falls back to configFile publicBaseUrl when cloudAssistantId is missing", () => {
432
431
  const config = {
433
432
  ...baseConfig,
434
433
  tunnelBaseUrl: "https://tunnel-dev.mycadet.ai",
@@ -483,7 +482,7 @@ describe("resolvePublicBaseWssUrl", () => {
483
482
  );
484
483
  });
485
484
 
486
- test("does not use a platform callback URL as the websocket base", () => {
485
+ test("does not use a cloud callback URL as the websocket base", () => {
487
486
  const config = {
488
487
  ...baseConfig,
489
488
  tunnelBaseUrl: "https://tunnel-staging.mycadet.ai",
@@ -2,7 +2,7 @@ import { LOCAL_ASSISTANT_ID } from "../../assistant-id.js";
2
2
  import type { ConfigFileCache } from "../../config-file-cache.js";
3
3
  import type { GatewayConfig } from "../../config.js";
4
4
  import { getLogger } from "../../logger.js";
5
- import { resolvePlatformAssistantIdOrUndefined } from "../../platform-identity.js";
5
+ import { resolveCloudAssistantIdOrUndefined } from "../../cloud-identity.js";
6
6
  import { resolveAdmissionPolicy } from "../../risk/admission-policy-cache.js";
7
7
  import {
8
8
  CircuitBreakerOpenError,
@@ -204,7 +204,7 @@ export function createTwilioVoiceWebhookHandler(
204
204
  }
205
205
 
206
206
  try {
207
- const platformAssistantId = await resolvePlatformAssistantIdOrUndefined();
207
+ const platformCloudId = await resolveCloudAssistantIdOrUndefined();
208
208
  const runtimeResponse = await forwardTwilioVoiceWebhook(
209
209
  config,
210
210
  params,
@@ -212,7 +212,7 @@ export function createTwilioVoiceWebhookHandler(
212
212
  resolvePublicBaseWssUrl(
213
213
  config,
214
214
  caches?.configFile,
215
- platformAssistantId,
215
+ platformCloudId,
216
216
  validation.validatedCandidateUrl,
217
217
  ),
218
218
  );
@@ -91,7 +91,6 @@ const baseConfig: GatewayConfig = {
91
91
  runtimeMaxRetries: 2,
92
92
  runtimeProxyRequireAuth: true,
93
93
  runtimeTimeoutMs: 30000,
94
- shutdownDrainMs: 5000,
95
94
  trustProxy: false,
96
95
  };
97
96