Platform

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

RuleWhat 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.

Functional composition: surfaces enter through governance into the agent, which is granted tools and attaches skills, knowledge and memory, and produces a run record Organization · workspace · ring — everything inside is tenancy-scoped Chat Canvas Voice Slack · Teams Automations API · MCP · A2A Governance policy engine · DLP · allowlists · approvals · budgets · injection screen · audit Skills playbooks, loaded on demand Knowledge collections · cited retrieval Memory durable user / agent facts Agent Agent.md identity · tool grant policy boundary Built-in tools web · code · media · utilities Connectors delegated OAuth, as the user MCP servers Tools Library · gateway Run record events · messages · artifacts usage ledger · audit trail A skill may only use tools the agent already grants — it packages procedure, never access. A connector is a tool that carries the user's own identity. An MCP server is a tool surface resolved through the library. Platform services PostgreSQL + pgvector · Redis/Valkey streams · object storage · ephemeral sandbox · the LLM gateway Reached only through interfaces — a router never touches them directly.
Functional composition. Requests enter from any surface, pass governance, and reach the agent — the one entity that carries identity, holds the tool grant, and forms the policy boundary.

Reading the diagram

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.

The six steps of a run, from trigger to persistence, with the governance check enforced at each step Execution Enforced at that step 1 · Trigger chat · schedule · webhook · channel · A2A · MCP Plan gates on automation create · webhook signature verification and rate limit · scoped API key 2 · Run created queued, then leased by a worker Emergency stop · agent paused or killed · budget hard stop, refused before a run row exists 3 · Assemble tool registry + system prompt for this run Tool allowlist — a denied MCP server is hidden, not failed · autonomy = min(declared, org floor) 4 · Model turn streamed through the LLM gateway DLP on input · model allowlist · tokens and cost metered · auto-routing with cross-provider failover 5 · Tool call executed, then fed back to the model Writes park for approval · DLP on arguments and results · gateway rate limit and budget · injection screen 6 · Deliver and persist the answer, its artifacts, and the record DLP on output · events, messages, artifacts, usage ledger and audit — all of it replayable afterwards next turn
Steps 4 and 5 loop until the agent has an answer or hits its turn cap. A parked approval suspends the loop here, with its state saved, and resumes it on a decision.

What the loop actually does

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.

Deployment topology: clients reach an ingress that fronts the service plane, which uses an async plane, a data plane, and governed egress to external systems Clients Browser Slack · Teams MCP & A2A clients IdP · SCIM directory TLS ingress — / → web · /api → api · /docs → docs · /gateways → gateway Service plane Web Next.js · BFF proxy API FastAPI · the executor Gateway external MCP face Docs nginx · this site Async plane Worker Celery — runs · scans · ingest Beat dispatch · sweeps · rollups Sandbox ephemeral, network-less Data plane PostgreSQL + pgvector Valkey / Redis queues · run streams Object storage S3 · MinIO · Azure Blob Keycloak IdP broker External — governed egress Model providers via the LLM gateway MCP servers via the Tools Library SaaS systems via connectors, as the user A2A peers signed agent cards
Outbound traffic originates from the API, the worker, and the gateway. The sandbox has no network at all, so nothing a model writes can call out from inside it.

What each plane is responsible for

PlaneContainsScales on
ServiceThe 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.
AsyncCelery 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.
DataPostgreSQL with pgvector for rows and embeddings alike, Valkey 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.
ExternalModel providers, MCP servers, SaaS systems reached through connectors, and A2A peers.
Why the sandbox is a separate concern Code the model wrote runs in a throwaway container with no network, a non-root user, dropped capabilities, capped memory, CPU, process count and wall clock, and no package managers installed. Files move in and out as archives rather than host mounts. In production the container runtime class swaps to a stronger isolation boundary — a configuration change, not a code change.

Module map

The API is one deployable but twelve owned boundaries. Each module owns its tables and exposes a service; nothing reaches across.

ModuleOwnsCode
Identity & tenancyOrganizations, teams, users, workspaces, API keys, SCIM, the visibility primitiveidentity/
Registry & definitionsAgents, skills, versions, validation, the catalogregistry/
Marketplace & promotionScanning, promotion pipeline, evaluations, reviews, packsmarketplace/ · promotion/ · evals/
Runtime & executionThe executor, LLM gateway, sandbox, DLP, memory, approvalsruntime/ · knowledge/
Interop & adaptersA2A, MCP client and server, connectors, credentials, secrets, Tools Library, gatewayinterop/ · connectors/ · credentials/ · secrets/ · tools_library/ · gateway/
Conversations & channelsThreads, messages, Slack and Teams adapters, canvas, voiceconversations/
AutomationTriggers, the dispatcher, delivery, notificationsautomation/ · notifications/
OrchestratorThe supervisor graph, routing, checkpointsorchestrator/
Workshop & meta-agentsSkill proposals, the Architect, Deep Research, the default Assistantworkshop/ · architect/ · research/ · assistant/
Governance & adminThe policy engine, kill switches, audit, legal holds, privacy tiers, risk, compliancegovernance/ · admin/ · audit/ · risk/ · compliance/
Analytics & observabilityRollups, dashboards, budgets, SLOs, showback, the opportunity mineranalytics/
PlatformDatabase session and base repository, migrations, crypto, storage, cache, telemetryplatform/ · workers/

Data model highlights

StructureWhy it is shaped that way
Immutable versions + a current pointerRollback 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 machineA 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 streamOne append-only sequence serves live streaming, the task tree, and later replay — the interface and the audit view read the same data.
Message partsA 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 ledgerOne row per run, including delegated children and remote invocations, so a cost figure is derived from records rather than estimated.
Policies as rowsKind, scope, and config in one table read by one engine — governance is data, editable at runtime, not code that needs a release.
Embeddings alongside rowspgvector 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.

SeamDefaultProduction swap
LLM driverScripted fakeAnthropic, OpenAI/Azure, Google
EmbeddingsDeterministic feature-hashedA provider embedding model
Object storageMinIOS3 or Azure Blob
Sandbox driverDocker, or disabledA hardened runtime class
Policy providersAllow-all stubsThe database-backed engine
MCP tool discoveryInjected inventoryA live MCP connection
Agent identity providerPlatform-signed tokensDirectory service accounts
A2A card verifierShared-secretReal signature verification
Injection detectorPattern matcherA trained classifier
Policy lintDeterministic backendA model-backed reviewer
Voice realtimeFake driverA realtime speech provider
Browser driverMockHeadless Chromium
Knowledge connectorsMock servicesConfluence, Google Drive tenants
Channel adaptersMock transportsSlack, Bot Framework

Tech stack

LayerChoice
APIPython 3.12 · FastAPI · Pydantic v2 · SQLAlchemy 2 (async) · Alembic
Async workCelery 5 with per-class queues, plus a beat scheduler
DatabasePostgreSQL with the pgvector extension
Cache, queue & streamsValkey (Redis-compatible)
Object storageS3-compatible (MinIO locally) or Azure Blob
IdentityKeycloak as the IdP broker · Auth.js in the web app · platform-issued JWTs
OrchestrationLangGraph with a PostgreSQL checkpointer
WebNext.js 15 · React 19 · TypeScript · Tailwind CSS · shadcn/ui · Monaco
Docs siteStatic HTML served by nginx — no build step, no runtime dependencies
SandboxEphemeral containers on python3.12 + Node 20, no network, no package managers
ObservabilityOpenTelemetry (GenAI semantic conventions) · Prometheus · structlog
Packaging & deployDocker · Helm (starter, team, enterprise value sets) · GitHub Actions
Quality gatesruff · 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

PackageLicenseUsed for
fastapiMITThe HTTP application and OpenAPI schema
uvicorn[standard]BSD-3-ClauseASGI server
pydantic, pydantic-settingsMITSchemas, validation, typed configuration
sqlalchemy[asyncio]MITORM and query construction
alembicMITDatabase migrations
asyncpgApache-2.0Async PostgreSQL driver
pgvectorMITVector column and index support
celeryBSD-3-ClauseBackground workers and the beat scheduler
redisMITQueues, run event streams, rate limits, semaphores
httpxBSD-3-ClauseAll outbound HTTP — connectors, A2A, MCP over SSE
sse-starletteBSD-3-ClauseServer-sent events for run streaming
tenacityApache-2.0Retries with jittered backoff
structlogMIT or Apache-2.0Structured logging
python-json-loggerBSD-2-ClauseJSON log formatting
prometheus-clientApache-2.0Metrics endpoint
opentelemetry-sdk, -exporter-otlp, -instrumentation-fastapiApache-2.0Tracing with GenAI semantic conventions
boto3Apache-2.0S3-compatible object storage
azure-storage-blobMITAzure Blob object storage
pyjwt[crypto]MITPlatform JWTs and IdP token verification
email-validatorCC0-1.0Email address validation
dnspythonISCDNS TXT lookups for domain capture
python-multipartApache-2.0Multipart uploads for skill bundles and documents
pip-auditApache-2.0The dependency-audit scan stage
anthropicMITAnthropic model driver
openaiApache-2.0OpenAI and Azure OpenAI driver, embeddings
google-genaiApache-2.0Google Gemini driver
dockerApache-2.0Sandbox driver — ephemeral containers
mcpMITMCP client and server
croniterMITCron validation and DST-safe next-run computation
pypdfBSD-3-ClausePDF text extraction for knowledge ingestion
python-docxMITDOCX text extraction
jsonschemaMITEvaluation assertions and approval-edit validation
qrcodeBSD-3-ClauseThe qr_code tool
langgraphMITThe orchestrator's supervisor graph
langgraph-checkpoint-postgresMITDurable graph checkpoints
psycopg[binary]LGPL-3.0Driver for the checkpointer
hatchlingMITBuild backend

Agent-format package

PackageLicenseUsed for
pydanticMITFrontmatter schema and validation
python-frontmatterMITSplitting YAML frontmatter from the markdown body
pyyamlMITYAML parsing

API — development and CI

PackageLicenseUsed for
pytest, pytest-asyncioMIT · Apache-2.0Test runner and async support
anyioMITAsync test utilities
moto[s3]Apache-2.0Mock S3 for storage tests
respxBSD-3-Clausehttpx request mocking
ruffMITLinting and formatting
mypyMITStrict static typing
pip-licensesMITThe CI license allowlist gate
boto3-stubs[s3]MITType stubs

Web application

PackageLicenseUsed for
nextMITThe application framework and BFF proxy
react, react-domMITUI runtime
next-auth (Auth.js)ISCSign-in, session handling, the assertion exchange
next-themesMITLight and dark theming
@monaco-editor/reactMITThe definition editor
@radix-ui/react-slotMITComposable component primitives
class-variance-authorityApache-2.0Component variant styling
clsx, tailwind-mergeMITClass-name composition
lucide-reactISCIcons
react-markdown, remark-gfmMITRendering agent responses
tailwindcss, tailwindcss-animateMITStyling system
shadcn/uiMITComponent source vendored into the app

Web — development and CI

PackageLicenseUsed for
typescriptApache-2.0Type checking
eslint, eslint-config-nextMITLinting
prettier, prettier-plugin-tailwindcssMITFormatting
postcss, autoprefixerMITCSS pipeline
@playwright/testApache-2.0End-to-end golden-path tests
@types/*MITType definitions

Security scanners

Pinned binaries baked into the API and worker image, each reporting its own stage.

ToolLicenseStage
OpengrepLGPL-2.1Static analysis, against Trellaris-authored OWASP LLM Top 10 rules
gitleaksMITSecret detection in skill bundles
TrivyApache-2.0Optional container and npm dependency audit
pip-auditApache-2.0Python dependency CVE audit
Why Opengrep and not Semgrep The Semgrep engine is open source, but its community rule registry moved to a source-available license with commercial-use restrictions. Trellaris uses the LGPL fork with its own rule set, so the whole scanning path stays OSI open source.

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.

PackageLicense
numpy, pandasBSD-3-Clause
matplotlibPSF-based (Matplotlib licence)
pillowMIT-CMU (HPND)
python-dateutilApache-2.0 and BSD-3-Clause
requestsApache-2.0
beautifulsoup4MIT
reportlabBSD-3-Clause
fpdf2LGPL-3.0
pypdfBSD-3-Clause

Infrastructure

ComponentLicenseRole
PostgreSQLPostgreSQL LicencePrimary datastore
pgvectorPostgreSQL LicenceEmbedding storage and similarity search
ValkeyBSD-3-ClauseQueues, run streams, rate limits — chosen over Redis to keep the stack permissive
MinIOAGPL-3.0Local S3-compatible storage; run unmodified as a service, and replaceable with S3, Azure Blob, or an Apache-2.0 alternative
AzuriteMITLocal Azure Blob emulator
KeycloakApache-2.0Identity broker for enterprise SSO
nginxBSD-2-ClauseServes this documentation site
DockerApache-2.0Images and the sandbox runtime
HelmApache-2.0Kubernetes packaging
gVisor · Kata ContainersApache-2.0Optional hardened sandbox runtime classes