Architecture
Last updated: 2026-06-01
A high-level view of how Nomatica AI is built. Internal names and hostnames are simplified for the public site; this is not a deployment guide.
At a glance
Nomatica AI is a multi-tenant SaaS application with three planes:
- Edge plane — Cloudflare's global anycast network terminates TLS, runs Workers, and provides caching, KV, and D1.
- Application plane — Python (FastAPI) and Node (Vite + React SSR) services run on containerized compute behind the edge.
- Data plane — Managed Postgres, Redis, and object storage hold tenant content, audit logs, and user uploads.
The three planes are connected by signed JSON Web Tokens (JWTs) and, for service-to-service calls, mTLS.
High-level diagram
Edge plane
| Component | Purpose |
|---|---|
| Frontend (Pages) | React 19 + Vite 6 SPA, served from Cloudflare Pages. Static assets are versioned and immutable. |
| Gateway Worker | First point of contact for all API traffic. Performs auth, rate limiting, request size cap, and routing to the correct origin. |
| Auth Worker | Issues and verifies JWTs. Stores refresh-token state in KV so a single login works across all subdomains. |
| Workers KV | Read-heavy cache: feature flags, public rate-limit counters, public config. |
| D1 (SQLite at the edge) | Small relational datasets: feature flags history, public leaderboards, search autocomplete. |
The edge plane is the only plane that touches the public internet.
Application plane
The application plane runs on containerized compute (ECS on Fargate). Each service is a stateless replica behind an internal load balancer.
- FastAPI Hub (
port 3040) — the primary API. Owns user accounts, billing, tenant management, and the public REST surface. - Inference Worker — talks to model providers. Routes prompts to the cheapest provider that meets the latency budget.
- Search Worker — runs the search index, manages the honeypot, and surfaces contract-hunter results.
- Chat Worker — long-running WebSocket connections for the chat UI; multiplexes many concurrent conversations onto a single inference session where possible.
All application-plane services are written in Python 3.14 or TypeScript / Node 22.
Data plane
| Store | Data | Notes |
|---|---|---|
| Postgres | Tenants, users, prompts, generated outputs, billing | Multi-AZ. Daily snapshot + PITR. Encrypted at rest. |
| Redis | Sessions, rate-limit counters, ephemeral cache | 1-day TTL on counters. No customer content. |
| Object storage | User uploads, model artifacts, exported reports | Per-tenant prefix. Lifecycle policy deletes tmp/ after 7 days. |
Cross-cutting concerns
Authentication and authorization
- Users authenticate with email + password (mandatory MFA), OAuth 2.0, or WebAuthn passkeys.
- A successful login returns a short-lived (15-minute) access JWT and a long-lived (7-day) refresh JWT.
- The refresh JWT is held in an
HttpOnlycookie scoped to the apex domain, so a single login persists across*.colorfulmoves.com. - The access JWT carries the user's scopes (
read,write,admin,agent,inference,payments). - Every API request goes through a centralized authorization layer. Application code never rolls its own permission checks.
Multi-tenancy
- Every row in every multi-tenant table carries a
tenant_id. - Postgres Row-Level Security is enabled; queries without an explicit
tenant_idfilter cannot return rows for other tenants. - A "canary" tenant exists in production with synthetic, decoy data; any cross-tenant read trips an alert within 60 seconds.
Observability
- Structured JSON logs ship to a managed log sink.
- Metrics (latency, error rate, queue depth, per-tenant spend) are scraped by Prometheus and visualized in Grafana.
- Distributed tracing (OpenTelemetry) follows a request from the edge through the application and into the model provider.
- Errors are captured by Sentry, with PII scrubbed at the source.
Deployment
- Edge workers and Pages are deployed on every push to
main(with a hold for review on Friday). - Application-plane services are deployed via a CI pipeline that builds the container, scans it with Trivy, signs it with cosign, and rolls it out behind a feature flag.
- Database migrations are versioned and run in a transaction; a failed migration is automatically rolled back.
Why these choices?
Why Cloudflare for the edge?
- Global anycast network — sub-50ms TTFB from anywhere
- Workers KV and D1 are eventually consistent, which is fine for the cache-y workloads we put on them
- Per-request pricing keeps our cost structure aligned with revenue (we don't pay for idle capacity)
- The Workers runtime has a small enough surface that we can reason about its security model
Why Python for the API?
- The team has deep Python expertise
- The data ecosystem (pandas, polars, sqlite) is unmatched
- The async story (asyncio + uvicorn) is finally good enough for our latency targets
- Type-checking with mypy gives us a level of confidence comparable to TypeScript
Why a separate Chat Worker?
- Long-lived WebSocket connections don't belong in a request/ response API
- We can scale the chat plane independently of the REST plane
- We can apply different rate limits and cost controls
What we don't do (and why)
- We don't run our own data centers. The total addressable market doesn't justify the capex.
- We don't operate a private LLM. Frontier model providers update faster than we can train; we route to them.
- We don't store raw payment card data. Tokenization via Stripe / Square is the right tradeoff between compliance burden and integration cost.
- We don't run our own email deliverability infrastructure. Resend does it better and cheaper than we could.
Read next
- Threat Model — the security lens on this architecture
- Public Security Details — controls and sub-processors
- Pricing — what the architecture costs you