v26.10.0 — Data Studio

Wurk.Flow User Manual

Wurk.Flow is a modern Data Engineering and End-to-End Analytics IDE. It combines a visual schema canvas, SQL query workbench, data lineage tracker, and STTM mapping tool into a single application for data architects, engineers, and analysts — native on Windows, macOS and Linux, or in your browser as the Web Studio at app.wurk.haus.

Built with Vue 3 · Electron · DuckDB WASM · Tailwind CSS

1 · Install Wurk.Flow

About 5 minutesFreeDesktop or browser

There are two ways to run Wurk.Flow. Both start on the Free edition, with no email address and no card.

Option Best for Get it
Web Studio Trying it right now, quick models, a locked-down machine you cannot install on Open app.wurk.haus in Chrome, Edge or Firefox. Nothing to install. See Web Studio for what differs from the desktop app.
Desktop app Live database connections, Git, Co-Op, working fully offline Download for your OS from wurk.haus/download

Desktop install by platform

  1. macOS — Open the .dmg and drag Wurk.Flow into Applications. Builds are signed and notarized by Apple, so Gatekeeper opens them normally. Apple Silicon (M1 and later) only. If you still see an "app is damaged" dialog, see Troubleshooting.
  2. Windows — Run the code-signed .exe installer and follow the prompts.
  3. Linux — Make the AppImage executable and run it. No package manager, no root:
    chmod +x Wurk.Flow-26.10.0.AppImage
    ./Wurk.Flow-26.10.0.AppImage

First run

  1. The canvas opens with two sample tables (Users and Posts) and a short guided tour. Take the tour, or dismiss it and come back later.
  2. Press ? for the shortcut overlay and +K for the Command Palette. Everything in this manual is reachable from the palette. On Windows and Linux, read as Ctrl throughout.
  3. Confirm the version: +KAbout Wurk.Flow DataStudio. The desktop app never updates itself in the background; Check for Updates in that window is the only time it contacts the release feed, and installing is a second explicit click.

What Free includes. Up to 15 tables and views on the canvas, DDL in DuckDB, PostgreSQL and SQLite, one SQL Workspace tab on the built-in DuckDB sandbox, DBML import and export, DDL export, and PNG/PDF diagram export. Free exports start with a short "Generated by Wurk.Flow Community Edition" comment. Everything else is marked PRO or TEAM in this manual; Licensing has the full comparison.

Next: 2 · Your First Model.

2 · Your First Model

About 10 minutesFreeDesktop and Web Studio

Build a two-table schema from scratch, watch the DDL write itself, and save the project.

  1. Start clean. +KNew Schema (or +N).
  2. Add a table. Click + Add Table in the toolbar, or run Add Table from the palette. Click the new node to open the Table Properties Drawer on the right and rename it customers.
  3. Add columns. In the drawer add id (INTEGER, PK), email (VARCHAR, Unique, Not Null) and created_at (TIMESTAMP). Every constraint you tick shows up in the DDL immediately.
  4. Add a second table called orders with id (PK), customer_id (INTEGER) and total (DECIMAL).
  5. Draw the relationship. Drag from the handle beside customers.id to orders.customer_id. The edge becomes a foreign key; click it to set cardinality and the ON DELETE / ON UPDATE actions in the Edge Properties Drawer.
  6. Group it. Add Subject Area, drop both tables inside and name it Sales. Subject areas drive the Schema Explorer grouping and every exported document.
  7. Read the SQL. Open the DDL tab. Choose PostgreSQL, DuckDB or SQLite and read the CREATE TABLE statements, including the FK constraint you just drew. You typed none of it.
  8. Check for problems. The status bar shows an issue count; click it to open the Issues drawer (missing primary keys, dangling foreign keys, naming).
  9. Save. +S writes a plain-JSON .dsm file. In the Web Studio, Save downloads the file, and the browser also keeps an autosave copy between visits (see Web Studio).

Recipe: CSV → table → SQL

The fastest way to model real data is to start from a file instead of an empty table.

  1. Drop a CSV on the canvas. Wurk.Flow infers the column names and types and creates a table node. JSON, SQLite and DuckDB files work the same way; a .sql file is reverse-engineered from its CREATE TABLE statements instead.
  2. Fix the mapping. Open the table's properties drawer and correct any inferred type (a zip code read as INTEGER, a date read as VARCHAR), mark the primary key, and rename columns to your standard. Enforce Naming Standards in the palette does the renaming for the whole canvas and previews what it will change first.
  3. Get the SQL. The DDL tab's Full Build mode gives you the CREATE TABLE. Insert Data (Real) PRO turns the rows you dropped into INSERT INTO statements; Seed Data (Mock) (Free) makes synthetic rows instead.
  4. Query it. Open the SQL Workspace. The Free tier's one tab runs against the built-in DuckDB sandbox, where every canvas table is scaffolded with seed data, so you can test joins and aggregations against the design before a database exists.
  5. Map it to a target PRO. In Data Mapping (STTM), add a source with your CSV's columns and a target with the destination shape, wire the columns, and drop transforms (CAST, CASE, COALESCE, JOIN, …) between them. The generated SQL appears in the output pane. See Data Mapping for the full transform list.

Next: 3 · Connect a Database, or skip to 5 · Export DDL & Reports.

3 · Connect a Database

About 5 minutesProDesktop only

Pull a live schema onto the canvas, then regenerate it as DDL for any dialect. Live connections are a desktop feature: the Web Studio has no server and never stores database credentials. In the browser, paste DDL with Import SQL DDL (free there) or Import DBML instead.

Recipe: introspect PostgreSQL → DDL

  1. +KManage Database Connections... (or File → Import Live Database).
  2. Choose PostgreSQL. Fill in host, port (5432 is pre-filled), database, schema, user and password. Toggle ALL next to Schema to introspect every non-system schema at once.
  3. Click Test Connection. A failure names the connection and a next step (firewall, certificate, wrong database) and keeps the driver's original message in brackets.
  4. Give the profile a name and click Save Profile. Profiles live only on this machine, AES-256 encrypted with a key derived from its hardware ID. They are never written into a .dsm file, so you can commit projects to Git without leaking a connection string.
  5. Click Connect & Introspect. The Selective Table Import dialog groups tables by schema; tick what you want and click Import. Tables, columns, types, primary and foreign keys and views land on the canvas with their schema qualifier.
  6. Open the DDL tab and choose a dialect. Keep PostgreSQL, or switch to Snowflake, SQL Server, BigQuery or any of the 13 to translate the schema. Identifiers are quoted per dialect ("sales"."orders", [sales].[orders], `sales`.`orders`).
  7. Export DDL... writes the .sql file. To ship only the difference against the live database, run Generate Migration Script...; to see what changed since you imported, Detect Schema Drift....

Where the traffic goes. Introspection and queries run from your machine straight to your database. Nothing is proxied through wurk.haus. Database Connections lists all 12 engines and their fields; Selective Import covers multi-schema catalogs.

Next: 4 · Buy & Activate.

4 · Buy & Activate

About 5 minutesPro · TeamDesktop and Web Studio

Plans are bought and managed at flow.wurk.haus, the customer portal. The desktop app is unlocked with a license key bound to one machine; the Web Studio is unlocked by signing in with the same account. Prices, what each plan unlocks, and what happens when a plan ends are in Licensing.

Buy

  1. Create an account at flow.wurk.haus and confirm your email.
  2. On the dashboard choose Pro (one seat) or Team (five seats), and monthly or annual. Annual plans carry perpetual fallback on the desktop app.
  3. Checkout runs on Stripe. When it completes, the dashboard lists your license seats. Each seat has its own key; click Copy Key on an unused seat.

Activate the desktop app

  1. Click the key icon at the bottom of the left sidebar (Manage License). Clicking any PRO feature opens the same window, with the feature that was denied named at the top.
  2. Paste the key and click Activate. You must be online for this one step: the license service binds the key to this machine's hardware ID and returns a signed token.
  3. The window now reads LICENSE ACTIVE with your edition and version year (for example PRO v26), and the status-bar badge changes from COMMUNITY to PRO or TEAM. Pro features are on, and you can go offline.

Recipe: activate Pro on a new machine

A key works on one machine at a time. Free the key in either of two ways, then activate on the new machine as above. Neither needs a support ticket.

Unlock the Web Studio

  1. At app.wurk.haus, click the key icon (Manage License) and sign in with your flow.wurk.haus email and password.
  2. The window reads SUBSCRIPTION ACTIVE. No key, no hardware binding, and it does not use up your desktop seat.
  3. Team members: the owner invites them by email from the dashboard; they create an account and sign in at app.wurk.haus. For the desktop app they take a seat key from the dashboard instead.

"I paid and features are still locked"

Check What to do
Key never entered Paying creates the key; it does not activate anything by itself. Copy it from the dashboard and activate in the app.
"License is already bound to another machine" The seat is in use elsewhere. Revoke the seat on the dashboard (or unbind on the old machine) and activate again.
"Invalid license key" Check for a truncated paste. Keys are shown shortened on the dashboard; use Copy Key rather than retyping.
Window says RENEWAL REQUIRED Your license covers an earlier version year than the build you are running (a v26 license on a v27 build). Renew, or install a release from your paid year, which keeps working under perpetual fallback.
Web: signed in, still Free Sign in with the account that owns, or was invited to, the subscription. The check re-runs at sign-in and every 6 hours.
Pro vanished mid-session with "License no longer active" The server answered revoked: a refund, chargeback or payment dispute is on record for the subscription. If that is wrong, get in touch.
Anything else Use the contact form with the Hardware Signature shown at the bottom of the License window and your dashboard email.

Next: 5 · Export DDL & Reports.

5 · Export DDL & Reports

About 3 minutesFree · ProDesktop and Web Studio

Everything you model can leave Wurk.Flow as SQL, as DBML, as a diagram, or as a self-contained documentation site.

DDL

  1. Open the DDL tab and choose a mode: Full Build (Free), Seed Data (Mock) (Free), Migration Diff PRO or Insert Data (Real) PRO. DDL Generation explains each.
  2. Choose a dialect. Free: DuckDB, PostgreSQL, SQLite. Pro adds MySQL, MariaDB, SQL Server, Azure Synapse, Snowflake, BigQuery, Databricks, Redshift, ClickHouse and CockroachDB.
  3. +KExport DDL... (or the export button on the DDL tab) writes a .sql file. On Free the file starts with a short "Generated by Wurk.Flow Community Edition" comment; Pro exports are clean.

Reports and documentation

Output Tier How
Documentation Portal (HTML) PRO +KExport Documentation Portal (HTML). One self-contained file with interactive schema diagrams, a searchable data dictionary, lineage, requirements and health KPIs. Opens in any browser, no server. See HTML Report Generator.
Markdown data dictionary PRO +KExport Documentation (Markdown). Drops straight into a wiki or a repository README.
Diagram image Free +KExport Diagram as PNG or as PDF.
DBML file Free +KExport DBML.... The file embeds your layout, so re-importing it rebuilds the diagram. Free exports carry a one-line header comment. Syntax and examples in DBML.
Query results (CSV) PRO From the SQL Workspace results grid.

That is the whole loop: model, generate, export. From here the reference chapters cover each workspace in depth, and the search box at the top of the sidebar (press /) finds anything else.

Installation

Wurk.Flow is distributed as a native desktop application for Windows, macOS, and Linux, and as the Web Studio in your browser. For a step-by-step walkthrough see 1 · Install.

Platform Format Notes
Windows NSIS Installer (.exe) Code-signed installer. Updates are click-initiated from the About window, never automatic.
macOS DMG Disk Image (.dmg) Signed and notarized by Apple. Apple Silicon (M1 and later).
Linux AppImage Portable — no installation required, just make executable
Web app.wurk.haus Nothing to install; Chrome, Edge or Firefox. Installable as an app in Chrome and Edge. See Web Studio.

System Requirements

Development Setup

# Clone and install
git clone <repository-url>
cd wurk.flow
npm install

# Run in development mode (Vite + Electron)
npm run dev

# Build for your platform
npm run build:win    # Windows
npm run build:mac    # macOS
npm run build:linux  # Linux

First Launch

When you first open Wurk.Flow, you'll see a default schema canvas with two sample tables (Users and Posts) connected by a relationship edge. An interactive guided tour will highlight each workspace tab and core feature.

Web Studio

The Web Studio at app.wurk.haus is the same Wurk.Flow, built for the browser. It runs the same renderer, the same modeling engines and the same DDL generator as the desktop app; the differences come down to what a browser tab can and cannot reach. There is no application server: the canvas, the DDL and DBML engines and the DuckDB SQL sandbox all run inside your browser, and the site only serves static files.

Opening it

What is shared with the desktop app

Schema Builder, Schema Explorer, referential integrity, custom types, DDL generation in every mode and dialect, DBML import and export and the DBML Workspace, the SQL Workspace on the DuckDB sandbox and on MotherDuck, Data Glossary, Data Lineage, Data Mapping, Requirements, the Audit Log, version snapshots, all exports (DDL, DBML, HTML portal, Markdown, PNG, PDF), bring-your-own-key AI, the Command Palette and the keyboard shortcuts.

Tier rules are identical: the same 15-table Free cap and the same PRO and TEAM gates. One deliberate difference in your favour: Import SQL DDL is free in the Web Studio (it is Pro on the desktop).

What is desktop-only

Feature Why it stays on the desktop In the browser, instead
Live database connections, introspection, migration scripts, schema drift The database drivers run in the desktop app's main process. A browser cannot open a database socket, and the Web Studio deliberately holds no credentials. Import SQL DDL (free on web) or Import DBML
API → Table import Uses the desktop's CORS-free fetch Save the JSON response and drop the file on the canvas
Git source control Needs a local repository and the git binary Download the .dsm and commit it yourself
Co-Op live sessions The relay is a desktop feature; the web page is not allowed to open a WebSocket to it
Custom YAML macros Macro files are read through the desktop file dialog The built-in bulk actions (naming standards, timestamps, soft deletes, surrogate keys) work everywhere
AWS Bedrock and Custom (OpenAI-compatible) AI providers Bedrock needs SigV4 request signing; custom hosts fall outside the fixed browser allow-list Ollama, OpenAI, Anthropic, Groq, Mistral AI, Azure OpenAI
Crash-recovery snapshots, update checks, crash reports Desktop process features Browser autosave (below). The web always serves the latest deploy, so there is nothing to update.

These controls are hidden in the browser rather than greyed out. A Git or live-database button appearing in the Web Studio is a bug worth reporting.

Files and autosave in the browser

Licensing in the browser

The desktop app licenses a machine; the Web Studio licenses a user. Sign in with your flow.wurk.haus account and the studio asks the license service whether your organization holds an active subscription, at sign-in and every 6 hours while the tab is open. If that check cannot be reached, the last good answer is kept for 24 hours and then the studio drops to Free until a check succeeds. It never locks you out of your files. Full rules in Licensing.

What the Web Studio talks to

The page ships a Content-Security-Policy that names every host the browser may contact; your browser enforces it independently of us. This is the complete list.

Host When What is sent
app.wurk.haus Loading the page The app's own files, including the DuckDB engine (about 35 MB on first load, cached after that)
Supabase (the license service) Only while signed in, and when you submit the Feedback form Sign-in and the subscription check; feedback text and any screenshot you attach. Signed out and not sending feedback, this host is never contacted.
Your AI provider (OpenAI, Anthropic, Groq, Mistral AI, Azure OpenAI) Only when you invoke an AI feature The prompt and your own API key, browser → provider directly. Nothing passes through wurk.haus.
localhost / 127.0.0.1 Only if you choose Ollama Model requests to your own machine
MotherDuck Only if you connect a MotherDuck database PRO Your MotherDuck token and queries, browser → MotherDuck directly

Not on the list, by decision: no analytics, no crash reporting, no update checks, no collaboration relay. There is nothing else the page is permitted to reach. The desktop app's list is in Security Architecture.

Reporting a problem

Errors surface on screen; no report leaves the browser on its own. Use Submit Feedback... from the palette, which attaches an anonymous browser ID (there is no hardware ID on the web) so a reply can find you.

Schema Builder

Schema Builder canvas showing the Users and Posts tables linked by a foreign key
The Schema Builder canvas — tables with columns, PK/FK markers, classification badges, and relationship edges.

The Schema Builder is the primary workspace — an infinite, pannable, zoomable canvas powered by Vue Flow where you visually design your database schema using interactive table nodes and relationship edges.

Canvas Controls

Action How
Pan the canvas Click and drag on empty space (Pan mode)
Zoom Scroll wheel
Select multiple nodes Switch to Select mode, then click-drag a selection box
Fit all nodes to view Click the fit-view button (⊡) in the toolbar
Search for a table +F — focuses the search input in the toolbar
Drop files onto canvas Drag CSV, JSON, SQLite, or DuckDB files directly onto the canvas to import

Adding Tables

Use the toolbar buttons or the Command Palette to add new objects to the canvas:

Table Nodes

Each table node on the canvas displays the table name and its columns. Click a table to select it and open the Table Properties Drawer on the right side, where you can edit:

Relationships (Edges)

To create a relationship, drag from a column handle on one table node to a column handle on another. Click an edge to open the Edge Properties Drawer where you can set:

The canvas renders proper crow's-foot notation markers for each cardinality type.

Schema Modes

Mode Description Tier
Physical Full column-level detail with data types and constraints. Default mode. Free
Conceptual Entity-only view — hides columns, shows just table names and relationships. Includes an AI prompt bar to generate entities from a natural-language business workflow description. PRO
Logical Shows columns and relationships but omits physical implementation details like data types. PRO

Auto-Link Relationships PRO

The ✨ Auto-Link button scans all tables for columns that follow foreign key naming conventions (e.g., user_idusers.id) and automatically creates relationship edges between them.

Auto-Organize Canvas PRO

The ⚡ Auto-Organize button applies an intelligent grid layout algorithm that arranges tables alphabetically within their respective Subject Area bounds, creating a clean, organized visual layout.

Copy & Paste

Select a table node and press +C to copy, then +V to paste a duplicate. The copy is offset by 50px and given a _copy suffix. All columns are duplicated with new IDs.

Undo & Redo

The Schema Builder maintains an undo/redo stack. Use +Z to undo and +Shift+Z (or +Y) to redo. The undo/redo buttons with their current stack depth are visible in the toolbar.

Schema Validation

Wurk.Flow continuously validates your schema in real time. The toolbar displays an error/warning count badge. Click it to open the Validation Issues Drawer which shows:

Many issues include a Target Table link to navigate directly to the offending table, and an Auto-Fix button to resolve the issue automatically.

Column Lineage Tracing

Click any column in a table node to activate Column Lineage Trace mode. All related upstream and downstream columns connected through foreign key relationships will be highlighted with a purple glow. A floating indicator bar appears at the bottom of the canvas — click another column to trace from there, or click empty space to dismiss.

Schema Explorer

The Schema Explorer is a 264px-wide sidebar panel docked to the left of the Schema Builder canvas. It provides a hierarchical, interactive tree view of every table in your schema, organized by Subject Area. It is the primary navigation and organization tool for managing large schemas.

Tree Hierarchy

The explorer organizes tables into a two-level tree:

📁 Subject Area: "User Management"        [👁 Hide] [3]
  ├── 📄 users                              [👁] [8]
  ├── 📄 roles                              [👁] [4]
  └── 📄 permissions                        [👁] [6]
📁 Subject Area: "Payments"                [👁 Hide] [2]
  ├── 📄 transactions                       [👁] [12]
  └── 📄 invoices                           [👁] [9]
📁 No Subject                              [👁 Hide] [1]
  └── 📄 audit_log                          [👁] [5]

Subject Area Accordions

Click a Subject Area header to collapse or expand its table list. The chevron icon (▶) rotates 90° to indicate expanded state. Collapsed state is maintained during the session — it is not persisted to the project file.

This is essential for large schemas: collapse irrelevant domains so you can focus on the area you're actively working on.

Click to Focus & Pan

Click any table name in the explorer to:

  1. Select the table node on the canvas (highlighted with an indigo glow border)
  2. Pan the canvas to center the selected table in the viewport
  3. Open the Table Properties Drawer on the right side for editing

The currently selected table is highlighted in the explorer with an indigo background and a subtle box-shadow glow, making it easy to see which table you're working on.

Drag-and-Drop Reassignment

Tables can be dragged between Subject Area groups to reassign them:

  1. Click and hold on a table entry to initiate a drag
  2. Drag the table over a different Subject Area group header
  3. Drop it — the table's parentNode is updated to the new Subject Area
  4. The table's canvas position is reset to (50, 50) relative to the new Subject Area bounding box

To unassign a table from a Subject Area, drag it to the "No Subject" group. This removes the parentNode reference and the table becomes a free-floating canvas node.

Drop Zone Feedback

Empty Subject Area groups display a "Drop tables here" placeholder text to indicate they accept drag-and-drop operations. If there are no tables and no Subject Areas at all, the sidebar shows: "Start shaping your data. Add a table to begin modeling."

Inline Rename

Double-click any table name in the explorer to enter inline rename mode:

Visibility Toggling

The Schema Explorer provides two levels of visibility control:

Table-Level Visibility

Subject Area-Level Visibility

Visibility toggling is perfect for managing complex schemas — hide "Archived" or "Legacy" Subject Areas to reduce visual clutter while keeping them in your project for reference and DDL generation.

Test (Sandbox) Button

At the bottom of the Schema Explorer sidebar, a fixed "Test (Sandbox)" button provides a quick shortcut to switch to the SQL Query Workspace tab with the DuckDB sandbox pre-selected. This lets you immediately query the tables you're viewing in the explorer.

This button is available at every tier — it jumps to the SQL Workspace with the DuckDB sandbox selected. Free includes one query tab against the sandbox; Pro and Team add unlimited tabs and the live/cloud engines below.

Active Selection State

When a table is selected (either by clicking in the explorer or by clicking on the canvas), it is highlighted in the explorer with:

This bidirectional sync means selecting a table on the canvas automatically highlights it in the explorer, and vice versa — you always know which table is active regardless of where you clicked.

Visual Relationships & Referential Integrity

Wurk.Flow supports the complete set of standard SQL Foreign Key constraints and referential actions. This guide explains how to model database relationships visually on the canvas, customize foreign key properties, and understand how they compile into target SQL DDL statements.

Visual Relationship Modeling

In Wurk.Flow, relationships are represented as directed canvas edges connecting columns between parent (Primary Key/Unique) and child (Foreign Key) tables.

1. Drawing Connections

2. Direction & Roles

Connections represent a Parent-to-Child mapping:

The Edge Properties Drawer

Clicking on any relationship edge on the canvas opens the Edge Properties Drawer in the right-hand panel. This drawer allows you to customize the physical database representation of that relationship:

Referential Integrity Actions

Wurk.Flow supports the five complete ANSI SQL referential actions:

Action SQL Keyword Behavior
Cascade CASCADE Automatically deletes or updates matching rows in the child table when the parent row is deleted or updated.
Restrict RESTRICT Prevents the parent row from being deleted or updated if any matching rows exist in the child table.
Set Null SET NULL Sets the foreign key column in the child table to NULL when the parent row is deleted or updated (requires the child column to be nullable).
Set Default SET DEFAULT Sets the foreign key column in the child table to its defined column default value when the parent row is deleted or updated.
No Action NO ACTION (Default) Enforces the relationship constraint at the end of the transaction. In generated DDL, this is either emitted explicitly as NO ACTION or omitted entirely to fall back to standard database defaults.

Dialect Compilation Behavior

Different database engines enforce foreign keys using different SQL syntaxes. Wurk.Flow compiles your visual relationships into optimized, dialect-specific DDL based on your active project settings:

1. Inline FK Constraints

For dialects that favor single-file local schemas or embedded execution (e.g., SQLite and DuckDB), foreign key constraints and their referential actions are compiled directly inline inside the CREATE TABLE statement:

CREATE TABLE orders (
    id INTEGER PRIMARY KEY,
    customer_id INTEGER,
    created_at TIMESTAMP,
    FOREIGN KEY (customer_id) REFERENCES customers (id) 
        ON DELETE SET DEFAULT 
        ON UPDATE CASCADE
);

2. Separate ALTER TABLE Statements

For production enterprise databases (e.g., PostgreSQL, MySQL, SQL Server, and MariaDB), foreign keys are emitted as separate ALTER TABLE statements placed at the end of the SQL script. This prevents deployment failures caused by forward-referencing tables or circular dependencies:

-- PostgreSQL / MySQL / SQL Server Example
ALTER TABLE orders 
    ADD CONSTRAINT fk_orders_customer 
    FOREIGN KEY (customer_id) REFERENCES customers (id) 
    ON DELETE SET NULL 
    ON UPDATE NO ACTION;

Note: For PostgreSQL, the DDL engine automatically wraps these operations in idempotent DO $$ BEGIN ... EXCEPTION blocks to prevent duplicate constraint creation errors during migrations.

3. FK-Skipping Dialects

Modern analytical data warehouses (OLAP) often prioritize high-throughput query performance over structural referential checks. In these dialects, physical foreign key constraints are either not supported or severely impact write performance:

When target DDL is compiled for these engines, Wurk.Flow skips physical FK generation in the final SQL scripts. However, the visual relationships are fully preserved in your canvas metadata (.dsm) and continue to drive Data Lineage, Schema YAML export, and Transformation SQL (STTM) generations.

Data Glossary PRO

Data Glossary showing schema-driven column documentation with classification badges
The Data Glossary — schema-driven column documentation with classification badges, AI Auto-Document, and CSV round-trip.

The Data Glossary is a dedicated tab for exploring, filtering, and documenting every table and column in your schema. It provides a spreadsheet-like data dictionary view where each table is rendered as a glassmorphic card with full column metadata, governance badges, and inline editing — essentially a living, interactive data dictionary that stays synchronized with your canvas.

Layout

The glossary renders each canvas table as a standalone card containing:

Column Grid Detail

Grid Column Content Editable?
Column Name Name with PK (🔑 yellow) or FK (🔗 blue) icon indicator No (edit on canvas)
Data Type SQL data type displayed in cyan monospace (e.g., VARCHAR, BIGINT, TIMESTAMP) No (edit on canvas)
Classification Data sensitivity tag — purple badge (e.g., PII, GDPR, PHI, HIPAA, CONFIDENTIAL) No (edit on canvas)
Constraints Color-coded constraint badges: PRIMARY KEY (yellow), FOREIGN KEY (blue), NOT NULL (orange), UNIQUE (green) No (edit on canvas)
Definition Inline text input for the column's business definition / description Yes — type directly

Changes made in the Definition field are immediately reflected on the canvas table node and in all exports (DDL comments, Documentation Portal).

Governance Metadata Badges

Each table card displays governance metadata as compact, color-coded badges in the header row. These are set via the Table Properties Drawer on the canvas and are reflected read-only in the glossary (except Domain, which is editable inline):

Badge Color Values Inline Editable?
Domain Blue Free-text (e.g., "Finance", "HR", "Analytics") Yes
Tier Red / Yellow / Green Tier 1 (Critical), Tier 2 (Important), Tier 3 (Informational) No
Owner Indigo Free-text team or person name No
Certification Gold / Green / Red Draft, Silver, Gold, Deprecated No
Classifications Red PII, GDPR, PHI, HIPAA, CONFIDENTIAL, etc. No

Subject Area Filtering

The glossary toolbar includes a Subject Area dropdown filter that lets you scope the view to a specific domain group:

This is essential for navigating large schemas with 300+ tables — filter down to "Payment Processing" to see only the 12 tables in that domain.

Column & Table Search

The search input in the glossary toolbar performs a dual-level fuzzy search across both table names and column names simultaneously:

AI Auto-Documentation (✨ Auto Button)

Each table card has an ✨ Auto button that triggers AI-powered documentation generation using your configured AI provider (Ollama, OpenAI, Anthropic, Azure OpenAI, Groq, Mistral, or AWS Bedrock):

  1. The AI receives the table name and all column names/types as context
  2. It generates a table-level business description (written into the table's description field)
  3. It generates per-column definitions (written into each column's description field)
  4. A loading spinner replaces the button while generation is in progress
  5. Results are immediately visible in the glossary and synchronized to the canvas

This is particularly powerful after importing a schema from a live database — you can click ✨ Auto on each table to generate documentation for an otherwise undocumented production database.

CSV Export

Click Export CSV to generate a complete data dictionary export. The CSV includes the following columns:

CSV Column Content
Subject Area Parent Subject Area name (or "No Subject")
Table Name Table label from the canvas
Domain Domain governance tag
Classifications Semicolon-separated classification tags (e.g., "PII;GDPR")
Table Definition Table-level business description
Column Name Column name
Data Type SQL data type
Is Primary "Yes" or "No"
Is Foreign "Yes" or "No"
Column Definition Column-level business description

The export respects the current Subject Area filter — if you're filtered to a specific area, only those tables are included.

CSV Import (Round-Trip)

Click Import CSV to update table and column definitions from an external CSV file. This enables a powerful round-trip workflow:

  1. Export the glossary as CSV
  2. Distribute the CSV to subject matter experts, data stewards, or business analysts
  3. They fill in the Table Definition and Column Definition fields in Excel or Google Sheets
  4. Import the updated CSV back into Wurk.Flow

The import uses DuckDB WASM's read_csv_auto to parse the file, then matches rows to canvas tables by table name and column name. Only description fields are updated — structural changes (types, constraints, etc.) are ignored for safety. A toast notification summarizes the results (e.g., "Tables updated: 5 · Columns updated: 42").

Column Matching

The import uses fuzzy header matching — column headers like Table Name, TableName, and table_name are all recognized. This ensures compatibility with spreadsheet tools that may modify header formatting.

Data Profiling Stats

When sample data has been loaded into the DuckDB sandbox (via file drop, live introspection, or mock generation), the glossary can display column-level profiling statistics:

These statistics appear as compact badges below each column's definition field in the exported Documentation Portal HTML. They are computed during data import and stored in the column's profiling metadata object.

SQL Query Workspace

SQL Query Workspace with a connection selector, SQL editor, and results panel
The SQL Query Workspace — connection selector, multi-tab editor, and the results panel.

A full-featured SQL IDE with syntax highlighting via CodeMirror, supporting multiple query tabs, variable binding, query history, and execution against multiple targets. The workspace is designed to feel like a native database client embedded directly inside your schema design tool.

The Free edition runs the built-in DuckDB sandbox (one query tab). The live and cloud engines below — MotherDuck and saved database connections — are Pro.

Execution Targets

Target Description Connection
DuckDB Sandbox Embedded DuckDB WASM engine running entirely in-browser. Your canvas tables are auto-scaffolded with synthetic seed data so you can immediately query your schema design. Built-in — zero config
MotherDuck PRO Cloud-hosted DuckDB via the WASM client. Provides persistent cloud storage for your DuckDB databases with collaborative access. MotherDuck API token (set in Settings)
Saved Connections PRO Execute queries against any saved database connection profile. Queries are dispatched via secure Electron IPC to the main process database drivers. Any configured connection (see Database Connections)

DuckDB Sandbox — How It Works

The sandbox is the default execution target. When you switch to the SQL Workspace, Wurk.Flow automatically:

  1. Drops existing sandbox tables — Cleans up previous schema state (tables with real imported data from CSV/JSON/API drops are preserved)
  2. Applies your canvas DDL — Executes CREATE TABLE / CREATE VIEW statements generated from your current schema canvas
  3. Inserts synthetic seed data — Generates 5 realistic rows per table using the AI-powered seed data generator (with local fallback). Column names and types are used to generate contextually appropriate fake data (e.g., real-looking names, email addresses, and contextual values).

This means you can query your schema design as if it were a live database — immediately after modeling a table, you can write SELECT * FROM users and see results.

Real vs. Synthetic Data

When you drag-and-drop a .csv, .json, or .sqlite file onto the canvas, the actual data is loaded into DuckDB. These "real data" tables are protected — the sandbox sync process will never drop or overwrite them. Synthetic data is only generated for tables that don't already have imported data.

Multi-Tab Workspace

Free includes one query tab against the built-in DuckDB sandbox. Pro and Team add unlimited tabs and live/cloud connections. Each tab maintains its own independent state:

Tab Property Description
SQL Editor CodeMirror editor with SQL syntax highlighting and schema-aware autocomplete
Results Grid Tabular results with column headers and row data
Error Output Detailed error messages from failed queries
Execution Time Millisecond-precision timing for each query run
Variables Per-tab variable bindings (see Variable Binding below)
EXPLAIN Plan Cached explain output with toggle to show/hide
Connection Target Per-tab connection selection — each tab can target a different database
Scratchpad Notes Free-text notes area for documenting query intent or results

Multi-Statement Execution

The SQL Workspace supports multi-statement execution — write multiple SQL statements separated by semicolons and they will be executed sequentially in order. The results grid displays the output from the last statement that returned rows.

-- All three statements execute in sequence:
CREATE TABLE temp_report AS SELECT * FROM orders WHERE status = 'complete';
UPDATE temp_report SET total = total * 1.1;
SELECT * FROM temp_report ORDER BY total DESC;  -- ← results shown

If any statement fails in a multi-statement batch, execution stops and the error message indicates which statement number failed (e.g., "Statement 2 failed: ...").

Variable Binding

Use double-curly-brace syntax to parameterize your queries:

SELECT * FROM orders
WHERE created_at >= '{{ start_date }}'
  AND status = '{{ order_status }}'
LIMIT {{ row_limit }}

Open the Variables panel to define key-value pairs. Variables are interpolated before the query is sent to the execution engine — this is string substitution, not parameterized binding, so use caution with untrusted input.

EXPLAIN Plans

Click the Explain button (or select "Explain" from the run menu) to execute EXPLAIN <your query>. The execution plan is displayed in a monospaced text block below the editor. This works across all targets — DuckDB, MotherDuck, and remote connections.

Query History

Every successful query execution is recorded in the session history panel:

Result Pagination

Query results are paginated at 100 rows per page to keep the UI responsive with large result sets:

Cancel / Abort Queries

Click the Cancel button (visible while a query is running) to abort execution. Cancellation uses an AbortController and is checked between individual statements in multi-statement batches. The results area shows "Query cancelled by user" and the tab returns to an idle state.

CSV Export

Click the Export CSV button in the results toolbar to download the full result set as a .csv file. The export:

Database Explorer Tree

The left panel of the SQL Workspace displays an interactive database explorer tree showing the hierarchical structure of your current target:

📁 Catalog (database name)
  📁 Schema (e.g., public, main)
    📄 Table 1
    📄 Table 2
    📄 Table 3

DDL Generation

Real-time DDL engine showing generated SQL with mode tabs and a dialect selector
The real-time DDL engine — generated SQL for the modeled schema, with mode tabs (Full Build / Migration Diff / Seed Data / Insert Data), a dialect selector, and one-click export.

The DDL tab generates SQL output in real time as you model on the canvas — every table you add, column you modify, or relationship you draw is instantly reflected in the generated DDL output. It supports 5 generation modes and 13 SQL dialects, with deep awareness of dialect-specific syntax, type mapping, and constraint handling.

Generation Modes

Mode Output Tier
Full Build Complete CREATE TABLE / CREATE VIEW / CREATE MATERIALIZED VIEW DDL with all constraints, indexes, stored procedures, and triggers Free
Migration Diff ALTER TABLE statements comparing your current canvas to the last saved baseline on disk PRO
Seed Data (Mock) AI-powered synthetic INSERT INTO statements with context-aware realistic fake data for testing (with local fallback) Free
Insert Data (Real) INSERT INTO statements using actual imported data from CSV/JSON/API file drops PRO

Full Build Mode — What Gets Generated

The Full Build mode produces a complete, deployment-ready DDL script with the following sections in order:

  1. Table metadata comments — Block comments with Domain, Description, and Classification tags for each table
  2. CREATE TABLE statements — Column definitions with data types, DEFAULT values, NOT NULL, UNIQUE, CHECK constraints, and inline PRIMARY KEY (for single-column PKs)
  3. Composite PRIMARY KEY — For tables with multi-column primary keys, a separate PRIMARY KEY (col1, col2) clause is appended
  4. Foreign Key constraints — Either inline REFERENCES (DuckDB/SQLite) or separate ALTER TABLE ADD CONSTRAINT statements with ON DELETE / ON UPDATE actions
  5. COMMENT ON statements — Table and column-level comments (PostgreSQL, Snowflake, Redshift, CockroachDB)
  6. CREATE INDEX statements — Including UNIQUE indexes and PostgreSQL-specific index types (GIN, HASH, etc.)
  7. CREATE VIEW / MATERIALIZED VIEW — With CREATE OR REPLACE and the user-defined SQL definition (or a NULL AS col stub when no definition is provided)
  8. Stored Procedures — Dialect-aware procedure generation (PostgreSQL functions, MySQL DELIMITER blocks, SQL Server CREATE OR ALTER PROCEDURE, Snowflake procedures)
  9. Triggers — Dialect-aware trigger generation (PostgreSQL trigger functions, MySQL DELIMITER, SQL Server AFTER triggers). Dialects that don't support triggers emit informational comments.

Topological Sorting

Tables are topologically sorted before DDL emission so that parent (referenced) tables are always created before their children. This ensures the DDL script can be executed top-to-bottom without foreign key reference errors. Circular dependencies are detected and handled gracefully (the cycle is broken safely).

CREATE Behavior Options

Each table has a configurable CREATE behavior that controls how the DDL handles existing tables:

Option PostgreSQL / DuckDB SQL Server MySQL
Default CREATE TABLE IF NOT EXISTS or CREATE OR REPLACE TABLE (dialect-dependent) CREATE TABLE CREATE TABLE
If Not Exists CREATE TABLE IF NOT EXISTS IF OBJECT_ID(N'...', N'U') IS NULL BEGIN ... END; CREATE TABLE IF NOT EXISTS
Replace CREATE OR REPLACE TABLE CREATE TABLE (not supported) CREATE TABLE (not supported)

PostgreSQL also supports UNLOGGED tables — when the "Unlogged" flag is set on a table, the DDL emits CREATE UNLOGGED TABLE for write-heavy staging tables that don't need WAL durability.

Identifier Quoting

Identifiers (table names, column names) are quoted using the correct syntax for each dialect:

Dialect Group Quoting Style Example
PostgreSQL, DuckDB, Snowflake, Redshift, CockroachDB ANSI double-quotes "users"
MySQL, MariaDB, BigQuery, Databricks, ClickHouse Backticks `users`
SQL Server, Synapse Square brackets [users]

Fully-qualified names respect schema prefixes: "public"."users" or `dataset.users` (BigQuery/Databricks use dotted backtick paths).

Data Type Mapping

Wurk.Flow includes a comprehensive type mapping engine that translates generic SQL types into dialect-specific equivalents. This means you can model your schema once and generate valid DDL for any target:

Generic Type PostgreSQL SQL Server BigQuery ClickHouse SQLite
VARCHAR(255) VARCHAR(255) NVARCHAR(255) STRING String TEXT
INTEGER INTEGER INT INT64 Int32 INTEGER
BOOLEAN BOOLEAN BIT BOOL Bool INTEGER
TIMESTAMP TIMESTAMP DATETIME2 TIMESTAMP DateTime TEXT
UUID UUID UNIQUEIDENTIFIER STRING UUID TEXT
JSON / JSONB JSON / JSONB NVARCHAR(MAX) JSON String TEXT
SERIAL SERIAL INT IDENTITY(1,1) INT64 UInt64 INTEGER

User-specified precision is preserved (e.g., VARCHAR(100) stays VARCHAR(100) in PostgreSQL but maps to NVARCHAR(100) in SQL Server).

Foreign Key Constraints

Foreign keys are generated based on the relationship edges drawn on your canvas:

Column-Level Features in DDL

ClickHouse-Specific Generation

ClickHouse requires an ENGINE and ORDER BY clause instead of a simple closing parenthesis. The DDL generator automatically emits:

CREATE TABLE `events` (
    `id` Int64,
    `event_type` String,
    `created_at` DateTime
)
ENGINE = MergeTree()
ORDER BY (`id`);

If no primary keys are defined, it falls back to ORDER BY tuple().

Migration Diff Mode PRO

Migration Diff compares your current canvas state against the last saved baseline (the .dsm file on disk) and generates only the delta as ALTER statements. It detects:

Change Type DDL Generated
New table added Full CREATE TABLE statement
Table deleted DROP TABLE with optional CASCADE/RESTRICT
Table renamed ALTER TABLE ... RENAME TO (or sp_rename for SQL Server)
Column added ALTER TABLE ... ADD COLUMN
Column dropped ALTER TABLE ... DROP COLUMN
Column renamed ALTER TABLE ... RENAME COLUMN (or sp_rename for SQL Server)
Column type changed ALTER TABLE ... ALTER COLUMN ... TYPE (with USING cast for PostgreSQL)
No changes Informational comment: "No structural layout changes detected"

Seed Data (Mock) Mode

Generates INSERT INTO statements with contextually-aware synthetic data. The mock generator inspects column names and types to produce realistic values:

Column Pattern Generated Data
Column named email '[email protected]'
Column named first_name 'Charlie'
Column named city / location 'San Diego'
Column named status / state 'ACTIVE', 'PENDING', etc.
Column named phone '555-847-3291'
Column named ip '192.168.14.203'
UUID type 'a1b2c3d4-e5f6-4a7b-8c9d-...'
INTEGER PK Sequential: 1, 2, 3, ...
INTEGER FK Random reference: 1–N (within row count range)
BOOLEAN Alternating TRUE/FALSE
DATE Random date within the past year
TIMESTAMP Random datetime within the past year
FLOAT / DECIMAL Random value 0–1000 with 2 decimal places

Default row count is 50 rows per table (5 rows when used for sandbox sync). Snowflake timestamps use TO_TIMESTAMP() wrapper syntax.

Supported Dialects

Dialect Tier Notable Dialect Quirks
DuckDB Free CREATE OR REPLACE, inline FK REFERENCES, JSON type native
PostgreSQL Free SERIAL/BIGSERIAL, COMMENT ON, UNLOGGED tables, idempotent FK blocks, trigger functions
SQLite Free No schema prefix, TEXT for timestamps/booleans/UUIDs, B-Tree only indexes, no triggers/procedures
MySQL / MariaDB PRO Backtick quoting, AUTO_INCREMENT, DELIMITER blocks for procedures/triggers
Snowflake PRO TIMESTAMP_NTZ, VARIANT for JSON, AUTOINCREMENT, LANGUAGE-based procedures, no triggers
SQL Server / Synapse PRO Bracket quoting, IDENTITY(1,1), IF OBJECT_ID wrapper, NVARCHAR, sp_rename, CREATE OR ALTER
BigQuery PRO Backtick-dotted paths, STRING/INT64/FLOAT64 types, no indexes, no FK enforcement
Databricks PRO GENERATED BY DEFAULT AS IDENTITY, STRING type, informational FK comments
Redshift PRO IDENTITY(1,1), VARCHAR(256) default, SUPER for JSON, COMMENT ON support
ClickHouse PRO MergeTree() ENGINE, ORDER BY required, no FK enforcement, no triggers/procedures
CockroachDB PRO INT8 default, STRING type, unique_rowid() for auto-IDs, COMMENT ON support

Custom Types

Custom-type nodes on the Schema canvas — a domain, an enum, and a composite UDT
Custom-type nodes on the Schema canvas — a domain (cyan), an enum (purple), and a composite UDT (green), each generating its own per-dialect DDL.

Beyond plain tables, Wurk.Flow models first-class custom type objects on the Schema Builder canvas — enums, domains, sequences, and composite user-defined types. Each is its own node, participates in DDL generation with per-dialect output, and links visually to the columns that use it.

Enums

An enum defines a named, fixed set of allowed values (e.g. status = active | inactive | pending). Add one from the toolbar + Enum button, the Add Enum command, or via DBML import. Values are edited inline on the purple node itself (the sticky-note pattern — no properties drawer).

Domains

A domain is a reusable base type plus a constraint — e.g. an email domain over VARCHAR with a format CHECK — so many columns can share one validation rule.

Sequences

A sequence node models an auto-incrementing number generator. It generates CREATE SEQUENCE DDL and links visually to any column whose DEFAULT references it (e.g. DEFAULT nextval('order_id_seq')), so you can see which columns draw from it.

User-Defined Types (UDTs)

A composite UDT bundles several typed fields into one reusable structured type. UDT nodes support per-field editing and emit cross-dialect DDL (e.g. PostgreSQL CREATE TYPE … AS (…)), with a graceful fallback where a dialect has no composite-type support.

All custom types are saved with the project and emitted in DDL export in dependency order — the type definitions are written ahead of the tables that reference them. Enums also round-trip through DBML import.

Requirements Workbench

Requirements Workbench with a structured-requirements sidebar and a split-view Markdown editor
The Requirements Workbench — structured-requirements sidebar plus a split-view Markdown editor with live preview.

The Requirements tab provides a dedicated, two-pane workspace for capturing business requirements, data architecture context, and acceptance criteria alongside your schema design. It combines structured, card-based requirement tracking with a freeform markdown editor — giving you both formal traceability and unstructured documentation in a single view.

Layout

The workbench is split into two side-by-side panels:

Header Bar

The workbench header provides quick access to global actions and statistics:

Structured Requirement Cards

Each requirement is rendered as a glassmorphic card in the left panel. Clicking a card expands its edit form; clicking again collapses it. Each card contains:

Card Header (Always Visible)

Expanded Edit Form (On Click)

When a card is expanded, the full edit form is revealed with animated transition:

Field Type Description
Title Text input Short descriptive name for the requirement
Description Textarea Detailed description — resizable, monospace font for technical content
Priority Dropdown MoSCoW priority classification (see table below)
Status Dropdown Workflow status (see table below)
Category Dropdown Requirement category classification (see table below)
Linked Tables Multi-select Canvas tables linked to this requirement for traceability
Acceptance Criteria Checklist Per-requirement checklist with done/not-done checkboxes

MoSCoW Priority

Each requirement uses the MoSCoW prioritization framework:

Priority Badge Color Icon Meaning
Must Have Red 🔴 Non-negotiable — project fails without this
Should Have Amber 🟡 Important but not critical — workaround exists
Could Have Blue 🔵 Nice-to-have — include if time permits
Won't Have Gray Explicitly out of scope for this iteration

The left panel header includes a priority breakdown mini-bar — a thin horizontal bar with proportional colored segments showing the distribution of Must/Should/Could/Won't across all requirements at a glance.

Status Workflow

Requirements progress through a 4-stage workflow:

Status Badge Color Meaning
Draft Gray Newly created, under review
Approved Purple Stakeholder-approved, ready for implementation
Implemented Blue Schema changes and logic have been built
Verified Green Acceptance criteria met, testing complete

Categories

Requirements are classified into one of 5 categories:

Category Use For
Functional Business logic, data flows, transformations
Non-Functional SLAs, uptime, scalability, latency targets
Data Quality Null checks, referential integrity, dedup rules
Security Access controls, encryption, masking, compliance
Performance Query speed, indexing, partitioning strategies

Table Linking (Traceability)

Each requirement can be linked to one or more canvas tables via the "Linked Tables" dropdown:

  1. Expand a requirement card and scroll to the "Linked Tables" section
  2. Select a table from the dropdown — it is added as an indigo badge
  3. Click the × button on a badge to unlink a table
  4. Linked table names resolve in real-time from the canvas (if a table is renamed, the badge updates automatically)

The header badge shows coverage percentage: the ratio of requirements that have at least one linked table. This is a quick measure of how well your requirements are traced to your schema design.

Coverage Thresholds

  • ≥ 80% — Green (excellent traceability)
  • ≥ 50% — Amber (some gaps)
  • < 50% — Red (significant traceability gaps)

Acceptance Criteria

Each requirement has its own checklist of acceptance criteria:

The card header shows a compact progress bar with a numeric label (e.g., "3/5") that fills proportionally as criteria are marked done. This gives an immediate visual sense of completion state across all cards.

AI Proofreading (✨ Polish)

Each requirement card has a ✨ Polish button that sends the description text to your configured AI provider for grammar correction, tone refinement, and clarity improvements:

Requires an AI provider to be configured in Settings (Ollama, OpenAI, Anthropic, Azure OpenAI, Groq, Mistral, or AWS Bedrock). If no AI is configured, a warning toast is shown.

Freeform Markdown Editor

The right panel is a full-featured markdown editor for unstructured documentation. It supports three view modes, toggled via the mode switcher in the toolbar:

Mode Description
Edit Full-width monospace textarea for writing raw markdown
Split Side-by-side editor + live preview (default)
Preview Full-width rendered HTML preview with styled prose

Markdown Toolbar

The toolbar provides quick-insert buttons for common markdown syntax:

All toolbar actions work with text selection — select text first and the formatting wraps the selection. If no text is selected, placeholder text is inserted.

Requirement Templates

The template picker in the header provides prebuilt markdown structures for common data engineering scenarios:

Template Icon Sections Included
Data Warehouse 🏢 Business Context, Grain Definition, Slowly Changing Dimensions, Data Freshness, Historical Requirements
API Integration 🔌 Source Systems, Rate Limits & Throttling, Schema Contracts, Error Handling, Data Validation
ETL Pipeline ⚙️ Source-to-Target Mapping, Scheduling & Orchestration, Idempotency, Error Handling, Data Quality Checks
Data Migration 📦 Migration Phases (5-step), Rollback Strategy, Validation Checkpoints, Downtime Window
Compliance / GDPR 🔒 Data Classification, Retention Policies, Right to Erasure, Consent Management, Access Controls, Audit Trail
Blank 📝 Empty document — start from scratch

Applying a template to a non-empty editor triggers a confirmation dialog: "Replace current content with template? Current content will be lost." — preventing accidental overwrites.

CSV Export

Click ⬇ CSV to export all structured requirements as a requirements.csv file. The CSV includes:

CSV Column Content
ID Auto-generated requirement ID
Title Requirement title
Description Full description text
Priority must / should / could / wont
Status draft / approved / implemented / verified
Category functional / non-functional / data-quality / security / performance
Linked Tables Semicolon-separated table names
Acceptance Criteria Semicolon-separated checklist items with [x] / [ ] markers
Criteria Done Count of completed acceptance criteria
Criteria Total Total count of acceptance criteria
Created Creation date (localized format)

The CSV is exported with a UTF-8 BOM prefix for compatibility with Excel, SSMS, and Azure Data Studio.

Persistence

Both structured requirements (cards) and freeform markdown content are saved with your .dsm project file. They are also included in:

Data Lineage PRO

Data Lineage Tracker canvas with a Table Reference node and the lineage toolbar
A lineage graph auto-populated from a live database — a System node feeding table references via relationship edges, with the node toolbar (Add System/Job/Consumer/API/File, Auto-Organize, Derive from SQL).

The Data Lineage tab provides a separate, dedicated canvas for mapping the end-to-end journey of data through your organization — from source systems through transformations to consumers. It uses the same Vue Flow engine as the Schema Builder but with specialized node types designed for pipeline architecture rather than table design.

Node Types

The lineage canvas supports 6 distinct node types, each with unique color coding, icons, and handles:

Node Type Color Icon Default Label Purpose Examples
System Blue 🗄️ Database cylinder "New System" / DATABASE Source or destination systems — databases, data lakes, SaaS platforms Salesforce, SAP, PostgreSQL, S3 bucket, Snowflake
Job Indigo ⚡ Lightning bolt "New ETL Job" / TRANSFORMATION ETL/ELT processing steps, transformation logic, orchestration tasks Airflow DAG, Glue job, Spark job, SSIS package
Consumer Pink 👁 Eye "New Consumer" / BI TOOL End consumers of data — visualization, analytics, ML Tableau dashboard, Power BI, Looker, ML model, data product
API Call Cyan 🔗 Link "New API" / REST EXTERNAL External API integrations, webhooks, microservice calls REST endpoint, GraphQL, webhook, event stream
Flat File Amber 📄 Document "New Flat File" / S3 / BLOB File-based data sources and sinks CSV drops, Excel uploads, Parquet files, JSON feeds
Table Reference Emerald/Gray 📊 Grid table (Canvas table name) / DESIGNED SCHEMA Links to tables from your Schema Builder canvas — bridges schema design and data flow Any canvas table (live-synced name and columns)

Node Handles & Connections

Each node has two connection handles for creating directional data flow edges:

Drag from any source handle to any target handle to create a connection. Edges are rendered as animated indigo lines with a 2px stroke, indicating the direction of data flow.

Table Reference nodes have additional per-column handles — each column in the expanded column list has its own left/right handles, enabling column-level lineage tracking (e.g., mapping source.customer_id directly to target.cust_id).

Table Reference Nodes

Table Reference nodes are unique — they are live links to your Schema Builder canvas tables:

Toolbar

The lineage toolbar provides all node creation and management actions:

Button Action
+ Add System Create a new System node at position (100, 100)
+ Add Job Create a new Job node at position (300, 100)
+ Add Consumer Create a new Consumer node at position (500, 100)
+ Add API Create a new API Call node at position (500, 300)
+ Add File Create a new Flat File node at position (500, 500)
⚡ Auto-Organize Run the BFS-based auto-layout algorithm (see below)
Link Table dropdown Select a canvas table and click "Add" to create a Table Reference node
⚡ Populate from DB Open the database introspection popover (see below)
Derive from SQL Parse modeled view SQL definitions into column-level lineage edges (see below)

Properties Drawer

Click any lineage node to open the Properties Drawer (320px panel sliding in from the right). It provides:

Field Description Editable?
Node Title Name/label of the node Yes (except Table Reference — synced from canvas)
Component Classification Type sublabel (e.g., "DATA WAREHOUSE", "STAGING", "RAW", "MATERIALIZED VIEW") Yes (except Table Reference)
Notes Free-text textarea for implementation notes, SQL logic, or technical context Yes (all node types)

Click the × button or click on empty canvas space to close the drawer.

Populate from DB (⚡)

The "Populate from DB" button opens a popover that lets you auto-generate lineage nodes by introspecting a live database connection:

  1. Select a saved connection profile from the dropdown (uses connections saved via File → Import Live Database)
  2. Toggle which elements to include:
    • Tables & Columns — Creates Table Reference nodes for every table in the database (with full column metadata)
    • Foreign Key relationships — Creates animated edges between tables based on FK constraints
    • Views & Materialized Views — Creates Job nodes with appropriate type labels ("VIEW" or "MATERIALIZED VIEW"), and auto-parses the view SQL to detect source table dependencies
  3. Click 🚀 Populate Lineage

What Gets Generated

The populate process creates the following elements automatically:

Element Node Type Edge Style
Database system System node (with engine icon, e.g., 🐘 PostgreSQL, ❄️ Snowflake)
Each table Table Reference node (with columns, PKs, FK flags)
System → Table Blue dashed line (1.5px, dasharray 6 3)
FK relationships Indigo animated line (2px) with "col → col" label
Views Job node (typeLabel: VIEW or MATERIALIZED VIEW)
Source table → View Purple animated line (2px) with "reads from" label
View → Output table Cyan animated line (2px) with "produces" label

Tables are arranged in a 4-column grid (340px × 260px cells). Views are placed in a separate column block to the right. All nodes are deduplicated — running populate twice against the same connection will not create duplicates. A toast notification summarizes the results (e.g., "Populated 12 table(s), 3 view(s), 15 edge(s)").

Auto-Organize (⚡)

The auto-layout algorithm uses BFS-based layered graph layout to arrange all lineage nodes:

  1. Connected component detection — Identifies separate clusters of connected nodes
  2. Root detection — For each cluster, finds nodes with no incoming edges (data sources). Falls back to the node with the most outgoing connections if no pure root exists.
  3. BFS layering — Assigns each node to a horizontal layer based on its distance from roots
  4. Layer positioning — Nodes within each layer are sorted alphabetically and spaced vertically. Layers are spaced 420px apart horizontally.
  5. Isolated node grid — Unconnected nodes are arranged in a 4-column grid below the clustered layouts

This produces a clean left-to-right flow: Sources → Transformations → Destinations.

Edge Interactions

Column-Level Lineage

Beyond node-to-node flow, the lineage canvas tracks lineage at the individual column level. Expand a Table Reference node and drag from a column's right (source) handle to another column's left (target) handle to record a precise source.column → target.column relationship.

Hover Path-Trace

Hovering — or selecting — any node or edge highlights its connected upstream + downstream path and dims everything else, so you can see at a glance what a given element touches end-to-end. Move the cursor away to restore full opacity. The trace is purely visual and never changes the graph.

Impact Analysis — Blast Radius

Impact analysis showing the downstream blast radius of the CUSTOMERS table highlighted in red
Impact analysis on CUSTOMERS — the Impact panel reports the downstream count while affected tables (ORDERS, ORDER_ITEMS, ORDER_STATUS_HISTORY) light up red and everything off the blast radius dims.

Impact analysis answers "what breaks if I drop this?" by walking the graph downstream from a focus point and highlighting everything affected in danger red.

Co-Op safe. Impact highlighting, the hover path-trace, and the impact panel are ephemeral — computed on the fly, never written into the saved project or broadcast over Co-Op — so they're safe to use mid-session and during live collaboration.

Derive Lineage from View SQL

The Derive from SQL toolbar button parses the SQL definition of any view modeled in your Schema Builder (a node whose kind is VIEW / MATERIALIZED VIEW with a stored definition) and wires column-level lineage to the source tables already on the canvas — no database round-trip required.

MiniMap

The lineage canvas includes a MiniMap in the corner — a dark glassmorphic overview panel showing the positions of all nodes as indigo dots. This helps with navigation on large lineage graphs with dozens of nodes.

Persistence & Export

Data Mapping (STTM) PRO

Data Mapping STTM workspace with the transform palette
The Data Mapping (STTM) workspace — source/target palette and the transform library (JOIN, CASE, CAST, COALESCE, …).

The Data Mapping tab implements a visual Source-to-Target Mapping (STTM) canvas for designing data transformation flows from source systems to target schemas. It provides column-level wiring, 18 built-in transform operations, interactive lineage highlighting, and auto-generated SQL output — all within a drag-and-drop flow canvas.

Layout

The mapping workspace is split into three areas:

Node Types

Node Type Color Badge Purpose
Source Table Blue (blue dot indicator + right border) SOURCE Input table — the data you're reading from. Each column has a right-side output handle.
Target Table Indigo (indigo dot indicator + left border) TARGET Output table — the table you're writing to. Each column has a left-side input handle.
Transform Emerald (default), Blue, Purple, Orange, or Red (configurable) Operation name Intermediate transformation step between source and target columns.

Source & Target Table Nodes

Table nodes can be added in two ways:

Manual Creation

Live Database Import

Engine-Aware Headers

Table node headers are color-coded by database engine:

Engine Header Color
PostgreSQL Blue
Snowflake Cyan
SQL Server Red
MySQL Orange
BigQuery Emerald
Manual Gray (default)

Transform Operations

The Mapping Palette provides 18 built-in transform operations, each producing a compact node that can be wired between source and target columns:

Operation Icon SQL Generated Expression Field Usage
Direct Map Pass-through — "table"."column" Not used
JOIN INNER/LEFT/RIGHT/FULL/CROSS JOIN Join condition (e.g., "a"."id" = "b"."id"). Notes field controls join type.
FILTER WHERE expression Filter predicate (e.g., status = 'active')
AGGREGATE COUNT/SUM/AVG/MIN/MAX(...) + auto GROUP BY Aggregate function (e.g., SUM, COUNT(*))
CASE CASE ... END CASE body (e.g., WHEN status='A' THEN 'Active')
COALESCE COALESCE(col, fallback) Fallback value
CAST 📏 CAST(col AS type) Target type (e.g., INTEGER, DATE)
Concat 🔗 CONCAT(col1, col2, ...) Separator or additional literal
Split SPLIT_PART(col, delim, idx) Delimiter. Notes field = index position.
TRIM TRIM(col) Not used
UPPER UPPER(col) Not used
LOWER LOWER(col) Not used
REPLACE REPLACE(col, 'old', 'new') Comma-separated: old_value, new_value
SUBSTRING SUBSTRING(col, start, length) Start position and length (e.g., 1, 10)
Hardcode Literal value (auto-quoted strings, unquoted numbers/keywords) The literal value
GenUUID GEN_RANDOM_UUID() Not used
DISTINCT SELECT DISTINCT Not used
IFNULL COALESCE(col, fallback) Fallback value

Column-Level Connections

Connections are made at the individual column level, not at the table level:

  1. Each source column has a small blue output handle on its right edge
  2. Each target column has a small indigo input handle on its left edge
  3. Transform nodes have a left input handle and a right output handle
  4. Drag from a source column's output handle → through one or more transforms → to a target column's input handle

This creates a complete, traceable column-level lineage from source to target.

Interactive Lineage Highlighting

When you hover over any node or edge on the mapping canvas, the system traces the full data flow path in both directions:

This makes it trivial to answer questions like "where does this target column get its data from?" at a glance.

Edge Rendering

Mapping edges use smooth step paths (rounded corners with 15px border radius) and are rendered with:

Transform Properties Drawer

Click any transform node to open the Transform Properties Drawer (resizable panel sliding in from the right). It provides:

Field Description
Transformation Type Read-only display of the operation name (e.g., "CONCAT", "FILTER")
Color Theme 5 color swatches (emerald, blue, purple, orange, red) — click to change the node and edge colors
Logic / Expression Monospace textarea for the SQL expression, cast type, filter predicate, or literal value (context-dependent on operation)
Developer Notes Free-text textarea for rationale, context, or implementation notes
Delete Node Red button to remove the transform node and its edges

The drawer border, input focus rings, and handle colors all dynamically adapt to the selected color theme.

Mapping Palette (Sidebar)

The left sidebar provides all node creation buttons:

Section Actions
Sources Add Manual Source — create a sample source table
Connect Live DB — import real schemas from a database connection
Targets Add Manual Target — create a sample target table
Connect Live DB — import real schemas from a database connection
Transforms 2-column grid of 18 transform operation buttons (click to add to canvas)

SQL Generation

The mapping canvas can auto-generate executable SQL by reverse-walking the graph from each target table, tracing through transform nodes to build complete queries. Two SQL modes are supported:

INSERT INTO Mode

Generates INSERT INTO target SELECT ... FROM source statements:

CTAS Mode (Create Table As Select)

Generates CREATE TABLE target AS SELECT ... statements. The CREATE prefix is dialect-aware:

Dialect CREATE Prefix
PostgreSQL, Redshift CREATE TABLE IF NOT EXISTS
Snowflake, DuckDB, Databricks CREATE OR REPLACE TABLE
Others CREATE TABLE

Tabular STTM View

In addition to the visual canvas, the mapping workspace includes a Tabular STTM View. This spreadsheet-style interface allows you to view and edit your source-to-target mappings in a dense, data-rich grid.

Markdown Specification Export

The mapping can be exported as a markdown specification document with per-target-table sections. Each section contains a table with:

Column Content
Target Field Column name in the target table
Data Type Column data type
Transformation Logic Auto-traced logic chain (e.g., UPPERCONCAT, with expression and notes inline)
Source Field Fully-qualified source reference (e.g., Source_Table.column_name)

The trace logic recursively walks upstream through transform chains, so multi-step transformations (e.g., Source → TRIM → UPPER → CONCAT → Target) are fully documented with each step's operation and expression.

Persistence

Co-Op Mode — Real-Time Collaboration TEAM

Co-Op panel showing the relay URL, display name, and Host/Join buttons over a shared schema
The Co-Op panel — host or join a real-time session via a relay URL; peers then share the live CRDT document.

Co-Op Mode enables real-time, multi-user collaboration in Wurk.Flow via a lightweight WebSocket relay server. All participants see live schema changes and each other's cursors in real-time.

Aspect Detail
Architecture Stateless WebSocket relay (Node.js + ws). No data is stored on the relay — it is a pure message-forwarding pipe. All project data stays local to each client.
Sync Strategy Per-surface CRDT (Y.js). Schema, lineage, mapping, glossary, requirements and the audit log sync as conflict-free replicated documents, so concurrent edits from multiple peers merge automatically rather than clobbering one another. Freeform requirements text, query tabs and saved queries still use a debounced (500ms) full-state broadcast. All project data stays local to each client; the relay only forwards update messages.
Tier TEAM

How It Works

  1. Host creates a room — Opens the Co-Op panel and clicks Host Session. A 6-digit room code is generated.
  2. Peers join — Other users enter the room code and their display name. Joining adopts the session's project: the joiner's local canvas is replaced by the shared document. If the joiner has local work, Wurk.Flow asks for confirmation first (save or branch beforehand to keep it). The host's full state then syncs to the new peer automatically.
  3. Live editing — Any participant can edit the schema, add tables, modify columns, etc. Changes are serialized and broadcast to all peers within 500ms.
  4. Cursor tracking — Figma-style colored cursors with name tags show where each peer is working.
  5. Leave or disconnect — Peers can leave at any time. If the host disconnects, the relay auto-promotes the next peer to host.

Co-Op Panel UI

The Co-Op panel is a floating overlay accessible from the sidebar. It provides:

Wire Protocol

All messages are JSON over WebSocket.

Client → Relay

Type Payload Purpose
room:create { displayName } Host creates a room
room:join { roomCode, displayName } Guest joins by 6-digit code
room:leave Client leaves room
state:update { payload: string } Full project state broadcast
state:full { payload, targetPeerId } Host sends state to specific late-joiner
cursor:update { payload: { x, y } } Viewport-relative cursor position

Relay → Client

Type Payload Purpose
room:created { roomCode, peerId, peers } Room created confirmation
room:joined { roomCode, peerId, peers } Join confirmed
room:peer-joined { peer, peers } New peer notification
room:peer-left { peerId, peers } Peer disconnect
room:host-changed { newHostId, peers } Host promotion (auto)
room:left Leave confirmed
state:update { payload, from, displayName } Forwarded state from another peer
state:full { payload, from } Full state for late-joiner
state:request { requestingPeerId } Relay asks host for state
cursor:update { payload, from, displayName } Forwarded cursor position
error { message } Error response

Relay Server Deployment

The relay server lives in its own repository at gitlab.com/WurkHaus/wurk.relay. It is a ~300 LOC Node.js WebSocket server with a single dependency (ws, ~100KB). It can be deployed as a Docker container (~50MB) or as a standalone Node.js process.

Docker (Recommended)

# Clone the relay repo
git clone https://gitlab.com/WurkHaus/wurk.relay.git
cd wurk.relay

# Build the image
docker build -t wurkhaus/wurk-relay .

# Run on default port
docker run -d -p 9500:9500 --name wurk-relay wurkhaus/wurk-relay

# Custom port
docker run -d -p 8080:8080 -e PORT=8080 wurkhaus/wurk-relay

Standalone Node.js

git clone https://gitlab.com/WurkHaus/wurk.relay.git
cd wurk.relay
npm install
node server.js
# [wurkflow-relay] Listening on port 9500

# Custom port
PORT=8080 node server.js

The relay server is purely stateless — it forwards messages between peers and manages room lifecycle. No project data, credentials, or schema information is ever stored on the relay. Deploy it on your LAN for air-gapped collaboration with zero cloud dependency.

Cursor Tracking

Echo-Loop Prevention

When a client receives remote state, it must suppress its own outbound broadcast to prevent an infinite loop:

receive remote state
  → update local state
    → trigger Vue watcher
      → broadcast back  ← LOOP!

Implementation: A _suppressBroadcast flag is set to true before applying remote state, then cleared via nextTick() after Vue reactivity flushes. This ensures local watchers see the mutation but don't re-broadcast it.

Security Notes

Audit Log

The Audit Log records project changes across every surface — schema, lineage, mapping, glossary, and requirements — into a single searchable timeline, so you can see what changed, where, and (in a Co-Op session) who made the change.

Database Connections

Wurk.Flow can connect to live databases for schema introspection, query execution, and data mapping. Connections are managed via the Connection Modal (File → Import Live Database) and stored locally using AES-256 encryption bound to your machine's hardware ID.

Connection Modal Layout

The modal is a glassmorphic dialog with the following sections from top to bottom:

  1. Load Saved Connection — Dropdown to select a previously saved profile (only appears when saved profiles exist)
  2. Database Engine — Select from 12 supported engines
  3. Engine-specific fields — Dynamic form fields that change based on the selected engine
  4. Test result banner — Green success or red error banner after testing
  5. Profile Name + Save — Name field and "Save Profile" button
  6. Sample Data Fetch — Row limit selector for data import
  7. Test Connection / Connect & Introspect — Action buttons

Supported Engines

Wurk.Flow supports 12 database engines, each with specific connection fields and sensible defaults:

Engine Required Fields Default Port Notes
PostgreSQL Host, Port, Database, Schema, User, Password 5432 Standard libpq protocol
MySQL Host, Port, Database, Schema, User, Password 3306
MariaDB Host, Port, Database, Schema, User, Password 3306 Uses MySQL protocol
SQL Server Host, Port, Database, Schema, User, Password 1433 TDS protocol; Schema defaults to "dbo"
Azure Synapse Host, Port, Database, Schema, User, Password 1433 Uses SQL Server TDS protocol. Inline guidance with Azure Portal instructions.
Amazon Redshift Host, Port, Database, Schema, User, Password 5439 Uses PostgreSQL wire protocol. Inline guidance with AWS Console instructions.
CockroachDB Host, Port, Database, Schema, User, Password 26257 Uses PostgreSQL protocol
ClickHouse Host/Endpoint, Port, Database, User, Password 8443 HTTPS for Cloud, HTTP 8123 for self-hosted. Inline guidance. Username defaults to "default".
Snowflake Account, Warehouse, Role, Database, Schema, User, Password Account format: xy12345.us-east-2.aws
BigQuery Project ID, Dataset, Service Account JSON Path Uses GCP service account key file authentication
Databricks Server Hostname, HTTP Path, Access Token (PAT), Catalog, Schema Connects to SQL Warehouse. Inline guidance with connection details location.
MotherDuck Access Token, Database, Schema, Region DuckDB Cloud via PostgreSQL wire protocol. Token from app.motherduck.com → Settings.

Auto-Port Defaults

When you change the engine selection, the port field is automatically updated to the engine's default value:

Engine Default Port
PostgreSQL 5432
Redshift 5439
MySQL / MariaDB 3306
SQL Server / Synapse 1433
ClickHouse 8443
CockroachDB 26257

Engine-Specific Guidance

Several engines display inline guidance callouts (colored banners with icons) to help with configuration:

MotherDuck Region Selection

MotherDuck connections include a region selector with 5 options:

Test Connection

Click "Test Connection" to verify connectivity before saving or importing:

Connect & Introspect

Click "Connect & Introspect" to connect and immediately run schema introspection. This:

  1. Opens a connection to the database
  2. Introspects all tables, columns, data types, primary keys, foreign keys, and view definitions
  3. Opens the Select Tables modal to choose which tables to import onto the canvas
  4. Creates canvas table nodes with full metadata (columns, types, constraints)

Sample Data Fetch (Row Limit)

The Sample Data Fetch dropdown controls how many rows of real data are fetched per table during introspection:

Option Rows per Table
OFF (Schema Only) 0 — no data fetched, only schema metadata
100 rows 100
500 rows 500
1,000 rows 1,000
5,000 rows 5,000
10,000 rows 10,000

Fetched sample data is loaded into the DuckDB WASM sandbox so you can query it in the SQL Workspace.

Saved Connection Profiles

Connections can be saved as reusable profiles:

Credential Storage & Security

Connection profiles (including passwords and tokens) are stored in a dedicated electron-store file:

Where Connections Are Used

Saved connection profiles appear throughout the application:

Feature Usage
SQL Workspace Execution target dropdown — run queries against any saved connection
Data Mapping (STTM) "Connect Live DB" buttons — import source or target schemas from a live connection
Data Lineage "⚡ Populate from DB" — auto-generate lineage nodes from a connection's tables and relationships
Schema Builder File → Import Live Database — introspect and import tables to the canvas
Schema Drift Detection Compare canvas schema against a live database connection

Multi-Schema Introspection & Selective Import

Wurk.Flow provides powerful multi-schema database mapping and selective table importing, designed to handle massive enterprise databases without causing canvas bloat or performance degradation.

By utilizing wildcard schema querying and an interactive grouping selector, you can import only the specific tables required for your data engineering and lineage tasks.

Multi-Schema Introspection (All Schemas Mode)

When configuring a database connection, you can toggle the ALL mode next to the Schema input field. This configures Wurk.Flow to introspect the entire database catalog rather than a single database schema.

Core Introspection Rules

Selective Table Selector (SelectTablesModal)

Rather than dumping hundreds of introspected database objects onto your canvas at once, Wurk.Flow opens the Selective Table Import interface upon a successful connection.

This interface groups tables into collapsible schema panels and provides granular controls:

Features & Controls

  1. Schema Grouping: Tables are automatically grouped under their parent schema header (e.g., all sales.* tables appear under a single "sales" header).
  2. Collapsible Views: You can expand or collapse individual schema groups by clicking the header dropdown arrow. This makes navigating large catalogs with dozens of schemas simple.
  3. Granular Selection:
    • Select All Toggles: Click the global select toggle to select or deselect every table in the database.
    • Per-Schema Toggles: Toggle the checkbox in a schema header to select or deselect all tables belonging to that schema.
    • Table Checkboxes: Select individual tables by checking their respective boxes within expanded schema lists.
  4. Selection Indicators: Schema headers display a counter badge showing how many tables are selected within that schema (e.g., 3 of 15 selected).

Canvas & Downstream Integration

Once you confirm your selection and click Import, the canvas and modeling engines adapt to the multi-schema layout:

1. Visual Node Qualification

Tables on the canvas are drawn with their schema qualifier visible on the node label. This allows you to visually identify and map relationships between tables residing in different database schemas on the same canvas layout.

2. Schema-Aware FK Resolution

The AST and relationship mapping engine (astMapper.ts) resolves Foreign Key mappings using qualified schema keys. An FK from sales.orders targeting customers.profiles is resolved uniquely, avoiding collision risks even if both schemas contain tables with identical names.

3. Dialect DDL Generation

When generating target DDL or migrations, the DDL engine outputs qualified identifiers, using the correct quoting characters for your target dialect:

Importing Data

Import Methods

Exporting Data

Export Description Tier
Save (.dsm) Save the full project to a .dsm JSON file Free
Export DDL (.sql) Export generated DDL as a .sql file. Free exports start with a short "Community Edition" header comment; Pro exports are clean. Free
Export DBML (.dbml) Export the canvas as DBML with your diagram layout embedded in the file (positions, sizes, colors, subject areas via versioned // wurk:1: comments) — a Wurk.Flow .dbml rebuilds the whole diagram, layout included. Verified against the official DBML parser. Never paywalled: your model is always portable. Free exports carry a one-line header comment. See DBML. Free
Copy as DBML Copy the canvas to the clipboard as DBML (DDL panel or File menu) Free
Export Docs (HTML) Generate a self-contained Documentation Portal with interactive schema diagrams, data dictionary, lineage graphs, and KPI dashboards PRO
Export Markdown Export a markdown-formatted data dictionary PRO
Export CSV (Query) Export SQL Workspace query results as CSV PRO
Export CSV (Glossary) Export the full schema topology as a CSV PRO

DBML

DBML (Database Markup Language) is the plain-text schema notation popularised by dbdiagram.io. Wurk.Flow reads and writes it natively. Import, export, and the Workspace are free on every surface, because a model you cannot take with you is not yours; the DBML Workspace goes one step further and lets you edit the live canvas as text.

Wurk.Flow ships its own parser rather than wrapping a library, and it is deliberately forgiving: a malformed line is skipped and reported with its line number, and everything else still imports. Every file Wurk.Flow exports is checked in the test suite against the official @dbml/core reference parser, so a Wurk.Flow .dbml opens cleanly in dbdiagram.io and any other DBML tool.

Syntax reference

What each construct becomes on the canvas. Anything not listed is reported in the import dialog and skipped, never silently dropped. Declaration order does not matter: refs, groups and enum types resolve after the whole document is read.

Construct Syntax On the canvas
Table Table users { ... }
Table sales.orders { ... }
Table "order items" as OI { ... }
Table users [headercolor: #3498DB, note: 'People'] { ... }
A table node. schema. becomes the schema prefix that generated DDL uses (unqualified tables live in public). Quote names that contain spaces or keywords. An alias can stand in for the table in refs. headercolor colours the node header; a header note becomes the description. Only one level of qualification (schema.table) is supported.
Column id integer [pk, increment]
email varchar(255) [not null, unique]
tags text[]
status account_status [note: 'Lifecycle']
Types pass through exactly as written, so use your target dialect's spelling; array types keep their brackets; an Enum name is a valid type. Settings: pk or primary key, not null, null, unique, increment, default:, note:, ref:. A check: setting is reported and not imported yet.
Defaults [default: 0]
[default: 'active']
[default: `now()`]
Three forms, kept distinct: a bare literal, a quoted string, and a backtick expression. The distinction carries into DDL (strings are quoted, expressions are not) and back out on export.
Inline ref author_id integer [ref: > users.id]
author_id integer [ref: > U.id]
Flags the column as a foreign key and draws the relationship edge. Same operators as a standalone Ref; aliases resolve. Referential actions need the standalone form.
Ref Ref: posts.author_id > users.id
Ref fk_name: a.x > b.y [delete: cascade, update: no action]
Ref fk_name { a.x > b.y }
Ref: a.(x, y) > b.(p, q)
A relationship edge with cardinality and referential actions. Operators: > many-to-one (the FK is on the left), < one-to-many (FK on the right), - one-to-one (FK on the second endpoint, per the DBML spec), <> many-to-many (no FK column is flagged). Actions: cascade, restrict, set null, set default, no action. A composite ref draws one edge per column pair. Omit the column and the referenced table's primary key is assumed. dbdiagram's color and inactive settings are display-only there and ignored here.
Indexes indexes {
  (order_id, line_no) [pk]
  email [unique, name: 'idx_email', type: btree]
  (customer_id, placed_at)
}
A composite entry marked [pk] is the composite primary key, and this is the only way to declare one. Other entries become indexes with optional unique, name: and type: (btree, hash, …); an unnamed index gets a generated name. An index on a column the table does not declare is reported.
Table note Note: 'One row per billing account'
Note: '''
  several
  lines
'''
The table description, shown in the properties drawer and in exported documentation. Written inside the table body.
Enum Enum sales.account_status {
  active
  frozen
  "on hold"
}
A purple enum node. Columns typed with the enum name keep it, and the DDL engine writes the type definition ahead of the tables that use it. Per-value [note:] settings are ignored.
TableGroup TableGroup Sales {
  sales.customers
  sales.orders
}
Sets each member's domain, the grouping label the Schema Explorer and the exports use. A group Note: or [color:] is reported and ignored; a member that matches no table is reported.
Note (standalone) Note release_plan {
  '''
  Ship the tags table after
  the backfill finishes.
  '''
}
A sticky note on the canvas. The block name is optional and is not kept: canvas notes are text-only.
Comments & strings // line comment
/* block comment */
'single'  "double"  '''multi-line'''
Comments are stripped. Triple-quoted strings keep their line breaks, minus common indentation. Windows line endings are fine.
Layout sidecar // wurk:1:{"id":"t_orders","x":400,"y":50} Wurk.Flow's own comment channel for canvas positions, sizes, colours and subject areas. Written on export, reattached on import, invisible to every other DBML tool. See Exporting.
Recognised, not imported Project, TablePartial and ~partial injections, Records, DiagramView, multifile imports, check constraints Each is reported with its line number. A table that injects a partial will be missing those columns; paste them into the table instead.

A complete example

Paste this into Import DBML (+D). It builds a three-table Sales model with an enum, a composite primary key, two indexes, a cascading foreign key, a table group and a sticky note, and it imports with no warnings.

// Sales — a small model that uses every construct Wurk.Flow imports.

Enum sales.account_status {
  active
  frozen
  closed
}

Table sales.customers [headercolor: #3498DB] {
  id integer [pk, increment]
  email varchar(255) [not null, unique, note: 'Login identity']
  status account_status [not null, default: 'active']
  created_at timestamp [not null, default: `now()`]
  indexes {
    email [unique, name: 'idx_customers_email', type: btree]
  }
  Note: 'One row per billing account'
}

Table sales.orders {
  id integer [pk]
  customer_id integer [not null, ref: > sales.customers.id]
  total decimal(12,2) [default: 0]
  placed_at timestamp
  indexes {
    (customer_id, placed_at) [name: 'idx_orders_customer_placed']
  }
}

Table sales."order items" as OI {
  order_id integer
  line_no integer
  sku varchar(64) [not null]
  qty integer [default: 1]
  indexes {
    (order_id, line_no) [pk]
  }
}

Ref fk_items_order: OI.order_id > sales.orders.id [delete: cascade, update: no action]

TableGroup Sales {
  sales.customers
  sales.orders
  sales."order items"
}

Note {
  'Reindex nightly after the ETL run'
}

The dialog reports 3 tables · 2 relationships · 1 enum · 1 note. On the canvas: order items shows order_id and line_no both marked PK, orders.customer_id and order items.order_id carry the FK marker, the three tables sit under the Sales domain in the Schema Explorer, and the orders → order items edge shows ON DELETE CASCADE in its drawer. Switch to the DDL tab and PostgreSQL output starts with CREATE SCHEMA IF NOT EXISTS "sales" and CREATE TYPE "sales"."account_status" AS ENUM (…) ahead of the tables.

The same model, as Wurk.Flow exports it

Move the nodes around, run Export DBML..., and this is the file. Sections come out in a fixed order (enums, tables, refs, notes, groups, subject areas), alphabetically within each, so two exports of the same model diff cleanly in Git. The // wurk:1: lines are the layout sidecar; the inline ref has become a standalone Ref: written from the referenced side with the < operator, which means the same thing.

// wurk:1:{"id":"enum_1","x":1100,"y":50}
Enum sales.account_status {
  active
  frozen
  closed
}

// wurk:1:{"id":"t_customers","x":50,"y":50}
Table sales.customers [headercolor: #3498DB] {
  id integer [pk, increment]
  email varchar(255) [not null, unique, note: 'Login identity']
  status account_status [not null, default: 'active']
  created_at timestamp [not null, default: `now()`]
  indexes {
    email [unique, name: 'idx_customers_email', type: btree]
  }
  Note: 'One row per billing account'
}

// wurk:1:{"id":"t_order_items","x":750,"y":50}
Table sales."order items" as OI {
  order_id integer
  line_no integer
  sku varchar(64) [not null]
  qty integer [default: 1]
  indexes {
    (order_id, line_no) [pk]
  }
}

// wurk:1:{"id":"t_orders","x":400,"y":50}
Table sales.orders {
  id integer [pk]
  customer_id integer [not null]
  total decimal(12,2) [default: 0]
  placed_at timestamp
  indexes {
    (customer_id, placed_at) [name: 'idx_orders_customer_placed']
  }
}

Ref: sales.customers.id < sales.orders.customer_id
Ref: sales.orders.id < sales."order items".order_id [delete: cascade, update: no action]

// wurk:1:{"id":"note_1","x":50,"y":294,"w":200,"h":150,"color":"yellow"}
Note note_16z218a {
  'Reindex nightly after the ETL run'
}

TableGroup Sales {
  sales.customers
  sales."order items"
  sales.orders
}

Importing

Free on every tier. Five ways in, all landing in the same dialog:

The editor highlights DBML syntax and lints it live with the same parser that runs the import. Problems are underlined in place, with the severity in the gutter; click one to jump to its line. Above the buttons, a summary counts what will be imported: tables, relationships, enums, notes.

Append or Merge & update

Mode Use it when What happens
Append The text is new to this canvas: a dbdiagram export, a colleague's file, a snippet. Selected automatically for text without Wurk.Flow sidecar ids. Everything is added as new nodes. If some of it is already on the canvas, a Skip N elements already on the canvas checkbox (ticked by default) leaves those out and re-points their relationships at the existing nodes, so importing a file over its own tables never stacks duplicates.
Merge & update The text describes tables you already have: your own export edited in another tool, or a re-import after changes. Selected automatically when the text carries Wurk.Flow sidecar ids. Each incoming element is matched to the canvas and listed as new, updated or unchanged, with a checkbox to leave any of them out. Matched tables are patched in place. Elements on the canvas that the text no longer mentions are listed as missing and kept, unless you tick Remove missing.

How matching works (first hit wins): by sidecar id, then by name (schema plus table or enum name, case-insensitive; sticky notes by exact text; subject areas by title). Text that has no sidecar and renames a table therefore reads as one table removed and one added; your own exports rename cleanly because the sidecar id follows the node.

What a merge preserves. The canvas element keeps its identity: position, size, subject-area membership, and every field DBML cannot express (governance, profiling, lineage links, procedures, triggers, view definitions). The text wins for everything DBML owns: name, columns, indexes, description, header colour, alias, schema and group. Columns are matched by name so they keep their ids and the edges bound to them; a column absent from the text is removed from that table, and any relationship it anchored is dropped and counted in the summary. An ungrouped table in the text does not clear a domain you set on the canvas.

Layout. Text with sidecar lines lands exactly where it was. Text without them is laid out on a four-column grid whose rows are as tall as their tallest table, so a wide table never overlaps the row below. Nothing is deleted implicitly, and every import is a single undo step.

Free tier. An import that would exceed the 15-table cap says so up front and still imports in full; adding new tables afterwards is what needs Pro.

Exporting

Free on every tier. +KExport DBML... writes schema.dbml (a save dialog on the desktop, a download in the browser). The DDL tab has Export .dbml and Copy as DBML buttons for the same output on the clipboard. On the Free edition the file begins with one comment line naming Wurk.Flow Community Edition; Pro exports have no header. The toast tells you if anything could not be represented.

The layout sidecar

DBML has no place for canvas positions, so Wurk.Flow writes them as versioned comment lines that its own parser recognises and every other tool ignores. A sidecar line describes the block that follows it; subject areas, which are visual-only, are written as standalone lines at the end of the file.

Field Meaning
id The element's stable id: the key Merge & update uses to recognise it again
x, y Canvas position in pixels
w, h Size, for notes and subject areas
color Note or subject-area colour
parent The subject area a table sits inside
el, title Standalone subject-area lines: "el":"subject_area" plus the area's title

Unknown fields are ignored. A file from a newer Wurk.Flow (wurk:2:) imports with its layout hints skipped and an info note, never an error. Delete the sidecar lines and the file is plain DBML.

What does not fit in DBML

Views and materialized views (their SQL bodies), domains, sequences, composite types, procedures, triggers, governance fields, a default expression containing a backtick, and any relationship whose endpoint is not a table column. All of it stays in the .dsm project; the export lists it in a trailing // Not representable in DBML comment so nothing vanishes without a trace.

Diagnostics you may see

A deliberately imperfect paste and what the dialog reports for it:

Project shop { database_type: 'PostgreSQL' }

Table posts {
  id integer [pk]
  title varchar [check: `length(title) > 0`]
  ~timestamps
}

TablePartial timestamps {
  created_at timestamp
}

Records posts(id, title) { 1, 'Hello' }
Line 1   info     Project block ignored — project metadata isn't imported
Line 5   warning  Check constraint on posts.title isn't imported yet
Line 6   warning  TablePartial injection "~timestamps" references an unknown partial — and partials aren't supported
Line 9   warning  TablePartial "timestamps" isn't supported — tables injecting it will be missing its columns
Line 13  warning  Records (sample data) blocks aren't imported

posts still imports with its two columns. Other messages worth knowing:

Message What it means
Unknown top-level keyword "…" — line skipped A typo or a construct DBML does not have. The rest of the file imports.
Relationship skipped — unknown table / unknown column A ref names something the text never declares. Check spelling and schema qualification.
"auth.users" doesn't match any table in schema "auth" — using "users" The ref is schema-qualified but the table is not (or the reverse). The unique bare match is used; qualify consistently to silence it.
Conflicting duplicate ref (1:N vs N:M) — keeping the first The same column pair is declared twice with different operators. Delete one.
Duplicate ref ignored (already declared) Info only: an inline ref: and a standalone Ref: describe the same edge. Harmless; one edge is drawn.
Unterminated "Table" block A missing closing brace. Whatever was parsed before the end still imports.
Index on "…" references unknown column "…" The indexes block names a column the table does not declare.
Sidecar version wurk:2 is newer than this build — layout hint ignored The file came from a newer Wurk.Flow. Content imports; positions fall back to the grid.

DBML Workspace

The DBML Workspace is a two-way editing surface: a right-side pane on the Schema tab that renders your entire canvas as live DBML. Write your schema as code and apply it, or edit on the canvas and watch the text follow. Open it from the DBML pill in the status bar, the View menu, or +Shift+D.

Tips and gotchas

API Import PRO

The API Import modal lets you fetch data from any REST API endpoint, preview the JSON response, and auto-generate canvas table schemas with full type inference. Nested JSON structures can be automatically normalized into related tables with foreign key relationships.

Opening the Modal

Open via Command Palette → API Import or the menu. The URL input is auto-focused and pre-filled with a sample endpoint (https://jsonplaceholder.typicode.com/users).

Request Configuration

The modal provides a complete HTTP request builder:

Field Description
Method Dropdown: GET, POST, PUT, DELETE
URL Full endpoint URL. Press Enter to send immediately.
Bearer Token Optional password field — auto-prepended as Authorization: Bearer {token} header
Headers Key-value pairs. Starts with Content-Type: application/json. Click "+ Add Header" for more. Each header has a remove button.
Request Body JSON textarea — only shown for POST and PUT methods

CORS Bypass

In desktop mode, requests are dispatched via Electron IPC (api:fetch), which sends the HTTP request from the Node.js main process. This bypasses all CORS restrictions, allowing you to hit any API endpoint regardless of its CORS policy. In browser dev mode, requests use the browser's native fetch() API and may be blocked by CORS.

Response Preview

After sending a request, the response is displayed inline:

Import Controls

The import panel appears when a valid JSON response is received:

Normalize Toggle

A toggle switch labeled "Normalize nested objects" controls the import mode:

Import as Canvas Table(s)

Click this button to generate canvas table nodes from the response. The table name is auto-derived from the URL path (last path segment, lowercased, special characters replaced with underscores).

Flat Import Mode (Normalize OFF)

When normalization is disabled, or the response has no nested objects:

Normalized Import Mode (Normalize ON)

When normalization is enabled and the response contains nested structures, the JSON Normalizer engine recursively walks the JSON and produces a multi-table schema:

Type Inference

The normalizer samples up to 50 rows per level and infers SQL types:

JSON Value Inferred SQL Type
null / undefined VARCHAR
boolean BOOLEAN
integer number INTEGER
decimal number FLOAT
ISO date string (YYYY-MM-DD) DATE
ISO timestamp string (YYYY-MM-DDT...) TIMESTAMP
UUID string UUID
Other string VARCHAR

When the same column has conflicting types across rows, the broader type wins (e.g., INTEGER + FLOAT = FLOAT, anything + VARCHAR = VARCHAR).

Normalization Rules

Each JSON key is classified and processed according to these rules:

JSON Structure Result Relationship
Primitive value (string, number, boolean) Column on the current table
Nested object ({ "address": { "city": "..." } }) Separate child table (parent_address) with FK back to parent 1:1
Array of objects ({ "posts": [{ ... }, ...] }) Separate child table (parent_posts) with FK back to parent 1:N
Array of scalars ({ "tags": ["a", "b"] }) Junction table (parent_tags) with FK + value column 1:N (junction)

Generated Table Structure

Every generated table includes:

FK Edge Generation

For each parent-child relationship, a foreign key edge is created on the canvas linking the parent's id column to the child's FK column. Child columns are automatically marked with isForeign: true.

DuckDB Data Loading

After table generation, the actual row data for every table is loaded into the DuckDB WASM sandbox via loadJsonIntoDuckDB(). This means you can immediately query the imported data in the SQL Workspace.

Canvas Layout

Generated tables are arranged in a grid layout:

Custom YAML Macros

Custom YAML Macros allow you to define declarative governance, schema mutations, and naming policies that execute in bulk against your canvas elements. By utilizing YAML instead of arbitrary javascript execution, Wurk.Flow ensures that macros remain fast, highly reproducible, and fully integrated with the canvas state and undo/redo systems.

Execution Flow

Macros are executed purely on the renderer state, modifying canvas nodes in-place. This operation occurs entirely inside the application sandbox, using the following sequence:

  1. Invocation: Press Cmd+K or Ctrl+K to open the Command Palette, and select Run Custom Macro (YAML)....
  2. File Selection: A native Electron file dialog filters for .yaml and .yml files.
  3. Validation: The engine parses the definition and validates the required actions payload.
  4. State Snapshotting: The application pushes an undo snapshot (pushUndoSnapshot) before applying mutations to guarantee reliable Ctrl+Z recovery.
  5. Execution: The schema nodes are filtered and mutated sequentially. A success toast displays the macro name and total affected tables.

Schema Specification

A macro is structured as a declarative YAML document with the following root-level schema:

Property Type Required Description
name String Yes The title of the macro (displayed in UI toasts).
description String No A description of the macro's purpose.
scope String No Limits the tables evaluated. Options: tables, views, all (default).
filter Object No A set of filters to restrict execution to specific tables or views.
actions Array Yes A sequential array of governance mutations to perform.

The Filter Object

The filter configuration defines criteria that a schema node must meet to undergo mutations. If multiple criteria are defined, they must all match (logical AND).

Action Reference

The actions list contains one or more objects defining the schema changes. Below are the supported action specifications.

1. add-column

Inserts a column definition if a column with the same name (case-insensitive) does not already exist.

type: add-column
column:
  name: string       # Required. Name of the column.
  type: string       # Required. Canonical data type (e.g., VARCHAR, TIMESTAMP).
  isNotNull: boolean # Optional. Default is false.
  isPrimary: boolean # Optional. Default is false.

2. enforce-naming

Rewrites table labels and column names to adhere to standard naming styles.

type: enforce-naming
style: snake_case    # Required. Currently 'snake_case' is the active implementation.

Note: This style uses standard regular expressions to replace uppercase boundaries, spaces, and hyphens with underscores, converting all characters to lowercase.

3. add-index

Injects a database index definition onto the table.

type: add-index
columns: [string]    # Required. Array of columns included in the index.
isUnique: boolean    # Optional. Default is false.
name: string         # Optional. Custom index name. Defaults to idx_<table_label>_<index_count>.

4. set-property

Updates metadata properties on matching tables and views.

type: set-property
description: string  # Optional. Sets a new description comment.
tier: string         # Optional. Sets the classification tier (e.g., "Tier 1").
prefix: string       # Optional. Prepends this prefix to the table name if not already present.

5. rename-column

Renames columns based on a regular expression pattern and replacement template.

type: rename-column
pattern: string      # Required. Regex string matching the target columns (e.g., ".*_id$").
replacement: string  # Required. The string to replace matches (supports standard capture groups/backreferences).

6. add-constraint

Appends a check, unique, or not-null constraint to the table.

type: add-constraint
constraintType: string # Required. Must be 'CHECK', 'UNIQUE', or 'NOT NULL'.
column: string         # Optional. The column name to which the constraint is attached.
expression: string     # Optional. The SQL check statement or constraint expression.

7. remove-column

Drops columns whose name matches a regular expression pattern.

type: remove-column
pattern: string      # Required. Regex string matching the columns to drop (e.g., "^temp_").

8. set-tag

Appends a classification tag to the table.

type: set-tag
tag: string          # Required. The tag name to apply (e.g., "pii").

Practical Examples

Example 1: Standard Audit Logging Injection

This macro targets all base tables (excluding views) to inject auditing fields (created_at, updated_at), enforce standard database snake_case naming conventions, and tag them as standard data warehouse assets.

name: "Enforce DW Standards"
description: "Injects standard audit columns and enforces snake_case across all tables"
scope: "tables"
actions:
  - type: enforce-naming
    style: snake_case
  - type: add-column
    column:
      name: "created_at"
      type: "TIMESTAMP"
      isNotNull: true
  - type: add-column
    column:
      name: "updated_at"
      type: "TIMESTAMP"
      isNotNull: false
  - type: set-tag
    tag: "dw-managed"

Example 2: Sensitive Data (PHI/PII) Compliance

This macro locates tables with the classification tag phi or pii and enforces an indexing policy. It adds composite indexes and check constraints to verify primary ID formats, while setting their description and operational tier.

name: "HIPAA Data Governance"
description: "Applies indexes and CHECK constraints to tables containing PHI or PII"
filter:
  hasTag: "phi"
actions:
  - type: set-property
    tier: "Tier 1 - Restricted"
    description: "Contains protected health information. Monitored for regulatory compliance."
  - type: add-index
    columns: ["customer_id", "record_date"]
    isUnique: true
    name: "idx_phi_compliance_lookup"
  - type: add-constraint
    constraintType: "CHECK"
    column: "customer_id"
    expression: "customer_id IS NOT NULL AND length(customer_id) >= 8"

Example 3: Bulk Naming Cleanup & Dropping

This macro targets tables beginning with Staging_ to run key cleanup tasks: dropping staging-specific temporary fields, stripping the trailing _id suffix from primary/foreign identifiers to use id as the primary key name, and prepending a standard namespace prefix.

name: "Staging Pipeline Cleanup"
description: "Performs bulk drops and regex renames on incoming staging schemas"
scope: "tables"
filter:
  hasPattern: "^Staging_"
actions:
  - type: remove-column
    pattern: "^temp_.*$"
  - type: rename-column
    pattern: ".*_id$"
    replacement: "id"
  - type: set-property
    prefix: "stg_"

Source Control (Git)

The Source Control panel provides built-in Git integration for versioning your .dsm project files. It supports repository initialization, branching, committing, pushing, pulling, remote origin configuration, and commit-level revert — all within a side panel without leaving the application.

Panel Layout

The Source Control panel slides in from the right side of the screen with the following sections:

  1. Active Branch (Checkout) — Branch selector with dropdown
  2. Remote Origin (Sync) — Remote URL configuration with credentials
  3. Commit Message — Textarea with working state indicator
  4. Action Buttons — Pull, Commit (Local), Commit & Push
  5. Recent Commits — Scrollable commit history with revert capability

Initializing a Repository

If the project file is not tracked by a Git repository, the panel shows:

Note: The project file must be saved to disk before a repository can be initialized.

Branch Management

Active Branch Display

The current branch name is displayed as a clickable button at the top of the panel. Clicking it opens the branch selector dropdown.

Branch Selector Dropdown

The dropdown shows all local branches with:

Branch checkout reloads the project file from the checked-out branch and updates the canvas immediately.

Remote Origin Configuration

The Remote Origin section shows the configured remote URL (with credentials redacted for display). Click "Edit Origin" to configure:

Field Description
Remote URL Full Git URL (e.g., https://github.com/user/repo.git)
Git Username Optional — embedded into the URL for authentication
Access Token (PAT) Optional — Personal Access Token, embedded as password in the URL

Click "Save Config" to apply. Credentials are embedded into the URL in the format https://user:[email protected]/... for push/pull authentication. The displayed URL always redacts credentials for security.

Working State Indicators

A status badge next to the "Commit Message" label shows the current working state:

Badge Color Meaning
UNSAVED Yellow (pulsing dot) Canvas has been modified but not saved to disk yet
CHANGES Orange (pulsing dot) File has been saved but there are uncommitted Git changes
CLEAN Green (steady dot) All changes are committed — working tree is clean

Commit Workflow

A required commit message textarea must be filled before committing. Three action buttons are available:

Button Color Action Git Operations
Pull Remote Changes Emerald Fetch and merge remote changes into the current branch git pull — if the file content changes, the canvas auto-reloads
Commit (Local) Gray Stage and commit changes locally without pushing git add + git commit -m "..."
Commit & Push Indigo Stage, commit, and push to the remote origin in one step git add + git commit -m "..." + git push

Both commit buttons are disabled when there are no changes and the commit message is empty.

Recent Commits

A scrollable list of commit history cards, each showing:

Field Display
Commit hash Short hash in an indigo monospace badge (e.g., a1b2c3d)
Commit message Truncated message text with tooltip for full message
Author "BY author_name" in uppercase tracking
Timestamp Relative or absolute time

Revert to Commit

Hovering over any commit card reveals a red "Revert" button. Clicking it:

  1. Shows a danger confirmation modal: "Revert this document to commit [hash]?" with a warning that uncommitted changes will be irreversibly lost
  2. If confirmed, checks out the project file at that specific commit hash
  3. Reloads the canvas with the reverted content
  4. Refreshes the commit history

Underlying Git Operations

All Git operations are executed via Electron IPC handlers using the system's git CLI:

IPC Channel Operation
git:status Check working tree status and current branch
git:init Initialize a new repository
git:commit Stage, commit, and push
git:commit-local Stage and commit locally
git:pull Pull from remote origin
git:branches List all local branches
git:checkout Switch to a branch (creates if new)
git:checkout-commit Revert file to a specific commit hash
git:history Retrieve commit log (hash|msg|author|time format)
git:get-remote Read the current remote origin URL
git:set-remote Set or update the remote origin URL

Prerequisite: Git must be installed and available on your system PATH. If Git is not available, the panel gracefully degrades — no errors are thrown, and the branch indicator is simply hidden.

Version History

Independent of Git, Wurk.Flow maintains in-project snapshots — lightweight, named save points that capture the complete canvas state. Snapshots enable schema evolution tracking, diff comparison, and one-click rollback without any external version control system.

Opening the Panel

Open via Command Palette → Save Snapshot or View Snapshots. The Version History panel slides in from the right side of the screen (420px wide) with a smooth transition animation. The header displays the total snapshot count.

Creating a Snapshot

The top of the panel contains a snapshot creation form:

Field Description Required
Snapshot Name A label for this save point (e.g., "Before normalization") Yes
Description Optional notes about what this snapshot captures No

Click "📸 Save Snapshot" to capture the current state. The button is disabled until a name is entered.

What a Snapshot Captures

Each snapshot stores:

Field Content
id Unique identifier (snap_{timestamp})
name User-provided label
timestamp ISO 8601 creation time
description Optional notes
tableCount Number of table nodes at snapshot time
columnCount Total columns across all tables
elements Deep clone of the entire canvas (tables, edges, subject areas) — runtime metadata like sourceNode, targetNode, and class are stripped

Snapshot List

Saved snapshots are displayed in reverse chronological order (newest first). Each snapshot card shows:

When no snapshots exist, an empty state is shown with a clock icon and the message "Save a snapshot to track schema evolution."

Snapshot Actions

Each snapshot card has three action buttons:

Button Color Action
Compare Blue (toggleable) Generate a diff between this snapshot and the current canvas state. Click again to hide the diff.
Restore Emerald Replace the current canvas with this snapshot's saved state
Delete Red Permanently remove this snapshot

Restore

Restoring a snapshot triggers a danger confirmation modal:

If confirmed, the entire elements array is replaced with the deep-cloned snapshot data.

Schema Diff Engine

The Compare action runs a structural diff between the snapshot and the current canvas, detecting:

Change Type Detection Logic
Added Tables Tables in current canvas not found in snapshot (by ID)
Removed Tables Tables in snapshot not found in current canvas (by ID)
Renamed Tables Same table ID but different data.label
Added Columns Columns in current table not found in snapshot table (by column ID)
Removed Columns Columns in snapshot table not found in current table (by column ID)
Type Changes Same column ID but different type value

Migration DDL Generation

From any snapshot diff, Wurk.Flow can generate migration DDL — the SQL statements needed to transform the database from the snapshot state to the current state:

Change Generated SQL
Removed table DROP TABLE IF EXISTS "table_name";
Added table CREATE TABLE "table_name" (...columns...); with types, PRIMARY KEY, and NOT NULL constraints
Added column ALTER TABLE "table" ADD COLUMN "col" TYPE;
Removed column ALTER TABLE "table" DROP COLUMN "col";
Type change ALTER TABLE "table" ALTER COLUMN "col" TYPE new_type;
Renamed table ALTER TABLE "old_name" RENAME TO "new_name";

The generated DDL includes a header comment with the dialect, generation timestamp, and a note if no differences are detected.

Persistence & Lifecycle

Auto-Save & Dirty Indicator

File Format (.dsm)

Wurk.Flow projects are saved as .dsm files (Data Schema Model) — plain JSON with the following top-level structure:

{
  "schema": [],           // Canvas elements (tables, edges, subject areas)
  "lineage": [],          // Lineage canvas elements
  "mapping": [],          // Mapping canvas elements
  "requirements": "",     // Freeform markdown text
  "requirementItems": [], // Structured requirement objects

  "workspaceTabs": [],    // Query workspace tabs (SQL, notes)
  "snapshots": [],        // Version history snapshots

}

Files can be version-controlled with Git, diff'd as plain text, and merged manually when needed.

AI Configuration

Wurk.Flow supports AI-powered features including auto-documentation, conceptual schema generation, schema normalization, column inference, and intelligent suggestions. Configure your AI provider via the AI Automation Settings modal (Command Palette → AI Configuration).

Settings Modal

The modal contains the following fields:

Field Description
AI Execution Provider Radio button grid — select Ollama (Local), OpenAI, Anthropic, Azure OpenAI, Groq, Mistral AI, AWS Bedrock, or Custom (OpenAI-compatible). Bedrock and Custom are desktop-only (see Web Studio).
API Chat Target URL The base endpoint URL. Auto-populated with defaults when switching providers.
Inference Model Tag The model identifier to use (e.g., llama3.2, gpt-4o, claude-sonnet-4-20250514)
Bearer Auth Strategy (Key) API key or token. Password field. Hidden for Ollama unless a key has been set.

Supported Providers

When you switch providers, the URL and model fields auto-populate with sensible defaults:

Provider Default URL Default Model Auth Required
Ollama (Local) http://localhost:11434/v1 llama3.2 No (URL only)
OpenAI https://api.openai.com/v1 gpt-4o Yes (API key)
Anthropic https://api.anthropic.com claude-sonnet-4-20250514 Yes (API key via x-api-key header)
Azure OpenAI https://<your-resource>.openai.azure.com/openai/deployments/<deployment> gpt-4o Yes (API key via api-key header)
Groq https://api.groq.com/openai llama-3.3-70b-versatile Yes (API key)
Mistral AI https://api.mistral.ai mistral-large-latest Yes (API key)
AWS Bedrock us-east-1 (region, not URL) anthropic.claude-sonnet-4-20250514-v1:0 Yes (AWS Access Key ID + Secret + optional Session Token)
Custom (OpenAI-compatible) — desktop only, new in 26.9.0 Any /v1 base URL: OpenRouter, Together, Fireworks, DeepSeek, LM Studio, vLLM, or your own gateway Whatever the endpoint serves Optional (local endpoints need none)

All providers except Anthropic and Bedrock share the OpenAI-compatible /chat/completions shape. The Custom provider (desktop) points that shape at any endpoint you name, with the same bring-your-own-key posture as every other provider: requests go from your machine straight to the endpoint.

Temperature-strict models. Some models refuse the temperature parameter (Anthropic requests with extended thinking, OpenAI reasoning models). Since 26.9.0 Wurk.Flow strips the parameter and retries once when a provider rejects it, for every provider, so there is no per-model compatibility list to go stale.

AWS Bedrock is the recommended path for enterprise AWS accounts with strict egress policies. Calls go through the AWS SDK with SigV4 signing rather than direct HTTPS, so corporate firewalls that block api.openai.com / api.anthropic.com still allow AI features to work. Supports Claude (anthropic.*), Llama (meta.*), Titan (amazon.titan-*), and Mistral (mistral.*) model families through one connection.

Provider-Specific API Routing

The AI service automatically normalizes the endpoint URL and request format per provider:

Configuration Storage

AI settings are saved using a dual-layer approach:

API keys are stored using hardware-bound encryption — they never leave your machine unencrypted.

AI-Powered Features

Once configured, the following AI features are available throughout the application:

Feature Trigger What It Does Temperature
Generate Table Glossary Table Properties → "🤖 Generate" button Creates a business description for the table and concise descriptions for each column based on schema structure 0.1
Infer Column Types & Tags Table Properties → "Infer Types" button Infers SQL data types (INTEGER, VARCHAR, BOOLEAN, DATE, FLOAT) and security classification tags (PII, PCI, PHI, HIPAA, GDPR, Internal) for each column. If the table has no columns, it generates 4-8 probable columns for the entity. 0.2
Generate Conceptual Schema Canvas toolbar → "AI Generate" prompt Takes a business workflow prompt (e.g., "e-commerce platform") and generates a complete entity-relationship schema with 4-6 columns per table and relationship edges 0.3
Normalize to 3NF Table right-click → "Normalize (AI)" Takes a large, flat "BigAssTable" and decomposes it into normalized 3NF entities (dimensions/facts) with proper PKs, FKs, and relationship edges 0.2
Generate Policy Boilerplate Data Glossary → Policy section Generates a standard operating protocol (SOP) for a data policy based on its name and classification tags. Returns markdown text under 150 words. 0.3
Proofread Requirements Requirements Workbench → "Proofread" button Fixes spelling and grammar in business requirements text while preserving meaning, structure, and markdown formatting 0.2

Rate Limiting

A client-side rate limiter prevents rapid-fire API calls:

Retry Logic

Transient failures are automatically retried:

JSON Response Parsing

AI responses expected as JSON pass through a multi-stage extraction pipeline:

  1. Detect the outermost {...} or [...] boundary
  2. Strip trailing commas before closing brackets
  3. Attempt JSON.parse()
  4. Fallback: strip markdown code block fences (```json...```) and parse the cleaned string

If parsing fails entirely, a descriptive error is thrown with the first 150 characters of the raw response for debugging.

MCP Server PRO

The MCP server lets an AI coding agent — Claude Code, Cursor, Claude Desktop, or any client that speaks the Model Context Protocol — read the model that is open in Wurk.Flow right now and change it. It is a desktop-app feature, it is off until you turn it on, and it listens on this machine only. Nothing leaves the computer: the agent talks to the app over the loopback address, and every request has to carry a token that only exists on your machine.

Turning it on

  1. Open the Command Palette (Ctrl/Cmd+K) and run Settings: MCP Server (AI agents)...
  2. Flip Enable MCP server. The status line shows the endpoint, normally http://127.0.0.1:47831/mcp. Change the port there if something else already uses it.
  3. Pick your client under Connect a client and press Copy. Claude Code gets a one-line claude mcp add command; Cursor gets a mcp.json block; Claude Desktop gets a claude_desktop_config.json block that launches the bundled stdio shim through the Wurk.Flow binary.

The setting is remembered: the server starts with the app on the next launch. Wurk.Flow has to be running for any client to connect, including the Claude Desktop shim.

What an agent can do

ToolWhat it does
get_projectFile, unsaved-changes flag, tier, dialect, and element counts.
list_tables / get_tableTables, views, and materialized views; one table in full with columns, constraints, indexes, and relationships.
find_columnsFind a column by name across every table.
get_dbml / generate_ddlThe canvas as DBML, or as DDL in any of the 13 dialects.
get_glossaryBusiness glossary terms with status, owner, and linked references.
get_lineage / lineage_impactThe lineage graph, and the downstream columns a change would touch.
plan_dbmlDry run: how a DBML document would reconcile against the canvas (new, updated, unchanged, missing).
apply_dbmlMerge a DBML document into the canvas. Adds and updates; never removes unless the agent explicitly asks.

Writes go through DBML on purpose. Every model already speaks it, and the merge is the same reconcile the DBML Workspace uses for Apply, so an agent's change lands as one undo step: press Ctrl/Cmd+Z and it is gone. A toast announces each applied change.

Security

Settings

Settings are persisted using a dual-layer approach:

Settings include: AI provider configuration, MotherDuck token, and database connection profiles.

Licensing

Wurk.Flow has three editions. The names below are the public ones. The in-app status-bar badge and a few messages still call the Free edition COMMUNITY, and Free exports are stamped "Community Edition"; they mean the same thing.

Edition Price Seats Unlocks
Free $0, no account needed Modeling up to 15 tables and views, DDL in DuckDB / PostgreSQL / SQLite (Full Build and Seed Data modes), one SQL Workspace tab on the DuckDB sandbox, DBML import and export, DDL export (with a header comment), PNG/PDF diagrams, custom types, the Audit Log
Pro $9.99 / month or $99 / year 1 machine on the desktop, plus your account in the Web Studio Everything marked PRO: unlimited tables, all 13 dialects, Migration Diff and Insert Data DDL, live database connections and introspection, unlimited SQL tabs with live and cloud engines, Data Glossary, Data Lineage, Data Mapping, Git source control, version snapshots, Conceptual/Logical views, Auto-Link and Auto-Organize, documentation exports, API import, custom macros, clean exports
Team $39.99 / month or $399 / year 5 seats Everything in Pro plus TEAM features: Co-Op live sessions, the Requirements Workbench, and the option to self-host the Co-Op relay

Plans are bought and managed at flow.wurk.haus. Billing is monthly or annual through Stripe. 4 · Buy & Activate walks through the purchase, the first activation, and moving a key to a new machine.

Desktop: a key bound to one machine

  1. You paste a license key into the License window (key icon at the bottom of the sidebar). This one step needs an internet connection.
  2. The license service binds the key to this machine's hardware ID and returns a signed token (ES256). The app stores the token encrypted at rest and verifies its signature locally, in the main process, against a public key compiled into the app. A token whose signature does not verify is refused.
  3. On every launch, and about once an hour while running, the app sends its hardware ID to the license service. This heartbeat answers with a fresh token reflecting your subscription right now, or with a reason it will not issue one.
  4. The token is what gates Pro features. A subscription token is valid for 30 days from the moment it was issued, and the heartbeat replaces it long before that.

Offline. After activation the app works fully offline. When the heartbeat cannot reach the service (no network, or the service is down), the app keeps the token it already holds, so you stay Pro until that token's own expiry: 30 days after the last successful check. Free features never need a license or a network connection.

Moving machines. One key, one machine at a time. Click DEACTIVATE / UNBIND MACHINE in the License window, or Revoke Seat on the dashboard if the old machine is gone. The same key then activates on the next machine.

Version years

Releases are numbered by year: every 26.x release belongs to version year 26 (2026). A license carries the version year it was bought in and unlocks every release of that year. Running a release from a newer year (a v27 build on a v26 license) shows RENEWAL REQUIRED: Pro features are locked in that build until you renew, while every v26 release keeps working. There is no calendar grace period at year end. The year gate is about which build you run, not about a date.

What happens when…

Situation Desktop entitlement
Subscription active (monthly or annual) Full Pro or Team. Each heartbeat issues a fresh 30-day token.
A renewal payment fails Nothing changes while Stripe retries the card. Full access continues through the retry window; the subscription only ends if Stripe finally gives up.
Annual plan cancelled, or simply not renewed Perpetual fallback. At the first heartbeat after the paid period ends, the service issues a fallback token pinned to your paid version year, and keeps re-issuing it on every heartbeat, at no charge, indefinitely. Every release of that year keeps working. Releases of a later year show RENEWAL REQUIRED until you renew.
Monthly plan cancelled or ended No fallback. Pro features stop when the last issued token expires, within 30 days of the end of the final paid period. Your files and every Free feature are untouched.
Refund, chargeback, or payment dispute The subscription is marked revoked. The next heartbeat wipes the stored token, the app drops to Free with a "License no longer active" notice, and perpetual fallback no longer applies. A dispute decided in Wurk.Haus's favour restores standing.
Seat revoked from the dashboard That machine drops to Free at its next heartbeat; the key is free to activate elsewhere.

Two things to know about perpetual fallback.

  • It needs one online heartbeat after your plan ends to arrive. A machine that has been offline since before the plan ended still holds an ordinary subscription token, which lapses 30 days after its last check; reconnect once and the fallback token replaces it.
  • Fallback tokens last about 13 months and renew on every heartbeat, so a machine that is online even occasionally never notices. A machine kept offline longer than that drops to Free until it reconnects once. For a truly air-gapped installation, ask for an offline perpetual key through the contact form.

The "expiring soon" notice in the License window keys off the token currently held. For an active subscription that is always a rolling 30-day token, so the notice is not a sign that your subscription is ending. The dashboard shows your real renewal date.

Team seats

A Team subscription is five seats. Each seat is its own key, listed on the dashboard, and counts as "in use" once a machine has activated it. The owner can revoke any seat at any time. Team features (Co-Op, Requirements) follow the seat. In the Web Studio the owner invites members by email and they sign in with their own account; no key is involved.

Web Studio: an account, not a machine

At app.wurk.haus there is no key and no hardware ID. Sign in with your flow.wurk.haus account; the studio asks the license service whether your organization has an active subscription, at sign-in and every 6 hours. The service applies the same standing rules as the desktop heartbeat, including good-standing fallback for a lapsed annual plan. An unreachable check keeps the last good answer for 24 hours, after which the studio drops to Free until a check succeeds. Web access never consumes a desktop seat. The Web Studio always runs the current release, so version-year pinning has no meaning there.

Command Palette

Press +K to open the Command Palette — a searchable, keyboard-navigable action launcher for accessing any feature in the application. The palette is the fastest way to perform actions without navigating menus.

Interface

The palette opens as a centered glassmorphic dialog (positioned at 15% from the top of the viewport) with a slide-in animation:

Keyboard Navigation

Key Action
Arrow Down Move selection to next action
Arrow Up Move selection to previous action
Enter Execute the selected action
Escape Close the palette

The selected action auto-scrolls into view. Mouse hover also moves the selection. All keyboard events use capture phase to intercept before Vue Flow's canvas handlers.

Search & Filtering

Type in the search input to filter actions. The filter uses case-insensitive substring matching against the action label. The selection index resets to 0 whenever the search query changes.

Pro Feature Gating

Actions marked with a lock icon (🔒) require a Pro license. Selecting a Pro action without a valid license opens the License Activation modal instead of executing the action. Pro validation uses offline-first license checking.

Registered Actions

The command palette contains 37 actions organized into 9 categories:

Navigation

Action Tier Description
Go to Schema Builder Free Switch to the Schema Builder canvas tab
Go to Data Glossary PRO Switch to the Data Glossary panel
Go to SQL Workspace Free Switch to the SQL Workspace tab
Go to DDL Engine Free Switch to the DDL Generation panel
Go to Business Requirements TEAM Switch to the Requirements Workbench
Go to Data Lineage PRO Switch to the Data Lineage canvas
Go to Data Mapping (STTM) PRO Switch to the Data Mapping canvas
Go to Source Control (Git) PRO Open the Source Control panel
Fit View (Re-center Canvas) Free Re-center and zoom the canvas to fit all nodes

Database

Action Tier Description
Add Table Free Create a new table node on the canvas
Add View Free Create a new view node on the canvas
Add Materialized View Free Create a new materialized view node
Import Live Database... PRO Open the Connection Modal for database introspection
Manage Database Connections... PRO Open the Connection Modal to manage saved profiles
Generate Migration Script... PRO Open the Migration DDL generator
Detect Schema Drift... PRO Compare canvas schema against a live database

Macro

Action Tier Description
Inject Soft Deletes (All Tables) Free Add is_deleted / deleted_at columns to all tables
Generate Missing Primary Keys Free Add surrogate id column to tables without a PK
Inject Timestamps (All Tables) Free Add created_at / updated_at columns to all tables
Enforce Naming Standards (Snake Case) Free Convert all table and column names to snake_case
Auto-Link Relationships PRO Auto-detect and create FK edges based on column naming patterns
Auto-Organize Canvas Layout PRO Automatically arrange table nodes in an optimized grid layout
Clean Up Canvas (Dedupe Tables) Free Collapse duplicate same-name tables down to one and drop orphaned edges (undoable) — useful after re-importing the same database

File

Action Tier Description
New Schema Free Create a new blank project
Open File... Free Open a .dsm project file
Save Free Save the current project
Save As... Free Save to a new file path
Import SQL DDL... PRO Parse .sql files and create tables from DDL

Export

Action Tier Description
Export DDL... Free Export generated DDL as a .sql file
Export Documentation Portal (HTML) PRO Generate a self-contained HTML documentation site
Export Documentation (Markdown) PRO Export a markdown-formatted data dictionary

DataOps

Action Tier Description
Fetch API → Table PRO Open the API Import modal

Version Control

Action Tier Description
Save Schema Snapshot... PRO Open the Version History panel to save a snapshot
View Version History... PRO Open the Version History panel to browse snapshots
Git: Checkout Branch... PRO Open a prompt to switch Git branches

Edit

Action Tier Description
Undo Free Undo the last canvas action
Redo Free Redo the last undone action

System / Configuration

Action Tier Description
Configure AI Assistant... Free Open the AI Automation Settings modal
About Wurk.Flow DataStudio Free Show the About modal with version info
Submit Feedback... Free Open the Feedback modal
Keyboard Shortcuts Free Show the keyboard shortcuts overlay
Quit Application Free Quit the application (with unsaved changes confirmation)

Keyboard Shortcuts

Shortcut Action
+K Open Command Palette
+S Save project
+Shift+S Save As (new file)
+F Focus canvas search (Schema tab)
+Z Undo
+Shift+Z Redo
+Y Redo (alternate)
+C Copy selected node
+V Paste copied node
Delete / Backspace Delete selected node or edge
? Show keyboard shortcuts overlay

Note: On Windows/Linux, replace with Ctrl.

Security Architecture

Process Isolation

Wurk.Flow uses Electron's security best practices:

Data at Rest

Network Access

Everything the desktop app can contact, and when. The browser build's shorter list is in Web Studio.

Not on the list: analytics of any kind. Wurk.Flow ships no usage tracking, on the desktop or on the web.

HTML Report Generator

The HTML Report Generator exports your entire project — schema architecture, data dictionary, lineage graph, business requirements, DDL preview, and model health — into a single, self-contained HTML file that can be opened in any browser without a server. The output is a fully interactive documentation portal designed for sharing with stakeholders, auditors, and downstream teams.

Invocation

There are three ways to trigger an HTML report export:

Method Path
Command Palette +K → "Export Documentation Portal (HTML)"
Menu Bar Export → Documentation Portal (HTML)
Toolbar Export dropdown → HTML Documentation Portal

After invocation, a Report Title modal prompts for the project name. The default is derived from the current .dsm filename. Pressing Cancel aborts the export; submitting proceeds to the native Save dialog.

Portal Tabs

The generated HTML portal contains 7 navigable tabs, each rendered as a discrete content section with CSS-based show/hide toggling:

Tab Content Key Features
Summary Executive overview with KPI cards and health status 5 KPI cards, dual coverage progress bars (column + table documentation), Subject Area breakdown grid, health status badge
Requirements Business requirements and architectural context Structured requirements table with priority/status/category badges, linked table references, acceptance criteria progress, freeform Markdown rendering via marked.js
Schema Architecture Interactive ER diagrams with conceptual/physical sub-views HTML5 Canvas renderer with pan/zoom, dot-grid background, color-coded node kinds (table/view/mview), Bézier edge curves, relationship reference table with cardinality badges
Data Dictionary Column-level documentation grouped by Subject Area Table Index sidebar (TOC), search filter, governance badges (domain, certification, tier, owner, refresh frequency, classifications), profiling statistics, constraint badges (PK/FK/UQ/CHECK), index and trigger listings, stored procedure bodies
Data Lineage End-to-end data flow DAG Mermaid graph LR diagram, color-coded component specification cards (system, job, consumer, apiCall, flatFile), classification and notes display
DDL Preview Auto-generated SQL DDL Syntax-highlighted DDL output with dialect badge, select-all for easy copy
Health Model validation results Error/warning severity badges, entity reference, validation failure descriptions, "All Checks Passed" zero-state

KPI Computation

The Summary tab computes 5 key performance indicators at generation time:

KPI Computation Color Logic
Tables elements.filter(type === 'table').length Indigo (static)
Columns Sum of all table.data.columns.length Cyan (static)
Relationships elements.filter(type === 'relationship').length Purple (static)
Doc Coverage Math.round((documentedCols / totalColumns) * 100) ≥80% = emerald · ≥50% = amber · <50% = red
Issues errorCount + warningCount errors > 0 = red · warnings only = amber · 0 = emerald

Two additional progress bars show Column Documentation Coverage (documentedCols / totalColumns) and Table Documentation Coverage (documentedTables / totalTables) with the same color thresholds.

Interactive Canvas Renderer

The Schema Architecture tab uses a custom HTML5 Canvas renderer (not Mermaid) to display the full ER diagram with interactive controls. This is embedded directly in the generated HTML and requires no external dependencies.

Feature Implementation
Pan Mouse drag on canvas (mousedown/mousemove/mouseup events)
Zoom Mouse wheel with focal-point zoom (wheel event, non-passive)
Fit All Toolbar button — computes bounding box of all nodes, sets zoom to fit with 60px padding
Zoom In/Out Toolbar buttons with 1.2× / 0.8× scale factor centered on viewport
DPR Scaling window.devicePixelRatio-aware rendering for Retina/HiDPI displays
Dot Grid 24px grid of 0.8px radius dots at 3% white opacity
Resize Handling window.resize event listener recalculates canvas dimensions

Canvas Node Rendering

Node Kind Border Color Header Color Badge
table rgba(99,102,241,0.4) (indigo) rgba(99,102,241,0.15)
view rgba(34,211,238,0.4) (cyan) rgba(34,211,238,0.15)
materialized_view rgba(251,191,36,0.4) (amber) rgba(251,191,36,0.15)

In Conceptual mode, nodes render as 160×40px labels. In Physical mode, nodes expand to 200px width with column listings showing name, type (right-aligned in cyan), and PK indicators (yellow highlight strip with bold "PK" badge).

Canvas Edge Rendering

Relationships are drawn as Bézier curves (bezierCurveTo) with indigo stroke (rgba(99,102,241,0.35)) and directional arrow heads. Control point offset is calculated as 50% of the horizontal distance between source and target nodes.

Mermaid Diagram Generation

The generator produces three Mermaid diagram strings:

Diagram Type Used In Content
Conceptual ER erDiagram Schema tab (fallback) Entity names only, relationships with cardinality connectors and ON DELETE labels
Physical ER erDiagram Schema tab (fallback) Entity names + column definitions (type, name, PK/FK), relationships with connectors
Lineage DAG graph LR Lineage tab Left-to-right flow graph with tableRef nodes as cylindrical shapes, other nodes as rectangles

Cardinality Mapping

Cardinality Mermaid Connector Meaning
1:1 ||--|| One-to-one
1:N ||--o{ One-to-many (default)
N:1 }o--|| Many-to-one
N:M }o--o{ Many-to-many

Data Dictionary: Governance Metadata

Each table card in the Data Dictionary tab renders governance badges from the schema model:

Badge Source Field Color
Domain table.data.domain Blue
Certification table.data.certification Gold = yellow · Deprecated = red · Other = green
Tier table.data.tier Tier 1 = red · Tier 2 = yellow · Tier 3 = green
Owner table.data.owner Indigo
Refresh Frequency table.data.refreshFrequency Cyan
Classifications (tags) table.data.classifications[] Red (per tag)

Column-Level Detail

Each column row includes:

Additional Table Metadata

If present in the schema model, each table card also renders:

CDN Dependencies

The generated HTML loads the following external resources at runtime:

Resource CDN Source Purpose
Tailwind CSS cdn.tailwindcss.com (with typography plugin) Utility-first styling engine for the portal layout
Mermaid cdn.jsdelivr.net/npm/mermaid@10 (ESM) Lineage DAG rendering — initialized with dark theme, transparent background
marked.js cdn.jsdelivr.net/npm/marked Markdown → HTML rendering for freeform business requirements
Google Fonts fonts.googleapis.com Inter (UI text) and JetBrains Mono (code/monospace)

Note: An internet connection is required to view the generated HTML portal. The CDN resources are not embedded inline. Offline viewing is not supported.

Print Mode

The generated portal includes a @media print stylesheet that:

Troubleshooting

macOS: "App is damaged" or Gatekeeper Warning

macOS Gatekeeper may block unsigned or notarization-pending builds. To resolve:

  1. Open Terminal
  2. Run: xattr -cr /Applications/Wurk.Flow.app
  3. Re-launch the application

Alternatively, right-click the app in Finder and select "Open" to bypass the Gatekeeper dialog once.

Database Connection Fails

If connection testing fails in the Connection Modal:

Symptom Solution
Timeout / no response Check firewall rules — Wurk.Flow connects directly from your machine via the Electron main process
Authentication error Verify credentials with an external tool (psql, mysql, sqlcmd, etc.) before retrying
SSL/TLS error Ensure certificates are valid and accessible. For self-signed certs, check the engine's SSL mode settings.
Databricks: "thrift" error Known vulnerability in the @databricks/sql thrift dependency — isolated to the sandboxed desktop environment and does not affect security
MotherDuck: token rejected MotherDuck tokens can expire. Generate a fresh token from the MotherDuck dashboard.
Wrong port Auto-port defaults are applied per engine (e.g., 5432 for PostgreSQL, 3306 for MySQL). Override in the Port field if your server uses a custom port.

DuckDB Sandbox Not Loading Data

The DuckDB WASM sandbox auto-scaffolds tables from your canvas schema. If data isn't appearing:

Issue Solution
Tables not visible in SQL Workspace Ensure your tables have at least one column defined — empty tables are skipped during scaffold
WASM loading error in console Check the browser DevTools console for DuckDB Init Error messages. May indicate a browser incompatibility or CDN issue.
"Sandbox cleanup issue" warning Some tables couldn't be dropped during re-sync. Try switching SQL targets and switching back to force a full re-scaffold.
Seed data not inserting Batch inserts may silently skip if data types don't match. Check console for Seed insert skipped warnings.

AI Features Not Working

Error Message Cause & Solution
"AI Provider is not fully configured" Open Command Palette → Configure AI Assistant and set your provider, URL, and API key
"AI is not configured — Please set an API key in Settings first" Cloud providers (OpenAI, Anthropic, Azure OpenAI, Groq, Mistral) require an API key. Ollama only requires the base URL.
"Rate limited — please wait a moment" A 2-second cooldown exists between AI requests. Wait briefly and retry.
AI API Error (401) or (403) Invalid or expired API key. These 4xx errors are not retried. Check your key is correct and has sufficient quota.
AI API Server Error (5xx) The AI provider is experiencing issues. The request will auto-retry once (with 1.5s backoff). If it persists, try again later.
"Failed to parse AI response into strict JSON" The AI model returned malformed JSON. Try a different model (e.g., gpt-4o tends to produce cleaner JSON than smaller models).

License Activation Issues

The checklist in 4 · Buy & Activate covers the common "I paid and it is still locked" cases; the rows below add the rest.

Problem Solution
Pro features locked after purchase Paying creates a key on the dashboard; it does not activate anything by itself. Copy the key and activate it in the License window (key icon, bottom of the sidebar). Activation needs a one-time internet connection.
"License is already bound to another machine" The seat is in use elsewhere. Click Revoke Seat on the dashboard, or DEACTIVATE / UNBIND MACHINE on the old machine, then activate again.
RENEWAL REQUIRED / "Your license covers v26" The build you are running belongs to a newer version year than your license. Renew at flow.wurk.haus, or run a release from your paid year, which keeps working (perpetual fallback on annual plans).
License lost after an OS reinstall or new hardware The hardware ID changed, so the seat still points at the old machine. Revoke the seat on the dashboard and activate the same key again. No support transfer is needed.
"License verification failed" at launch The stored token failed its signature check or is corrupted. Re-activate with your key while online; the service issues a fresh, correctly signed token.
Pro dropped to Free while offline The held token reached its 30-day expiry without a successful heartbeat. Connect once; the next heartbeat issues a new token (a subscription token, or the perpetual fallback token if an annual plan has ended).
"License no longer active" mid-session The service revoked the license: a refund, chargeback or dispute is on record, or the seat was revoked from the dashboard. If that is wrong, use the contact form with your Hardware Signature.
"License expiring soon" notice Refers to the current token, which for an active subscription is a rolling 30-day token renewed hourly. It is not your renewal date; the dashboard is.
Web Studio: signed in but Free Sign in with the account that owns or was invited to the subscription. If the check is unreachable, the last good answer holds for 24 hours, then Free until it succeeds.

Git / Source Control Failures

Error Solution
Branch indicator missing Git may not be installed or the project directory isn't a Git repository. Install Git and initialize via the Source Control panel.
"Git checkout failed" You may have uncommitted changes that would be overwritten. Commit or stash changes first.
"Push failed" Check that a remote origin is configured and your credentials (username/PAT) are valid. Tokens expire — regenerate if needed.
"Git pull failed" May indicate merge conflicts. Resolve conflicts manually using an external Git client, then retry.
"Parse Error: historical commit file is invalid JSON" The .dsm file at the target commit is corrupted. Try reverting to a different commit.

API Import: CORS Errors

In desktop mode, API requests are proxied through Electron's Node.js process, bypassing CORS entirely. CORS errors should only occur in browser dev mode (running via npm run dev without Electron).

Large Schema Performance

For schemas with 300+ tables, canvas performance may degrade. Recommended optimizations:

Strategy Effect
Use Subject Areas Visually partition your canvas into logical sections
Hide inactive Subject Areas Toggle visibility via the eye icon to reduce rendering load
Switch to Conceptual mode Reduces rendering complexity by hiding column-level detail
Close unused workspace tabs Reduces memory usage from idle SQL editors

File Import Errors

Format Common Issue Solution
SQL DDL "No valid CREATE TABLE statements detected" Ensure the file contains standard CREATE TABLE DDL. Proprietary extensions or stored procedures are not parsed.
SQL DDL "Failed to parse SQL" The DDL parser expects standard ANSI SQL. Remove vendor-specific syntax before importing.
CSV "CSV Inference Failed" The file may have inconsistent column counts or encoding issues. Ensure UTF-8 encoding and consistent delimiters.
JSON "JSON file must contain an object or array of objects" Top-level scalars or arrays of primitives are not supported. Wrap data in an object.
JSON "JSON file contains an empty array" At least one record is needed for schema inference.
.dsm "Invalid Schema File" The .dsm file is corrupted or not valid JSON. Try opening a backup or previous Git commit.

MotherDuck Connection Issues

Wurk.Flow v26.10.0 · Built by Wurk.Haus · wurk.haus