Explore this system
Step through curated paths without changing the source diagram.
Beat
Next
ReadyChapter 01 / 01
Guided chapter
Diagram guideExplore this system
Inspecting compiled semantics
E ExportT ThemeS Style0 Reset+ Zoom in- Zoom outEsc Close
Find a node
⌕/
No matching nodes
Semantic passport
Verified source
Authored reach
Route probeChoose a start node
Pick two semantic nodes on the diagram
Choose the source, then the destination. Direction matters.
Semantic lensCompare system roles
Choose up to two semantic kinds. One reveals its real traffic; two compare only direct authored relationships.
Choose a kind to inspect its nodes and touching relationships.
Semantic radar
Building overview
Click nodeDrag to pan
Semantic radar needs more MAP space.
Access control
• user_groups is a pure join table: its primary key is (user_id, group_id), so a membership cannot be duplicated.
• permissions grants one capability on one project to either a user or a group; both columns are nullable and exactly one is filled.
• groups.name is unique because SSO reconciles IdP groups by name, not by id.
• users carries a partial unique index on (oidc_issuer, oidc_subject): the SSO identity anchor, null for local accounts.
Project catalog
• projects nests through parent_id, a self-reference not drawn as an arrow; RBAC is checked against the project.
• flow_versions keeps immutable definition history, unique on (flow_id, version).
• connections and datasources are each unique on (project_id, name), so names only have to be unique inside a project.
• A datasource can come from a flow, from a database connection, or from neither: all three columns are nullable.
• dbt_ai_texts remembers what the AI wrote for a flow's dbt export, per flow version, so exporting the same version again gives the same files.
Execution
• runs.parent_run_id is a self-reference, not drawn: a parent run fans out into child runs.
• runs is also the orchestration queue: a PENDING row is claimed by one orchestrator (claimed_by) with a conditional update and kept alive by heartbeat_at.
• runs.datasource_id and datasources.snapshot_run_id point at each other, so snapshot_run_id is a plain integer with no foreign key to avoid a circular constraint.
• started_at includes queue time; engine_started_at records when the engine actually began, and drives the staleness clock.
• pending_blob_deletions deliberately has no foreign key: it must survive the rows whose objects it deletes.
Foreign keys listed, not drawn
• Ownership and actor columns all point at users.id: projects.owner_id, flows.owner_id, connections.owner_id, datasources.owner_id, saved_views.owner_id, runs.launched_by, flow_versions.created_by, ai_models.updated_by, privacy_notice.updated_by, disabled_engines.disabled_by.
• Publish target: runs.publish_project_id points at projects.id. saved_views.project_id points at projects.id like every catalog object.
• Self-references: projects.parent_id and runs.parent_run_id.
• All 35 foreign keys are nullable except the project_id columns, so deleting a user never cascades away history.
Replicas and contracts
• login_attempts and flow_presence moved out of process memory so the gateway can run in several copies; the scheduler sweeps the expired rows.
• alembic_version holds the schema's revision: a migration step moves it forward before a release, and a gateway refuses a database behind its code.
• data_contracts, data_contract_versions and data_contract_results carry no foreign key on purpose: SQLite does not enforce them in tests, and the rows are removed explicitly when the datasource goes.
• data_contract_results.notify is the outbox of the alerts: pending rows are sent by the scheduler, claimed one at a time.