Architecture

Six layers, one governed control plane.

A modular monolith on TypeScript with sidecars in Python (AI orchestrator, pricing) and Java (FIX gateway). Five Next.js applications. Hash-chained audit, three-layer auth, first-class i18n. Built to extract, not to refactor.

The six layers

Each layer is an explicit contract.

Six-layer architecture

Experience

web-advisor · web-client · web-ops · web-compliance · web-admin. Five Next.js 14 apps, server-rendered, locale-aware, multi-calendar.

Gateway & Auth

Kong API gateway, Zitadel for OIDC, OPA for policy, OpenFGA for relationships. Every request is authenticated, authorized, and policy-checked.

Workflow

Temporal worker. Ten workflows: onboarding, trade order, audit verify, case management, AI interaction, recon, KYC recheck, suitability review.

Domain

NestJS modular monolith with ten bounded contexts. Strict DDD layout per context: domain, application, infrastructure, interface.

Data & Events

Postgres + TimescaleDB + pgvector. Redis for cache. Redpanda for events. MinIO for documents. OpenSearch for full-text search.

Governance

Hash-chained audit. Marquez for OpenLineage. Vault for secrets. OpenTelemetry pipelines into Prometheus, Loki, Tempo.

Component tour

Every service, every role.

Each component is independently deployable and owns its own Helm sub-chart, OTel resource, and Vault path.

Service catalog
ServiceStackPortRole
apps/apiNestJS 10 · Fastify · Drizzle · Zod3000Modular monolith with 10 bounded contexts; OpenAPI auto-generated.
apps/worker-temporalTemporal TS SDK 1.1710 workflows on task queues default, trading, ai, documents, reconciliation.
apps/ai-orchestratorFastAPI · LangGraph · NeMo · Langfuse71005 graphs (briefing, commentary, NBA, triage, suitability_draft); RAG via pgvector.
apps/pricing-serviceFastAPI · QuantLib · numpy7000Pricing, TWR / MWR, VaR (historical sim), exposure analytics.
apps/fix-gatewaySpring Boot 3.3 · QuickFIX/J 2.39876NewOrderSingle / OrderCancelRequest / ExecutionReport routing; embedded simulator.
apps/web-advisorNext.js 14 · Zitadel · TanStack Query3001Primary product surface — clients, portfolios, orders, AI insights.
apps/web-clientNext.js 143002Client portal — 7-stage onboarding wizard, portfolio, statements, requests.
apps/web-opsNext.js 143003Operations console — onboarding queue, stuck workflows, recon, KYC dashboard.
apps/web-complianceNext.js 14 · Monaco editor3004Compliance console — audit explorer, case queue, Rego rule editor.
apps/web-adminNext.js 143005Tenant admin — locale defaults, users, roles, branding.
Postgres + TimescaleDB + pgvector14+5432OLTP, time-series for positions, embeddings for RAG.
Redis76379Cache · BullMQ · idempotency · per-tenant cost counters.
RedpandaKafka API9092Event bus — 25 topics, ULID event IDs, Snappy compression.
MinIOS3 API9000Documents bucket. Evidence bucket with object-lock (compliance mode).
OpenSearch9200Locale-aware analyzers; client search; document full-text.
ZitadelGo8080OIDC, MFA, projects, roles. JWTs verified via JWKS.
OPAGo8181Policy decisions — access, suitability, restricted_list, aml, ai_actions, data_access.
OpenFGAGo8086Relationships — primary_advisor, delegated_advisor, household, viewer, editor, trader.
VaultHashiCorp8200Secrets engine; per-tenant KV path; transit signing for audit witnesses.
LangfusePostgres-backed3001LLM trace store. Cost capture per request.
LiteLLMPython4000Multi-provider LLM gateway with per-tenant rate limits + budget caps.
Modular monolith

Don't refactor. Extract.

The api app is a NestJS modular monolith with strict module boundaries. Cross-context imports are blocked at lint time. When a context needs to scale independently, we extract it to its own service without rewriting clients.

When we don't extract

Most contexts share latency budgets and operational footprint. Extracting them adds overhead without benefit. We keep them in the monolith with strict module boundaries enforced by ESLint and TypeScript paths.

  • Identity, client, audit, documents, onboarding, case
  • Cross-context calls go through public interfaces only
  • Domain events are typed and registered in @wealthos/contracts

When we do extract

Sidecars for fundamentally different runtimes or scaling profiles.

  • ai-orchestrator — Python LangGraph + NeMo Guardrails ecosystem
  • pricing-service — QuantLib needs Python and a different scaling pattern
  • fix-gateway — Spring Boot + QuickFIX/J in Java 21
  • worker-temporal — Temporal worker is a separate runtime by design
Multi-tenant isolation diagram
Multi-tenant isolation

Three layers of authorization. Belt, braces, RLS.

Every request flows through Zitadel (authentication), OpenFGA (relationships), and OPA (policy). Once authorized, the API runs in an AsyncLocalStorage scope holding the tenant id; Drizzle issues SET LOCAL app.tenant_id before each query. Postgres RLS enforces the tenant filter even if a query forgets to.

If any layer is bypassed, the others stop the request. Defense in depth, by construction.

Hash-chained audit

Every regulated action lands here. None of them can leave.

Audit hash chain construction

Construction

contentHash  = SHA256(canonicalJson({occurredAt, actor, action, resource, metadata}))
chainInput   = prevHash + ":" + contentHash
recordHash   = SHA256(chainInput)
genesisHash  = SHA256("wealthos-genesis:" + tenantId)

Per-tenant chain. BEFORE UPDATE and BEFORE DELETE triggers raise audit_events is append-only. auditVerificationWorkflow runs hourly via Temporal cron; opens a breach-severity case on any mismatch.

AI lifecycle

Every prompt walks through guards.

Input guardrails (NeMo) → OPA ai_actions policy → RAG retrieval (per-tenant pgvector, OPA-filtered) → LangGraph node execution → output guardrails → Langfuse trace span → human approval if state mutates.

LiteLLM enforces per-tenant cost ceilings in Redis. Approval mutations run as the approver's identity, not the AI's. The audit log shows the human, with metadata.aiInteractionId linking back.

AI lifecycle diagram
Workflow durability

Ten Temporal workflows. Always.

Long-running orchestrations are durable, replayable, and versioned via workflow.patched().

WorkflowTask queueTriggerDescription
onboardingWorkflowdefaultAPI callFull client onboarding: KYC → suitability → docs → e-sign → compliance review → provision
tradeOrderWorkflowtradingAPI callPre-trade checks → approval (if needed) → FIX route → fills → terminal
caseManagementWorkflowdefaultAPI callSLA timer with auto-escalation (max 3); states track API
auditVerificationWorkflowdefaultCron 0 * * * *Hourly chain verification; opens case on mismatch
periodicReviewWorkflowdefaultCron dailyPer-account suitability + compliance evaluation
aiInteractionWorkflowaiAPI callApproval gate (24h timeout) + commit-as-approver
documentSignatureWorkflowdocumentsAPI callDocuSeal envelope + reminders
reconciliationWorkflowreconciliationCron 0 23 * * *Daily position / transaction reconciliation
kycRecheckWorkflowdefaultCron 0 2 1 * *Monthly KYC refresh sweep
suitabilityReviewWorkflowdefaultCron annualPer-client annual suitability review
Deployment topology

Helm umbrella. Argo CD. Per-tenant region.

The umbrella Helm chart composes 10 sub-charts; consumed by Argo CD via infra/argocd/apps/wealthos-prime.yaml. Each env (dev / staging / prod) has its own values overrides.

Technical overview diagram
Architecture workshop

Map your stack to Modir in 90 minutes.

An architect-led discovery: jurisdictions, identity, integrations, calendars, deployment topology — costed pilot scope as the deliverable.