Book 2
Architecture Handbook
OPSQAI is a self-hosted, multi-tenant knowledge and workflow platform delivered as a small set of container images. This book explains what runs where, how data moves, and how the components stay isolated.
1. Deployment topology
+-------------------+
Users ---> | Reverse proxy | TLS
| (Caddy/Traefik) |
+---------+---------+
|
+---------v---------+ +---------------+
| opsqai-web | <---> | postgres | pgvector
| (SSR + API) | +---------------+
+---------+---------+
| +---------------+
+---------------> | redis | queue
| +---------------+
+---------v---------+ +---------------+
| opsqai-worker | <---> | minio / S3 | documents + backups
| (jobs, RAG) | +---------------+
+-------------------+2. Containers
opsqai-web— TanStack Start (React 19 + Vite 7) SSR, server functions for all app APIs.opsqai-worker— Node runtime pulling jobs from Redis: document ingestion, embeddings, backups, license refresh.postgres:16+pgvector— primary store, RLS on every tenant table.redis:7— BullMQ queues and short-lived caches.minio— default S3 backend; swappable for external S3 or Azure Blob at install time.
3. Data flow — a document question
- User uploads a PDF via the Portal →
opsqai-webwrites bytes to S3 and a row todocuments. opsqai-webenqueues aningestjob.opsqai-workerpulls the job, extracts text, chunks it, requests embeddings from the configured AI provider, writesdocument_chunkswithembedding vector(N)at the install's pinned embedding dimension.- User asks a question →
opsqai-webembeds the query, runsORDER BY embedding <-> :q LIMIT 8under the tenant's RLS policy, and passes the top chunks as context to the AI provider. - The answer streams back to the user with inline citations pointing to the source chunks.
4. License system
Licenses are Ed25519-signed JSON tokens issued by the Management Center. Every OPSQAI boot verifies the signature offline against a pinned public key; the worker refreshes entitlements every 24h against mc.opsqai.de. A grace window keeps the instance running for 14 days if the MC is unreachable.
Modules, seat counts and expiry live in the token; nothing about the customer's data leaves the install.
5. Security model
- Every app query runs as the authenticated user; RLS is the enforcement boundary.
- Server functions requiring auth use the
requireAuthmiddleware; admin server functions verify roles via a security-definerhas_role()before touching the admin client. - Roles live in
public.user_roles— never onprofiles. - Object storage uses per-tenant path prefixes plus signed URLs; direct bucket access is blocked at the proxy.
6. AI adapter
The AI layer is behind a single interface: chat(messages, opts), embed(texts), stream(messages, opts). Adapters ship for Lovable AI Gateway, Azure OpenAI, OpenAI-compatible, and Ollama. Switching provider is a config change, not a code change.
7. Updates & maintenance
Each release is a signed compose bundle plus SQL migrations. Migrations run behind a Postgres advisory lock so multi-replica upgrades are safe. Schema changes are always backward-compatible for one minor version to allow rolling upgrades.