Skip to content

Install with Docker Compose

The trial bundle is a production-shaped Docker Compose stack: the same images (ghcr.io/brutor-ai/*), same topology and same configuration surface you’d run self-hosted, sized for a single machine. This page is the full reference; for the fast path see the quickstart.

Service Container Host port(s) Purpose
Core Proxy (Rust) brutor-gateway-core 8100 Runtime proxy for LLM/MCP/A2A/OAuth/Skills + Portal API
Control Plane (Python) brutor-gateway-control 5050 Admin API, Alembic migrations, tenant seeding
Admin Console brutor-gateway-admin 3002 Management UI
User Portal brutor-gateway-portal 3001 End-user chat UI
KB Uploader brutor-kb-uploader — (internal :8200) Document extraction, chunking, embedding into Qdrant
KB Connector Sync brutor-kb-connector-sync — (internal :8201) Confluence/Notion/GDrive/Slack/GitHub/Jira/SharePoint sync
Skill Runner brutor-skill-runner — (internal :8210) Sandboxed skill execution — JWT-authed, read-only rootfs, all capabilities dropped
mcp-app-map brutor-mcp-app-map — (internal :3001) Demo MCP server (Leaflet map with MCP-Apps UI)
PostgreSQL 16 brutor-postgres 5432 Shared database for both planes
Redis 7.4 brutor-redis 6379 Cache, rate-limit counters
Qdrant 1.18 brutor-qdrant 6333 (REST) / 6334 (gRPC) Vectors — semantic cache + knowledge bases
MinIO brutor-minio 9000 (S3 API) / 9001 (console) S3-compatible media storage

All image tags follow one variable: BRUTOR_VERSION in .env (e.g. 0.9.68). See upgrades.

Profiles add capability without bloating the default stack:

Profile Services (host port) What it adds
local-embedding Ollama (11434) Local embeddings with nomic-embed-text — a fully offline embedding path instead of OpenAI. The Ollama image is ~4 GB plus a ~274 MB first-run model pull.
kms LocalStack (4566), Vault (8200) Emulated AWS KMS and a real Vault Transit engine for testing the KEY_PROVIDER=aws_kms / vault envelope-encryption modes locally.
presidio Presidio analyzer (3030) Microsoft Presidio NER engine as an external PII provider for guardrails — stronger entity detection than the built-in patterns.
anthropic-mock Anthropic Enterprise mock (8085) A mock of the Anthropic Enterprise Analytics API, used to demo Traffic Data Import without an Enterprise plan.
Terminal window
docker compose --profile local-embedding up -d
docker compose --profile kms --profile presidio up -d # profiles combine

The bundle ships with a working .env — your license key and a unique set of secrets are minted per download. The variables you must understand before going beyond a laptop:

.env (critical entries)
# Required — the core proxy refuses to start without it
BRUTOR_LICENSE_KEY=BRUTOR-V1-...
# Image tag for all services
BRUTOR_VERSION=0.9.68
# Shared secrets — MUST be identical for core + control plane
JWT_SECRET=... # openssl rand -hex 32
ENCRYPTION_KEY=... # Fernet key, see below
# Per-subsystem secrets
QDRANT_JWT_SIGNING_SECRET=... # openssl rand -hex 32
SKILL_RUNNER_JWT_SECRET=... # openssl rand -hex 32 — must match between core and skill-runner
PROXY_HEALTH_CHECK_API_KEY=sk_brutor_api_... # 43 url-safe chars after the prefix
# Seeded admin credentials — CHANGE THESE
SYSTEM_ADMIN_PASSWORD=Admin123!
DEFAULT_TENANT_ADMIN_PASSWORD=Admin123!
# MinIO credentials (media storage)
MEDIA_S3_ACCESS_KEY_ID=minioadmin
MEDIA_S3_SECRET_ACCESS_KEY=minioadmin

Generation commands for fresh secrets:

Terminal window
openssl rand -hex 32 # JWT_SECRET, QDRANT_JWT_SIGNING_SECRET, SKILL_RUNNER_JWT_SECRET
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" # ENCRYPTION_KEY

JWT_SECRET and ENCRYPTION_KEY must always match between the core proxy and the control plane — the portal JWT is minted by the proxy and encrypted secrets are written by the control plane and read by the proxy. The full variable reference (ports, database, Redis, semantic cache, KB, media, skills, KMS, CORS, logging) is on the configuration page.

Terminal window
cd trial_bundle
./docker-start.sh # interactive: checks Docker + license, asks about local embeddings
docker compose up -d # non-interactive (remote/OpenAI embeddings)
docker compose --profile local-embedding up -d
docker compose logs -f gateway-core # follow a service's logs
docker compose down # stop, KEEP data volumes
./docker-cleanup.sh # full teardown INCLUDING volumes

First boot takes a few minutes: the control plane runs its Alembic migrations, seeds the default tenant (admin user, a default group, enabled models, the demo MCP server) and only then reports healthy.

Health endpoints are public by design — Docker’s health checks depend on them, so never put auth in front of them.

Terminal window
curl http://localhost:8100/health # core proxy
{
"status": "healthy",
"service": "brutor-gateway-core",
"components": { "database": "up", "redis": "up" },
"stats": {
"active_mcp_sessions": 0,
"background_writes": { "scheduled": 12, "completed": 12, "failed": 0, "dropped": 0 }
}
}

The control plane answers on http://localhost:5050/health, and docker compose ps shows the per-container health-check state for everything else.

Surface URL Credentials
Admin Console http://localhost:3002 admin / Admin123! (tenant default)
User Portal http://localhost:3001 trial / Trial123!
MinIO console http://localhost:9001 minioadmin / minioadmin

There is also a seeded system admin (sysadmin / Admin123!) for cross-tenant administration. Change all of these before the gateway is reachable by anyone else — via SYSTEM_ADMIN_PASSWORD / DEFAULT_TENANT_ADMIN_PASSWORD in .env before first boot, or in the Admin Console afterwards.

After logging in, paste your provider API key (e.g. OpenAI) into the seeded models under AI Models — the models are enabled but can’t reach a provider until they have a key.