Multi-tenant AI app platform
One Go + TypeScript backend serving several consumer AI apps, isolated per tenant, shipped with blue-green deploys.

- Lead developer
- Dec 2025 – May 2026
TL;DR
- One backend platform for several consumer apps: a Go API and a TypeScript AI service shared by every app, with each tenant's data isolated by app ID.
- AI as a shared service: multi-provider LLM chat (OpenAI, Gemini, xAI) with RAG over pgvector, conversation memory, SSE streaming, and token and coin billing.
- Shipped with blue-green CI/CD on GitHub Actions: build, verify, health-check the idle slot, flip nginx, and roll back automatically on failure.
- 2
- 3
- 289
Follow a tenant request · ship it
Companion app
Expo · Next.js
App ID
Auth
Guards
AI routes
AI service
A companion-app user sends a chat message
The app calls the shared backend with its own tenant ID in the X-App-ID header.
POST /api/v1/... chat X-App-ID: <companion app uuid>
The problem
Every new consumer app, whether an AI character chat app or an Android payments app, needs the same plumbing: users and auth, subscriptions and plans, rate limits, notifications, AI billing, observability and a safe way to deploy. Rebuilding it per app is slow and multiplies the places things break.
This platform builds it once. Apps plug in as tenants: they share the common modules and the AI service, add their own routes on top, and can never see each other's data.
Lead developer (about 80% of 289 commits): the multi-tenant Go platform, the TypeScript AI service, and the CI/CD pipeline and server setup.
How it works
Tenant from the first byte
Every request carries X-App-ID. Middleware validates it against the apps table (cached in Redis), stores it in the request context, and rejects requests without one; only global admin routes are exempt.
Isolation in the schema
Every table has an indexed app_id (except the root apps table and global admins), UUID keys and soft deletes, and every query filters by app_id. PostgreSQL through GORM, with SQL migrations.
Shared modules, app-specific routes
Users, auth, subscriptions and notifications live once in common modules; each app (an AI companion app, NotifyPay) adds its own routes, handlers and YAML config on top. A new app is a new tenant, not a new backend.
A middleware chain every app gets
Gin middleware for request IDs, OpenTelemetry tracing, logging tagged with the app ID, timeouts, panic recovery, JWT auth with sessions, Redis-backed distributed rate limiting with an in-memory fallback, and plan checks against cached subscription state.
An AI service in TypeScript
Express with LangChain.js and LangGraph orchestrates OpenAI, Gemini and xAI models: RAG over documents stored in pgvector, memory extracted from conversations, replies streamed over SSE, and token quotas and coins enforced with Redis caching.
Observable, and shuts down cleanly
OpenTelemetry traces, metrics and logs go to SigNoz, and every component stops in an ordered graceful shutdown.
Blue-green CI/CD
GitHub Actions builds a static arm64 Go binary and the TypeScript service, uploads both into the idle slot with configs and migrations (verifying each upload), starts it, health-checks it, flips the current symlink and the nginx upstream, and rolls back automatically if anything fails. Dev deploys on every push to the dev branch; production deploys on demand.
Key decisions
One shared schema with app_id on every row
One migration path and one connection pool serve every app, with isolation enforced at the door by middleware and in every query.
Go for the platform, TypeScript for AI
Each side uses the ecosystem it needs: a fast, typed Go core for the API, and TypeScript for LangChain.js, LangGraph and the provider SDKs.
Blue-green deploys behind a health-gated switch
The new version runs and proves itself on the idle slot before nginx sends it a single request, and the previous slot stays on disk for an instant rollback.
Distributed rate limiting with an in-memory fallback
Limits hold across instances through Redis, and the API keeps protecting itself if Redis is unavailable.
A static arm64 binary built in CI
CI produces one self-contained, stripped binary that runs on the arm64 VM with no toolchain installed there.
Outcomes
- Two apps on one backend: an AI companion app (Expo mobile + Next.js web) and NotifyPay (an Android app).
- Multi-provider AI with RAG, memory, streaming and billing, shared by every tenant.
- Blue-green deploys for both services: dev on every push, production on demand, with automatic rollback.
Stack and links
Stack
- Go
- Gin
- GORM
- PostgreSQL
- pgvector
- Redis
- TypeScript
- LangGraph
- OpenTelemetry
- GitHub Actions