@vellumai/vellum-gateway 0.4.6 → 0.4.7

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/ARCHITECTURE.md CHANGED
@@ -88,6 +88,9 @@ Guardian verification endpoints are exposed directly by the gateway and forwarde
88
88
  | POST | `/v1/integrations/guardian/outbound/start` |
89
89
  | POST | `/v1/integrations/guardian/outbound/resend` |
90
90
  | POST | `/v1/integrations/guardian/outbound/cancel` |
91
+ | POST | `/v1/integrations/guardian/vellum/refresh` |
92
+
93
+ The `/vellum/refresh` endpoint is the only public ingress for rotating actor + refresh token credentials. Clients must call this through the gateway; the runtime endpoint is not directly exposed. The gateway validates the caller's bearer token and forwards to the runtime, which handles refresh token validation, rotation, and replay detection (see [`assistant/ARCHITECTURE.md`](../assistant/ARCHITECTURE.md) for refresh token lifecycle details).
91
94
 
92
95
  **Authentication boundary:**
93
96
 
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@vellumai/vellum-gateway",
3
- "version": "0.4.6",
3
+ "version": "0.4.7",
4
4
  "type": "module",
5
+ "exports": {
6
+ "./twilio/verify": "./src/twilio/verify.ts",
7
+ "./package.json": "./package.json"
8
+ },
5
9
  "scripts": {
6
10
  "dev": "bun run --watch src/index.ts",
7
11
  "dev:proxy": "GATEWAY_RUNTIME_PROXY_ENABLED=true GATEWAY_RUNTIME_PROXY_REQUIRE_AUTH=false bun run --watch src/index.ts",
@@ -107,5 +107,13 @@ export function createGuardianControlPlaneProxyHandler(config: GatewayConfig) {
107
107
  async handleCancelGuardianOutbound(req: Request): Promise<Response> {
108
108
  return proxyToRuntime(req, "/v1/integrations/guardian/outbound/cancel", "");
109
109
  },
110
+
111
+ async handleGuardianVellumBootstrap(req: Request): Promise<Response> {
112
+ return proxyToRuntime(req, "/v1/integrations/guardian/vellum/bootstrap", "");
113
+ },
114
+
115
+ async handleGuardianRefresh(req: Request): Promise<Response> {
116
+ return proxyToRuntime(req, "/v1/integrations/guardian/vellum/refresh", "");
117
+ },
110
118
  };
111
119
  }
package/src/index.ts CHANGED
@@ -522,6 +522,13 @@ function main() {
522
522
  }
523
523
  }
524
524
 
525
+ // ── Guardian vellum bootstrap (actor token) ──
526
+ if (url.pathname === "/v1/integrations/guardian/vellum/bootstrap" && req.method === "POST") {
527
+ const authError = requireRuntimeBearerAuth();
528
+ if (authError) return authError;
529
+ return guardianControlPlaneProxy.handleGuardianVellumBootstrap(tracedReq);
530
+ }
531
+
525
532
  // ── Guardian verification control-plane proxy ──
526
533
  if (
527
534
  (url.pathname === "/v1/integrations/guardian/challenge" && req.method === "POST")
@@ -548,6 +555,13 @@ function main() {
548
555
  return guardianControlPlaneProxy.handleCancelGuardianOutbound(tracedReq);
549
556
  }
550
557
 
558
+ // ── Guardian vellum refresh proxy ──
559
+ if (url.pathname === "/v1/integrations/guardian/vellum/refresh" && req.method === "POST") {
560
+ const authError = requireRuntimeBearerAuth();
561
+ if (authError) return authError;
562
+ return guardianControlPlaneProxy.handleGuardianRefresh(tracedReq);
563
+ }
564
+
551
565
  // ── Twilio integration control-plane proxy ──
552
566
  if (
553
567
  (url.pathname === "/v1/integrations/twilio/config" && req.method === "GET")
@@ -1,7 +1,7 @@
1
1
  import { createHmac, timingSafeEqual } from "node:crypto";
2
2
 
3
3
  /**
4
- * Validates a Twilio X-Twilio-Signature header using HMAC-SHA1.
4
+ * Compute a Twilio-compatible HMAC-SHA1 signature.
5
5
  *
6
6
  * Algorithm (from Twilio docs):
7
7
  * 1. Take the full URL of the request.
@@ -9,25 +9,35 @@ import { createHmac, timingSafeEqual } from "node:crypto";
9
9
  * 3. Concatenate the URL with each key-value pair (key + value, no delimiters).
10
10
  * 4. HMAC-SHA1 the result using the auth token as the key.
11
11
  * 5. Base64-encode the hash.
12
- * 6. Compare to the X-Twilio-Signature header value.
13
12
  */
14
- export function verifyTwilioSignature(
13
+ export function computeTwilioSignature(
15
14
  url: string,
16
15
  params: Record<string, string>,
17
- signature: string,
18
16
  authToken: string,
19
- ): boolean {
17
+ ): string {
20
18
  const sortedKeys = Object.keys(params).sort();
21
19
  let data = url;
22
20
  for (const key of sortedKeys) {
23
21
  data += key + params[key];
24
22
  }
25
23
 
26
- const computed = createHmac("sha1", authToken)
24
+ return createHmac("sha1", authToken)
27
25
  .update(data)
28
26
  .digest("base64");
27
+ }
28
+
29
+ /**
30
+ * Verify a Twilio X-Twilio-Signature header using HMAC-SHA1 with
31
+ * constant-time comparison to prevent timing attacks.
32
+ */
33
+ export function verifyTwilioSignature(
34
+ url: string,
35
+ params: Record<string, string>,
36
+ signature: string,
37
+ authToken: string,
38
+ ): boolean {
39
+ const computed = computeTwilioSignature(url, params, authToken);
29
40
 
30
- // Constant-time comparison to prevent timing attacks
31
41
  const a = Buffer.from(computed);
32
42
  const b = Buffer.from(signature);
33
43
  if (a.length !== b.length) return false;