Skip to content

Architecture

One hexagonal (ports and adapters) package. Dependencies point inward and one test enforces it: tests/fusion/architecture/test_dependency_rules.py walks every import, lazy ones included.

inbound adapters (HTTP, MCP, CLI, SDK) --> application (services) --> ports (Protocols)
                                                  |                        ^
                                                  v                        | implemented by
                                           domain (pure)        outbound adapters (databases, DuckDB,
                                                                 sqlglot, LLM providers, catalog store)
truejoin.bootstrap wires adapters into the services.
Layer Package May import
Domain truejoin.domain.{olap,gateway,governance,catalog,mcp}, truejoin.domain.settings_catalog domain; pydantic for the catalog's file format
Ports truejoin.ports domain, ports, truejoin.application.settings; pydantic
Application truejoin.application.{olap,gateway,catalog,mcpgen} and its modules (settings, settings_service, access_keys, local_users, databases, usage, container) domain, ports, application; pydantic, structlog
Observability truejoin.observability domain, application, observability
Outbound adapters truejoin.adapters.outbound.* domain, ports, truejoin.application.settings, their drivers
Inbound adapters truejoin.adapters.inbound.{http,rest,mcp,cli,sdk} domain, ports, application, observability, truejoin.bootstrap, truejoin.runtime_mcpgen and their frameworks
Composition truejoin.bootstrap, truejoin.main anything

tests/fusion/architecture/test_dependency_rules.py holds the exact rules and checks every import, lazy ones included.

One process

truejoin serve runs one ASGI app: the REST gateway (a router per table), the OLAP routes at /olap, and the MCP tools over streamable HTTP at /mcp. The gateway's authentication guards all three: API keys, and bearer tokens from an identity provider, the directory or local users. --transport stdio serves the MCP tools alone. See ADR 10.

The gateway is async (asyncpg, aiomysql, aioodbc) and the OLAP engine is synchronous (DuckDB behind one lock). They meet on a loop thread that owns the OLAP side's database pools; see ADR 11.

The path of an analytical query

validate (SELECT only, no file or network functions)
  -> ground (joins against the declared keys: advise or repair)
  -> restrict (each table replaced by the caller's view of it: row rules, masks)
  -> plan (reuse what is loaded, load whole, load a slice, semi-join, or refuse)
  -> execute on DuckDB -> one audit event

Every surface reaches this path: query_data, /olap/query, query_metrics with a join, and answer_question. See ADR 12.

The catalog, and who may read it

Analysis writes a draft: descriptions, semantic types, relationships checked against the data, and business terms. A reviewer approves tables; the CLI review, which shows each table's term, certifies it as well. Only an approved catalog labels columns for masking, adds relationships to the join graph, names measures and answers questions. See ADR 13.