@vellumai/vellum-gateway 0.4.45 → 0.4.46
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/README.md +37 -78
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Vellum Gateway
|
|
2
2
|
|
|
3
|
-
Standalone service that serves as the public ingress boundary for all external webhooks and callbacks. It owns Telegram integration end-to-end, routes Twilio voice
|
|
3
|
+
Standalone service that serves as the public ingress boundary for all external webhooks and callbacks. It owns Telegram integration end-to-end, routes Twilio voice webhooks, handles OAuth callbacks, and optionally acts as an authenticated reverse proxy for the assistant runtime.
|
|
4
4
|
|
|
5
5
|
## Architecture
|
|
6
6
|
|
|
@@ -12,7 +12,7 @@ Client → gateway/ (Bearer auth) → Assistant Runtime (any path)
|
|
|
12
12
|
|
|
13
13
|
The web app is **not** in the Telegram request path. When proxy mode is enabled, non-Telegram requests are forwarded to the assistant runtime with optional bearer token authentication.
|
|
14
14
|
|
|
15
|
-
For ingress and channel architecture details, see [`ARCHITECTURE.md`](ARCHITECTURE.md)
|
|
15
|
+
For ingress and channel architecture details, see [`ARCHITECTURE.md`](ARCHITECTURE.md).
|
|
16
16
|
|
|
17
17
|
## Setup
|
|
18
18
|
|
|
@@ -47,9 +47,8 @@ bun run dev
|
|
|
47
47
|
| `GATEWAY_MAX_WEBHOOK_PAYLOAD_BYTES` | No | `1048576` | Max inbound webhook payload size (rejects with 413) |
|
|
48
48
|
| `GATEWAY_MAX_ATTACHMENT_BYTES` | No | `20971520` | Max single attachment size (oversized are skipped) |
|
|
49
49
|
| `GATEWAY_MAX_ATTACHMENT_CONCURRENCY` | No | `3` | Max concurrent attachment download/upload operations |
|
|
50
|
-
| `TWILIO_ACCOUNT_SID` | No | — | Twilio Account SID for
|
|
51
|
-
| `TWILIO_AUTH_TOKEN` | No | — | Twilio Auth Token for HMAC-SHA1 webhook signature validation
|
|
52
|
-
| `TWILIO_PHONE_NUMBER` | No | — | Twilio phone number (E.164) used as the `From` for outbound SMS |
|
|
50
|
+
| `TWILIO_ACCOUNT_SID` | No | — | Twilio Account SID for voice integration |
|
|
51
|
+
| `TWILIO_AUTH_TOKEN` | No | — | Twilio Auth Token for HMAC-SHA1 webhook signature validation |
|
|
53
52
|
|
|
54
53
|
Channel operational settings (Telegram API base URL, timeouts, deliver auth bypass flags) are managed via `workspace/config.json` through `ConfigFileCache`. See the channel-specific sections in `ARCHITECTURE.md` for details.
|
|
55
54
|
|
|
@@ -57,10 +56,9 @@ Channel operational settings (Telegram API base URL, timeouts, deliver auth bypa
|
|
|
57
56
|
|
|
58
57
|
v1 uses deterministic settings-based routing (no database):
|
|
59
58
|
|
|
60
|
-
1. **
|
|
61
|
-
2. **
|
|
62
|
-
3. **
|
|
63
|
-
4. **Unmapped policy** — `reject` (drop with message) or `default` (forward to `GATEWAY_DEFAULT_ASSISTANT_ID`)
|
|
59
|
+
1. **conversation_id match** — explicit `conversation:<conversation_id>` entry in routing JSON
|
|
60
|
+
2. **actor_id match** — explicit `actor:<actor_id>` entry in routing JSON
|
|
61
|
+
3. **Unmapped policy** — `reject` (drop with message) or `default` (forward to `GATEWAY_DEFAULT_ASSISTANT_ID`)
|
|
64
62
|
|
|
65
63
|
### Routing JSON format
|
|
66
64
|
|
|
@@ -102,7 +100,7 @@ The `/webhooks/twilio/voice` endpoint handles both outbound and inbound voice ca
|
|
|
102
100
|
|
|
103
101
|
### Inbound voice routing
|
|
104
102
|
|
|
105
|
-
When the voice webhook is called without a `callSessionId` query parameter, the gateway treats it as an inbound call and resolves the assistant using the
|
|
103
|
+
When the voice webhook is called without a `callSessionId` query parameter, the gateway treats it as an inbound call and resolves the assistant using the standard routing chain:
|
|
106
104
|
|
|
107
105
|
1. **`resolveAssistantByPhoneNumber(config, To)`** — Reverse lookup of the inbound `To` number against `assistantPhoneNumbers`. If the dialed number matches an assistant's configured phone number, that assistant handles the call.
|
|
108
106
|
2. **Fallback to `resolveAssistant(From, From)`** — If no phone number match is found, the standard routing chain is used: `conversation_id` match, `actor_id` match, then the unmapped policy.
|
|
@@ -120,35 +118,6 @@ Caller → Twilio → Gateway /webhooks/twilio/voice (no callSessionId)
|
|
|
120
118
|
→ RelayConnection detects inbound (`initiatedFromConversationId == null`), optional guardian verification gate, then receptionist-style LLM greeting
|
|
121
119
|
```
|
|
122
120
|
|
|
123
|
-
## SMS Ingress (Twilio)
|
|
124
|
-
|
|
125
|
-
The `/webhooks/twilio/sms` endpoint receives inbound SMS messages from Twilio. On each request:
|
|
126
|
-
|
|
127
|
-
1. **Signature validation** — The `X-Twilio-Signature` header is validated using HMAC-SHA1 with the `TWILIO_AUTH_TOKEN`. When behind a tunnel or reverse proxy, the gateway reconstructs the canonical request URL from the ingress public base URL (read via `ConfigFileCache` from `ingress.publicBaseUrl` in workspace config, falling back to the `INGRESS_PUBLIC_BASE_URL` env var) for validation.
|
|
128
|
-
2. **MessageSid dedup** — Each `MessageSid` is tracked in an in-memory dedup cache. Duplicate webhook deliveries (Twilio retries) are silently accepted without re-forwarding.
|
|
129
|
-
3. **MMS detection** — The gateway treats a message as MMS when any of the following conditions are met: `NumMedia > 0`, any `MediaUrl<N>` key has a non-empty value, or any `MediaContentType<N>` key has a non-empty value. This catches media attachments even when Twilio omits `NumMedia`. The gateway replies with an unsupported notice ("MMS is not supported yet") and does not forward the payload to the runtime.
|
|
130
|
-
4. **`/new` command** — When the message body is exactly `/new` (case-insensitive, trimmed), the gateway resolves routing first. If routing is rejected, the gateway sends a rejection notice SMS to the sender (matching Telegram rejection semantics) and does not forward the message. If routing succeeds, the gateway resets the conversation via the runtime API and sends a confirmation SMS. The message is never forwarded to the runtime.
|
|
131
|
-
5. **Normalization** — The form-encoded Twilio payload is normalized into a `GatewayInboundEvent` with `sourceChannel: "sms"`. The sender's phone number (`From`) is used as both `conversationExternalId` and `actorExternalId`.
|
|
132
|
-
6. **Routing** — Phone-number-based routing is checked first: the `To` number is looked up in `assistantPhoneNumbers` to find the target assistant. If no match, the standard routing chain (conversation_id -> actor_id -> default/reject) is used.
|
|
133
|
-
7. **Forwarding** — The event is forwarded to the runtime via `POST /channels/inbound` with SMS-specific transport hints (`chat-first-medium`, `sms-character-limits`, etc.) and a `replyCallbackUrl` pointing to `/deliver/sms`.
|
|
134
|
-
|
|
135
|
-
SMS is text-only in v1 — MMS payloads are explicitly rejected with a user-facing notice.
|
|
136
|
-
|
|
137
|
-
## SMS Deliver Endpoint Security
|
|
138
|
-
|
|
139
|
-
The `/deliver/sms` endpoint requires the same fail-closed bearer auth as `/deliver/telegram`:
|
|
140
|
-
|
|
141
|
-
| Condition | Result |
|
|
142
|
-
| ------------------------------------------------------------------------------------ | -------------------------- |
|
|
143
|
-
| Bearer token configured + valid `Authorization` header | Request allowed |
|
|
144
|
-
| Bearer token configured + missing/invalid `Authorization` header | 401 Unauthorized |
|
|
145
|
-
| No bearer token configured + `sms.deliverAuthBypass=true` in `workspace/config.json` | Request allowed (dev-only) |
|
|
146
|
-
| No bearer token configured + bypass not set | 503 Service Not Configured |
|
|
147
|
-
|
|
148
|
-
The endpoint also requires `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN`, and `TWILIO_PHONE_NUMBER` to be configured. If any are missing, requests return `503 SMS integration not configured`.
|
|
149
|
-
|
|
150
|
-
Outbound SMS is sent via the Twilio Messages API using the configured `TWILIO_PHONE_NUMBER` as the `From` number. The request body accepts either `{ to, text }` or `{ chatId, text }` — `chatId` is an alias for `to`, allowing the runtime channel callback (which sends `{ chatId, text }`) to work without translation. When both `to` and `chatId` are provided, `to` takes precedence.
|
|
151
|
-
|
|
152
121
|
## Callback Query Handling
|
|
153
122
|
|
|
154
123
|
The gateway normalizes Telegram `callback_query` updates (inline button clicks) into the same `GatewayInboundEvent` format used for regular messages. When a `callback_query` is present in the webhook payload, the normalizer extracts:
|
|
@@ -201,36 +170,34 @@ This can be sent as an action-only payload (without `text` or `attachments`) whe
|
|
|
201
170
|
|
|
202
171
|
The gateway serves as the single public ingress point for all external callbacks. The following routes are handled directly by the gateway before any proxy forwarding:
|
|
203
172
|
|
|
204
|
-
| Route | Method | Description
|
|
205
|
-
| ------------------------------------------ | --------------- |
|
|
206
|
-
| `/webhooks/telegram` | POST | Telegram bot webhook (validated via `TELEGRAM_WEBHOOK_SECRET`)
|
|
207
|
-
| `/deliver/telegram` | POST | Internal endpoint for the assistant runtime to deliver outbound messages/attachments to Telegram chats
|
|
208
|
-
| `/webhooks/twilio/voice` | POST | Twilio voice webhook (validated via HMAC-SHA1 signature)
|
|
209
|
-
| `/webhooks/twilio/status` | POST | Twilio status callback (validated via HMAC-SHA1 signature)
|
|
210
|
-
| `/webhooks/twilio/connect-action` | POST | Twilio connect-action callback (validated via HMAC-SHA1 signature)
|
|
211
|
-
| `/webhooks/twilio/relay` | WS | Twilio ConversationRelay WebSocket (bidirectional proxy to runtime, requires `callSessionId` query param)
|
|
212
|
-
| `/webhooks/
|
|
213
|
-
| `/
|
|
214
|
-
| `/
|
|
215
|
-
| `/v1/channel-verification-sessions`
|
|
216
|
-
| `/v1/channel-verification-sessions`
|
|
217
|
-
| `/v1/channel-verification-sessions/
|
|
218
|
-
| `/v1/
|
|
219
|
-
| `/v1/
|
|
220
|
-
| `/v1/integrations/telegram/
|
|
221
|
-
| `/v1/
|
|
222
|
-
| `/v1/
|
|
223
|
-
| `/v1/contacts`
|
|
224
|
-
| `/v1/
|
|
225
|
-
| `/v1/contacts/
|
|
226
|
-
| `/v1/
|
|
227
|
-
| `/v1/contacts/invites`
|
|
228
|
-
| `/v1/
|
|
229
|
-
| `/
|
|
230
|
-
| `/
|
|
231
|
-
| `/
|
|
232
|
-
| `/readyz` | GET | Readiness probe |
|
|
233
|
-
| `/schema` | GET | Returns the OpenAPI 3.1 schema for this gateway |
|
|
173
|
+
| Route | Method | Description |
|
|
174
|
+
| ------------------------------------------ | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
175
|
+
| `/webhooks/telegram` | POST | Telegram bot webhook (validated via `TELEGRAM_WEBHOOK_SECRET`) |
|
|
176
|
+
| `/deliver/telegram` | POST | Internal endpoint for the assistant runtime to deliver outbound messages/attachments to Telegram chats |
|
|
177
|
+
| `/webhooks/twilio/voice` | POST | Twilio voice webhook (validated via HMAC-SHA1 signature) |
|
|
178
|
+
| `/webhooks/twilio/status` | POST | Twilio status callback (validated via HMAC-SHA1 signature) |
|
|
179
|
+
| `/webhooks/twilio/connect-action` | POST | Twilio connect-action callback (validated via HMAC-SHA1 signature) |
|
|
180
|
+
| `/webhooks/twilio/relay` | WS | Twilio ConversationRelay WebSocket (bidirectional proxy to runtime, requires `callSessionId` query param) |
|
|
181
|
+
| `/webhooks/oauth/callback` | GET | OAuth2 callback endpoint — receives authorization codes from OAuth providers (Google, Slack, etc.) and forwards them to the assistant runtime |
|
|
182
|
+
| `/v1/channel-verification-sessions` | POST | Authenticated control-plane proxy for creating verification sessions (inbound challenge or outbound verification) |
|
|
183
|
+
| `/v1/channel-verification-sessions` | DELETE | Authenticated control-plane proxy for cancelling active verification sessions |
|
|
184
|
+
| `/v1/channel-verification-sessions/resend` | POST | Authenticated control-plane proxy for resending outbound verification code |
|
|
185
|
+
| `/v1/channel-verification-sessions/status` | GET | Authenticated control-plane proxy for verification binding status |
|
|
186
|
+
| `/v1/channel-verification-sessions/revoke` | POST | Authenticated control-plane proxy for revoking verification binding (cancels sessions and removes binding) |
|
|
187
|
+
| `/v1/integrations/telegram/config` | GET/POST/DELETE | Authenticated control-plane proxy for Telegram integration config |
|
|
188
|
+
| `/v1/integrations/telegram/commands` | POST | Authenticated control-plane proxy for Telegram command registration |
|
|
189
|
+
| `/v1/integrations/telegram/setup` | POST | Authenticated control-plane proxy for Telegram setup orchestration |
|
|
190
|
+
| `/v1/contacts` | GET/POST | Authenticated control-plane proxy for listing/searching and creating/updating contacts |
|
|
191
|
+
| `/v1/contacts/:id` | GET | Authenticated control-plane proxy for retrieving a contact by ID |
|
|
192
|
+
| `/v1/contacts/merge` | POST | Authenticated control-plane proxy for merging two contacts |
|
|
193
|
+
| `/v1/contact-channels/:contactChannelId` | PATCH | Authenticated control-plane proxy for updating a contact channel's status/policy |
|
|
194
|
+
| `/v1/contacts/invites` | GET/POST | Authenticated control-plane proxy for listing/creating contact invites |
|
|
195
|
+
| `/v1/contacts/invites/:id` | DELETE | Authenticated control-plane proxy for revoking a contact invite |
|
|
196
|
+
| `/v1/contacts/invites/redeem` | POST | Authenticated control-plane proxy for redeeming a contact invite |
|
|
197
|
+
| `/v1/health` | GET | Authenticated runtime health proxy (`/v1/health` on runtime) |
|
|
198
|
+
| `/healthz` | GET | Liveness probe |
|
|
199
|
+
| `/readyz` | GET | Readiness probe |
|
|
200
|
+
| `/schema` | GET | Returns the OpenAPI 3.1 schema for this gateway |
|
|
234
201
|
|
|
235
202
|
### Tunnel Setup
|
|
236
203
|
|
|
@@ -270,15 +237,7 @@ The assistant runtime uses this URL to construct all webhook and OAuth callback
|
|
|
270
237
|
|
|
271
238
|
## Ingress Boundary Guarantees
|
|
272
239
|
|
|
273
|
-
The gateway is the **sole public ingress point** for all external webhooks
|
|
274
|
-
|
|
275
|
-
### SMS Ingress
|
|
276
|
-
|
|
277
|
-
Inbound SMS follows the same gateway-only pattern as voice and Telegram:
|
|
278
|
-
|
|
279
|
-
1. **Twilio → Gateway** (`/webhooks/twilio/sms`) — Gateway validates `X-Twilio-Signature` using HMAC-SHA1 with the configured `TWILIO_AUTH_TOKEN`.
|
|
280
|
-
2. **Gateway → Runtime** (`/v1/channels/inbound`) — Gateway forwards the normalized event to the runtime with JWT bearer auth.
|
|
281
|
-
3. **Runtime rejects direct SMS webhooks** — Any direct POST to `/webhooks/twilio/sms` or `/v1/calls/twilio/sms` on the runtime returns `410 GATEWAY_ONLY`.
|
|
240
|
+
The gateway is the **sole public ingress point** for all external webhooks. The assistant runtime never directly accepts public webhook traffic — all Twilio and Telegram webhook routes on the runtime return `410 GATEWAY_ONLY` when accessed directly.
|
|
282
241
|
|
|
283
242
|
### Signature URL Tightening
|
|
284
243
|
|