Architecture
How Trellaris is built, and why it is built that way. The through-line is a single rule: there is one path that runs an agent. Every surface, every trigger, and every integration converges on it, which is what lets one governance decision be authoritative everywhere instead of being re-implemented per feature.
The rules the code obeys
| Rule | What it buys |
|---|---|
| One execution path | Chat, automations, channels, the orchestrator, A2A and MCP all call the same executor. One event bus, one usage ledger, one set of governance checks — no surface can drift into a second set of semantics. |
| Tenancy is not optional | Every repository extends a base that force-injects the organization filter and the visibility predicate. There is no raw database access outside a repository, so a query cannot return another tenant's row. |
| Strict layering | Router handles HTTP, service holds business logic, repository owns all data access. No logic in routers, no queries outside repositories. |
| Modules talk through services | A module never reads another module's tables or imports its models. Cross-module references are a UUID plus a service call, so a module can be reasoned about — and changed — on its own. |
| Dependency inversion at every seam | Model providers, storage, the sandbox, policy providers, tool discovery, identity — each sits behind an interface with a deterministic default. Swapping a real backend in is configuration, not a rewrite, and CI can run without any of them. |
| One error contract | Every failure is a problem document with a stable machine-readable code — including terminal events on a stream — so clients handle errors identically wherever they arise. |
| Immutability where it matters | Agent versions, skill versions, and canvas revisions are immutable with a pointer to the current one. Rollback moves a pointer; nothing is mutated or lost. |
How the pieces fit together
The functional picture: what an agent is made of, what it is granted, where requests come from, and what a run leaves behind.
Reading the diagram
- The workspace and ring are the outer frame: every entity inside belongs to one organization and is visible only within its ring.
- The agent is the only thing that runs. Skills, knowledge, and memory are attached to it; tools, connectors, and MCP servers are granted by it.
- Governance sits on the way in, not beside it. A surface cannot route around it, because the surface's only route to the agent goes through the executor.
- The run record is what makes the whole thing auditable after the fact — events for replay, an artifact set, a cost line, the highest classification label the run read (its taint), and audit entries.
- Canvas appears as a surface because that is what it is: a shared document the agent writes to and the human edits back, with each revision immutable.
What happens on every run
The same six steps regardless of what triggered the work. The right-hand column is where governance actually lands — each check is at a specific point, not a general aspiration.
What the loop actually does
- Assembly is per run, not per agent. The tool registry is rebuilt every time from the declaration plus configuration, so a policy change or a revoked credential takes effect on the next run without touching the definition.
- The system prompt is composed from the guardrail preamble, the agent body, a compact skill index, and injected context — memories, knowledge citation rules, attached documents, and a canvas diff if the human edited past the agent.
- The model turn streams. Tokens, tool calls, and status land on the run's event stream as they happen, so the interface is live and the same sequence can be replayed later.
- Tool results are treated as data. They are screened before re-entering the model — this is where a prompt-injection attempt planted in a fetched page is caught — and a labelled result raises the run's taint, which the data perimeter reads before the next outbound call.
- Parking is real suspension. Loop state is serialized to the run row; the worker is released. Approving reconstructs the loop and executes the call exactly once.
- Nothing is left hanging. Workers hold a heartbeat lease. If one dies, a reaper resolves the run — resuming a background run from its last checkpoint, or failing it with a reason.
Deployment topology
Four planes and one ingress. Each service scales independently, and the documentation site you are reading is its own container with no runtime dependency on the API.
What each plane is responsible for
| Plane | Contains | Scales on |
|---|---|---|
| Service | The Next.js web app (which also proxies /api and /docs in development), the FastAPI application, the MCP gateway as its own deployment, and this documentation container. | Request volume; the gateway independently, since it is the external face. |
| Async | Celery workers on per-class queues — runs, scans, ingest — plus the beat scheduler for dispatch, sweeps, and rollups, and one ephemeral sandbox container per code execution. | Run volume and scan/ingest backlog. |
| Data | A relational database with a vector store for rows and embeddings alike, a distributed cache for queues and per-run event streams, object storage for documents and artifacts, Keycloak as the identity broker. | Managed externally at the team and enterprise tiers. |
| External | Model providers, MCP servers, SaaS systems reached through connectors, and A2A peers. | — |
Module map
The API is one deployable but fifteen owned boundaries. Each module owns its tables and exposes a service; nothing reaches across. The worker runs the same code base, so a module is a module in both processes.
| Module | Owns | Code |
|---|---|---|
| Identity & tenancy | Organizations, teams, users, API keys, SCIM, login hooks, line managers for approval routing, the sign-in log, the ADR-001 visibility primitive and the ring/default workspaces it filters on | identity/ |
| Workspaces | Named workspaces and membership, the board, the shared memory pool, activity, stage-gated processes, blueprints, settings and agent instructions, document templates, and Word/PowerPoint/Excel generation | workspace/ (membership rows and the crossing live with identity/ and promotion/) |
| Registry & definitions | Agents, skills, versions, validation, the catalog, @handles, built-in skills, the template pack | registry/ |
| Marketplace, evals & promotion | Scanning, evaluation sets and runs, continuous sampling, the promotion pipeline (agents and whole workspaces), reviews, packs | marketplace/ · evals/ · promotion/ |
| Runtime & execution | The one executor, LLM gateway, sandbox, DLP, memory, approvals, the run's taint and the perimeter gate, background runs, fan-out, charts, the keyless market and weather tools | runtime/ |
| Knowledge | Collections, ingestion, chunk search with source ACLs, classification labels on collections, documents and chunks (with the map for synced sensitivity labels), citations, the sync connectors (Confluence, Google Drive, Gmail, Google Calendar, Outlook, Teams) | knowledge/ |
| Avatar & knowledge graph | One avatar per user: profile, configuration and its versions, consents, activity, the owner-scoped reads, and the personal knowledge graph (nodes, edges, proposals, extraction, recall) | avatar/ · avatar/graph/ |
| Interop & adapters | A2A server and client, the MCP server, A2UI surfaces, connectors, delegated credentials, secrets, the Tools Library, the MCP gateway | interop/ · connectors/ · credentials/ · secrets/ · tools_library/ · gateway/ |
| Conversations & channels | Threads and the surface each belongs to (chat, workspace, document, research), messages, the Slack and Teams adapters, canvas documents and their images, voice sessions | conversations/ |
| Automation & notifications | Cron, webhook, watch, email-in and wake-up triggers, the dispatcher, delivery, the inbox | automation/ · notifications/ |
| Orchestrator | The supervisor graph, capability-filtered routing, checkpoints, the durable task tree | orchestrator/ |
| Built-in agents & authoring | The Assistant, Architect, ITHelp, IT Fulfilment, Travel Desk, Market Watcher, Weather, Document Author, the four Knowledge Agents and their corpus; Deep Research and its research engine (plan approval, parallel researchers, the evidence store, verification); per-org seeding on login; skill proposals; the Playground | assistant/ · architect/ · research/ · ithelp/ · it_fulfilment/ · travel_desk/ · markets/ · weather_agent/ · doc_author/ · knowledge_agents/ · onboarding/ · workshop/ · playground/ |
| Governance & admin | The policy engine with floors, DLP simulation, the classification vocabulary and the data perimeter, breakers, incidents and their containment actions, kill switches, audit, legal holds, privacy tiers, risk, compliance, blueprints | governance/ · incidents/ · admin/ · audit/ · risk/ · compliance/ |
| Analytics & observability | Rollups, dashboards, budgets, SLOs, showback, the opportunity miner; the durable run trace behind Observe, with each run's taint | analytics/ · observability/ |
| Platform | Database session and base repository, migrations, crypto, object storage drivers, cache, telemetry, the worker's event loop | platform/ · workers/ |
Data model highlights
| Structure | Why it is shaped that way |
|---|---|
| Immutable versions + a current pointer | Rollback is a pointer move, promotion can copy and freeze a known-good version, and a diff between any two versions is always available. |
| A run state machine | A transition table is the single gate on every status write, so parked, resumed, reaped and cancelled runs can never reach an undefined state. |
| A per-run event stream | One append-only sequence serves live streaming, the task tree, and later replay — the interface and the audit view read the same data. |
| Message parts | A message is a list of typed parts (text, tool call, artifact update, data card), so approval prompts, connect cards, canvas updates, and citations all persist without schema churn. |
| A usage ledger | One row per run, including delegated children and remote invocations, so a cost figure is derived from records rather than estimated. |
| Policies as rows | Kind, scope, and config in one table read by one engine — governance is data, editable at runtime, not code that needs a release. |
| A label column everywhere, a taint on the run | Every content row carries its classification, so the label comes back with the chunk in the same read; the run keeps the highest label it has seen, so the decision about what may leave is a comparison, not a search. See Data classification. |
| One open incident per agent and kind | A partial unique index makes a second trigger append to the open incident rather than open a twin, so the record of what happened and what was done stays in one place. |
| Embeddings alongside rows | The vector store keeps semantic search in the same database and the same transaction as the visibility filter, so search can never leak what a query would not return. |
Extension seams
Every external dependency sits behind an interface with a deterministic default, which is why the whole test suite runs with no cloud account and no model provider.
| Seam | Default | Production swap |
|---|---|---|
| LLM driver | Scripted fake | Anthropic, OpenAI/Azure, Google |
| Embeddings | Deterministic feature-hashed | A provider embedding model |
| Object storage | MinIO | S3 or Azure Blob |
| Sandbox driver | Docker, or disabled | Kubernetes: one hardened Pod per run, under gVisor or Kata |
| Policy providers | Allow-all stubs | The database-backed engine |
| MCP tool discovery | Injected inventory | A live MCP connection |
| Agent identity provider | Platform-signed tokens | Directory service accounts |
| A2A card verifier | Shared-secret | Real signature verification |
| Injection detector | Pattern matcher | A trained classifier |
| Policy lint | Deterministic backend | A model-backed reviewer |
| Voice realtime | Fake driver | A realtime speech provider |
| Browser driver | Mock | Headless Chromium |
| Breaker trip dispatcher | In-process, on its own session | A worker task, so a trip never runs inside the run that produced the signal |
| Source sensitivity labels | Unmapped labels land as Confidential with a warning | A per-organisation map from Drive or Microsoft 365 labels |
| Knowledge connectors | Mock services | Confluence, Google Drive tenants |
| Channel adapters | Mock transports | Slack, Bot Framework |
Tech stack
| Layer | Choice |
|---|---|
| API | Python 3.12 · FastAPI · Pydantic v2 · SQLAlchemy 2 (async) · Alembic |
| Async work | Celery 5 with per-class queues, plus a beat scheduler |
| Database | A relational database with a vector extension — every durable row, every embedding, and the avatar knowledge graph (typed nodes and edges as ordinary tables; no graph database) |
| Cache, queue & streams | A distributed cache (an in-cluster engine locally, a managed cache in the cloud) — Celery queues, run event streams, rate limits, the policy cache, locks |
| Object storage | S3-compatible (MinIO locally) or Azure Blob, behind one driver interface; presigned downloads |
| Secrets | Envelope encryption (AES-256-GCM) in the secrets table for tokens and BYO keys; a cloud secret store synced into pods by the secrets CSI driver |
| Identity | Keycloak as the IdP broker · Auth.js in the web app · platform-issued JWTs for people and per-agent identities |
| Model providers | Anthropic · OpenAI-compatible (OpenAI, Azure OpenAI) · Google Gemini, behind one gateway with tiers, auto-routing and failover; a scripted fake in CI |
| Orchestration | LangGraph with a database checkpointer — the orchestrator's supervisor graph and the research engine |
| Web | Next.js 15 · React 19 · TypeScript · Tailwind CSS · shadcn/ui · Monaco · Apache ECharts |
| Interactive surfaces | A2UI v0.9 declarative blueprints, validated against a trusted catalog; charts as full ECharts options validated at one boundary |
| Voice | The browser's own SpeechRecognition and speechSynthesis — no audio reaches the platform; org switches to turn either off |
| Docs site | Static HTML served by nginx — no build step, no runtime dependencies |
| Sandbox | One isolated workload per run on python3.12 + Node 20, no network, no package managers — a child container in development, a hardened pod (gVisor or Kata) in production; secure code execution |
| Observability | OpenTelemetry (GenAI semantic conventions) · Prometheus · structlog · a durable per-run trace in the database behind Observe |
| Packaging & deploy | Docker (multi-stage, production stage by default) · Helm (starter, team, enterprise and cloud value sets) · GitHub Actions · infrastructure as code for the cloud environment |
| Quality gates | ruff · mypy --strict · pytest · ESLint · Prettier · Playwright · a license allowlist |
Open-source packages
Everything Trellaris depends on is OSI open source, and CI enforces an allowlist on every build so a proprietary transitive dependency fails the pipeline rather than shipping. Licenses below are as published by each project; the build's own check is the authoritative gate.
API — runtime
| Package | License | Used for |
|---|---|---|
fastapi | MIT | The HTTP application and OpenAPI schema |
uvicorn[standard] | BSD-3-Clause | ASGI server |
pydantic, pydantic-settings | MIT | Schemas, validation, typed configuration |
sqlalchemy[asyncio] | MIT | ORM and query construction |
alembic | MIT | Database migrations |
asyncpg | Apache-2.0 | Async PostgreSQL driver |
pgvector | MIT | Vector column and index support |
celery | BSD-3-Clause | Background workers and the beat scheduler |
redis | MIT | Queues, run event streams, rate limits, semaphores |
httpx | BSD-3-Clause | All outbound HTTP — connectors, A2A, MCP over SSE |
sse-starlette | BSD-3-Clause | Server-sent events for run streaming |
tenacity | Apache-2.0 | Retries with jittered backoff |
structlog | MIT or Apache-2.0 | Structured logging |
python-json-logger | BSD-2-Clause | JSON log formatting |
prometheus-client | Apache-2.0 | Metrics endpoint |
opentelemetry-sdk, -exporter-otlp, -instrumentation-fastapi | Apache-2.0 | Tracing with GenAI semantic conventions |
boto3 | Apache-2.0 | S3-compatible object storage |
azure-storage-blob, azure-identity | MIT | Azure Blob object storage, signed with a workload identity rather than a stored key |
pyjwt[crypto] | MIT | Platform JWTs and IdP token verification |
email-validator | CC0-1.0 | Email address validation |
dnspython | ISC | DNS TXT lookups for domain capture |
python-multipart | Apache-2.0 | Multipart uploads for skill bundles and documents |
pip-audit | Apache-2.0 | The dependency-audit scan stage |
anthropic | MIT | Anthropic model driver |
openai | Apache-2.0 | OpenAI and Azure OpenAI driver, embeddings |
google-genai | Apache-2.0 | Google Gemini driver |
docker | Apache-2.0 | Sandbox driver in development — ephemeral containers |
kubernetes | Apache-2.0 | Sandbox driver in production — one hardened Pod per run |
playwright | Apache-2.0 | The real browser engine behind browser_use (worker image only) |
mcp | MIT | MCP client and server |
croniter | MIT | Cron validation and DST-safe next-run computation |
pypdf | BSD-3-Clause | PDF text extraction for knowledge ingestion |
python-docx | MIT | DOCX text extraction and Word generation |
python-pptx | MIT | PowerPoint generation, slide previews and reopening a deck for editing |
openpyxl | MIT | Excel generation |
trafilatura | Apache-2.0 | A web page's main text, title and date for web_fetch |
maxminddb | Apache-2.0 | Reads the local IP-location database for the sign-in log |
jsonschema | MIT | Evaluation assertions and approval-edit validation |
qrcode | BSD-3-Clause | The qr_code tool |
langgraph | MIT | The orchestrator's supervisor graph and the research engine |
langgraph-checkpoint-postgres | MIT | Durable graph checkpoints |
psycopg[binary] | LGPL-3.0 | Driver for the checkpointer |
hatchling | MIT | Build backend |
Agent-format package
| Package | License | Used for |
|---|---|---|
pydantic | MIT | Frontmatter schema and validation |
python-frontmatter | MIT | Splitting YAML frontmatter from the markdown body |
pyyaml | MIT | YAML parsing |
API — development and CI
| Package | License | Used for |
|---|---|---|
pytest, pytest-asyncio | MIT · Apache-2.0 | Test runner and async support |
anyio | MIT | Async test utilities |
moto[s3] | Apache-2.0 | Mock S3 for storage tests |
respx | BSD-3-Clause | httpx request mocking |
ruff | MIT | Linting and formatting |
mypy | MIT | Strict static typing |
pip-licenses | MIT | The CI license allowlist gate |
boto3-stubs[s3] | MIT | Type stubs |
Web application
| Package | License | Used for |
|---|---|---|
next | MIT | The application framework and BFF proxy |
react, react-dom | MIT | UI runtime |
next-auth (Auth.js) | ISC | Sign-in, session handling, the assertion exchange |
next-themes | MIT | Light and dark theming |
@monaco-editor/react | MIT | The definition editor |
@radix-ui/react-slot | MIT | Composable component primitives |
class-variance-authority | Apache-2.0 | Component variant styling |
clsx, tailwind-merge | MIT | Class-name composition |
lucide-react | ISC | Icons |
react-markdown, remark-gfm | MIT | Rendering agent responses, with citation pills for knowledge chunks |
echarts, echarts-for-react | Apache-2.0 · MIT | Charts in chat — the agent's ECharts option, drawn theme-aware with full screen, PNG export and click-a-point |
simple-icons | CC0-1.0 | Brand marks on connector and source cards |
mermaid | MIT | Diagrams in documents and chat |
tailwindcss, tailwindcss-animate | MIT | Styling system |
| shadcn/ui | MIT | Component source vendored into the app |
Web — development and CI
| Package | License | Used for |
|---|---|---|
typescript | Apache-2.0 | Type checking |
eslint, eslint-config-next | MIT | Linting |
prettier, prettier-plugin-tailwindcss | MIT | Formatting |
postcss, autoprefixer | MIT | CSS pipeline |
@playwright/test | Apache-2.0 | End-to-end golden-path tests |
@types/* | MIT | Type definitions |
Security scanners
Pinned binaries baked into the API and worker image, each reporting its own stage.
| Tool | License | Stage |
|---|---|---|
| Opengrep | LGPL-2.1 | Static analysis, against Trellaris-authored OWASP LLM Top 10 rules |
| gitleaks | MIT | Secret detection in skill bundles |
| Trivy | Apache-2.0 | Optional container and npm dependency audit |
| pip-audit | Apache-2.0 | Python dependency CVE audit |
Sandbox image
Preinstalled so sandboxed scripts start fast and work entirely offline — the container has no network, so nothing can be installed at run time.
| Package | License |
|---|---|
numpy, pandas | BSD-3-Clause |
matplotlib | PSF-based (Matplotlib licence) |
pillow | MIT-CMU (HPND) |
python-dateutil | Apache-2.0 and BSD-3-Clause |
requests | Apache-2.0 |
beautifulsoup4 | MIT |
reportlab | BSD-3-Clause |
fpdf2 | LGPL-3.0 |
pypdf | BSD-3-Clause |
python-docx, python-pptx, openpyxl | MIT |
Infrastructure
| Component | License | Role |
|---|---|---|
| PostgreSQL | PostgreSQL Licence | Primary datastore |
| pgvector | PostgreSQL Licence | Embedding storage and similarity search |
| Valkey | BSD-3-Clause | Queues, run streams, rate limits — chosen over Redis to keep the stack permissive |
| MinIO | AGPL-3.0 | Local S3-compatible storage; run unmodified as a service, and replaceable with S3, Azure Blob, or an Apache-2.0 alternative |
| Azurite | MIT | Local Azure Blob emulator |
| DB-IP City Lite | CC BY 4.0 | IP-to-location data for the sign-in log, read locally — never sent to a lookup service |
| Keycloak | Apache-2.0 | Identity broker for enterprise SSO |
| nginx | BSD-2-Clause | Serves this documentation site |
| Docker | Apache-2.0 | Images and the sandbox runtime |
| Helm | Apache-2.0 | Kubernetes packaging |
| Kubernetes | Apache-2.0 | The deployment target; one chart, one cluster per environment |
| ingress-nginx · cert-manager | Apache-2.0 | The ingress with SSE-friendly timeouts, and TLS certificates from Let's Encrypt |
| KEDA | Apache-2.0 | Installed as a cluster add-on for queue-depth worker scaling; the chart ships no scaler object yet |
| Playwright | Apache-2.0 | The browser image the end-to-end tests and the docs screenshots run in |
| gVisor · Kata Containers | Apache-2.0 | Optional hardened sandbox runtime classes |