Skip to main content

Overview

Port: 4000 The Gateway is the central entry point for all frontend requests. It routes to backend services via HTTP proxy and is completely stateless — JWT signature is verified in middleware (verifyJwt) and context is passed via headers to downstream services.

Proxy Routes

The public API /v1/* routes (e.g. POST /v1/agents:from-url, GET /v1/agents/builds/:buildId, POST /v1/chat, GET /v1/contacts/export) use API-key authentication (Bearer API key, validated via the auth service) rather than JWT, and proxy to the bot service’s agent-build, chat, and contact-export routes. GET /v1/contacts/export requires the contacts:read scope and is streamed via a raw pipe (no buffered timeout).
When adding new backend routes, you must add the corresponding proxy route in services/gateway/src/routes/proxy.ts. Without this, the frontend cannot reach your new endpoint.

Auth Middleware

The gateway handles JWT authentication for all proxied requests:
1

Extract Token

JWT is extracted from the Authorization: Bearer header or the access_token cookie.
2

Verify Signature

The gateway verifies the JWT signature with the shared secret:
3

Validate Membership

Validates that the user has at least one organization membership.
4

Attach Auth Context

Sets request.auth with: userId, organizationId, email, platformRole, orgRole.
5

Forward Headers

Forwards auth context to downstream services as headers:
  • user-id
  • organization-id
  • user-email
  • x-platform-role
  • x-org-role
The gateway verifies the JWT signature; downstream services trust forwarded headers. The gateway is the trust boundary — it is the only public ingress, and downstream service ports (4001–4006) are not exposed to the internet.

Every proxy scope declares its gate

Each route group registered in proxy.ts either runs authMiddleware or carries an // auth-exempt: <reason> comment directly above it (the auth-service lane, public catalogue routes, provider webhooks, the optional-auth /public/* lane, and similar). The gateway test proxy-scope-auth-guard.test.ts fails the build when a new scope has neither. When you add a route group, decide which it is and say so in the code.

Dot segments are refused

The gateway answers 400 Bad Request for any request path containing a . or .. segment (including %2e spellings), a backslash, or a tab/newline, before any scope’s auth runs. Upstream URLs are built by concatenating the client’s path onto a service origin, and fetch removes dot segments — so such a path would otherwise be routed by one scope and served by the upstream as a different route. Query strings are not inspected.

Plugins

Configuration Variables

Critical Patterns

The gateway follows AUTHENTICATION_PATTERN.md — the JWT signature is verified with the shared secret (verifyJwt(token, config.JWT_SECRET)). Downstream services trust requests that come through the gateway.
Auth context is passed via HTTP headers to downstream services, not via cookies. Services read request.user from their own JWT middleware that parses these headers.
Custom parser passes raw buffer for multipart uploads. The realtime-audio service has a 50MB upload limit for voice samples.
When a downstream service is unavailable, the gateway returns 503 with a descriptive error message.
Headers like host, connection, and transfer-encoding are filtered before forwarding to downstream services.