Skip to content

gateway — Architecture

The gateway service is the always-on GraphQL, WebSocket, and MCP ingress for Mnemose. All external clients — the console, external integrators, and MCP-capable tools — communicate through this single entry point.

Responsibilities

  • Serve the GraphQL API via graphql-yoga with WebSocket subscriptions over graphql-ws
  • Authenticate requests using Firebase ID tokens; validate the custom mnemose_role JWT claim stamped by the Firebase Blocking Function
  • Scope every operation to a tenant by reading the x-tenant-id header and resolving the caller’s role from the database
  • Expose an MCP HTTP/SSE endpoint for tool-calling clients
  • Proxy long-lived agent-loop requests to the per-tenant loop-host child processes via LoopManager
  • Expose Google Cloud OAuth2 callback routes for GCP delegation flows

Key Components

FileRole
src/server.tsHono application wiring — OTEL init, route registration, createYoga(), server start
src/schema/builder.tsPothos SchemaBuilder with Context type and custom scalars (DateTime, JSON, UUID)
src/schema/index.tsSide-effect imports that register all type definitions against the builder
src/schema/*.tsDomain-scoped type modules: iam, tenants, users, kernels, resources, kms-keys, reports, infrastructure, gcp-discovery, bootstrap, chat, platform
src/context.tscreateContextFactory — resolves platform, db, tenantId, actorSub, actorRoleClaim per request
src/services/LoopManager.tsLifecycle manager for per-tenant agent-loop child processes
src/routes/agent-loop.tsProxy routes for streaming agent chat to loop-host
src/routes/gcp-auth.tsGCP OAuth2 callback handler for cloud project delegation

GraphQL Contract

The full SDL is committed at services/gateway/schema.graphql.

To regenerate after schema changes:

Terminal window
pnpm --filter @mnemose/gateway schema:export

CI rejects PRs that change the SDL without updating the committed file.

Authentication Flow

Client → Gateway (Hono)
↓
Firebase Admin verifyIdToken()
↓ success
Extract claims: uid, email, mnemose_role
↓
Auth-guard resolves effective role from DB (first query only; cached on Context)
↓
Resolver executes with fully-populated Context

Scalability

The gateway is stateless except for the LoopManager child-process registry. In production Cloud Run, each instance maintains its own registry; the loop-host processes are ephemeral and re-spawned on demand.