Architecture
ProBeya is built as a modern, multi-tenant SaaS platform using a TypeScript-first monorepo architecture. This page provides an overview of the system components and how they interact.
High-Level Architecture
Browser / API client
|
| HTTPS: pages, Server Actions, tRPC, REST
v
Next.js 16 `web` (port 8000)
- React 19 application and Auth.js
- API routes from packages/api
- durable item-event outbox worker
|
+--------------------+
| |
v v
PostgreSQL 16 Redis 7
(Drizzle + FORCE RLS) (cache and pub/sub)
|
v
S3-compatible storage (private attachments)
Browser -- WSS --> `ws` (Node.js, port 8003) -- Redis pub/sub
Nginx (80/443) routes web, ws, docs, and file-storage hostnames
Technology Stack
| Layer | Technology |
|---|---|
| Frontend | Next.js 16, React 19, TailwindCSS 4, shadcn/ui, Zustand |
| API | tRPC v11 inside Next.js, Server Actions, Zod validation |
| Database | PostgreSQL 16, Drizzle ORM, FORCE RLS |
| Cache / PubSub | Redis 7 |
| Auth | Auth.js v5 (JWT sessions and optional OIDC) |
| File Storage | S3-compatible (MinIO for local dev) |
| Monorepo | Turborepo, pnpm workspaces |
| Language | TypeScript 5.8 throughout |
Multi-Tenancy
ProBeya uses a shared database, shared schema multi-tenancy model:
- Every tenant-scoped table includes an
organization_idcolumn. - Authenticated tenant procedures derive the organization from trusted session and membership state, then set transaction-local database context.
- Canonical
FORCE ROW LEVEL SECURITYpolicies restrict theprobeya_appruntime role at the database level. - Tenant isolation is enforced at both the application and database layers.
Locale-Aware Subdomain Resolution
The middleware supports locale-suffixed subdomains using the pattern
{slug}-{locale}.probeya.com. The last hyphen-separated segment is checked
against the supported locales (en, fr, nl, de, it, es, pt, ja,
ko, zh, ar, ru, id). If it matches, the prefix becomes the tenant
slug and the suffix becomes a locale override:
| Subdomain | Resolved Tenant | Locale Override |
|---|---|---|
acme | acme | — (browser/cookie) |
acme-fr | acme | fr |
my-company | my-company | — (browser/cookie) |
my-company-de | my-company | de |
Locale priority: subdomain suffix > NEXT_LOCALE cookie > Accept-Language header > default (en). The subdomain locale also persists into the cookie so subsequent visits to the plain subdomain retain the language choice.
Monorepo Structure
probeya/
├── apps/
│ ├── web/ # Next.js UI plus tRPC/HTTP runtime
│ ├── ws/ # Node.js WebSocket service
│ ├── docusaurus-docs/ # Product/operator documentation
│ └── mintlify-docs/ # Developer documentation source
├── packages/
│ ├── api/ # tRPC routers and business logic
│ ├── db/ # Drizzle schema and database operations
│ ├── shared/ # Shared validators, constants, and types
│ ├── ui/ # Shared UI components
│ └── config/ # Shared TypeScript and lint configuration
└── docker/ # Docker and compose files
Data Flow
- Client sends a request to the Next.js application.
- Next.js/tRPC middleware validates the session, resolves the organization, and checks permissions.
- tRPC router dispatches to the appropriate procedure handler.
- Service layer executes business logic, reads/writes the database via Drizzle ORM.
- Durable item events are committed to the PostgreSQL outbox in the same transaction as the mutation; the worker dispatches internal and external side effects with retries. Redis pub/sub carries realtime fan-out.
- Response is returned to the client with full type safety.
Real-Time Updates
Real-time collaboration is powered by WebSocket connections:
- Clients establish a WebSocket connection on page load.
- The server publishes events to Redis PubSub when data changes.
- The WebSocket server subscribes to relevant channels and pushes updates to connected clients.
- The client applies optimistic updates and reconciles with server state.
See the Real-Time guide for implementation details.
ADR-016 Offline/PWA Strategy
ProBeya treats offline resilience as a first-class product requirement for shop-floor and pharma/manufacturing use cases where connectivity is not always reliable.
- The web app uses a Progressive Web App strategy for installability and offline shell behavior.
- Edge-aware and offline-safe workflows prefer queued synchronization over direct online-only writes.
- Local caches and sync queues are designed to preserve tenant isolation and replay mutations safely once connectivity returns.
This ADR drives the broader offline architecture described in the PWA Offline guide.
ADR-023
ProBeya's AI architecture is designed around controlled, auditable assistance instead of opaque autonomous behavior.
- AI features run behind explicit server-side orchestration and validation layers.
- Tenant boundaries, permissions, and business rules still apply to AI-driven suggestions and document analysis flows.
- AI outputs are treated as assistive recommendations that require product and user-visible review paths when the workflow is sensitive.
This ADR informs the implementation approach described in the AI Insights guide.
Phase 4: Integration & API Layer Architecture
Phase 4 introduces a comprehensive external integration layer, enabling ProBeya to exchange data with enterprise systems (SAP, MES, QMS, LIMS) and third-party automation platforms (Zapier, Make, Slack, Teams).
Architecture Overview
External Systems ProBeya Platform
┌──────────────┐ ┌──────────────────────┐
│ SAP / MES │──── API Key ────│ REST API (tRPC) │
│ QMS / LIMS │ Auth (Bearer) │ /api/v1/* │
│ Custom ETL │ │ │
└──────────────┘ │ ┌─ Ingest Router ─┐ │
│ │ Batch upsert │ │
┌──────────────┐ │ │ Audit logging │ │
│ Zapier/Make │──── Webhooks ───│ │ Threshold eval │ │
│ Slack/Teams │ (outgoing) │ └─────────────────┘ │
│ PagerDuty │ │ │
└──────────────┘ │ ┌─ Webhook Engine ┐ │
│ │ HMAC-SHA256 │ │
│ │ 3x retry │ │
│ │ Delivery log │ │
│ └─────────────────┘ │
└──────────────────────┘
Key Components
| Component | Package | Description |
|---|---|---|
| API Keys | packages/db/src/schema/api-keys.ts | Bearer token auth with bcrypt hash storage, scopes, rotation |
| Webhooks | packages/db/src/schema/webhooks.ts | HMAC-SHA256 signed outgoing events, 3x exponential backoff |
| Integrations | packages/api/src/routers/integrations.ts | Slack & Teams incoming webhook notifications |
| Ingest Router | packages/api/src/routers/ingest.ts | Batch KPI data ingestion from external systems |
| Ingest Log | packages/db/src/schema/ingest-log.ts | Immutable audit trail for all ingestion attempts |
| Item Event Outbox | packages/db/src/schema/item-event-outbox.ts | Transactional source of item side effects |
| Outbox Worker | packages/api/src/workers/item-event-outbox-worker.ts | Claimed delivery with retries and idempotency metadata |
| Webhook Sender | packages/api/src/lib/webhook-sender.ts | Signed HTTP delivery and delivery logging |
API Key Authentication
API keys use a prefix-based lookup strategy for efficient authentication:
- Key format:
probeya_sk_live_{40 random hex chars} - Storage: Only the bcrypt hash + 8-char prefix are stored in the database
- Lookup: Extract prefix from incoming key, query by prefix + orgId, then bcrypt.compare
- Scopes: JSONB array restricting operations (e.g.,
["read:kpis", "write:kpis"])
Webhook Delivery Pipeline
Item mutations use a transactional outbox so a successful write cannot lose its required automation, notification, integration, or webhook side effects:
- The item mutation and versioned outbox event commit in the same PostgreSQL transaction.
- The production
webprocess claims pending events with bounded locks. - Internal effects run in tenant context before external delivery.
- External deliveries receive stable event/idempotency metadata and are retried with backoff.
- Failed events remain observable in the outbox instead of being silently dropped.
KPI Ingestion Flow
The ingest endpoint (POST /api/v1/ingest/batch) processes external KPI data:
- Authentication: API key with
writescope required - Validation: Zod schema validates batch payload (max 500 items)
- Resolution: Each KPI is resolved within the caller's org context (tenant isolation)
- Upsert: Values are inserted/updated using
INSERT ... ON CONFLICT DO UPDATE - Audit: Every attempt (success/failure) is logged to
ingest_log - Events:
kpi.value_enteredwebhook dispatched for each value - Alerts: Threshold evaluation triggers
kpi.threshold_breachedwebhooks
API Versioning Strategy
ProBeya uses URL path versioning (/api/v1/, /api/v2/) with an additive change policy:
- Non-breaking changes (new fields, new endpoints) are added to the current version
- Breaking changes (removed fields, changed types) require a new version
- Sunset header: Deprecated versions include
Sunset: {date}in response headers - Migration window: Minimum 6 months between deprecation and removal
See the API Reference page for current endpoint conventions.