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_roleJWT claim stamped by the Firebase Blocking Function - Scope every operation to a tenant by reading the
x-tenant-idheader 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-hostchild processes viaLoopManager - Expose Google Cloud OAuth2 callback routes for GCP delegation flows
Key Components
| File | Role |
|---|---|
src/server.ts | Hono application wiring — OTEL init, route registration, createYoga(), server start |
src/schema/builder.ts | Pothos SchemaBuilder with Context type and custom scalars (DateTime, JSON, UUID) |
src/schema/index.ts | Side-effect imports that register all type definitions against the builder |
src/schema/*.ts | Domain-scoped type modules: iam, tenants, users, kernels, resources, kms-keys, reports, infrastructure, gcp-discovery, bootstrap, chat, platform |
src/context.ts | createContextFactory — resolves platform, db, tenantId, actorSub, actorRoleClaim per request |
src/services/LoopManager.ts | Lifecycle manager for per-tenant agent-loop child processes |
src/routes/agent-loop.ts | Proxy routes for streaming agent chat to loop-host |
src/routes/gcp-auth.ts | GCP OAuth2 callback handler for cloud project delegation |
GraphQL Contract
The full SDL is committed at services/gateway/schema.graphql.
To regenerate after schema changes:
pnpm --filter @mnemose/gateway schema:exportCI 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 ContextScalability
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.