Documentation Index
Fetch the complete documentation index at: https://docs.ibex.ajna.cloud/llms.txt
Use this file to discover all available pages before exploring further.
Architecture Overview
Overview
Ibex is a multi-service data platform deployed on AWS EC2 behind Traefik, with two React UIs served via S3 + CloudFront. It provides federated SQL analytics, BI reporting, AI-powered data chat, and pipeline management across heterogeneous data sources.
Deployment Topology
Internet
│
┌───────────┴───────────┐
│ │
CloudFront (CDN) api.triviz.cloud
S3-hosted SPAs EC2 Instance
│ │
┌──────┴──────┐ ┌──────┴──────┐
│ ibex. │ │ Traefik │ :80/:443
│ triviz. │ │ TLS proxy │ Let's Encrypt
│ cloud │ └──────┬──────┘
│ (Config UI)│ │
└─────────────┘ Routes by PathPrefix / Host
┌─────────────┐ │
│ bi.triviz │ (see Routing table below)
│ .cloud │
│ (BI UI) │
└─────────────┘
DNS / Domains
| Domain | Purpose |
|---|
api.triviz.cloud | All backend APIs (Traefik entry point) |
ibex.triviz.cloud | Config UI — pipelines, data sources, monitoring (CloudFront → ibex-platform-ui) |
bi.triviz.cloud | BI UI — dashboards, charts, reports, AI chat, RAG (CloudFront → ajna-data-platform-ui-lib) |
listmonk.triviz.cloud | Email marketing (Listmonk, Traefik) |
Services
Application Services
| Service | Language | Port | Image |
|---|
ibex-data-platform | Python/FastAPI | 8080 | ghcr.io/ajnacloud-ksj/ibex-data-platform |
ibex-identity-service | Python/FastAPI | 8090 | ghcr.io/ajnacloud-ksj/ibex-identity-service |
ibex-analytics-service | Python/FastAPI + DuckDB | 8000 | ghcr.io/ajnacloud-ksj/ibex-analytics-service |
ibex-bi-backend | Go | 8085 | ghcr.io/ajnacloud-ksj/ibex-bi-backend |
ibex-ai-service | Python/FastAPI | 8010 | ghcr.io/ajnacloud-ksj/ibex-ai-service |
ibex-agent-engine | Node.js | 3000 | ghcr.io/ajnacloud-ksj/ibex-agent-engine |
ibex-listmonk | Go (Listmonk) | 9000 | ghcr.io/ajnacloud-ksj/ibex-listmonk |
Infrastructure Services
| Service | Purpose | Port |
|---|
traefik | TLS reverse proxy + routing | 80, 443 |
postgres-metadata | Central metadata PostgreSQL DB | 5432 (internal), 5433 (host) |
minio | S3-compatible object storage (Iceberg warehouse, file uploads) | 9000 |
minio-setup | One-shot: creates warehouse + data-ingestion buckets | — |
iceberg-rest | Apache Iceberg REST catalog (backed by MinIO) | 8181 |
vault-secrets | HashiCorp Vault — credential store | 8200 |
vault-init | One-shot: unseals Vault, seeds credentials | — |
redpanda | Kafka-compatible event streaming | 9092 |
watchtower | Auto-redeploys containers when new images are pushed | — |
Container Reference
Startup Order
Containers start in dependency order. The chain ensures each service has its dependencies healthy before it launches:
postgres-metadata ──► vault-secrets ──► vault-init (one-shot)
──► redpanda
──► listmonk
minio ──► minio-setup (one-shot) ──► iceberg-rest
postgres + redpanda + minio + vault-init + iceberg-rest + listmonk
└──► ibex-data-platform
└──► ibex-analytics-service
└──► ibex-agent-engine
└──► ibex-ai-service
ibex-identity-service + ibex-analytics-service
└──► ibex-bi-backend
traefik (ibex-traefik)
Image: traefik:v3.1
Ports: 80 (HTTP redirect), 443 (HTTPS)
Purpose: TLS-terminating reverse proxy. All external traffic enters here.
How it works:
- Reads Docker labels from other containers at runtime via the Docker socket (
/var/run/docker.sock)
- Each container self-registers its own routing rules via
traefik.http.routers.* labels — no central config file needed for app routes
- Automatically provisions and renews Let’s Encrypt TLS certificates via ACME (stored in
traefik_letsencrypt volume)
- Routes requests by
Host + PathPrefix to the correct backend container
- Config template (
traefik.yml.tpl) is rendered at startup by render-config.sh into /tmp/traefik.yml
postgres-metadata (postgres-metadata)
Image: pgvector/pgvector:pg15
Port: 5433 (host) → 5432 (container)
Purpose: Central PostgreSQL database. Shared by multiple services.
Databases it hosts:
metadata_db — main application database (pipelines, data sources, file uploads, business rules, demo data, Vault KV store)
listmonk — email marketing database (lists, subscribers, campaigns)
Init scripts run on first boot (via docker-entrypoint-initdb.d/):
01-init-business-data.sql — business tables, demo MySQL/orders/products/users data
02-init-vault.sql — creates vault_kv_store table used by Vault as its storage backend
03-init-bi-metadata.sql — BI metadata schema (reports, connections)
04-enable-pgvector.sql — enables the pgvector extension for vector similarity search
Uses the pgvector image (PostgreSQL 15 + pgvector extension) to support embedding storage for RAG.
redpanda (redpanda)
Image: redpandadata/redpanda:v25.1.7
Ports: 9092 (external Kafka), 29092 (internal Kafka)
Purpose: Kafka-compatible event streaming broker for CDC pipelines.
How it works:
ibex-data-platform publishes pipeline events and CDC change records to Redpanda topics
- Auto-creates topics on first publish (
auto_create_topics_enabled=true)
- Single-node, single-partition setup (sufficient for current load)
- Data persisted in
redpanda_data volume
- Two listeners:
PLAINTEXT://redpanda:29092 for internal container-to-container, OUTSIDE://localhost:9092 for host access
minio (minio)
Image: minio/minio:latest
Ports: 9010 (S3 API), 9011 (console UI)
Purpose: S3-compatible object storage. Stores Iceberg table data (Parquet files) and pipeline ingestion staging files.
Two buckets (created by minio-setup):
warehouse/ — Iceberg table data, managed by iceberg-rest. Files are Parquet-format column data for analytics tables
data-ingestion/ — CDC and pipeline staging area where ibex-data-platform writes ingested records before they’re committed to Iceberg
Credentials stored in Vault and referenced by iceberg-rest and ibex-data-platform.
minio-setup (one-shot)
Image: minio/mc:latest
Purpose: Runs once at startup to create the warehouse and data-ingestion buckets in MinIO. Exits after completion. Never restarts.
iceberg-rest (iceberg-rest)
Image: tabulario/iceberg-rest:0.9.0
Port: 8181
Purpose: Apache Iceberg REST catalog. Tracks table metadata (schemas, partition specs, snapshots) for Iceberg tables stored in MinIO.
How it works:
ibex-analytics-service (DuckDB) connects to this catalog via the Iceberg REST API to discover and read Iceberg tables
- The catalog stores table metadata; actual data files live in MinIO
warehouse/ as Parquet
- When
ibex-data-platform writes a CDC pipeline record to an Iceberg table, it registers the new snapshot here
- DuckDB then reads the latest snapshot via the catalog and executes queries directly against the Parquet files in MinIO
vault-secrets (vault-secrets)
Image: hashicorp/vault:1.15
Port: 8200
Purpose: HashiCorp Vault — secrets manager. Stores all database passwords, MinIO credentials, and API keys at runtime so they’re never hardcoded in config files.
How it works:
- Uses PostgreSQL (
vault_kv_store table in metadata_db) as its storage backend — no separate volume for secret data
- Starts sealed;
vault-init unseals it and seeds initial secrets
ibex-data-platform reads credentials from Vault at startup via a root token file mounted from the vault_keys shared volume
- Vault config is rendered from a template (
render-vault-config.sh) on startup
vault-init (one-shot)
Image: hashicorp/vault:1.15
Purpose: Runs once to initialize, unseal, and seed Vault with initial credentials. Writes the root token to vault_keys volume so ibex-data-platform can read it. Exits after completion.
Secrets it seeds: PostgreSQL password, MySQL password, MinIO credentials, Listmonk API credentials, config manager admin token.
ibex-listmonk (ibex-listmonk)
Image: ghcr.io/ajnacloud-ksj/ibex-listmonk:latest
Port: 9000
Purpose: Email marketing and transactional email platform (Listmonk). Manages mailing lists, subscribers, campaigns, and SMTP delivery.
How it works:
- Stores all data in the
listmonk database on postgres-metadata
- SMTP configured for Gmail (configurable via env vars)
- Exposed externally at
listmonk.triviz.cloud via Traefik (full app, not just API)
- File uploads stored in
listmonk_uploads volume
ibex-identity-service (ibex-identity-service)
Image: ghcr.io/ajnacloud-ksj/ibex-identity-service:latest
Port: 8090
Purpose: Authentication and user management. Issues and validates JWTs.
How it works:
- Supports two auth modes (set via
IDENTITY_AUTH_MODE):
local — users stored in SQLite (/data/ajna-identity.db), passwords hashed locally
cognito — delegates auth to AWS Cognito (User Pool ID + Client ID from env)
- Issues JWT access tokens (default 24h) and refresh tokens (default 7d)
- All other services call
GET /validate to verify tokens on every request
- Exposed via Traefik at
/auth/* and /users/*
Image: ghcr.io/ajnacloud-ksj/ibex-data-platform:latest
Port: 8080
Purpose: Config/platform manager. The control plane for data sources, CDC pipelines, business rules, and configurations.
How it works:
- Registers and manages data sources: MySQL, PostgreSQL, Iceberg, S3, REST APIs
- Configures CDC (Change Data Capture) pipelines using Redpanda as the message bus
- Syncs registered sources to
ibex-analytics-service so DuckDB can attach them as catalogs
- Reads DB credentials from Vault at startup via the root token file mounted at
/vault/keys/root_token
- Stores all state in
postgres-metadata (metadata_db)
- Exposed via Traefik at
/api/*, /configs/*, /business-configs/*, /health, /metrics (priority 10 — catch-all)
- UI: ibex.triviz.cloud (Config UI —
ibex-platform-ui)
ibex-analytics-service (ibex-analytics-service)
Image: ghcr.io/ajnacloud-ksj/ibex-analytics-service:latest
Port: 8000
Purpose: Federated SQL execution engine. Embeds DuckDB and attaches heterogeneous data sources as virtual catalogs.
How it works:
- On startup, fetches the data source registry from
ibex-data-platform (GET /api/data-sources)
- Attaches each registered source to DuckDB:
- MySQL → DuckDB MySQL scanner extension
- PostgreSQL → DuckDB Postgres scanner extension
- Iceberg → Iceberg REST catalog → MinIO Parquet files (via S3FileIO)
- S3/CSV/Parquet → direct file scan
- Queries can reference multiple sources in one SQL statement:
SELECT * FROM mysql_business.orders JOIN postgres_metadata.users
- Internal only — no Traefik label, only reachable inside the
ajna Docker network
- Called by:
ibex-bi-backend (report/analytics queries), ibex-ai-service (direct SQL), ibex-agent-engine (agent SQL execution)
- Protected by an API key (
ANALYTICS_SERVICE_API_KEY)
ibex-bi-backend (ibex-bi-backend)
Image: ghcr.io/ajnacloud-ksj/ibex-bi-backend:latest
Port: 8085
Purpose: Go-based BI metadata service. The primary API for the BI UI.
How it works:
- Validates every request by calling
ibex-identity-service (GET /validate) — acts as an auth gateway
- Stores BI metadata in SQLite (
/data/ajna-bi-metadata.db → ajna_bi_metadata volume): reports, database connections, dashboards, charts, RAG documents
- Proxies SQL execution to
ibex-analytics-service for report preview and analytics queries
- Exposes CRUD for: reports, metadata, database connections, dashboards, charts, RAG knowledge base
- Exposed via Traefik at
/api/metadata, /api/reports, /api/analytics, /api/database-connections, /internal/data-sources, /api/dashboards, /api/charts, /api/rag (priority 90)
- UI: bi.triviz.cloud (BI UI —
ajna_data_platform_ui_lib)
ibex-ai-service (ibex-ai-service)
Image: ghcr.io/ajnacloud-ksj/ibex-ai-service:latest
Port: 8010
Purpose: AI conversation orchestrator. Manages chat sessions and routes user messages to the agent engine.
How it works:
- Receives chat messages from the BI UI (
POST /api/chat)
- Manages conversation history (CRUD via
/api/conversations)
- Forwards each user message to
ibex-agent-engine (POST /api/agents/{name}/query)
- Validates JWTs via
ibex-identity-service on every request
- Streams the agent response back to the browser
- Default agent:
demo_data_analyst (configurable via DEFAULT_AGENT_NAME)
- Exposed via Traefik at
/api/chat and /api/conversations (priority 90)
ibex-agent-engine (ibex-agent-engine)
Image: ghcr.io/ajnacloud-ksj/ibex-agent-engine:latest
Port: 3000
Purpose: Node.js LLM agent runtime. Executes multi-step reasoning pipelines that combine LLM calls with SQL execution.
How it works:
- Each agent is a defined pipeline of nodes (e.g.
tool-kb-1 → tool-exec-1 → tool-gen-1):
- tool-kb-1 — searches the RAG knowledge base for relevant context (table schemas, business docs)
- tool-exec-1 — LLM generates SQL; executes it against
ibex-analytics-service; returns data
- tool-gen-1 — LLM formats the data into a natural language response
- LLM routing via
llm-routing.json: endpoint https://openrouter.ai/api/v1, model openai/gpt-4o-mini
- Uses
OPENROUTER_API_KEY for authentication to OpenRouter
- Internal only — no Traefik label; called exclusively by
ibex-ai-service
ibex-watchtower (ibex-watchtower)
Image: containrrr/watchtower:1.7.1
Purpose: Automatic container updater. Polls GHCR every 5 minutes and restarts containers when a new image is published.
How it works:
- Only monitors containers with the label
com.centurylinklabs.watchtower.enable=true
- Pulls the new image → stops the old container → starts a new one (rolling restart)
- Uses the Docker credentials file (
/root/.docker/config.json) to authenticate to GHCR
--cleanup removes old image layers after update to save disk
- Important: Watchtower only updates the image — it does NOT re-read
docker-compose.yml. Changes to labels, env vars, or volumes require a manual docker compose up -d <service> on the server.
Traefik Routing
All requests to api.triviz.cloud are routed by path prefix:
| PathPrefix | → Service | Priority |
|---|
/auth, /users | ibex-identity-service :8090 | default |
/api/metadata, /api/reports, /api/analytics, /api/database-connections, /internal/data-sources, /api/dashboards, /api/charts, /api/rag | ibex-bi-backend :8085 | 90 |
/api/chat, /api/conversations | ibex-ai-service :8010 | 90 |
/api, /configs, /business-configs, /health, /metrics | ibex-data-platform :8080 | 10 (catch-all) |
listmonk.triviz.cloud (all paths) | ibex-listmonk :9000 | — |
Note: ibex-analytics-service and ibex-agent-engine are internal only — no Traefik labels, only reachable inside the Docker ajna network.
Service Responsibilities
ibex-identity-service
- JWT-based authentication (local SQLite or AWS Cognito)
- User/role management
- Token validation endpoint consumed by
ibex-bi-backend middleware
- Store:
/data/ajna-identity.db (SQLite, persisted in Docker volume)
- Registers data sources: MySQL, PostgreSQL, Iceberg, S3, APIs
- Manages CDC pipelines and Redpanda Kafka topics
- Syncs connection credentials from HashiCorp Vault
- Propagates source registry to
ibex-analytics-service
- UI: bi.triviz.cloud (Config UI)
ibex-analytics-service
- Embeds DuckDB for in-process federated SQL execution
- Attaches MySQL/Postgres/Iceberg sources as DuckDB catalogs
- Supports fully-qualified cross-source queries:
mysql_business.orders JOIN postgres_metadata.public.users
- Iceberg tables via REST catalog → MinIO S3 Parquet files
- Adhoc file uploads (CSV/Parquet) queryable as temporary DuckDB tables
- Not directly exposed to the internet — called by
ibex-bi-backend, ibex-ai-service, ibex-agent-engine
ibex-bi-backend
- Go service for BI metadata: reports, dashboards, charts, DB connections, RAG knowledge base
- Proxies SQL execution to
ibex-analytics-service
- Auth middleware validates JWT via
ibex-identity-service
- Store:
/data/ajna-bi-metadata.db (SQLite, persisted in Docker volume)
- UI: bi.triviz.cloud (BI UI — dashboards, charts, reports, connections, RAG pages)
ibex-ai-service
- Conversation/chat session management (CRUD via
/api/conversations)
- Orchestrates AI queries: receives user message → calls
ibex-agent-engine → returns response
- Passes analytics context to agents
ibex-agent-engine
- Node.js LLM agent runtime
- Connects to LLM via OpenRouter (
OPENROUTER_API_KEY, endpoint https://openrouter.ai/api/v1)
- Executes SQL via
ibex-analytics-service to answer data questions
- Exposes pre-defined agents (e.g.
demo_data_analyst)
ibex-listmonk
- Email marketing and transactional email
- Uses
postgres-metadata DB (separate listmonk database)
- SMTP via Gmail (configurable)
Data Flow
Federated SQL Query (Reports / SQL Lab)
Browser (BI UI)
└─► POST /api/analytics/query → ibex-bi-backend :8085
└─► POST /analytics/query → ibex-analytics-service :8000
└─► DuckDB executes federated SQL
├─► mysql_business.* (attached MySQL)
├─► postgres_metadata.* (attached PostgreSQL)
└─► iceberg_catalog.* (Iceberg REST → MinIO S3)
AI Chat Query
Browser (BI UI)
└─► POST /api/chat → ibex-ai-service :8010
└─► POST /api/agents/{name}/query → ibex-agent-engine :3000
├─► LLM (OpenRouter/Claude/GPT) — generates SQL
└─► POST /analytics/query → ibex-analytics-service :8000
└─► DuckDB executes SQL → returns results
└─► LLM — formats natural language response
└─► Response streamed back to browser
Authentication Flow
Browser
└─► POST /auth/login → ibex-identity-service → SQLite / Cognito
└─► Returns JWT access token
└─► All subsequent API requests: Authorization: Bearer <token>
└─► ibex-bi-backend WithAuth middleware
└─► GET /validate → ibex-identity-service
└─► 200 OK → proceed | 401 → reject
DB Connections — Unified List (UI)
Browser calls getAllConnectionsUnified():
├─► GET /api/database-connections/ → ibex-bi-backend (user-created connections)
├─► GET /api/data-sources → ibex-data-platform (platform: MySQL, Postgres, Iceberg, S3)
└─► GET /analytics/catalogs → ibex-analytics-service (DuckDB: data_lakes, storage, databases)
└─► Deduplicated and merged → displayed in Connections page
Storage Architecture
┌─────────────────────────────────────────────────────────┐
│ postgres-metadata │
│ DB: metadata_db (23 tables) │
│ ├─ ajna_poller_checkpoint pipeline state │
│ ├─ business_pipelines CDC pipeline config │
│ ├─ business_data_sources registered sources │
│ ├─ file_uploads adhoc file registry │
│ ├─ configurations / business_rules │
│ ├─ orders / products / users (demo data) │
│ └─ vault_kv_store Vault backend │
│ │
│ DB: listmonk email marketing │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ MinIO (S3-compatible) │
│ Bucket: warehouse/ │
│ ├─ Iceberg table data (Parquet files) │
│ └─ Managed via iceberg-rest REST catalog │
│ │
│ Bucket: data-ingestion/ │
│ └─ CDC / pipeline ingestion staging │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ HashiCorp Vault │
│ Stores: DB passwords, MinIO keys, API tokens │
│ Backend: postgres-metadata (vault_kv_store table) │
│ Consumed by: ibex-data-platform (via vault_keys vol) │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ SQLite volumes │
│ /data/ajna-identity.db → users, roles, tokens │
│ /data/ajna-bi-metadata.db → reports, DB connections │
└─────────────────────────────────────────────────────────┘
Frontend Architecture
Two UIs, One Component Library
Both UIs are built from the shared ajna_data_platform_ui_lib component library (React + TypeScript + Vite + Tailwind + shadcn/ui).
| UI | Repo | Hosted | Purpose |
|---|
| BI UI | ajna_data_platform_ui_lib | bi.triviz.cloud (CloudFront → S3 /ajna-data-platform-ui-lib) | Dashboards, charts, reports, DB connections, AI chat, RAG |
| Config UI | ibex-platform-ui | ibex.triviz.cloud (CloudFront → S3 /ibex-platform-ui) | Pipeline management, data sources, monitoring |
Frontend → Backend Mapping
| UI Page | UI | API Calls |
|---|
| Login | Both | POST /auth/login → ibex-identity-service |
| DB Connections | BI UI | GET /api/database-connections/ (bi-backend) + GET /api/data-sources (data-platform) + GET /analytics/catalogs (analytics-service) |
| Reports (create/edit) | BI UI | POST/PUT /api/metadata/reports → bi-backend → analytics-service (SQL exec) |
| SQL Lab | BI UI | POST /api/analytics/query → bi-backend → analytics-service |
| AI Chat | BI UI | POST /api/chat, GET/POST /api/conversations/ → ai-service → agent-engine → analytics-service |
| Dashboards | BI UI | GET/POST /api/dashboards → bi-backend |
| Charts | BI UI | GET/POST /api/charts/, POST /api/charts/data → bi-backend |
| RAG Knowledge Base | BI UI | GET /api/rag/documents, POST /api/rag/upload, POST /api/rag/query → bi-backend |
| Pipelines | Config UI | GET/POST /api/pipelines → ibex-data-platform |
| Data Sources | Config UI | GET/POST /api/data-sources → ibex-data-platform |
Build & Deploy
git push origin main
└─► GitHub Actions: build Vite bundle (VITE_* vars baked in at build time)
└─► Upload to S3
└─► CloudFront invalidation
└─► New bundle live (no server restart needed)
VITE environment variables (baked at build time in GitHub Actions):
BI UI (ajna_data_platform_ui_lib → bi.triviz.cloud):
VITE_API_BASE_URL — https://api.triviz.cloud/api — base for bi-backend calls
VITE_AI_SERVICE_URL — https://api.triviz.cloud — base for ai-service calls
VITE_ANALYTICS_API_URL — https://api.triviz.cloud/api — analytics proxy via bi-backend
VITE_BI_SERVICE_URL — https://api.triviz.cloud — bi-backend direct calls
VITE_PLATFORM_API_URL — https://api.triviz.cloud — ibex-data-platform URL
VITE_IDENTITY_SERVICE_URL — https://api.triviz.cloud — identity service
Config UI (ibex-platform-ui → ibex.triviz.cloud):
VITE_CONFIG_MANAGER_URL — https://api.triviz.cloud/api — ibex-data-platform
VITE_IDENTITY_SERVICE_URL — https://api.triviz.cloud — identity service
VITE_ANALYTICS_API_URL — https://api.triviz.cloud/api/analytics
VITE_INGESTION_API_URL — https://api.triviz.cloud/api
Auto-Deployment (Watchtower)
Developer pushes code
└─► GitHub Actions CI: build Docker image → push to ghcr.io
└─► Watchtower polls every 5 min (--label-enable)
└─► Detects new image digest for labeled containers
└─► Pulls new image → rolling restart of container
⚠️ Watchtower updates container images only.
It does NOT re-read docker-compose.yml for label/env changes.
For label or env changes: SSH to server → git pull → docker compose up -d <service>
Inter-Service Communication
All services communicate over the internal Docker bridge network ajna. No inter-service traffic leaves the host.
ibex-data-platform ──► ibex-analytics-service (sync data sources)
ibex-bi-backend ──► ibex-analytics-service (execute SQL)
ibex-bi-backend ──► ibex-identity-service (validate JWT)
ibex-ai-service ──► ibex-agent-engine (run agent)
ibex-ai-service ──► ibex-analytics-service (direct SQL)
ibex-ai-service ──► ibex-identity-service (validate JWT)
ibex-agent-engine ──► ibex-analytics-service (execute agent SQL)
ibex-data-platform ──► vault-secrets (fetch credentials)
ibex-data-platform ──► postgres-metadata (metadata DB)
ibex-data-platform ──► redpanda (pipeline events)
Security
- TLS: Let’s Encrypt via Traefik ACME, all external traffic HTTPS only
- Auth: JWT issued by
ibex-identity-service, validated on every request by ibex-bi-backend and ibex-ai-service
- Credentials: All DB passwords and API keys stored in HashiCorp Vault, injected via environment at runtime
- CORS: Explicit allowlist —
https://ibex.triviz.cloud,https://bi.triviz.cloud on all backend services
- Network isolation: Analytics and Agent Engine are internal-only (no Traefik exposure)
- Mixed Content: All frontend API calls use HTTPS; FastAPI services configured to avoid HTTP redirects
Key Configuration Files
| File | Location | Purpose |
|---|
docker-compose.aws.yml | /opt/ibex-platform-runner/ on EC2 | Production service definitions |
docker-compose.deploy-base.yml | same | Infrastructure services (Postgres, MinIO, Vault, etc.) |
docker-compose.demo-data.yml | same | Demo MySQL + seed data |
.env | /opt/ibex-platform-runner/ | Secrets and overrides (not in git) |
deploy/traefik/traefik.yml.tpl | same | Traefik config template |
deploy/init-db/init.sql | same | PostgreSQL schema + demo data |
deploy/scripts/vault-init.sh | same | Vault unseal + credential seeding |