Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Irodori Table

Irodori Table is a fast, local-first database workbench. This site is the public user, developer, and contributor documentation.

Getting started

User guides

Development

How it works

Reference

Planning

Install Guide

Irodori Table ships desktop installers from GitHub Releases. The latest public release checked for this guide is v0.6.0, published on 2026-07-02:

https://github.com/hjosugi/irodori-table/releases

Use the newest release for normal desktop installs. cargo install does not install the desktop application. It is only for the separate headless irodori-server binary in irodori-kit.

This guide is for packaged desktop installs. Source build prerequisites and WebView troubleshooting live in the platform development guides: Windows, macOS, and Linux.

Quick terminal install

The shortest terminal path uses GitHub CLI. Install gh first if your shell does not already have it. When no release tag is passed, gh release download downloads assets from the latest release.

Linux: Debian or Ubuntu

tmp="$(mktemp -d)"
gh release download --repo hjosugi/irodori-table --pattern "*.deb" --dir "$tmp"
sudo apt install "$tmp"/*.deb

Linux: Fedora, RHEL, or compatible

tmp="$(mktemp -d)"
gh release download --repo hjosugi/irodori-table --pattern "*.rpm" --dir "$tmp"
sudo dnf install "$tmp"/*.rpm

Linux: portable AppImage

mkdir -p "$HOME/Applications"
gh release download --repo hjosugi/irodori-table --pattern "*.AppImage" --dir "$HOME/Applications"
chmod +x "$HOME/Applications"/Irodori*.AppImage
"$HOME/Applications"/Irodori*.AppImage

macOS

mkdir -p "$HOME/Downloads/irodori-table"
gh release download --repo hjosugi/irodori-table --pattern "*.dmg" --dir "$HOME/Downloads/irodori-table"
open "$HOME/Downloads/irodori-table"/*.dmg

Move Irodori Table to Applications from the mounted disk image. The current preview release includes an Apple Silicon .dmg; use a source build for Intel macOS until an Intel .dmg is published.

Windows PowerShell

$dir = New-Item -ItemType Directory -Force "$env:TEMP\irodori-table"
gh release download --repo hjosugi/irodori-table --pattern "*.msi" --dir $dir.FullName
$msi = Get-ChildItem $dir.FullName -Filter "*.msi" | Select-Object -First 1
Start-Process $msi.FullName -Wait

Downloads by OS

OSRecommended assetNotes
Windows.msi or setup .exeDownload the Windows installer asset and run it from Explorer.
macOS.dmgOpen the disk image, move Irodori Table to Applications, then launch it. Current preview assets are Apple Silicon only.
Linux.deb, .rpm, or .AppImageUse .deb on Debian/Ubuntu-style systems, .rpm on Fedora/RHEL-style systems, or AppImage for portable local runs.

Windows

  1. Download the Windows installer from the release assets.
  2. Run the installer.
  3. Launch Irodori Table from the Start menu.

If Windows SmartScreen appears for a development-preview build, verify that the installer came from the official GitHub release page before continuing.

macOS

  1. Download the .dmg from the release assets.
  2. Open the disk image and move Irodori Table into Applications.
  3. Launch it from Applications.

During the development-preview phase, macOS may require Control-click > Open the first time you run a downloaded build.

The current preview release publishes an Apple Silicon .dmg. Intel macOS users should build from source until an Intel .dmg appears in the release assets.

Linux

For Debian or Ubuntu-style systems, download the .deb and install it with your package manager:

sudo apt install ./path/to/downloaded-package.deb

For Fedora, RHEL, or compatible distributions, download the .rpm and install it with your package manager:

sudo dnf install ./path/to/downloaded-package.rpm

For other desktop distributions, download the AppImage, make it executable, and run it:

chmod +x ./Irodori*.AppImage
./Irodori*.AppImage

Some distributions ship FUSE 3 by default while AppImage still expects FUSE 2. If the AppImage does not launch, either install the distribution’s FUSE 2 package or run it in extract-and-run mode:

APPIMAGE_EXTRACT_AND_RUN=1 ./Irodori*.AppImage

For release channel details and future package-manager plans, see Distribution and updates.

Headless server

Rust users who need the local HTTP API, not the desktop app, can install the headless server from irodori-kit:

cargo install --git https://github.com/hjosugi/irodori-kit --tag v0.5.0 --locked irodori-server

See Headless local data API for runtime configuration.

Getting Started

This path gets a new user from install to a successful query without reading the architecture notes.

Install

Download the current desktop build from https://github.com/hjosugi/irodori-table/releases. The Linux build is the most actively exercised preview target.

For source builds, use the platform development guide for your OS instead of the desktop install guide: Windows, macOS, or Linux.

Create A Connection

  1. Open the connection manager.
  2. Choose a database engine.
  3. Enter host, port, database, user, and authentication details.
  4. Use the diagnostic or test action before saving.

Secrets are stored through the local OS credential store where supported. Export connection definitions without secrets when sharing project setup.

Run The First Query

  1. Open a SQL editor tab bound to the saved connection.
  2. Type a query such as select 1;.
  3. Run the current statement or selected text.
  4. Cancel from the running-query control if the statement takes too long.

The editor is local-first. SQL is inserted, formatted, or generated in the editor first; it is not sent to a database until you run it.

Read Results

Results appear in the result grid. Use copy/export for a selected range or the current page, switch result modes when available, and keep large exports bounded by using the export flow instead of copying an entire result set.

Next reads:

Connections

Connections bind editor tabs, browser metadata, result history, and diagnostics to a database source.

Daily Flow

  1. Create or duplicate a profile from the connection manager.
  2. Keep profiles organized in folders when working across projects.
  3. Test the profile before opening an editor tab.
  4. Bind a tab to the connection before running SQL.

Transport And Secrets

Profiles can use direct sockets, SSH, SOCKS/HTTP proxies, and ordered proxy chains where the engine supports them. Store reusable hops separately so several profiles can share the same transport path.

Exported connection definitions exclude secrets. Re-enter credentials after importing a profile on another machine.

Troubleshooting

Use connection diagnostics first. They separate DNS/network/proxy failures from database authentication and feature support errors. When a connector is not part of the default build, install the marketplace connector or choose an engine that is shipped in the current desktop binary.

Query Editor And Vim

The editor is the primary workspace surface. It supports multiple tabs, saved sessions, connection-bound editors, snippets, formatting hooks, and command palette actions.

Running SQL

  • Run the current statement for quick iteration.
  • Select text and run the selection for focused checks.
  • Run the whole file when reviewing a complete script.
  • Cancel long-running statements from the running-query control.

Completion

Completion uses local metadata first: schemas, tables, columns, aliases, CTEs, functions, snippets, and dialect keywords. AI assistance is optional and must be configured separately.

Vim Mode

Vim mode is meant for daily-driver editing: normal/insert/visual modes, counts, registers, marks, macros, search, and command-line style workflows. Keybindings can be remapped from settings, and conflicts should be resolved before relying on a custom map.

Results Grid And Export

The result grid is optimized for repeated inspection, copying, and export from large result sets.

Inspecting Results

  • Scroll large pages without loading the entire result into memory.
  • Use row details when a record is wider than the visible grid.
  • Keep filters and sorting scoped to the loaded page unless a server-side workflow is explicitly available.

Copy And Export

Use copy for small selected ranges. Use export for reproducible files:

  • CSV/TSV with header and delimiter controls
  • JSON and NDJSON
  • SQL insert/upsert scripts where supported
  • Avro and Parquet for columnar workflows

Native save dialogs are used by desktop exports, including spreadsheet-friendly formats.

ERD And Schema Designer

The ERD surface is both a browser aid and a design surface.

Explore A Schema

Seed a diagram from a live connection to inspect tables, columns, primary keys, foreign keys, and relationships. Move tables to make the shape readable and keep large schemas focused on one subject area at a time.

Design Changes

Create or edit tables, columns, keys, and relationships in the designer before generating SQL. Forward engineering emits ordered CREATE and ALTER statements so foreign-key dependencies are applied after the base tables exist.

Exchange Diagrams

Export diagram JSON when sharing a design or keeping a review artifact with a change proposal. Import JSON to continue work later or compare a proposed design with a live connection.

Import And Migration Studio

Import/export and migration planning are separate workflows: import moves data into a table, while Migration Studio plans schema and data verification steps.

Import

Preview incoming CSV, TSV, JSON, and NDJSON data before writing it. Check inferred columns, target table names, null handling, and delimiter/quote settings before running an import.

Migration Studio

Migration Studio compares source and target metadata, builds preview SQL, and surfaces destructive changes before execution. The reusable planning and data-diff primitives live in irodori-migration; the desktop app supplies live connections, jobs, cancellation, and UI review.

Verification

Use row counts, checksums, bucket fingerprints, and row-level diffs to verify a migration. For very large tables, prefer bucketed verification first and inspect only the failed buckets.

Git, Terminal, And Search

Irodori includes development-adjacent tools so SQL work can stay in one workspace.

Git Panel

Use the Git panel to inspect repository state, branches, and commits near the SQL or migration files you are editing. Commit-specific actions such as copy hash, open remote URL, and file summaries are tracked as follow-up hardening work.

Terminal

The terminal panel runs a local PTY in the desktop shell. Use it for nearby commands such as database fixtures, migration scripts, or project-local checks. Treat it like any local shell: commands run with your user permissions.

Search And Replace

Cross-tab search helps update SQL across open tabs and project buffers. Review matches before replacing, especially when connection-specific SQL dialects are mixed in one workspace.

AI Assist

AI features are optional. Deterministic completion and editor workflows work without a model or cloud provider.

Provider Setup

Configured providers share one abstraction across SQL generation and chat. Local providers can run on-device; cloud providers require user-supplied credentials stored outside the repository.

Read-Only Agent Mode

The schema-aware chat sidebar can inspect context and propose SQL. Read-only agent mode is cancellable and should not execute writes without explicit user action.

Privacy Expectations

Only enable a cloud provider when the schema or prompt content is appropriate for that provider. Keep privacy mode and redaction settings enabled when sharing logs, screenshots, or reports.

Windows Development

This guide covers local desktop development on Windows. The Tauri desktop shell should run on the Windows host because it depends on the Windows WebView2 and MSVC toolchain. WSL is useful for some Rust-only work, but it is not the primary path for desktop UI development.

Prerequisites

  • Windows 10 or Windows 11 on x64.
  • Git.
  • Node.js 24.x, matching the repository .nvmrc.
  • Rust from rustup; the repository pins Rust 1.96.0 in rust-toolchain.toml.
  • Visual Studio 2022 Build Tools with the Desktop development with C++ workload, including MSVC and a Windows SDK.
  • Microsoft Edge WebView2 Runtime. It is already present on most current Windows installs; install the Evergreen Runtime if Tauri reports that WebView2 is missing.
  • Optional: Docker Desktop or Podman Desktop for sample database containers.

Setup

Use PowerShell or Git Bash from the irodori-table repository root:

rustup toolchain install
npm --prefix apps/desktop ci
node tools/dev/doctor.mjs

If GNU Make is available, the repository shortcuts are also supported:

make setup
make doctor

Run the desktop app

Start the Tauri development shell:

npm --prefix apps/desktop run tauri -- dev

Or, when make is available:

make desktop-dev

The development command starts Vite on http://localhost:1420 and launches the desktop shell. Launching a debug binary directly without Vite running will show a blank window or a connection-refused message.

Common checks

npm --prefix apps/desktop run format:check
npm --prefix apps/desktop run lint
npm --prefix apps/desktop run test
npm --prefix apps/desktop run build:verified

Use cargo test --workspace for Rust backend changes.

Sample databases

Sample database containers live in the sibling irodori-samples repository. Clone it next to irodori-table when you need local integration fixtures:

git clone https://github.com/hjosugi/irodori-samples ../irodori-samples

Then use Docker Desktop or Podman Desktop as the container engine.

macOS Development

This guide covers local desktop development on macOS. Tauri uses the system WebKit/WKWebView stack on macOS, so no WebKitGTK packages are needed.

Prerequisites

  • macOS on Apple Silicon or Intel.

  • Xcode Command Line Tools:

    xcode-select --install
    
  • Node.js 24.x, matching the repository .nvmrc.

  • Rust from rustup; the repository pins Rust 1.96.0 in rust-toolchain.toml.

  • npm for reproducible dependency installs.

  • Optional: Docker Desktop or Podman for sample database containers.

Setup

From the irodori-table repository root:

rustup toolchain install
make setup
make doctor

The root is not an npm workspace. Prefer the root Makefile shortcuts, or run desktop scripts with npm --prefix apps/desktop ....

Run the desktop app

make desktop-dev

make desktop-dev starts Vite on http://localhost:1420 and launches the Tauri desktop shell. If you start a debug binary directly without Vite running, the window will be blank or show a connection-refused message.

Common checks

make desktop-format-check
make desktop-lint
make desktop-test
make desktop-build-verified

Use cargo test --workspace for Rust backend changes.

Sample databases

Sample database containers live in the sibling irodori-samples repository:

git clone https://github.com/hjosugi/irodori-samples ../irodori-samples
make db-up DB=postgres
make db-verify DB=postgres

Use the container engine that works best on your machine. The development doctor detects Docker or Podman when either is available.

Linux Development

This guide covers local desktop development on Linux, including the common WebKitGTK dependencies and troubleshooting steps for Arch Linux, CachyOS, Debian, Ubuntu, Fedora, and similar desktop distributions.


1. System Dependencies

Required local versions are pinned in the app repository: Node.js 24.x in .nvmrc and Rust 1.96.0 in rust-toolchain.toml.

Tauri v2 requires GTK3, WebKitGTK using the 4.1 API, libsoup3, OpenSSL, pkg-config, and the normal app indicator/image packages used by Linux desktop bundles. The x86_64 Linux build also uses mold because .cargo/config.toml passes -fuse-ld=mold.

Debian / Ubuntu

sudo apt-get update
sudo apt-get install -y \
  build-essential curl file libayatana-appindicator3-dev librsvg2-dev \
  libssl-dev libwebkit2gtk-4.1-dev libxdo-dev mold pkg-config wget

Fedora

sudo dnf install \
  gcc gcc-c++ make pkgconf-pkg-config webkit2gtk4.1-devel \
  libsoup3-devel openssl-devel libxdo-devel librsvg2-devel mold

Arch Linux / CachyOS

sudo pacman -S --needed \
  base-devel webkit2gtk-4.1 libsoup3 openssl pkgconf mold

After installing the OS packages, install the pinned Rust toolchain and desktop dependencies, then run the repository doctor:

rustup toolchain install
make setup
make doctor

Build temp directory

If /tmp is a small tmpfs, large Rust/Tauri builds can fail while compiling or linking. Use a repo-local temp directory for those runs:

mkdir -p .irodori-local/tmp
TMPDIR=$PWD/.irodori-local/tmp make desktop-build-verified

AppImage FUSE Dependency

Arch Linux and CachyOS default to fuse3. To run built AppImages directly without extracting them, you can optionally install FUSE v2:

sudo pacman -S --needed fuse2

If you do not want to install fuse2, you can execute the AppImage by extracting it inline using the environment variable:

APPIMAGE_EXTRACT_AND_RUN=1 ./Irodori-Table.AppImage

(This is handled automatically by the project’s make run-linux and apps/desktop/tools/install-linux.mjs wrapper).


2. Troubleshooting GPU & WebKit Crashes

On Arch-based distros (especially those using NVIDIA drivers, Wayland, or Mesa hybrid graphics), the WebKit2Gtk rendering engine might crash silently or produce white screens. Use the following workarounds:

WebKit DMA-BUF Crash (White/Blank Screen)

If the application launches but renders a solid white screen or crashes with WebProcess failures in journalctl, disable DMA-BUF rendering:

export WEBKIT_DISABLE_DMABUF_RENDERER=1

Compositing Mode Crash

If the window still crashes, force software compositing:

export WEBKIT_DISABLE_COMPOSITING_MODE=1

Wayland vs. X11 Backend

If WebKit has input focus or window rendering issues under Wayland, force GDK to run via XWayland:

export GDK_BACKEND=x11

Or force Wayland native:

export GDK_BACKEND=wayland

3. Running and Debugging Locally

Running in Dev Mode

To run the hot-reloading development server:

make desktop-dev

make desktop-dev runs the Tauri CLI from apps/desktop, which starts Vite and the desktop shell together.

Direct Binary Execution

Heads up: a debug binary loads its UI from the Vite dev server (devUrl http://localhost:1420 in tauri.conf.json). Launching the debug binary on its own shows a blank window with “Could not connect to localhost: Connection refused” because nothing is serving port 1420. See the troubleshooting entry below.

To inspect Rust stdout/logs while still pointing the webview at a running UI, start the dev server first, then launch the binary in a second terminal:

# Terminal 1 - serve the frontend on :1420
make desktop-vite
# Terminal 2 - run the already-built debug binary
./.irodori-local/target/debug/irodori-table-desktop

To run a standalone binary that needs no dev server, build one with the frontend embedded from frontendDist (../dist):

make desktop-build       # populate apps/desktop/dist
npm --prefix apps/desktop run tauri -- build --debug
# Or a full AppImage with embedded assets:
make run-linux

Error: “Could not connect to localhost: Connection refused”

A blank window with this message is not a database error - the webview could not reach the dev server URL baked into a debug build (http://localhost:1420).

Checklist:

  • Use make desktop-dev instead of launching the debug binary directly - it starts Vite (beforeDevCommand) and the app together.
  • If you must run the binary directly, confirm Vite is up: ss -ltnp | grep 1420 should show a listener. If not, run make desktop-vite first.
  • The dev port is fixed and strictPort: true, so if :1420 is already taken, Vite exits and the app has nothing to connect to. Free the port (fuser -k 1420/tcp) or stop the other process, then retry.
  • For a no-dev-server run, use an embedded-assets build (npm --prefix apps/desktop run tauri -- build --debug or make run-linux); a debug binary alone always expects :1420.

Reading Console and Rust Logs

  • Developer Tools: Open Help > Open Developer Tools from the app menubar during debug/dev runs. The WebView’s default right-click inspection menu is suppressed so product context menus stay clean.
  • Stdout/Stderr Console: Run the app from a terminal. Console logs (console.log) from the React frontend and println!/log events from the Rust backend will print directly to the terminal stdout.
  • Core Dumps & Journal: If the app segfaults or WebKit crashes, inspect system logs:
    journalctl -xe --user -u irodori-table
    # Or simply view core dump stats:
    coredumpctl list
    

Extension Development

Irodori Table treats extensions as a product surface. The extension system starts with TypeScript because that is the fastest path for commands, UI integrations, themes, result-grid tools, and SQL dialect metadata. Rust/Wasm remains available for high-performance drivers, parsers, renderers, and formatters.

Current Implementation

Manifest

Extensions declare a strict irodori.extension.json manifest. The schema rejects unknown top-level and contribution fields so the host can safely validate packages before loading them.

{
  "$schema": "../../extension.schema.json",
  "manifestVersion": 1,
  "id": "example.quick-export",
  "name": "Quick Export",
  "version": "0.1.0",
  "license": "MIT OR 0BSD",
  "apiVersion": "0.1",
  "runtime": "typescript",
  "entry": "dist/main.js",
  "permissions": ["commands", "queryResults:read", "resultRenderers"],
  "contributes": {
    "commands": [
      {
        "id": "quickExport.copyAsMarkdown",
        "title": "Copy Result as Markdown Table",
        "category": "Result Grid",
        "enablement": "resultGridFocus"
      }
    ],
    "resultGridActions": [
      {
        "id": "quickExport.copyMarkdownAction",
        "title": "Copy as Markdown",
        "command": "quickExport.copyAsMarkdown",
        "when": "resultGridFocus"
      }
    ]
  }
}

Core manifest fields:

  • manifestVersion: currently 1.
  • apiVersion: currently 0.1.
  • runtime: typescript, javascript, wasm, or native.
  • permissions: explicit capability scopes such as commands, queryResults:read, themes, sqlDialects, native, and wasm.
  • contributes: declarative commands, keybindings, result-grid actions/renderers, themes, and SQL dialects.
  • capabilities: Wasm and native module declarations.
  • dev: watch paths, fake database fixtures, and log file configuration for local development.

Validate all checked-in extension templates and examples from the repository root:

make extension-manifests

The same guard runs in CI and checks required fields, unknown keys, safe relative paths, permission/contribution consistency, template/example licenses, and manifest sample fixtures.

Type Generation

The extension SDK uses the same typeship pattern as the desktop app.

cargo test -p irodori-extension export_typescript_bindings

The Rust crate owns the serde/TS contracts for:

  • manifest and contribution data;
  • permission scopes and permission inspection;
  • result-grid columns, rows, selections, and snapshots;
  • theme definitions and token color rules;
  • SQL dialect definitions, keywords, snippets, and formatter config;
  • Wasm/native module capability metadata;
  • local development fixtures and logs.

The generated file is committed. In CI, the same test runs in check mode and fails if the generated SDK types drift from Rust.

TypeScript SDK

The SDK exposes generated contracts plus host-facing interfaces:

  • commands.registerCommand and commands.executeCommand;
  • keybindings.registerKeybinding;
  • resultGrid.getActiveSnapshot, resultGrid.getSelection, resultGrid.registerAction, and resultGrid.copyText;
  • themes.registerTheme;
  • sqlDialects.registerDialect;
  • permissions.has, permissions.require, and permissions.inspect;
  • structured extension logging.

Extensions implement:

import type { ExtensionContext } from "@irodori-table/extension-sdk";

export async function activate(context: ExtensionContext): Promise<void> {
  context.subscriptions.push(
    context.commands.registerCommand("example.hello", async () => {
      context.log.info("hello from an extension");
    }),
  );
}

Local Development

The local dev CLI reads a manifest, inspects declared permissions, loads fake database fixtures, writes JSON-line logs, and watches declared files for reload requests.

node packages/extension-sdk/bin/irodori-extension-dev.mjs packages/extension-sdk/templates/typescript-basic --once

The --once mode is useful for CI and smoke checks. Without it, the command stays running and reports reload requests when watched paths change.

Runtime Safety Rules

  • Extensions must declare permissions before using privileged APIs.
  • Query text, query results, schema metadata, file access, native code, and Wasm modules are sensitive.
  • Secrets are never exposed directly; extension APIs should receive handles or scoped operations.
  • Native modules require platform metadata and should include sha256 before marketplace distribution.
  • Wasm modules must declare an ABI string. Current templates use irodori-sql-dialect-v0 while the host ABI is still stabilizing.

Next Host Work

  • Wire manifest validation into the desktop extension loader.
  • Implement the real desktop extension host behind the SDK interfaces.
  • Add package archive verification and install/uninstall flows.
  • Surface logs, reload state, and permission inspection in a developer panel.
  • Add runtime validation for extension-provided theme, dialect, and renderer data.

i18n

Irodori Table keeps desktop UI translations in apps/desktop/src/i18n.

Supported Locales

  • en - English, the fallback locale.
  • ja - Japanese.

The language is selected from Settings -> General -> Language. The selected locale is saved in local storage as irodori.locale.v1 and is also included in Settings JSON as locale.

Adding Or Updating Text

  1. Add the English source string to apps/desktop/src/i18n/locales/en.ts.
  2. Add the Japanese translation to apps/desktop/src/i18n/locales/ja.ts.
  3. Use createTranslator(locale) or translate(key, options) at the UI boundary.
  4. Add or update unit coverage in apps/desktop/src/tests/unit/i18n/i18n.test.ts.

English is the source of truth for message keys. Japanese is typed against the same key set, so missing translations fail during TypeScript checks instead of rendering a blank label.

Interpolated values use {name} style placeholders:

translate("settings.theme.colorMode.customDescription", {
  locale: "ja",
  values: { name: "Dark+" },
});

Keep translation calls near the React component boundary. Business logic, storage, and query execution code should store stable IDs or raw data, not localized display text.

Development Security

Last updated: 2026-06-26 JST.

This document covers day-to-day security controls for Irodori Table development. Release/distribution requirements live in distribution.md; clean-room and license rules live in clean-room.md and licensing.md.

Package Manager Policy

  • npm lockfiles are the reproducible source of truth for JavaScript dependency resolution.
  • Bun is allowed for local script execution through JS_PM=bun, but do not commit Bun lockfiles unless the project explicitly migrates package managers.
  • Use npm ci for CI-equivalent installs and before release work.
  • Use make setup-fast only as a local convenience path.
  • Do not run npm audit fix --force as a blind cleanup. Review the dependency tree and behavior changes, then update intentionally.

Supply Chain Checks

Use:

make security

The target runs:

  • scripts/check-licenses.sh;
  • scripts/dependency-review.mjs for install scripts, remote tarball integrity, non-registry npm resolution, Cargo git sources, and external Cargo path dependencies;
  • cargo metadata --locked;
  • npm audit --package-lock-only --audit-level high for each npm lockfile;
  • npm audit signatures --package-lock-only for npm registry signature checks;
  • cargo audit --deny warnings when cargo-audit is installed.

Set NPM_AUDIT_LEVEL=moderate to make npm advisory checks stricter. CI sets REQUIRE_CARGO_AUDIT=1 so missing RustSec coverage fails the workflow. New install scripts, Cargo git dependencies, or external Cargo path dependencies must be reviewed and documented in tools/security/dependency-review-allowlist.json.

Dependency Review Rules

Treat a new dependency as a design decision, not a convenience import.

Before adding one:

  • prefer the standard library, existing project dependencies, or small local code when the behavior is simple;
  • check license compatibility against docs/licensing.md;
  • check install scripts, native binaries, generated code, postinstall downloads, package ownership, and recent maintainer churn;
  • pin behavior through the existing lockfile and include the lockfile diff in the same change;
  • add tests around the behavior the dependency is expected to provide.

Extra scrutiny is required for database drivers, parser/grammar packages, cryptography, compression/archive libraries, native modules, browser-executed WASM, and release/signing tools.

Secrets And Logs

  • Do not persist plaintext database passwords or tokens.
  • Do not commit .env files, sample credentials beyond local throwaway containers, private certs, database dumps, or screenshots containing secrets.
  • Redact passwords, tokens, connection URLs, and certificate material before logging or surfacing errors.
  • Keep import/export paths explicit and user-chosen; avoid background writes to broad directories.

GitHub Actions

  • Keep workflow permissions minimal. Default to contents: read.
  • Use official actions where possible and let Dependabot update action versions.
  • Be cautious with new third-party actions; prefer shell commands or existing toolchains when the action is only a thin wrapper.
  • Any workflow that publishes artifacts, signs releases, uploads SARIF, or uses OIDC must declare the narrow permissions it needs.

Current security workflows:

  • Security: dependency policy, npm advisory/signature checks, and RustSec.
  • CodeQL: static analysis for JavaScript/TypeScript and Rust.
  • OpenSSF Scorecard: repository supply-chain posture surfaced through code scanning SARIF. Public score publishing is disabled.

Release Hardening Backlog

These are intentionally not solved by the current local checks:

  • sign desktop artifacts and document verification;
  • generate SBOMs for release artifacts;
  • pin container base images by digest once the release cadence is stable;
  • add secret scanning/push protection in the hosted repository settings;
  • evaluate SLSA provenance for release builds.

Repository Settings Checklist

These are configured in GitHub settings rather than committed files:

  • enable Dependabot alerts and security updates;
  • enable secret scanning and push protection;
  • require Security, CodeQL, and normal CI checks before merging to main;
  • protect release tags once signed releases are introduced;
  • restrict who can approve and run workflows from external pull requests.

Reference And License Policy

Irodori Table’s own code is MIT OR 0BSD: downstream users should be able to choose familiar MIT terms or the almost-no-conditions 0BSD path. Reference rules still depend on license and source type. Proprietary products require clean-room treatment. OSS projects may be studied at code level when their license permits it, but copied or adapted code must keep attribution and must be compatible with Irodori Table’s permissive core.

Allowed

  • Read public documentation, public issue trackers, release notes, product pages, and license files.
  • Read code from OSS repositories after checking the relevant license.
  • Adapt small, well-scoped OSS implementation ideas only when the license is compatible with a permissive MIT OR 0BSD project, attribution is preserved where required, and the copied/adapted boundary is explicit in the commit.
  • Use installed apps manually to understand workflows and expected DB-client behavior.
  • Record feature-level observations in our own words.
  • Build independent prototypes from requirements, public standards, database documentation, and permissively licensed dependencies.
  • Compare behavior after our implementation exists, at the level of user-visible outcomes.

Not Allowed

  • Copy proprietary or license-incompatible source code, private APIs, icons, images, theme files, distinctive UI wording, snippets, or exact layout measurements from reference clients.
  • Translate proprietary or license-incompatible code into Rust, TypeScript, or another language.
  • Recreate a competitor screen pixel-for-pixel.
  • Use decompiled binaries, network traces, private builds, paid-only assets, or non-public materials.
  • Paste third-party code into issues, docs, prompts, commits, or tests unless its license is verified and the source is attributed.
  • Use code under commercial-only directories or dual-license sections unless the license explicitly permits our use.

Reference Tiers

  • Proprietary or unclear license: public behavior and docs only; clean-room implementation.
  • Public-domain-like or no-attribution permissive OSS such as 0BSD/Unlicense/CC0: easiest to reuse, while still recording provenance.
  • Permissive OSS such as MIT/Apache/BSD: code may be read and selectively adapted with attribution and dependency/license tracking.
  • Copyleft OSS such as GPL/AGPL: code may be read for learning; do not copy/adapt into the permissive core without an explicit separate license boundary and compatibility review.
  • Commercial/paid edition source: do not use unless we have explicit rights.

Named Reference Licenses

Verify the local LICENSE before any code-level use; this table records the current understanding.

  • .irodori-local/ref/beekeeper-studio-master/ — Community Edition GPLv3-or-later (copyleft); src-commercial is separately licensed and off-limits. Learning only; no copy into the permissive core.
  • .irodori-local/ref/vscode-sqltools-dev/ — MIT. Code may be read and selectively adapted with attribution and license tracking.
  • .irodori-local/ref/vscode-mssql-main/ — MIT. Same as above.
  • .irodori-local/ref/budibase-master/ — verify before code-level adaptation (GPL/AGPL components exist); treat as behavior reference unless a specific file’s license is confirmed compatible.
  • .irodori-local/ref/duckdb-ui-main/ — MIT. Permissive; adaptable with attribution.
  • .irodori-local/ref/kibana-main/ — Elastic License 2.0 / SSPL / AGPL-3.0 (source-available, restrictive/copyleft). Behavior-only; no code adaptation into the core.
  • zed-industries/zed (GitHub, not vendored) — GPL-3.0/AGPL-3.0 with some Apache-2.0 crates (copyleft). Study architecture; do not copy copyleft code into the MIT OR 0BSD core.
  • A5:SQL Mk-2 (a5m2.mmatsubara.com, not vendored) — freeware with private source repository. Public site / behavior reference only. Capture useful feature observations in docs/reference-a5sql.md; do not copy UI expression or private implementation into the MIT OR 0BSD core.
  • outerbase/studio (GitHub, not vendored) — AGPL-3.0 (copyleft). Study the data-editor/schema-editor/large-table UX and architecture; do not copy code into the MIT OR 0BSD core.
  • .irodori-local/ref/dbeaver-ce (DBeaver Community) — Apache-2.0 (permissive). Code may be read and selectively adapted with attribution and NOTICE retention; avoid the 2 EPL-2.0 files (HippieCompletionEngine.java, SQLMatchingCharacterPainter.java) unless accepting EPL terms.
  • zequel-labs/zequel (GitHub, not vendored) — Elastic License 2.0 (source-available, restrictive). Behavior-only; no code into the core.
  • rust-dd/rsql (GitHub, not vendored) — README says open source and links a LICENSE, but that path returned 404 during review. Treat as public README/behavior reference only until compatible license terms are verified. Capture performance and UX observations in docs/reference-rsql.md; do not copy code into the MIT OR 0BSD core.

Reference Workflow

  1. Check the license before opening code for implementation guidance.
  2. Capture feature observations in a neutral matrix: what the workflow does, why users need it, and how important it is.
  3. Prefer public specifications and vendor docs for implementation details: SQL dialect docs, driver docs, SSH/proxy standards, LSP, Tree-sitter, VS Code theme schema.
  4. Implement from our own abstractions and tests unless the OSS license explicitly permits adaptation and the PR records that fact.
  5. Review pull requests for accidental copied strings, asset reuse, incompatible code, and too-close UI expression.
  6. Keep third-party dependencies explicit with license review before shipping.

Contribution Checklist

  • The change is written from project requirements or from license-compatible OSS with required attribution.
  • Any third-party dependency has a compatible license.
  • Public references are linked when a behavior is based on public docs.
  • OSS code references are linked when implementation is influenced by code-level review.
  • User-visible names, empty states, dialogs, and shortcuts are ours unless they are generic platform conventions.
  • Tests assert Irodori behavior, not another product’s private behavior.

Completion And AI Strategy

Last checked: 2026-06-26 JST.

AI can make Irodori nicer, but it must not be required for a great query editor. The base product should feel nearly perfect with no network, no model, and no external account.

Current product status: the desktop editor now has deterministic schema/table/column autocomplete from live metadata, with unit coverage for the local completion engine and browser E2E coverage for CodeMirror popup suggestions. The remaining cross-platform product requirement is the shared serializable completion request/response contract for local API and future hosts, plus broader engine fixtures beyond the desktop smoke.

Layering rule:

  1. Deterministic completion is the core editor contract. It runs locally, works offline, uses structured metadata, and must be good enough before any AI path matters.
  2. Optional AI is an overlay. It can explain, draft, repair, or propose text/diffs, but it is off by default, permission-scoped, and must never replace or block deterministic completion.
  3. ML infrastructure is required even though user-facing AI is optional. Ranking experiments, provider/model evaluation, local dataset preparation, and quality regression checks must run through the same background job and privacy model as huge index builds and other batch work.

Deterministic Completion First

The completion engine should be built from structured local knowledge. SQL is the first target, but the design must also cover Cypher, time-series SQL/native queries, document queries, key-value commands, search DSLs, and warehouse dialects.

  • Incremental SQL parse context from Tree-sitter or dialect-specific parsers.
  • Dialect metadata for keywords, functions, operators, DDL/DML syntax, bind variables, comments, quoting rules, and procedural SQL.
  • Metadata cache populated by introspection, including schemas, tables, views, columns, indexes, constraints, foreign keys, functions, procedures, packages, triggers, sequences, enum-like values, comments, and privileges.
  • Scope resolver for aliases, CTEs, recursive CTEs, derived tables, subqueries, lateral joins, temp tables, table-valued functions, window definitions, and DDL files.
  • Query-local symbols from the current editor, unsaved scratch buffers, selected connection, active schema, and tab/session binding.
  • Ranking from current context, foreign-key paths, recently used objects, pinned/favorite objects, history, cursor position, and statement type.

Completion categories:

  • Keywords and syntax snippets.
  • Database, schema, table, view, column, alias, CTE, and subquery output names.
  • Function/procedure/package signatures, named parameters, overloads, and return types.
  • Join suggestions from foreign keys and naming conventions.
  • Insert/update column lists, generated select lists, and group-by/order-by helpers.
  • Query parameters and local variables.
  • File paths, connection names, tab/session names, and run configurations.
  • Dialect-specific explain, analyze, transaction, and administrative commands.
  • Graph labels, relationship types, property keys, path patterns, Cypher procedures/functions, and graph result variables.
  • Time-series buckets/measurements/tables, tags, fields, time columns, retention policies, aggregate/window helpers, downsampling snippets, and time-range templates.
  • Document/KV/search names such as collections, indexes, keys, commands, aggregation stages, JSON paths, search fields, and module-specific functions.

Quality bars:

  • No false confidence: prefer fewer precise suggestions over noisy lists.
  • Suggestions must be cancellable, fast, and stable while metadata refreshes in the background.
  • Completion must work offline and without AI.
  • Completion must never leak result data or secrets to an external provider.
  • Large metadata/search indexes must build incrementally in background jobs with progress, cancellation, checkpoint/resume, bounded memory, and measurable throughput.

ML And Batch Requirements

ML is a product-quality requirement for ranking, evaluation, and optional assistant quality, but it must not turn the editor into a network-dependent product.

  • Dataset generation uses only permitted local artifacts: dialect facts, source snapshots, schema metadata, query history, selected editor context, execution errors, and opt-in result samples.
  • Evaluation runs are versioned jobs with reproducible inputs, model/provider metadata, quality metrics, latency/cost metrics, and artifact hashes.
  • Huge index builds, embedding/vector indexes, metadata indexes, and source-search indexes are cancellable, checkpointed, and disk-backed where needed.
  • Batch work uses the shared job model so desktop, local API, and future hosts can inspect progress, cancel work, resume where safe, and collect logs/artifacts consistently.
  • External provider calls are forbidden unless workspace policy explicitly permits the specific data classes used by that run.

Optional AI Layer

AI should sit above the deterministic engine as an opt-in assistant. The items below are target capabilities, not a claim that the product ships them today:

  • Natural-language-to-SQL generation.
  • Inline SQL ghost text and patch-style suggestions, only when enabled.
  • AI Shell (open work): a dockable chat panel scoped to the current connection/workspace.
  • Query/error explanation and suggested fixes.
  • Explain-plan summary.
  • Schema-aware chat.
  • Refactor SQL, format intent, or generate migration draft.
  • Test data or sample query generation.

Provider model:

  • Local providers: Ollama or other local OpenAI-compatible endpoints.
  • Cloud providers: OpenAI-compatible, Anthropic, Gemini, Azure OpenAI, Amazon Bedrock, and similar via extension providers.
  • MCP/Copilot-compatible bridge: expose Irodori context through scoped local tools so supported clients can request schema/query context where allowed; do not share database credentials, result samples, or query text unless the workspace policy explicitly permits that class of data.

Privacy rules:

  • AI is off by default.
  • The user must opt in per provider and per workspace.
  • Schema metadata, query text, result samples, and execution plans are separate permissions.
  • Result data is never sent unless the user explicitly allows it for the current action.
  • Redaction should run before provider calls.
  • Every AI request should be inspectable in an audit panel.

Execution boundary:

  • AI suggestions are text until the user inserts them into the editor.
  • The AI Shell cannot run SQL directly; it can only populate the editor or call explicitly scoped read-only tools.
  • Query Magics are not AI. The desktop baseline is local, explicit, line-leading commands such as \describe, \explain, \export, \erd, and \params; command-palette equivalents, structured action audit/history, and run-to-file magic remain open.

Copilot Compatibility Direction

Directly embedding GitHub Copilot inside a standalone app may not be available as a stable public integration path. The safer first target is a cross-platform Irodori MCP server plus optional editor/provider extensions. The same scoped tool contracts should serve desktop, the local API, and future hosts instead of creating a desktop-only Copilot path.

Milestones:

  • irodori-mcp: expose safe tools for schema search, object details, explain-plan fetch, scoped read-only query execution, SQL history search, and SQL diagnostics.
  • Shared context envelope: separate selected SQL, cursor context, schema metadata, execution plans, result samples, and history snippets so policy can allow or deny each class independently.
  • Copilot-style inline autocomplete: provide opt-in text/diff suggestions from selected SQL, cursor context, and permissioned schema metadata; never execute suggestions automatically.
  • VS Code config generator: create .vscode/mcp.json for connecting Copilot Chat to a local Irodori MCP server.
  • Extension SDK provider API: allow third-party AI providers and Copilot-style bridges without hard-coding a vendor into core.
  • Policy controls: disable external AI, allow only local models, allow only schema metadata, or allow result samples per workspace.

Sources:

  • https://docs.github.com/en/copilot/concepts/context/mcp
  • https://docs.github.com/en/copilot/how-tos/provide-context/use-mcp-in-your-ide/extend-copilot-chat-with-mcp
  • https://docs.snowflake.com/en/user-guide/snowflake-copilot-inline
  • https://www.jetbrains.com/help/datagrip/ai-assistant.html

Research Notes

  • JetBrains’ full-line completion work is relevant because it emphasizes local, latency-aware, syntax-safe suggestions rather than pure cloud generation.
  • The Mellum work is relevant because it focuses on compact, IDE-oriented completion models with context packing and permissively licensed training data.
  • Control/gating models are relevant because AI completion should trigger only when useful, reducing noise, cost, and privacy exposure.

Sources:

  • https://arxiv.org/abs/2405.08704
  • https://arxiv.org/abs/2510.05788
  • https://arxiv.org/abs/2601.20223

Data Verification And Migration

Last checked: 2026-06-28 JST.

This page records the current product contract for migration planning, data verification, and result-grid repair helpers. It separates what is implemented today from the target live diff architecture.

Current Desktop Scope

The desktop app currently ships SQL generation and review workflows, not an end-to-end data mover.

  • Migration Studio generates a plan, source SQL, target SQL, diff SQL, and a runbook.
  • The default migration shape is Hive -> Snowflake.
  • Planner engines include Hive, Snowflake, PostgreSQL, Oracle, MySQL/MariaDB, Redshift, Databricks/Spark SQL, Trino/Presto, DuckDB/DuckDB-Wasm, Apache Iceberg REST, and AWS S3 Tables.
  • Generated validation SQL uses source and target row-hash manifests, row count, key count, min/max hash fingerprints, manifest tables, and row-level diff SQL.
  • The desktop planner exposes Parquet/CSV extraction formats. The shared irodori-sql migration helpers also include TSV-oriented load snippets for workflows where Hive text exports are unavoidable.
  • DuckDB/Iceberg paths generate INSTALL/LOAD iceberg, CREATE SECRET, and ATTACH ... TYPE ICEBERG patterns for local or browser-side DuckDB compute.

The current planner does not connect to both databases, execute the generated SQL, or reconcile results by itself. The user reviews the SQL/runbook and runs it through the normal query workflow or an external migration runner.

Result-Grid Repair Workflow

Editable results are intentionally review-oriented.

  • The grid uses Save Changes for pending edits.
  • Editable updates require a direct single-table result with a visible primary key or unique key.
  • Refresh with unsaved result changes asks for confirmation before discarding work.
  • Row SQL turns the selected result row into a reviewable transaction:
-- Generated from the selected result row. Review before running.
-- Edit the SET values, then run this transaction.
BEGIN;
UPDATE "schema"."table"
SET
  "column" = 'new value'
WHERE
  "id" = 42
;
COMMIT;

SQL Server uses BEGIN TRANSACTION; and bracket identifiers; MySQL/MariaDB/TiDB use backticks. JSON-like result values are serialized as SQL string literals before insertion into the editor. The generated SQL is never executed automatically.

Editor Support

The SQL editor is CodeMirror 6 based. Current editor behavior includes:

  • dialect-aware highlighting and completion;
  • Vim mode;
  • format and comment toggle commands;
  • run current, run selection, and run all flows;
  • query history;
  • search and replace through the CodeMirror search panel opened by Ctrl/Cmd+F.

Target Live Diff Architecture

The future live data-diff capability should be a separate execution boundary. It should compare two sources without pulling whole tables unless the cheap gates fail.

The target tiers are:

  1. Count and table fingerprint: row count plus order-independent aggregate of row hashes.
  2. Partition or hash-bucket fingerprint: localize mismatches to small key ranges.
  3. Failed-bucket row diff: compare (key, row_hash) manifests and fetch full rows only for changed keys.

For 100B-row scale, the product contract is to push hashing and bucketing into the source engines, stream narrow manifests, spill to disk when needed, and keep the UI cancellable. The UI should present summary status, bucket heatmap, row list, cell-level differences, and exportable evidence.

Not Yet Implemented

  • Live source-target execution and reconciliation.
  • Recursive bucket localization.
  • Bucket heatmap UI.
  • Headless /v1/diff API.
  • Automatic reconciliation script export beyond selected-row UPDATE generation.

Safety Rules

  • Keep migration credentials in connection profiles or secret storage, not in shareable URLs or exported runbooks.
  • For DuckDB-Wasm and Iceberg REST, verify endpoint reachability and CORS before relying on a browser-only flow.
  • Prefer Parquet for Hive -> Snowflake. Use text formats only when unavoidable, and keep delimiter/null-token policies explicit.
  • Keep generated Row SQL in the editor for human review before execution.

Data Source Coverage Strategy

Last checked: 2026-06-21 JST.

Irodori Table should become a general high-performance database workbench, not only a SQL table browser. SQL databases stay the first vertical slice, but the architecture must allow distributed SQL, time-series, graph, document, key-value, search, warehouse, and local embedded sources.

Direction

The core abstraction should be data source adapter, not just SQL driver.

Each adapter declares:

  • connection profile schema;
  • transport needs, including direct, TLS, SSH, SOCKS/HTTP, and proxy chains;
  • query language and parser strategy;
  • introspection model;
  • result shapes: table, document, graph, time-series frame, key-value set, stream, or binary/file;
  • editable operations and safety rules;
  • completion providers;
  • explain/profile support;
  • import/export capabilities.

The UI should keep one familiar workbench while changing the specialized panes per source type. Snowsight-style parity gaps must be designed as shared product capabilities, not as desktop-only widgets: schema-aware autocomplete, optional Copilot-style inline help/MCP, explain/query profile, advanced filters, inline editing, query-result graph views, and charts/dashboards need reusable contracts that desktop, local API, and future hosts can share.

Coverage Tiers

P0: First Usable Core

  • SQLite
  • PostgreSQL

These prove connection management, editor execution, object browser, result grid, history, keybindings, themes, and command palette.

P1: Daily Driver SQL And Enterprise

  • MySQL/MariaDB
  • SQL Server
  • Oracle Database
  • DuckDB

This tier stays focused on daily-driver relational workflows and enterprise SQL coverage. DuckDB is included early because it doubles as a local analytics engine and lakehouse execution option. Wire-compatible or partially landed adapters can exist before this tier is complete, but first-class source UX remains tracked by the backlog ticket for each source.

Oracle is a roadmap non-negotiable, so its connection story must be as easy as the rest. The target is a thin driver: a pure-Rust implementation of the Oracle Net/TNS protocol that needs no Oracle Instant Client — the same approach that makes A5:SQL Mk-2’s “direct connection” mode (and the JDBC Thin / python-oracledb thin / node-oracledb thin drivers) client-free. The user supplies a connection descriptor (host/port/service), not a tnsnames.ora plus a client install.

  • Primary path: inherit and harden the permissively licensed oracle-rs crate (MIT/Apache-2.0, pure Rust, Tokio, TLS/Wallet) rather than writing the TNS stack from scratch. It is young (v0.1; many enterprise features still “planned”), so a spike (SRC-004a) must validate it against real Oracle 19c/23ai before Oracle becomes first-class. This may grow into its own crate/sub-project, like typeship.
  • Clean-room note: A5:SQL Mk-2 is closed source (the author’s GitHub confirms it is not public) and its direct mode uses the commercial UniDAC component, so we study the approach (thin TNS), never A5/UniDAC code. oracle-rs is permissive and may be adapted with attribution.
  • Optional fallback: thick OCI via the ODPI-C oracle crate behind a build feature, for environments that already have the Instant Client and need full OCI features.

P2: Broader Modern Workloads

  • InfluxDB and other time-series engines
  • Neo4j and graph databases
  • YugabyteDB YSQL
  • CockroachDB
  • Cassandra/ScyllaDB and wide-column stores
  • ClickHouse
  • BigQuery
  • Snowflake
  • Redshift
  • Trino/Presto
  • Firebird
  • Elasticsearch/OpenSearch
  • Couchbase
  • DynamoDB
  • TiDB
  • Databricks/Spark SQL
  • Apache Hive
  • Apache IoTDB
  • QuestDB
  • TimescaleDB
  • ArangoDB
  • Memgraph
  • Redis
  • MongoDB

YugabyteDB should start through its PostgreSQL-compatible YSQL surface, then add distributed-database affordances such as tablets, regions, follower reads, query diagnostics, xCluster, and node/session visibility where public APIs allow it.

MongoDB, Redis, Cassandra, CockroachDB, ClickHouse, BigQuery, Snowflake, and wire-compatible engines may have landed adapter pieces before the full source experience is done. Treat those as implementation progress snapshots, not completion of native browsing, editing, completion, explain/profile, visualization, or cross-platform contracts.

Priority Within P2: Lakehouse And Cloud Warehouse Auth

  • Apache Iceberg is the priority lakehouse target. Reach it through catalogs — Hive Metastore, AWS Glue, REST, and JDBC — and through AWS S3 Tables. Treat object stores (S3, GCS, Azure Blob) as first-class connection backends.
  • Execution options: embeddable engines (DuckDB, Apache DataFusion) for local reads, or Trino/Presto and warehouse-native engines for pushdown. Current Migration Studio generates DuckDB/DuckDB-Wasm + Iceberg REST/S3 Tables attach patterns; live execution and catalog browsing are still future work. Add Delta Lake and Apache Hudi after Iceberg.
  • Snowflake needs full authentication coverage, not just password: key-pair (JWT), OAuth, external-browser/SSO, MFA/passcode, and programmatic access tokens, with warehouse/role/database context switching.
  • Apache Hive stays in scope mainly as a catalog/metastore source for Iceberg and legacy warehouses.
  • Elasticsearch/OpenSearch are the first search sources. Treat them as a deep source family, not a thin REST endpoint, because current general DB clients are weak here. Study Kibana Discover and Dev Tools console for behavior: data views, index/data-stream browsing, mappings and field capabilities, filter/query composition, saved searches, request history, explain/profile, shard/index health cues, and JSON/ES|QL-style authoring. Kibana is a behavior-only reference because it is source-available under Elastic License 2.0 / SSPL / AGPL.

Managed Wire-Compatible Targets

Some hosted services should be supported as connection templates over existing adapters instead of adding duplicate engine variants:

  • Supabase Postgres routes through the PostgreSQL adapter. Product work is around direct vs. pooler connection strings, SSL, RLS-aware docs, hosted extension discoverability, and connection-limit guidance.
  • Amazon Aurora routes through PostgreSQL or MySQL depending on cluster engine. Product work is around writer/reader/custom endpoint guidance, IAM auth, cluster topology, serverless scaling context, and AWS Performance Insights links.
  • Google Cloud SQL routes through PostgreSQL, MySQL, or SQL Server. Product work is around public/private IP, Cloud SQL Auth Proxy, IAM database auth, SSL certs, and instance metadata.

Shared Visual Model, Heavy Consoles Later

  • P1 shared query-result graph, chart, worksheet visualization, and dashboard definition model (open; not implemented). Plan the serializable model/API early enough to shape result frames, filters, saved queries, exports, and extension visualizers across desktop, local API, and future hosts.
  • Advanced ERD analysis/authoring beyond the current schema ERD baseline.
  • Full graph exploration workspace beyond query-result graph visualization.
  • BI-dashboard polish beyond the shared result-to-visualization model.
  • Long-running monitoring consoles.
  • Admin dashboards for every distributed engine.

These are valuable, but they should not delay the editor, completion, result handling, connection, proxy, source-adapter, and extension foundations. The visual/dashboard model is early product architecture; heavy managed/admin console coverage is later product depth.

Source-Type UI Requirements

Relational And Distributed SQL

  • SQL editor with dialect-aware completion. Desktop schema/table/column completion is wired from live metadata; shared completion service/API parity and broader per-engine fixtures remain a P0 cross-platform gap.
  • Object browser for databases, schemas, tables, views, indexes, constraints, routines, triggers, packages, extensions, and jobs.
  • Result grid with streaming, cancellation, copy/export, safe editing, advanced filters, and explain/profile. Current desktop editing uses Save Changes for direct single-table results with visible primary/unique keys, plus Row SQL generation for selected-row review. Advanced filters and explain/query profile remain open P1 work.
  • Distributed SQL add-ons: regions, replicas, shards/tablets/ranges, session/lock insight, follower reads, consistency notes, and topology-aware warnings.

Time-Series

  • SQL or native query editor, depending on the engine.
  • Time range picker bound to query templates without forcing dashboard-first UX.
  • Result table plus time-series frame model.
  • Fast downsampling/preview for huge ranges.
  • Retention policy, bucket/measurement/table, tag/field, and partition browser.
  • Query helpers for windows, aggregates, selectors, gap filling, derivative/difference, and calendar-aware grouping.

InfluxDB 3 should be treated as a first-class time-series source with SQL support, not as a generic PostgreSQL clone.

Graph

  • Cypher or native graph query editor.
  • Schema/introspection for labels, relationship types, properties, constraints, indexes, procedures, and functions.
  • Results can be table, graph, path, scalar, or mixed records.
  • Graph visualization starts with query-result graph rendering; full graph workspace features can come later.
  • Completion must understand labels, relationship types, variables, path patterns, procedures, and property keys.

Neo4j should be the first graph benchmark because Neo4j Browser defines a common developer expectation: write Cypher, run queries, view tabular results, and visualize node/relationship results.

  • Query/editor surface for native syntax and JSON-like pipelines.
  • Collection/keyspace/index browser.
  • Document viewer/editor with patch preview.
  • Safe bulk edit flow and export.
  • Completion for fields, operators, aggregation stages, commands, and index names.

Research Targets

Track both database-specific tools and broad clients:

  • TablePlus, A5:SQL Mk-2, DataGrip, DBeaver, DbVisualizer, Beekeeper Studio, DbGate, SQLTools, VS Code MSSQL, pgAdmin, MySQL Workbench, Oracle SQL Developer, SSMS, HeidiSQL, SQuirreL SQL, OmniDB, PopSQL.
  • MongoDB Compass, Studio 3T, RedisInsight, Neo4j Browser, Neo4j Workspace/Query, InfluxDB UI/Data Explorer, ArangoDB Web UI, Couchbase Capella UI, Cassandra/ScyllaDB tools, Grafana data-source workflows.

Use these products to identify expected behavior, not to copy protected expression.

Official Sources To Track

  • YugabyteDB docs and release notes: https://docs.yugabyte.com/
  • YugabyteDB YSQL API: https://docs.yugabyte.com/stable/api/ysql/
  • InfluxDB 3 SQL docs: https://docs.influxdata.com/influxdb3/core/query-data/sql/
  • InfluxDB 3 client libraries: https://docs.influxdata.com/influxdb3/core/reference/client-libraries/v3/
  • Neo4j Browser docs: https://neo4j.com/docs/browser/
  • Neo4j Cypher docs: https://neo4j.com/docs/cypher-manual/current/
  • DBeaver supported database list: https://dbeaver.com/databases/
  • DataGrip feature reference: https://www.jetbrains.com/datagrip/features/
  • Apache Iceberg spec and REST catalog: https://iceberg.apache.org/spec/ and https://iceberg.apache.org/rest-catalog-spec/
  • AWS S3 Tables (managed Iceberg): https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables.html
  • Snowflake authentication options: https://docs.snowflake.com/en/user-guide/admin-security-fed-auth-overview and https://docs.snowflake.com/en/user-guide/key-pair-auth
  • DuckDB SQL and extensions (incl. Iceberg/Parquet): https://duckdb.org/docs/

Add these to knowledge/sources.json so future implementation and bug fixes can query a local snapshot.

Design Guardrails

  • Do not force non-SQL data into a fake relational model.
  • Do not make graph and time-series support depend on dashboard/visualization features.
  • Keep every source keyboard-first: editor, command palette, object browser, result focus, history, and quick open.
  • Make adapters extension-friendly so uncommon databases do not require core releases forever.
  • Keep protocol and driver choices swappable; some sources will use native Rust clients, some JDBC/ODBC bridges, some HTTP APIs, and some official CLIs or cloud APIs.
  • Preserve the same security model across all sources: keychain secrets, proxy chains, TLS, privacy mode, audit-friendly logs, and permission-scoped extensions.

Implementation Architecture

This document explains the implementation shape of the irodori-table repository. It is intentionally practical: use it to decide which directory owns a change, how frontend code reaches Rust, and where shared logic should live.

Goals

  • Keep the desktop app as the product integration point.
  • Keep Rust/TypeScript command payloads generated from Rust definitions.
  • Keep shared crates small and earned by stable boundaries.
  • Keep database execution, streaming, cancellation, and result paging in Rust.
  • Keep UI workflow, layout, and interaction state in the desktop frontend.
  • Keep extension contracts separate from app-only implementation details.

System View

flowchart LR
  user["User"]
  desktop["Desktop app<br/>apps/desktop"]
  frontend["React + TypeScript UI<br/>apps/desktop/src"]
  tauri["Tauri command bridge<br/>apps/desktop/src-tauri/src"]
  db["DB runtime<br/>src-tauri/src/db"]
  adapters["Engine adapters<br/>postgres mysql sqlite mssql duck etc."]
  crates["Shared Rust crates<br/>../irodori-kit/irodori-*"]
  sdk["Extension SDK<br/>../irodori-kit/packages/extension-sdk"]
  samples["Sample DBs<br/>../irodori-samples/*"]
  external["External foundations<br/>irodori-sql typeship irodori-knowledge"]

  user --> desktop
  desktop --> frontend
  frontend -->|"generated irodori-api.ts"| tauri
  tauri --> db
  db --> adapters
  db --> crates
  adapters --> samples
  crates --> external
  tauri --> sdk

Repository Ownership

PathOwns
apps/desktop/src/React UI, workbench layout, commands, settings, editor/result interactions, client stores.
apps/desktop/src-tauri/src/Tauri command handlers, database sessions, Git, terminal, local AI, security state, background jobs.
../irodori-kit/irodori-connection/Portable connection profile model.
../irodori-kit/irodori-security/Security/audit-facing shared model.
../irodori-kit/irodori-core/Shared core model and composition around connection/security/job foundations.
../irodori-kit/irodori-completion/Metadata-driven completion and inspection logic.
../irodori-kit/irodori-connector-abi/Native connector ABI helpers and export macro.
../irodori-kit/irodori-generate/Local SQL generation planning, schema projection, runtime, verification.
../irodori-kit/irodori-extension/Rust definitions for extension API generation.
../irodori-kit/irodori-io/Import/export encoders and tabular data helpers.
../irodori-kit/irodori-proxy/Direct/SSH/proxy transport planning and forwarding.
../irodori-kit/irodori-secure-store/OS secret store integration boundary.
../irodori-kit/irodori-server/Optional local HTTP API/headless surface.
../irodori-kit/packages/extension-sdk/TypeScript SDK and templates for extension authors.
tools/Code generation, docs checks, extension validation, security checks.
../irodori-samples/Sibling checkout with database fixtures and compose files for manual and integration testing.

Frontend Shape

The frontend is feature-first. Cross-feature composition happens in apps/desktop/src/app/AppWorkbench.tsx, while feature behavior should stay in apps/desktop/src/features/*.

flowchart TB
  app["AppWorkbench<br/>composition and command wiring"]
  shell["WorkbenchShell<br/>chrome, menu, status bar"]
  stores["Zustand stores<br/>workbench/results/preferences/connections"]
  editor["query-editor<br/>CodeMirror, selection, params, magics"]
  results["results<br/>grid, WebGL, chart, BI, row detail, export"]
  connections["connections<br/>profiles, import/export, validation"]
  sidebars["workbench views<br/>Sidebar, Inspector, Git, history, completion"]
  dialogs["dialogs<br/>settings, ERD, migration, import, AI"]
  api["generated API<br/>src/generated/irodori-api.ts"]

  app --> shell
  app --> stores
  app --> editor
  app --> results
  app --> connections
  app --> sidebars
  app --> dialogs
  editor --> api
  results --> api
  connections --> api
  dialogs --> api

Frontend rules of thumb:

  • Put user workflow code under the owning features/<area>/ directory.
  • Keep reusable pure SQL helpers under src/sql/.
  • Keep global keyboard metadata in src/core/keybindings.ts.
  • Keep command orchestration in features/workbench/command-handlers.ts.
  • Keep large repeated UI surfaces under feature components/.
  • Keep cross-feature UI primitives under src/components/ — currently DialogShell (shared modal chrome) and ErrorBoundary.
  • All modals render through DialogShell: it owns the scrim overlay, ESC-to-close, click-outside, focus trap, focus restoration, and role="dialog"/aria-modal. Do not hand-roll modal overlays.
  • Wrap fallible subtrees in ErrorBoundary so a render error degrades locally instead of white-screening the whole app (the root is wrapped in App.tsx).
  • Pull cohesive workbench command/state orchestration out of the AppWorkbench shell into kebab-case controller hooks under apps/desktop/src/app/controllers/, such as use-editor-commands.ts, use-result-export.ts, and use-workspace-actions.ts.
  • Keep apps/desktop/src/app/hooks/ for UI-local extracted hooks whose naming already follows the older camelCase pattern, such as result-grid scroll, filtering, and selection helpers. New cross-feature orchestration should use controllers/; new UI-local hooks should stay near the UI surface or in hooks/ when they are shared inside src/app.
  • Split multi-tab dialogs into one component per tab (e.g. features/settings/tabs/); the dialog file stays a thin shell.
  • Use the design tokens in styles/base.css for spacing, radius, and elevation (--space-*, --radius-*, --elevation-*) instead of ad-hoc px, and theme color tokens instead of hardcoded colors.
  • Add or change Tauri payloads in Rust first, then regenerate TypeScript.

Backend Shape

Tauri owns local privileged work. The frontend should not know driver details, secret storage details, or streaming/paging internals.

flowchart TB
  lib["src-tauri/src/lib.rs<br/>Tauri builder and command registration"]
  state["Managed state<br/>DbState JobState SecurityState AiState PtyState"]
  dbmod["db module<br/>commands, connection, query, stream, spill, meta, edit"]
  engine["engine adapters<br/>postgres mysql sqlite mssql duck snowflake ..."]
  git["git module<br/>status, log, diff, branch, push/pull"]
  pty["pty module<br/>integrated terminal sessions"]
  jobs["jobs module<br/>desktop job commands"]
  ai["ai module<br/>local generation state"]
  security["security module<br/>audit and redaction state"]
  crates["shared crates<br/>completion proxy io generate server"]

  lib --> state
  lib --> dbmod
  lib --> git
  lib --> pty
  lib --> jobs
  lib --> ai
  lib --> security
  dbmod --> engine
  dbmod --> crates
  ai --> crates
  jobs --> crates

Backend rules of thumb:

  • Add a Tauri command only when the UI needs a privileged/local boundary.
  • Keep per-engine driver logic in src-tauri/src/db/<engine>.rs.
  • Keep engine-independent query splitting, result shaping, streaming, and spill behavior in db/query.rs, db/stream.rs, and db/spill.rs.
  • Keep metadata conversion in db/meta.rs.
  • Keep connection profile normalization and redaction in db/profile.rs.
  • Keep generated command DTOs serializable with serde(rename_all = "camelCase") and ts-rs where they cross to TypeScript.

Command And Type Boundary

Rust is the source of truth for desktop command payloads. TypeScript consumes generated bindings rather than hand-writing copies.

flowchart LR
  rust["Rust structs/enums<br/>serde + ts-rs"]
  tests["cargo test export_typescript_bindings"]
  generated["apps/desktop/src/generated/irodori-api.ts"]
  ui["React features"]
  extRust["../irodori-kit/irodori-extension"]
  extGenerated["../irodori-kit/packages/extension-sdk/src/generated"]
  sdk["../irodori-kit/packages/extension-sdk"]
  check["typegen drift check<br/>npm run typegen:check"]

  rust --> tests --> generated --> ui
  extRust --> tests --> extGenerated --> sdk
  generated --> check
  extGenerated --> check

Use these commands:

make desktop-typegen
make desktop-typegen-check
make desktop-build-verified

Query Execution Flow

The query path is intentionally Rust-heavy so large result sets, cancellation, and disk offload stay bounded.

sequenceDiagram
  participant Editor as QueryEditorPane
  participant App as AppWorkbench
  participant API as generated irodori-api.ts
  participant Tauri as Tauri command
  participant DB as DbState / db module
  participant Conn as Connection trait
  participant Grid as ResultsPane

  Editor->>App: run current / selection / all
  App->>API: dbRunQueryStream or dbRunQuerySpill
  API->>Tauri: invoke command
  Tauri->>DB: validate profile, query, limits
  DB->>Conn: execute through engine adapter
  Conn-->>DB: rows, metadata, elapsed time
  DB-->>API: stream events or spill handle
  API-->>App: result sets or windowed result
  App->>Grid: build result view model
  Grid->>API: dbResultWindow when spilled pages are needed

Important implementation details:

  • db_run_query_stream streams batches for fast first paint.
  • db_run_query_spill writes huge results to a bounded Rust-side result store.
  • db_result_window pages spilled results back into the grid.
  • db_cancel cancels active work through cancellation tokens.
  • The UI result grid handles sorting/filtering/editing for resident results.
  • Spilled results are browse-first; server-side sort/filter is a separate workflow.

Shared Crate Layers

flowchart TB
  app["apps/desktop/src-tauri"]
  server["irodori-server"]
  extension["irodori-extension"]
  core["irodori-core"]
  connection["irodori-connection"]
  security["irodori-security"]
  proxy["irodori-proxy"]
  secureStore["irodori-secure-store"]
  completion["irodori-completion"]
  generate["irodori-generate"]
  io["irodori-io"]
  external["external git/crates<br/>irodori-sql, irodori-error, irodori-jobs"]

  app --> core
  app --> completion
  app --> generate
  app --> io
  app --> proxy
  app --> secureStore
  app --> external
  server --> core
  server --> external
  extension --> external
  core --> connection
  core --> security
  core --> external
  proxy --> core
  secureStore --> core
  completion --> external
  generate --> external
  io --> core
  io --> external

Do not add a crate just because a future feature sounds independent. Start inside the owning app or crate, then split when there is a stable API, separate test cadence, or a real second consumer.

Extension Surface

Extensions are intentionally not the same as app internals.

flowchart LR
  manifest["irodori.extension.json"]
  sdk["TypeScript SDK<br/>../irodori-kit/packages/extension-sdk"]
  generated["generated extension API"]
  host["desktop extension host/store"]
  app["workbench commands/results/themes"]

  manifest --> sdk
  generated --> sdk
  sdk --> host
  host --> app

Extension rules:

  • Public extension payloads belong in ../irodori-kit/irodori-extension.
  • SDK convenience wrappers belong in ../irodori-kit/packages/extension-sdk/src.
  • App-only plugin registry and loading state belongs under apps/desktop/src/features/extensions.
  • Templates must stay permissively licensed and validate through make extension-manifests.

Parallel Agent Development

Parallel work is organized around explicit workstreams rather than informal file ownership. The detailed policy lives in parallel-agent-architecture.md, and the machine-readable source is registry/agent-workstreams.json.

The key split is:

  • One coordinator agent owns registry and generated-catalog source files: knowledge/engines.json, registry/catalog/*.json, and apps/desktop/src-tauri/src/db/engine.rs.
  • Connector agents are repeatable and own exactly one generated sibling repository under ../irodori-extensions/irodori-extension-*.
  • Extension-host, DB-runtime, migration/diff, and workbench-UI agents own separate source trees and serialize changes only when shared contracts move.
  • Generated outputs are changed by the owner of the generator input, not by downstream agents.

Validate the workstream map with:

node tools/docs/agent-workstreams.mjs

Adding A Feature

  1. Decide the owner.
    • UI workflow: apps/desktop/src/features/<area>.
    • Local privileged action: apps/desktop/src-tauri/src/<area>.
    • Stable shared model: an existing crate, not a new crate by default.
  2. If TypeScript calls Rust, define or update the Rust DTO/command first.
  3. Regenerate bindings with make desktop-typegen.
  4. Wire the UI through generated/irodori-api.ts, not handwritten invoke calls.
  5. Add focused unit tests near the owning feature.
  6. Run the smallest relevant checks, then broaden if the boundary changed.

Typical checks:

make desktop-typegen-check
npm --prefix apps/desktop test
npm --prefix apps/desktop run build
cargo test --workspace

Current Refactor Pressure Points

These areas work but should be watched because they are high-change/high-size:

  • apps/desktop/src/app/AppWorkbench.tsx: integration point. Move feature logic into hooks, stores, or feature components when touching it.
  • apps/desktop/src/features/connections/connection-transfer.ts: importer coverage is broad. Keep parsers test-backed.
  • apps/desktop/src/sql/completion.ts: completion logic should continue moving toward irodori-completion when it becomes engine-independent.
  • apps/desktop/src/theme/index.ts: theme normalization should stay data-driven.
  • apps/desktop/src/features/results/components/WebGlResultGrid.tsx: rendering behavior should stay isolated from result model construction.

Architecture Guardrails

  • apps/desktop is allowed to orchestrate product UX.
  • crates/* should not know about React, Tauri windows, or app layout.
  • Rust command payloads should use camelCase at the JSON/TS boundary.
  • Long-running work should expose progress/cancel/logs through jobs.
  • Huge data should stream or spill; do not buffer full result sets in the UI.
  • Secret values should not be logged, copied into generated bindings, or stored in plain frontend state.
  • Reference-project research belongs in docs or requirements, not copied code.

Local SQL generation

Irodori Table can generate SQL from natural language with a lightweight local model, designed so correctness comes from a real SQL grammar rather than from model size. A tiny quantized model runs entirely on-device; every token it emits is forced through a schema-specialized SQL grammar, and the result is parsed back and validated against the schema. Deterministic completion stays the default — this is an opt-in, offline generation path.

Why it’s correct by construction

A small model is unreliable on its own. The design removes that risk:

  • Grammar-backed SQL structure — a real, dialect-aware SQL parser + AST + GBNF grammar (irodori-sql: ast.rs, parser.rs, grammar.rs).
  • Grammar-constrained decoding — the model can only sample tokens the GBNF grammar allows, so output is always syntactically valid.
  • Schema projection — table/column names become closed grammar terminals, so a hallucinated relation is literally unsamplable.
  • Planning before decoding — tables and foreign-key joins are resolved deterministically before the model runs, shrinking its job so a 0.5B model suffices.
  • Scoped grammar — on large schemas the GBNF is projected down to just the planned tables (+ their FK neighbors), so the grammar a big DB produces stays small: faster decode, lower memory, and the model can’t even mention an irrelevant table.
  • Validate + repair — output is parsed and every identifier is proven to exist; anything else is rejected, not returned.

Pipeline

NL prompt + connection schema
  → project   (GenSchema → GBNF grammar + SchemaIndex)
  → plan      (mentioned tables + FK joins + compact prompt)
  → decode    (llama.cpp, grammar-constrained, greedy)
  → verify    (parse + schema-validate + canonical re-render)
  → SQL inserted into the editor (never executed)

Crates

  • irodori-sqlast, parser, grammar (the shared syntax tree). Pure Rust, dialect-parameterized.
  • irodori-generateproject, plan, verify, runtime and the orchestrator. The llama feature adds the embedded runtime (llama.rs). Without it (or without a model) the engine returns unsupported/not found.
  • desktop (src-tauri/src/ai) — the ai_generate_sql, ai_engine_status, ai_set_provider, and ai_get_provider commands. The schema comes from the existing completion metadata cache; the dialect from the connection engine.

Runtime & model

  • Embedded llama.cpp via llama-cpp-2, CPU only, behind the llama cargo feature (the C++ build stays opt-in, like duckdb).
  • Default model: Qwen2.5-Coder-0.5B-Instruct Q4_K_M (~0.4 GB), strong at text-to-SQL at its size.
  • Lightness: mmapped weights, model loaded once and reused, a fresh context per request (KV cache freed between calls), small n_ctx, capped threads, and the grammar prunes the token space so fewer decode steps are needed.

Providers — connect any model (extensibility)

Everything talks to one trait, GrammarModel (runtime.rs), so backends are fully pluggable. The verify gate is what makes every provider safe: output is parsed and schema-validated regardless of source, so a backend that can’t honor the GBNF grammar just gets its mistakes rejected instead of prevented — never returned as invalid/hallucinated SQL.

Built-in providers:

ProviderCrate itemFeatureNotes
Embedded llama.cppLlamaSqlModelllamaGBNF-constrained, fully local/offline
OllamaOllamaModelhttpany local Ollama model (7B/14B/32B…)
OpenAI-compatible APIOpenAiCompatModelhttpOpenAI, Azure, OpenRouter, gateways, many self-hosted/Anthropic-compatible
External CLICommandModel(none)Claude Code, Codex, Copilot, or any command — reuses your subscription
Echo (tests)EchoModel(none)deterministic stand-in

The desktop selects a provider at runtime via ai_set_provider / ai_get_provider (AiProviderConfig { kind, model, endpoint, apiKey, program, args }), surfaced in the generate dialog’s “Model provider” section. The http and CLI providers are compiled in by default, so external/subscription models work without the heavy llama build; the embedded model stays opt-in. API keys are held in memory only (persist via the OS keychain through security_store_secret).

Adding another provider (e.g. Anthropic-native, a local server, a queue) is a new impl GrammarModel plus one AiProviderKind arm — no pipeline changes.

Build & use

# Standard build: AI generation compiled out, everything else unaffected.
cargo build -p irodori-table-desktop

# With local generation (compiles llama.cpp; set TMPDIR for the C++ build):
TMPDIR=.irodori-local/cc-tmp cargo build -p irodori-table-desktop --features llama

The embedded model is read only when it already exists in the app data dir under models/. The desktop UI does not start a background download job; preinstall the model for local generation, or use Ollama / API / CLI providers.

Safety

AI generation is opt-in, only ever inserts SQL into the editor (it never runs it), and references only objects that exist in the connected schema. This keeps the A5SQL-style read-only / AI-disabled posture intact.

Follow-ups

  • Optional admin tooling for preinstalling local models outside the desktop UI.
  • Expand the grammar/AST beyond SELECT (DML/DDL) over later iterations.
  • Optional idle-unload timer to drop the model from memory after inactivity.

Query plan explorer

Design for a cross-engine execution-plan viewer + analyzer: capture a query’s EXPLAIN, normalize it, visualize it legibly, analyze it for problems, and explain it in plain language — including teaching how to read the metrics. AI narration is optional; the deterministic explanation always works.

Status: design (greenfield — no plan handling exists today beyond explain analyze appearing in completion keywords).

Goals (from the ask)

  • Capture and analyze execution plans.
  • Explain the plan in natural language and identify likely bottlenecks.
  • Teach how to read metrics such as cost, rows, loops, and elapsed time.
  • Visualize the hot path with a tree/flame view.
  • Normalize plan data across PG/MySQL/Oracle/SQL Server/ SQLite/Snowflake/Hive/Trino/DuckDB…

The core problem: every engine’s EXPLAIN is different

There is no portable plan format. The design’s spine is a per-engine capture + parser that lowers each native format into one normalized plan IR, after which visualization, analysis, and explanation are engine-agnostic.

EngineCaptureNative shape
PostgreSQL (+ CRDB/Yugabyte/Timescale/Neon)EXPLAIN (FORMAT JSON[, ANALYZE, BUFFERS, VERBOSE]) <q>JSON tree, est+actual
MySQL / MariaDB / TiDBEXPLAIN FORMAT=JSON <q> · EXPLAIN ANALYZE <q> (8.0.18+)JSON / tree text
SQL ServerSET SHOWPLAN_XML ON / SET STATISTICS XML ONXML showplan
OracleEXPLAIN PLAN FOR <q> + DBMS_XPLAN.DISPLAY, or /*+ GATHER_PLAN_STATISTICS */ + DBMS_XPLAN.DISPLAY_CURSORrow table / cursor stats
SQLiteEXPLAIN QUERY PLAN <q>simplified tree
SnowflakeEXPLAIN USING JSON <q>; profile via GET_QUERY_OPERATOR_STATS()JSON / operator stats
Trino / Presto / HiveEXPLAIN (FORMAT JSON) <q> · EXPLAIN ANALYZEJSON / text
DuckDBEXPLAIN [ANALYZE] <q> (json)JSON tree

Each engine gets a PlanProvider that knows (a) the EXPLAIN SQL to issue and (b) how to parse its output into the IR. This reuses irodori-sql’s trait SqlDialect for identifier rendering and lives next to the existing dialect/metamodel code.

Normalized plan IR

pub struct QueryPlan {
    pub engine: DbEngine,
    pub analyzed: bool,            // EXPLAIN ANALYZE (actuals) vs estimate-only
    pub root: PlanNode,
    pub totals: PlanTotals,        // total cost, total actual time, planning/exec ms
    pub warnings: Vec<String>,     // capture caveats (e.g. "estimates only")
}

pub struct PlanNode {
    pub op: PlanOp,                // canonical operator (below)
    pub label: String,            // engine's raw node name, preserved
    pub relation: Option<String>, // table/index/CTE touched
    pub est: NodeEstimate,        // rows, cost (start/total), width
    pub actual: Option<NodeActual>, // rows, loops, time_ms, returned (ANALYZE only)
    pub io: Option<NodeIo>,       // buffers/blocks read, spill bytes, network
    pub detail: Vec<(String, String)>, // condition, filter, sort key, join type…
    pub children: Vec<PlanNode>,
}

pub enum PlanOp {                  // engine names mapped to one vocabulary
    TableScan, IndexScan, IndexOnlyScan, BitmapScan,
    NestedLoop, HashJoin, MergeJoin,
    Aggregate, GroupAggregate, HashAggregate, WindowAgg,
    Sort, IncrementalSort, Limit, Materialize, Memoize,
    Gather, Exchange, Subquery, CteScan, Result, ModifyTable, Other(String),
}

The IR carries both the engine’s raw label and the canonical PlanOp, so visualization stays uniform while node cards can still show native terminology.

Analysis — deterministic heuristics first

A rule pass over the IR produces Finding { severity, node_path, title, explanation, suggestion }. These are engine-aware but deterministic (no AI), so analysis is reliable and offline. Starter rules:

  • Full scan of a large relation where a predicate could be indexed → suggest index on the filter/join column.
  • Estimate vs actual skew (actual_rows / est_rows far from 1, ANALYZE only) → planner mis-estimate; suggest ANALYZE/stats refresh; explains why the plan shape may be wrong.
  • Hot subtree: node(s) accounting for the majority of total time/cost → “spend your effort here.”
  • Sort/hash spilled to disk (buffers/spill bytes) → raise work_mem / add index to avoid the sort.
  • Nested loop over a large outer/inner → likely should be hash/merge join.
  • Row explosion across a join (output ≫ inputs) → missing/incorrect join key.
  • Redundant materialize/sort, non-sargable predicate (function on indexed column), SELECT * width blowup.

Each finding maps to a node so the UI can highlight it on the tree.

Natural-language explanation — two layers

  1. Deterministic narrative (always on, offline). Template the IR + findings into prose: “This query scans orders sequentially (1.2M rows, ~78% of total time) because no index covers status, then hash-joins to customers. The planner expected 200 rows but got 48,000 — stale statistics. Biggest win: an index on orders(status).” This is the baseline and never depends on a model.
  2. AI narration (opt-in). Feed the normalized plan + findings to a GrammarModel provider (the existing irodori-generate stack: LlamaSqlModel embedded, OllamaModel/OpenAiCompatModel over HTTP, CommandModel via CLI) using unconstrained decoding for fluent prose and a tutoring “how would you fix this?” voice. Per the roadmap, AI is optional and audited — the deterministic narrative stands alone; AI only enriches it.

The IR (not raw EXPLAIN text) is what’s fed to the model, so the prompt is small, engine-neutral, and consistent across providers.

Metric glossary

A small, structured glossary keyed by (engine, metric) powers inline tooltips and a “how to read this plan” panel:

  • cost (PG): arbitrary units, startup..total; only comparable within one plan, not across engines.
  • rows: estimate (and, with ANALYZE, actual); the est/actual ratio is the signal, not the absolute.
  • loops (PG nested loop): a node’s time is actual_time × loops.
  • actual time: startup..total ms per loop.
  • buffers / blocks (PG BUFFERS): cache vs disk reads — IO pressure.
  • width: bytes per row — wide rows = more memory/IO.
  • SQL Server cost %, MySQL filtered/rows, Oracle Cardinality/Bytes, etc.

This directly serves the metric glossary: the user learns what each number means and how to act on it, inline where the number appears.

Visualization — make the hot path pop

A new ResultMode = "plan", rendered in the result area alongside grid/chart/graph/webgl/structure (features/results/components/ ResultBody.tsx dispatches on resultMode). A features/query-plan/ view with:

  • Tree / flame view: collapsible operator tree; each node sized/heat-colored by its share of total time (or cost when estimate-only) so the expensive subtree is obvious at a glance.
  • Node card: canonical op + raw label, relation, est vs actual rows with a skew badge, time/cost %, IO/spill, and the condition/filter/sort detail.
  • Findings rail: the heuristic findings, each clicking-through to highlight its node; severity-colored.
  • Explanation panel: the deterministic narrative, with an optional “Explain with AI” button (only if a provider is configured) and the glossary tooltips.
  • Estimate ⇄ Actual toggle: estimate (plain EXPLAIN, safe) vs actual (EXPLAIN ANALYZE, runs the query — see safety).

Safety — EXPLAIN ANALYZE executes the query

Plain EXPLAIN is read-only and always safe. EXPLAIN ANALYZE runs the statement — dangerous for DML. Rules:

  • Classify the statement with the existing read-only guard (../irodori-kit/irodori-server guard::classify, already used for the headless API).
  • Offer ANALYZE freely for read-only queries.
  • For writes: either refuse ANALYZE, or wrap in a transaction the engine can roll back (BEGIN; EXPLAIN ANALYZE …; ROLLBACK; on PG) with a loud confirm. Never silently execute a DML to profile it.

Where the code lives

  • Query-plan module (new, engine-agnostic). The IR, per-engine EXPLAIN SQL + parsers, the heuristic rules, and the deterministic narrative + glossary. Depends on irodori-sql (dialect) only; no DB driver. Start app-local under apps/desktop/src-tauri/src/query_plan/, then promote to a sibling shared repo only after the IR has a stable release/test boundary.
  • Capture command. Tauri explain_query(connection, sql, { analyze }) -> QueryPlan in src-tauri/src/db/ — issues the engine’s EXPLAIN via the existing pool and hands the output to irodori-plan. Headless: POST /v1/explain.
  • AI narration. Reuse irodori-generate providers via a thin free-text explain_plan(plan, provider) path (sibling to ai_generate_sql), opt-in.
  • UI. features/query-plan/ (tree/flame view, node cards, findings rail, explanation + glossary), wired as ResultMode = "plan"; a “Explain plan” action/keybinding next to Run.

Phased plan

  1. IR + PostgreSQL provider (richest JSON, est+actual+buffers) end-to-end: capture → IR → tree view → 3–4 heuristics → deterministic narrative + glossary. This proves the whole pipe on one engine.
  2. More providers: SQLite (EXPLAIN QUERY PLAN), MySQL JSON, DuckDB — fast wins; then SQL Server XML, Oracle DBMS_XPLAN, Snowflake/Trino.
  3. Flame heat view + findings rail polish; estimate⇄actual toggle + ANALYZE safety guard.
  4. AI narration behind the existing provider config (opt-in), feeding the IR.
  5. Headless /v1/explain + a CLI verb for plan capture in automation.

Relationship to the data-diff design

Both new capabilities are engine-agnostic analysis crates over the same seams (dialect, the db read path, the headless API, optional AI providers). irodori-diff (data-verification-diff.md) and irodori-plan share the pattern: lower heterogeneous engines into one IR, then analyze/present uniformly. Worth keeping their crate conventions aligned.

Integrated terminal

A VSCode-style bottom terminal panel, rendered with xterm.js + WebGL for Ghostty/WezTerm-class throughput, backed by a real PTY. Toggle it with **Ctrl+\``** or the command palette ("Toggle Terminal"). It pairs with the AI CLI providers — run claude/codex` / any tool right in the app.

Architecture

xterm.js (WebGL)  ──onData──▶  pty_write
      ▲                         ▲
      │ term.write(bytes)       │  Tauri commands
   base64 decode                │
      │                         ▼
  Channel<PtyEvent> ◀──── pty_spawn (portable-pty: shell in a PTY)
                                 │  pty_resize / pty_kill
  • Backend (src-tauri/src/pty/mod.rs): portable-pty (wezterm’s cross-platform PTY). pty_spawn starts the shell, a reader thread streams output base64-encoded over a Tauri Channel (base64 preserves bytes across chunk boundaries); pty_write / pty_resize / pty_kill drive the session. Sessions live in a PtyState map and are dropped on exit/kill.
  • Frontend (src/features/terminal/): TerminalView binds one xterm.js instance (fit + WebGL addons) to a PTY session; TerminalPanel is the tabbed bottom dock. PTY wrappers are hand-written in src/lib/tauri/pty.ts (the Channel arg, like the query-stream wrapper).
  • Workbench: terminal.toggle command (palette + `Ctrl+``), docked as a fixed bottom panel below modals.

Notes & follow-ups

  • Default shell: $SHELL (unix) / %ComSpec% (windows); cwd defaults to the app’s working dir.
  • The dock is a fixed overlay for now (low conflict with the in-flight workbench refactor); a resizable, content-pushing split panel is a natural follow-up.
  • Persisting open terminals / splitting panes are future enhancements.

Headless local data API (irodori-server)

An optional local HTTP server that exposes read and safe-write data operations over the same token-scoped auth, read-only-by-default SQL guard, and audit trail the desktop uses — for scripting, tests, and external tools. (PostgREST / DuckDB httpserver were behavior references; the implementation is independent.)

Endpoints

Method + pathPurposeScope
GET /healthlivenessread
GET /v1/sourceslist configured sourcesread
GET /v1/sources/{id}/objectslist tables/viewsread
POST /v1/sources/{id}/queryrun SQL ({ "sql": "...", "maxRows": 1000 })read / write

Every response is JSON; errors use { "error", "code" }. A query returns { columns, rows, rowCount, elapsedMs, truncated } (rows are a row-major matrix of JSON cells, the same shape the desktop/web grids consume).

Safety model (layered, all pre-existing + now wired)

  • Auth (auth.rs): bearer tokens → scopes (read/write), constant-time compare. No tokens = open mode = read-only for anyone (localhost/dev); writes always require an explicit write-scoped token.
  • Read-only guard (guard.rs): classifies each statement (ReadOnly/Write/Forbidden) after blanking strings/comments; multiple statements are forbidden. A Write statement needs the write scope and a writable source; otherwise 403.
  • Audit (audit.rs): one structured entry per request (JSON lines to stderr by default), success or rejection.

Architecture

  • server.rsApiServer::dispatch(method, path, auth, body) -> ApiResponse is transport-agnostic (unit-tested without sockets); serve(addr, server) is the hyper adapter.
  • source.rs — the DataSource trait + a built-in SqliteDataSource (synchronous rusqlite on spawn_blocking, so it owns the statement and avoids the 'static/Send borrow constraints async SQLite drivers impose inside an async_trait future). The desktop can implement DataSource over its live connection registry to expose the same adapters/proxy/security.
  • Registry maps source ids → Arc<dyn DataSource>.

Run standalone

IRODORI_SERVER_ADDR=127.0.0.1:8787 \
IRODORI_SERVER_SQLITE=/path/to.db \
IRODORI_SERVER_WRITABLE=1 \
IRODORI_SERVER_TOKEN=secret \
cargo run -p irodori-server
curl -s -H 'authorization: Bearer secret' \
  -d '{"sql":"select * from users limit 10"}' \
  http://127.0.0.1:8787/v1/sources/default/query

Follow-ups

  • Bounded streaming for large result sets (current SQLite source caps + flags truncated; a streaming/cursor path mirrors the desktop’s spill design).
  • Parameterized queries (params) and dedicated row read/insert/update/delete endpoints.
  • A desktop DataSource impl over the live connection registry + a Tauri server_start/server_stop command.

Local Knowledge Base

Irodori should keep a local, searchable memory of database specifications, release notes, DB-client product expectations, AI integration docs, and implementation notes.

The goal is to make future implementation and bug fixing less dependent on memory or scattered browser tabs.

Storage

  • Generated SQLite DB: knowledge/irodori-knowledge.sqlite
  • Schema: knowledge/schema.sql
  • Source registry: knowledge/sources.json
  • Refresh script: tools/knowledge/refresh.mjs
  • Analysis script: tools/knowledge/analyze.mjs
  • Query script: tools/knowledge/query.mjs

The SQLite DB is intentionally ignored by Git. The schema and source registry are tracked.

Task Navigation

Use this document for the knowledge-base system itself: source registry shape, refresh/query workflow, source policy, and what kinds of facts belong in the local store. It should not duplicate the product backlog.

When a task depends on vendor behavior or DB-client precedent, query this knowledge base first, then record the implementation outcome in the owning task doc. A knowledge snapshot or source-registry update is evidence for future work; it does not by itself close a product feature without linked implementation and verification.

Source Registry Schema

knowledge/sources.json is a JSON array. Each entry is one stable source that can be registered into the sources table from knowledge/schema.sql.

Required fields:

  • id: stable lowercase kebab-case identifier. Do not rename it after snapshots or facts may reference it; add a replacement source instead.
  • name: human-readable source name.
  • product: product, project, or service this source documents.
  • category: source family. Current values are database, db_client, ai, and tooling.
  • sourceType: document kind. Current values are spec, release_notes, driver_docs, product_docs, and oss_project.
  • url: canonical upstream URL. It must be unique across the registry.

Optional fields:

  • official: defaults to true; set to false only for clearly labeled non-official references.
  • cadence: refresh expectation such as weekly or monthly; defaults to weekly.
  • enabled: defaults to true; set to false to keep a source registered but skip network refresh.
  • notes: short reason this source matters to Irodori implementation work.

The refresh script maps sourceType to the SQLite source_type column and keeps official, cadence, enabled, and notes synchronized on every run.

Usage

Initialize the DB and register sources without network access:

node tools/knowledge/refresh.mjs --no-fetch

Fetch the first few sources:

node tools/knowledge/refresh.mjs --limit 5

Fetch one source:

node tools/knowledge/refresh.mjs --source sqlite-changes

List registered sources:

node tools/knowledge/query.mjs

Search snapshots:

node tools/knowledge/query.mjs "ALTER TABLE"

Generate local facts and implementation notes from the latest stored snapshots:

node tools/knowledge/analyze.mjs

Preview generated facts without writing:

node tools/knowledge/analyze.mjs --dry-run

Analyze only text segments added since the previous stored snapshot for each source. This is the recommended mode for routine refreshes because it focuses implementation notes on new upstream behavior:

node tools/knowledge/analyze.mjs --changed-only

On the first snapshot for a source, --changed-only falls back to full analysis so the source can be seeded. Add --strict-changed to skip sources without a previous snapshot.

List or search generated facts and implementation notes:

node tools/knowledge/query.mjs --facts auth
node tools/knowledge/query.mjs --notes driver

What To Store

  • Official release notes and migration notes.
  • SQL syntax/reference pages.
  • Non-SQL source references: Cypher, time-series SQL/native query docs, document/KV/search APIs, and distributed SQL operational metadata.
  • Catalog/introspection references.
  • Driver documentation and behavior changes.
  • DB client feature docs and market scans.
  • Source-specific GUI docs such as Neo4j Browser, InfluxDB UI/Data Explorer, MongoDB Compass, RedisInsight, and DbGate.
  • AI/Copilot/MCP integration docs.
  • Manual facts discovered while fixing bugs.
  • Implementation notes linking a source fact to an Irodori component.

Source Policy

  • Prefer official docs and release notes.
  • Keep coverage across database specs, database release notes, DB-client product docs, AI/MCP references, and type/tooling references.
  • Use versioned URLs where the upstream publishes stable versioned specs.
  • Keep IDs stable even if a URL redirects or an upstream page is renamed.
  • Add new category or sourceType values only with a matching documentation update here and downstream handling in the refresh/query code when needed.
  • Run JSON validation and node tools/knowledge/refresh.mjs --no-fetch before marking registry changes done.
  • Store URL, source product, fetch time, and hash for every snapshot.
  • Summarize implementation facts in our own words.
  • Do not store proprietary docs that we do not have rights to retain.
  • Do not treat scraped docs as vendored source code.

Automation Direction

  • Local: run the refresh script manually while developing.
  • Scheduled: later add a weekly GitHub Actions job or local cron that refreshes sources and opens a report.
  • Large index builds: source snapshots, generated facts, implementation notes, schema metadata, and query-history search indexes must be built through the shared job runtime with progress, cancellation, checkpoint/resume, bounded memory, and disk-backed state where needed.
  • Smarter extraction: tools/knowledge/analyze.mjs starts with deterministic rule-based classification for versions, breaking changes, SQL syntax, authentication, metadata, result UI, visualization, and driver-impacting changes. Add per-product extractors when a source needs higher precision.
  • ML/evaluation: any model-ranking or provider-evaluation dataset derived from knowledge snapshots must record source IDs, snapshot hashes, privacy inputs, metrics, and artifact hashes so runs are reproducible and auditable.
  • Integration: surface relevant facts in the app when implementing a dialect feature or debugging a query issue.

Repository Boundaries And Archive Policy

This file is the public, repo-local version of the Irodori documentation layout. Use it when deciding whether a document belongs in irodori-table, the public mdBook, a samples repo, or the private archive.

Repositories

RepositoryVisibilityOwns
irodori-tablePublicDesktop app, app-local crates, local knowledge tools, packaging templates, and generated docs consumed by the app/CI.
irodori-docsPublicPublic mdBook/site, durable user/contributor docs, policy pages, reference pages, feature matrix, backlog/progress views, ADR-style docs, and public long-form explanations.
irodori-samplesPublicDatabase compose files, seed data, and DB-specific sample query projects.
irodori-sqlPublicReusable SQL dialect, placeholder, metamodel, and schema-diff helpers.
irodori-knowledgePublicShared error/job/knowledge crates used by this workspace.
irodori-kitPublicShared Rust foundation crates, extension SDK, manifest schema, extension-dev helper, generated SDK API, starter templates, and packaging templates.
irodori-migrationPublicExecution-free migration planning and schema/data-diff primitives that can be reused outside the desktop app.
irodori-extension-*Public per connectorOne installable connector implementation per repository, usually generated under the local sibling parent ../irodori-extensions/.
irodori-archivePrivateHistorical internal planning/status snapshots, audits, private research notes, bulky discarded docs, and material that should not be public.

What Stays In irodori-table

  • Root project entry points: README.md, CONTRIBUTING.md, SECURITY.md, ROADMAP.md.
  • Generated snapshots that are consumed by tooling or the desktop app: registry/data-source-support-status.md, selected registry/cheatsheets/, and registry/catalog/*.json.
  • Machine-readable coordination files that local tools validate, such as registry/agent-workstreams.json.
  • Short repo-local pointers, such as docs/README.md and this boundary policy.

What Moves To irodori-docs

  • Stable user guides and contributor guides.
  • Public site pages.
  • Policy pages: clean-room, licensing, security process, release process.
  • Reference pages that should have durable public URLs.
  • Feature matrix, implementation progress, backlog, and release-readiness pages.
  • ADR-style decisions that explain why the system is shaped a certain way.
  • Long-form feature architecture docs, including implementation architecture, migration/data-diff, local SQL generation, query plan explorer, integrated terminal, headless API, distribution, and store/package registration.

When a page is moved out, keep a local link from docs/README.md only if a code workflow still needs to discover it from this repository.

What Moves To irodori-samples

  • compose.yaml, seed SQL/JS, and per-engine fixture data.
  • DB-specific sample query projects under projects/<engine>/.
  • db-feature-samples.json and sample catalog data.

This repo expects the samples repo as a sibling checkout by default:

git clone https://github.com/hjosugi/irodori-samples ../irodori-samples

Override the location with IRODORI_SAMPLES=/path/to/irodori-samples.

What Moves To irodori-archive

Archive instead of deleting when the material has historical value but is not a current public contract:

  • superseded implementation plans;
  • one-off status reports and audit dumps;
  • raw product/research notes;
  • internal screenshots or bulky generated exports;
  • private planning context that should not be published;
  • old docs that confuse current source-of-truth ownership.

Do not link private archive paths from public docs. If public context is needed, write a short replacement summary in irodori-docs or this repo.

Generated-Doc Flow

Generated docs are edited at their inputs:

OutputEdit Instead
registry/data-source-support-status.mdknowledge/engines.json, registry/catalog/*.json, tools/docs/support-status.mjs
registry/cheatsheets/*.mdknowledge/cheatsheets/*.json, the knowledge DB, tools/knowledge/cheatsheet.mjs
registry/catalog/catalog.jsonregistry/catalog/index.json, tools/docs/build-extension-catalog.mjs

Generated snapshots should be mirrored into irodori-docs for public reading. Keep a Markdown snapshot in irodori-table only when a local generator, CI guard, or app-consumed source needs it.

Parallel Agent Boundary

Use parallel-agent-architecture and agent-workstreams.json when assigning work to multiple coding agents. The default split is:

  • app/runtime/registry contract changes stay in irodori-table;
  • extension SDK, manifest schema, and template changes stay in irodori-kit under packages/extension-sdk;
  • one connector implementation agent writes one irodori-extension-* repository;
  • generated docs/catalog files are updated by the agent that owns the source data or generator;
  • shared contracts are serialized through the coordinator workstream.

The check is:

node tools/docs/agent-workstreams.mjs

Decision Checklist

Before adding a new document, answer:

  1. Does app code or tooling consume it? Keep it here.
  2. Is it stable public documentation? Put it in irodori-docs.
  3. Is it a DB fixture or sample query catalog? Put it in irodori-samples.
  4. Is it historical/private/internal? Put it in irodori-archive.
  5. Is it generated? Edit the generator or source data, not the output.

Data Source Support Status

Last generated: 2026-06-26 JST (hand-authored seed; target is auto-generation from irodori-table registry inputs).

This is the single inventory of what Irodori connects to today vs. what is declared, planned, or not yet started. The authoritative source of truth is the DbEngine registry in apps/desktop/src-tauri/src/db/engine.rs and the connect dispatch in apps/desktop/src-tauri/src/db.rs. Roadmap intent lives in https://hjosugi.github.io/irodori-docs/data-source-coverage-strategy.html; this file reconciles intent against the code.

Managed-service verification procedures live outside the public user path; keep this page focused on selectable engine support and registry drift.

Status legend:

  • Wired — has a production connect path and a dedicated adapter or a wire-compatible adapter it routes through.
  • Verified — Wired and exercised against a real instance in tests/integration_db.rs through the sample harness (make db-verify).
  • Pending — recognized by the engine enum, adapter scaffolding exists, but the connector intentionally returns a “not ready” result.
  • Extension — recognized by the engine enum and published through the extension marketplace; the app browses registry/catalog/catalog.json and install/details stay in registry/catalog/index.json instead of being compiled into the core desktop build.
  • Recognized, extension required — present in DbEngine but rejected at connect by is_unimplemented_wire() until an installable connector extension is present.
  • Not registered — named in the roadmap/coverage strategy but absent from the DbEngine enum — i.e. not selectable in the app at all yet.

1. Wired engines (selectable and connectable today)

EngineDbEngine idWire / driverAdapter filePortMaturityShipped release build
PostgreSQLpostgresPostgres / sqlxdb/postgres.rs5432VerifiedBuilt-in
MySQLmysqlMySQL / sqlxdb/mysql.rs3306VerifiedBuilt-in
MariaDBmariadbMySQL wire / sqlx(via mysql.rs)3306VerifiedBuilt-in
SQLitesqlitefile / sqlxdb/sqlite.rsVerified (unit)Built-in
OracleoracleThin TNS / oracle-rsdb/oracle.rs1521Verifiedlegacy-connectors
SQL ServersqlserverTDS / tiberiusdb/mssql.rs1433Verifiedlegacy-connectors
DuckDBduckdbembedded libduckdbdb/duck.rsVerifiedduckdb
MotherDuckmotherduckDuckDB service / extensionirodori.motherduck443ExtensionMarketplace extension
CockroachDBcockroachdbPostgres wire / sqlx(via postgres.rs)26257VerifiedBuilt-in
YugabyteDB (YSQL)yugabytedbPostgres wire / sqlx(via postgres.rs)5433WiredBuilt-in
RedshiftredshiftPostgres wire / sqlx(via postgres.rs)5439Wired (AWS, no local container)Built-in
TimescaleDBtimescaledbPostgres wire / sqlx(via postgres.rs)5432VerifiedBuilt-in
NeonneonPostgres wire / sqlx(via postgres.rs)5432WiredBuilt-in
H2h2Postgres wire / sqlx(via postgres.rs)5435Wired (experimental)Built-in
TiDBtidbMySQL wire / sqlx(via mysql.rs)4000WiredBuilt-in
MongoDBmongodbdocument / mongodbdb/mongo.rs27017Verifiedlegacy-connectors
Neo4jneo4jBolt / neo4rsdb/neo4j.rs7687Wired (graph) — see cheatsheetlegacy-connectors
RedisredisRESP / redisdb/redis.rs6379Wired (adapter)legacy-connectors
CassandracassandraCQL / scylla driverdb/cassandra.rs9042Wired (adapter)legacy-connectors
ClickHouseclickhouseHTTPdb/clickhouse.rs8123Wired (HTTP client)legacy-connectors
SnowflakesnowflakeHTTPdb/snowflake.rs443Wired (password/JWT subset)legacy-connectors
BigQuerybigqueryHTTPdb/bigquery.rs443Wired (HTTP client)legacy-connectors
BigtablebigtablegRPC/HTTPdb/bigtable.rs443Wired (adapter)legacy-connectors
InfluxDBinfluxdbHTTP (SQL/v3)db/influx.rs8086Wired (adapter)legacy-connectors
ScyllaDBscylladbCQL / scylla driver(via cassandra.rs)9042Wired (CQL-compatible)legacy-connectors
QuestDBquestdbPostgres wire / sqlx(via postgres.rs)8812WiredBuilt-in

Maturity is a coverage signal, not a UX guarantee. “Wired (adapter)” means the connect/query path exists; first-class browsing, completion, editing, explain/profile, and visualization per source remain tracked by SRC tickets. Release builds pass --features legacy-connectors,duckdb; local default development builds may omit feature-gated connectors for speed.

2. Pending (recognized, scaffolded, returns “not ready”)

None today. If an adapter has a dedicated Wire but intentionally returns a not-ready error, list it here instead of mixing it with production connectors.

3. Recognized, extension required (in the enum, rejected at connect)

These appear in DbEngine but is_unimplemented_wire() rejects them before a connection is opened. Most public connector targets ask the user to install the matching installable connector from registry/catalog/index.json.

EngineDbEngine idFamilyClosest existing wireNote
MemgraphmemgraphGraph (Bolt/Cypher)Neo4jInstallable connector; can reuse the Neo4j/Bolt path internally.
QdrantqdrantVectorInstallable vector connector extension.
MilvusmilvusVectorInstallable vector connector extension.
PineconepineconeVector (HTTP)Installable vector connector extension.
Cloud SpannercloudSpannerDistributed SQL / Google APICloudSpannerInstallable connector; Spanner SQL/catalog handling is separate from Postgres wire.
Trino / PrestotrinoPrestoFederated SQLJdbcInstallable JDBC-style connector extension.
FirebirdfirebirdRelationalJdbcInstallable JDBC-style connector extension.
Databricks / Spark SQLdatabricksWarehouseJdbcInstallable SQL Warehouse connector extension.
ElasticsearchelasticsearchSearchSearchInstallable search connector extension with index/data-stream workflows.
OpenSearchopenSearchSearchSearchInstallable search connector extension with index/data-stream workflows.
CouchbasecouchbaseDocumentDocumentInstallable document connector extension.
DynamoDBdynamodbKey-valueKeyValueInstallable key-value connector extension.
ArangoDBarangodbGraph / multi-modelGraphInstallable graph/multi-model connector extension.
Apache IoTDBiotdbTime-seriesTimeSeriesInstallable time-series connector extension.
Apache HivehiveLakehouse / catalogJdbcInstallable Hive/Hive Metastore connector extension.
Amazon AthenaathenaLakehouse / query-engineLakehouseInstallable Athena/Glue/workgroup connector extension.
Apache IcebergicebergLakehouseLakehouseInstallable catalog-backed Iceberg connector extension.
AWS S3 Tabless3TablesLakehouseLakehouseInstallable managed Iceberg connector extension.
Delta LakedeltaLakeLakehouseLakehouseInstallable Delta Lake connector extension.
Apache HudihudiLakehouseLakehouseInstallable Hudi connector extension.

4. Not registered (roadmap intent, not in the engine enum yet)

Named in the public data-source coverage strategy / feature matrix but not selectable in the app — adding any of these starts with a new DbEngine variant + Wire + adapter.

All roadmap sources currently promoted into the registry are listed above. Keep this section for future coverage-strategy ideas that are not selectable in the app yet.

5. Managed wire-compatible targets

These should not become separate DbEngine variants unless they need native API surface beyond connection templates. They route through existing adapters:

TargetRoute throughStatusProduct work
Supabase PostgrespostgresCovered by Postgres wire; needs preset/docsDirect vs. pooler connection strings, SSL, RLS notes, hosted extensions such as pgvector.
Amazon Aurora PostgreSQLpostgresCovered by Postgres wire; needs preset/docsWriter/reader/custom endpoint hints, IAM auth, cluster topology.
Amazon Aurora MySQLmysqlCovered by MySQL wire; needs preset/docsWriter/reader/custom endpoint hints, IAM auth, cluster topology.
Google Cloud SQL for PostgreSQLpostgresCovered by Postgres wire; needs preset/docsPublic/private IP, Auth Proxy, IAM DB auth, SSL cert handling.
Google Cloud SQL for MySQLmysqlCovered by MySQL wire; needs preset/docsPublic/private IP, Auth Proxy, IAM DB auth, SSL cert handling.
Google Cloud SQL for SQL ServersqlserverCovered by TDS path; needs preset/docsPublic/private IP, Auth Proxy, SQL Server connection-string guidance.

6. Gaps worth deciding on

  • Vector DBs are extension-first. Qdrant/Milvus/Pinecone are registry entries with marketplace extensions; core still needs the shared vector source-type contract for collection/index browsing and similarity-search query surfaces.
  • Memgraph is extension-first. It speaks Bolt/Cypher like Neo4j; the extension can reuse the Neo4j path internally before core promotes it to a wired adapter.
  • ScyllaDB now rides the existing cassandra.rs CQL path; the remaining work is verification against a real ScyllaDB instance and source-specific UX polish.
  • Iceberg/lakehouse is now extension-first: Apache Iceberg, S3 Tables, Delta Lake, Hudi, Hive, and Athena all have marketplace connectors. Core still needs shared table/catalog UX and execution-backend contracts for those extensions.

When section 1-4 membership changes, regenerate this page from the registry inputs rather than hand-editing the mirrored snapshot.

Engine Connection & Query Reference

For every database Irodori supports: how to connect, the query model, and the driver/dialect quirks. This doubles as the basis for the per-engine SqlDialect (SRC-001a) and as a reference for extension authors. All engines go through the same Connection trait (apps/desktop/src-tauri/src/db/).

Related docs: data-source-support-status.md is the coverage inventory (what connects today vs. what is declared/planned), and cheatsheets/ holds the task-oriented, copy-pasteable per-engine pages. Generated support-status and cheatsheet snapshots are mirrored from the registry and knowledge inputs in irodori-table. This file stays the deep driver/decoding reference.

Coverage at a glance

EngineWire / driverDefault portQuery modelTest (tests/integration_db.rs / unit)Container
PostgreSQLpostgres / sqlx5432SQLpostgres_samplessamples/postgres
MySQLmysql / sqlx3306SQLmysql_samplessamples/mysql
MariaDBmysql wire / sqlx3306SQLmariadb_connectsamples/mariadb
TimescaleDBpostgres wire / sqlx5432SQLtimescaledb_samplessamples/timescaledb
CockroachDBpostgres wire / sqlx26257SQLcockroachdb_connectsamples/cockroachdb
YugabyteDBpostgres wire (YSQL) / sqlx5433SQLyugabytedb_connectsamples/yugabytedb
TiDBmysql wire / sqlx4000SQLtidb_connectsamples/tidb
SQL ServerTDS / tiberius1433SQLsqlserver_samplessamples/sqlserver
DuckDBembedded libduckdbSQLduckdb_in_memorynone (embedded)
MongoDBdocument / mongodb27017documentsmongo_samplessamples/mongodb
SQLitefile / sqlxSQLsqlite_connect_and_query_round_trip (unit)none (file)
Oraclethin TNS / oracle-rs1521SQLoracle_samplessamples/oracle
Redshiftpostgres wire / sqlx5439SQL— (AWS-only, no local container)

Run them with make db-verify DB=<engine> or make db-all. Env-gated tests skip unless the matching IRODORI_* variable is set; the sample harness sets it per engine.

Connection syntax

Irodori accepts either structured fields (host/port/user/password/ database) or a raw url/DSN that overrides them.

EngineURL / DSN form
PostgreSQL & wire-compatiblepostgres://user:pass@host:5432/db
CockroachDB (insecure)postgres://root@host:26257/defaultdb?sslmode=disable
YugabyteDB (YSQL)postgres://yugabyte@host:5433/yugabyte?sslmode=disable
MySQL / MariaDB / TiDBmysql://user:pass@host:3306/db
SQLitesqlite://path/to.db?mode=rwc or sqlite::memory:
SQL Server (ADO)server=tcp:host,1433;User Id=sa;Password=…;TrustServerCertificate=true
DuckDBa file path or :memory: (in database/url)
MongoDBmongodb://user:pass@host:27017/db?authSource=admin
Oraclestructured host/port/user/password/service fields

Per-engine notes

PostgreSQL (+ CockroachDB, YugabyteDB, Redshift, TimescaleDB)

  • Driver: sqlx native PgPool. The wire-compatible engines reuse it; only the default port and a few catalog quirks differ (modeled later by the metamodel).
  • Version: select version().
  • Identifier quoting: ANSI double quotes — "My Col"; escape " by doubling.
  • Paging: LIMIT n OFFSET m.
  • Decoding: NUMERIC→string (exact, via BigDecimal), TIMESTAMPTZ→RFC3339, TIMESTAMP/DATE/TIME→string, JSON/JSONB→object, UUID→string, BYTEA\xHEX, bool/ints/floats native.
  • Quirks: arrays decode best-effort to text today (rich array decode is a follow-up); CockroachDB omits some OID-typed system columns tools expect.

MySQL / MariaDB / TiDB

  • Driver: sqlx native MySqlPool.
  • Version: select version().
  • Identifier quoting: backticks — `My Col`.
  • Paging: LIMIT n OFFSET m (or LIMIT m, n).
  • Decoding: DECIMAL→string, DATETIME/TIMESTAMP/DATE→string, JSON→object, BLOB/BINARY\xHEX, ints/floats/strings native.
  • Quirks: MySQL 8.4 defaults to caching_sha2_password; sqlx handles it over a non-TLS connection via the server’s RSA public key. MariaDB defaults to mysql_native_password. TiDB speaks the MySQL protocol on port 4000.

SQLite

  • Driver: sqlx native SqlitePool, capped at one connection.
  • Version: select sqlite_version().
  • Quoting: double quotes or backticks.
  • Decoding: dynamically typed — values are decoded by trying integer, real, text, then blob (\xHEX).
  • Quirks: single writer; :memory: is per-connection (use a file to share).

SQL Server

  • Driver: pure-Rust tiberius (TDS) — no SQL Server client library.
  • Version: select @@version (first line kept).
  • Quoting: brackets — [My Col] (or ANSI " with QUOTED_IDENTIFIER ON).
  • Paging: OFFSET n ROWS FETCH NEXT m ROWS ONLY (2012+), or TOP n.
  • Decoding (MVP): bool/int/float/string. Decimals currently come back as float (lossy) and datetimes/binary as null — precision-safe decimals and temporals are a tracked follow-up (EXEC-009b).
  • Quirks: tiberius sends statements via sp_executesql, so local #temp tables are scoped away between calls — use ##global temp tables, real tables, or (for self-contained checks) a VALUES table constructor: select a,b from (values (1,'x')) v(a,b).

DuckDB (--features duckdb)

  • Driver: embedded libduckdb. The bundled build compiles libduckdb (C++) and is heavy — link a prebuilt/system libduckdb to skip the compile.
  • Version: select version().
  • Quoting: double quotes.
  • Paging: LIMIT n OFFSET m.
  • Decoding: rich (bool/ints/uints/float/double/text/blob); other types render as their text form.
  • Quirks: statements are classified — DDL/DML run through execute, only row-returning statements through query; column metadata is read after execution (DuckDB materializes the schema then). This avoids a driver panic.

MongoDB (document store — not SQL)

  • Driver: mongodb crate (pure Rust).
  • Version: buildInfo.version.
  • “Query”: a bare collection name (e.g. customers), or a JSON object { "collection": "orders", "filter": { "tier": "gold" } }. Documents project to a table by the ordered union of top-level keys (missing keys → null); values render as relaxed extended JSON (ObjectId{ "$oid": "…" }, dates → ISO).
  • Quirks: no SQL. Aggregation pipelines, nested-field projection, and sort are follow-ups (SRC-012). Auth usually needs ?authSource=admin for the root user.

Oracle

  • Driver: pure-Rust thin TNS through oracle-rs; no Oracle Instant Client is required for the default path.
  • Connection: descriptor //host:1521/service, or structured host/port/user/ password/database fields. database can be a service name, service:<name>, or sid:<name>. Wallet paths can be supplied on the URL query as wallet and wallet_password.
  • Explain: EXPLAIN PLAN FOR ... returns DBMS_XPLAN.DISPLAY output as a one-column result set.

Bounded results (all engines)

Every engine streams rows and stops at max_rows (default 10,000), returning truncated: true when more remain — so a select * over a 10M-row table stays memory-bounded instead of exhausting RAM. Full extraction is run-to-file (IO-001); optional disk offload for very large windows is EXEC-010.

DB Feature Samples

samples/db-feature-samples.json is the source of truth for DB-specific sample projects, feature checks, and official learning resources. It is intentionally machine-readable so the repository docs and public website can stay aligned with the engine registry.

Local sample projects

Use these when you want to check DB-specific behavior in Irodori Table beyond the generic connection smoke test.

EngineSample fileVerification commandFocus
PostgreSQLsamples/projects/postgres/queries.sqlmake db-verify DB=postgresJSONB, arrays, GIN, extensions, explain JSON
MySQLsamples/projects/mysql/queries.sqlmake db-verify DB=mysqlJSON functions, FK metadata, windows, explain JSON
MariaDBsamples/projects/mariadb/queries.sqlmake db-verify DB=mariadbJSON_VALID/JSON_VALUE, recursive CTEs, windows
SQLitesamples/projects/sqlite/queries.sqlcargo test --manifest-path apps/desktop/src-tauri/Cargo.toml sqliteEmbedded SQL, JSON, FTS5, PRAGMA
DuckDBsamples/projects/duckdb/queries.sqlcargo test --manifest-path apps/desktop/src-tauri/Cargo.toml --features duckdb duckdb_in_memoryLocal analytics, structs, summarize, explain
SQL Serversamples/projects/sqlserver/queries.sqlmake db-verify DB=sqlserverT-SQL JSON, identity, TOP, OFFSET/FETCH
Oraclesamples/projects/oracle/queries.sqlmake db-verify DB=oracleThin TNS, SQL/JSON, analytic functions, DBMS_XPLAN
MongoDBsamples/projects/mongodb/queries.jsmake db-verify DB=mongodbCollection query, JSON filter, aggregation reference
TimescaleDBsamples/projects/timescaledb/queries.sqlmake db-verify DB=timescaledbHypertables, time_bucket, time-series metadata
CockroachDBsamples/projects/cockroachdb/queries.sqlmake db-verify DB=cockroachdbunique_rowid, UPSERT, range inspection
YugabyteDBsamples/projects/yugabytedb/queries.sqlmake db-verify DB=yugabytedbYSQL, split tablets, table properties
TiDBsamples/projects/tidb/queries.sqlmake db-verify DB=tidbMySQL wire, tidb_version, shard_row_id_bits, explain analyze

Reference-only engines

Some engines are wired but cloud-only, hosted-only, or still missing a local verification fixture. Their entries stay in samples/db-feature-samples.json so the capability map remains complete: Redshift, Neon, H2, Neo4j, Redis, Cassandra, ClickHouse, Snowflake, BigQuery, Bigtable, InfluxDB, Memgraph, Qdrant, Milvus, and Pinecone.

Managed wire-compatible targets

These are not new low-level adapters. They should be surfaced as connection templates/presets that route through existing engines.

TargetRoute throughFocus
Supabase PostgrespostgresDirect/pooler connection strings, SSL, RLS-aware browsing, hosted Postgres extensions such as pgvector
Amazon Aurorapostgres or mysqlAurora PostgreSQL/MySQL endpoints, reader/writer endpoint guidance, IAM auth, cluster topology
Google Cloud SQLpostgres, mysql, or sqlserverPublic/private IP, Cloud SQL Auth Proxy, IAM DB auth, SSL certs, instance metadata

Lakehouse targets

Iceberg and S3 Tables are not SQL wire-compatible databases. They need a catalog/table-format connection model plus an execution backend.

TargetRouteFocus
Apache IcebergREST/Hive/AWS Glue/JDBC catalogs + object store credentialsCatalog, namespace, table, schema, partition, snapshot, manifest, and metadata browsing
Amazon S3 TablesManaged Iceberg table bucketsTable buckets, namespaces, tables, AWS IAM, and query execution through Athena/Redshift/Spark-compatible engines

Checks

Run the catalog guard directly:

node tools/docs/db-feature-samples.mjs

It is also part of:

make docs-check

The check fails when a registered engine has no catalog entry, a sample project points at a missing file, a local compose sample is not represented, or an engine has no official resource link.

Distribution & updates

How users get Irodori Table and how it updates. Current public release: 0.6.0 (v0.6.0, published 2026-07-02).

Already in place

.github/workflows/release.yml — on a v* tag push, tauri-action builds installers for macOS, Windows (.msi/NSIS), and Linux (.deb/.rpm/.AppImage) on the matrix runners and creates a GitHub Release. The download channel is live today; publish a release tag such as v0.6.0.

Channel matrix

ChannelForStatusNotes
GitHub Releases (installers)end users✅ existsrelease.yml; cut by tagging the current release, e.g. v0.6.0
Tauri in-app updaterend users (auto-update)⬜ nextthe fastest update path for the GUI; needs a signing key
Terminal installer downloadend users✅ existsuse gh release download and install the .deb, .rpm, .AppImage, .dmg, or .msi
cargo install --gitRust devs (headless server)✅ existsinstalls irodori-server from irodori-kit, not the desktop app
crates.ioRust devs⬜ latercrates.io forbids git/path deps; all irodori-* must be published first
Homebrew cask / Scoop / wingetmac/Windows⬜ latermanifests auto-bumped from releases
AUR / FlatpakLinux⬜ laterfrom releases

Public registration text, support/privacy/disclaimer URLs, and package manager channel notes are collected in store-registration.md. Package-manager manifests are still a future packaging task; do not link to template paths until those files exist in irodori-table.

Quick terminal install

Use GitHub CLI to fetch the newest matching release asset without opening a browser. When no release tag is passed, gh release download uses the latest release:

tmp="$(mktemp -d)"
gh release download --repo hjosugi/irodori-table --pattern "*.deb" --dir "$tmp"
sudo apt install "$tmp"/*.deb

Replace the pattern and install command as needed:

PlatformPatternInstall command
Debian/Ubuntu*.debsudo apt install "$tmp"/*.deb
Fedora/RHEL*.rpmsudo dnf install "$tmp"/*.rpm
Linux portable*.AppImagechmod +x "$tmp"/*.AppImage && "$tmp"/*.AppImage
macOS*.dmgopen "$tmp"/*.dmg
Windows PowerShell*.msilaunch the downloaded .msi

The current v0.6.0 assets are:

  • Irodori.Table_0.6.0_amd64.deb
  • Irodori.Table-0.6.0-1.x86_64.rpm
  • Irodori.Table_0.6.0_amd64.AppImage
  • Irodori.Table_0.6.0_x64_en-US.msi
  • Irodori.Table_0.6.0_x64-setup.exe
  • Irodori.Table_0.6.0_aarch64.dmg
  • Irodori.Table_aarch64.app.tar.gz

On “cargo is fastest”

cargo install only installs Rust binaries. It is not a desktop app installer: the Tauri app bundles a webview, native packaging metadata, and a built frontend. Use GitHub Release installers for the desktop app.

For the headless local HTTP API, install irodori-server from irodori-kit:

cargo install --git https://github.com/hjosugi/irodori-kit --tag v0.5.0 --locked irodori-server

The old irodori-table repo command is no longer correct because irodori-server moved to irodori-kit.

  1. Tag the current release → installers ship immediately (channel already built; for the current docs, that means v0.6.0).
  2. Tauri updater for in-app auto-update (the real “get updates” for the GUI):
    • cd apps/desktop && npm run tauri signer generate → keypair.
    • Add the private key as the TAURI_SIGNING_PRIVATE_KEY GitHub Actions secret.
    • In tauri.conf.json: set bundle.createUpdaterArtifacts = true and plugins.updater with the public key + an endpoints entry pointing at the releases latest.json (tauri-action emits it per release).
  3. Keep desktop terminal installs installer-based — document gh release download paths for release assets; keep cargo install scoped to the irodori-kit headless server.
  4. Package managers (brew/scoop/winget/AUR/Flatpak) once you want them — each is a small manifest auto-updated from the GitHub Release assets.

Store and Package Registration

This is the public registration pack for Irodori Table. It collects the text, URLs, IDs, assets, and per-channel checklist needed for app stores and package managers. Keep secrets, tax/banking data, personal addresses, signing certificates, app-specific passwords, and store account IDs out of this file. Use a private local note or private archive for operational submission notes.

Current public release: 0.6.0.

Public listing metadata

FieldValue
Product nameIrodori Table
Generic nameSQL workbench / database client
App identifierdev.irodori.table
Repositoryhttps://github.com/hjosugi/irodori-table
Homepagehttps://hjosugi.github.io/irodori-docs/
Releaseshttps://github.com/hjosugi/irodori-table/releases
Documentationhttps://hjosugi.github.io/irodori-docs/
Support URLhttps://hjosugi.github.io/irodori-docs/support.html
Privacy URLhttps://hjosugi.github.io/irodori-docs/privacy.html
Disclaimer URLhttps://hjosugi.github.io/irodori-docs/disclaimer.html
LicenseMIT OR 0BSD
Source license URLhttps://github.com/hjosugi/irodori-table/blob/main/LICENSE
CategoryDeveloper Tools, Database, Productivity
Age rating baselineDeveloper tool; no user-generated public content
Paid contentNone
Ads / trackingNone in this repository
Account requiredNo hosted Irodori account required
Primary binaryTauri desktop app
Headless binaryirodori-server from irodori-kit

Package IDs

ChannelSuggested ID
Tauri app identifierdev.irodori.table
GitHub Releasesirodori-table
Homebrew caskirodori-table
Scoopirodori-table
wingetIrodori.Table
Chocolateyirodori-table
AUR binary packageirodori-table-bin
Flatpakdev.irodori.table
Snapirodori-table
crates.io binary crateNone yet; irodori-server is git-installable from irodori-kit

Listing copy

Short description:

Open-source SQL workbench for querying, inspecting, and editing data across multiple database engines.

Japanese short description:

複数のデータベースを軽く扱うためのオープンソース SQL ワークベンチ。

Long description:

Irodori Table is an open-source desktop database workbench built with Rust, Tauri, React, and CodeMirror. It focuses on fast SQL editing, schema-aware completion, Vim-friendly keyboard workflows, large-result browsing, query plan inspection, import/export, and extension-ready database connectivity.

The app is local-first and does not require a hosted Irodori account. It connects to database servers, local files, and optional AI/model providers only when configured by the user.

Irodori Table is currently a development preview. Review SQL, backups, and target connections before running destructive commands.

Japanese long description:

Irodori Table は Rust / Tauri / React / CodeMirror で作られた オープンソースのデスクトップ DB ワークベンチです。SQL 編集、schema-aware completion、Vim に配慮したキーボード操作、大きな結果セットの閲覧、query plan の確認、import/export、拡張可能な接続機構を重視しています。

ホスト型の Irodori アカウントは不要です。ユーザーが設定したデータベース、 ローカルファイル、任意の AI/model provider にだけ接続します。

現在は development preview です。破壊的な SQL を実行する前に、対象接続、 バックアップ、実行内容を必ず確認してください。

Feature bullets:

  • SQL editor with CodeMirror, formatting, snippets, command palette workflows, and Vim mode.
  • Schema navigation, metadata inspection, completion, ERD, and query plan views.
  • Result grid designed for large result sets with copy/export and row details.
  • Connection workflows for local and remote database engines.
  • Local-first desktop app with open-source release artifacts.
  • Extension-oriented architecture for future connector packages.

Keywords:

SQL, database, PostgreSQL, MySQL, SQLite, DuckDB, Tauri, Rust, developer tools, query editor, data browser, database client, Vim.

Required public pages

These pages are published from this mdBook by GitHub Pages:

The corresponding editable Markdown sources are:

Asset inventory

Current app/repository assets:

  • apps/desktop/src-tauri/icons/icon.png
  • apps/desktop/src-tauri/icons/icon.icns
  • apps/desktop/src-tauri/icons/icon.ico
  • apps/desktop/src-tauri/icons/StoreLogo.png
  • apps/desktop/src-tauri/icons/Square*.png
  • src/irodori-icon.svg

Still needed before store submission:

  • screenshots for macOS, Windows, Linux;
  • at least one 16:9 product screenshot showing the workbench;
  • optional short demo GIF/video for stores that support rich media;
  • signed/notarized release artifacts where the channel requires them.
  • committed package-manager, desktop-entry, and appstream manifest templates if those channels become active.

Channel checklist

GitHub Releases

Status: already wired by .github/workflows/release.yml.

Before publishing a release:

  • run the release dry run;
  • verify generated bindings and frontend build;
  • verify release artifacts on each platform;
  • fill release notes with known limitations and checksums;
  • publish the draft release only after artifact names and signatures are stable.

Tauri updater

Status: next.

Needed:

  • generate the Tauri updater signing key;
  • store the private key in GitHub Actions secrets;
  • commit the public key and updater endpoint in tauri.conf.json;
  • emit updater artifacts from release builds;
  • document rollback when an updater artifact is bad.

Homebrew cask

Status: planned; no template is committed yet.

Create a cask after the macOS release asset and sha256 are available. Submit to the appropriate tap or to the project-owned tap first.

Scoop

Status: planned; no template is committed yet.

Create a manifest after the Windows portable zip or installer strategy is stable. A portable zip is usually cleaner for Scoop than an interactive installer.

winget

Status: planned; no template is committed yet.

Create manifests after the Windows installer URL, sha256, publisher name, installer type, and installer switches are final.

Chocolatey

Status: planned; no template is committed yet.

Create a package if Chocolatey is a target channel. Keep the install script non-interactive and checksum-pinned.

AUR

Status: planned; no template is committed yet.

Create a PKGBUILD for irodori-table-bin once the Linux AppImage artifact name and checksum are stable.

Flatpak / Flathub

Status: design work needed.

Flatpak needs a sandboxed runtime plan, file/network permissions, appstream metadata, icon assets, and a stable build source. Treat this as a separate packaging project.

Snap

Status: design work needed.

Snap needs confinement choices, interface declarations, metadata, and store review. Treat this as separate from the existing AppImage path.

crates.io

Status: later.

The current git install path is:

cargo install --git https://github.com/hjosugi/irodori-kit --tag v0.5.0 --locked irodori-server

crates.io publication requires all irodori-* git/path dependencies to be published or removed from the publishable crate graph.

Private doc boundary

Keep this public:

  • product description, screenshots, homepage/support/privacy URLs;
  • package IDs, release URLs, checksums, and manifest plans;
  • public troubleshooting and security-disclosure process.

Keep private:

  • Apple Developer, Microsoft Partner Center, Snapcraft, Flathub, Chocolatey, and package registry account IDs;
  • legal entity details, addresses, phone numbers, tax forms, and banking data;
  • signing certificates, private keys, app-specific passwords, notarization credentials, API tokens, and recovery codes;
  • review conversation history that includes account-specific data;
  • release-blocking legal decisions that have not been approved for publication.

Official references

Re-check these before submitting because store and package-manager requirements change:

A5:SQL Mk-2 Reference Notes

Last reviewed: 2026-06-26 JST.

Source: https://a5m2.mmatsubara.com/

License Boundary

A5:SQL Mk-2 is distributed as freeware, and the official site says development happens in a private GitHub repository. Treat it as public site / public behavior reference only. Do not copy source code, screenshots, icons, UI wording, exact layout, or private implementation details.

Use this note to turn A5’s useful database-development workflows into Irodori-owned requirements and tests. Its UI is not the target.

Useful Reference Points

  • Oracle-aware workflows: A5 supports Oracle client and direct connection modes, explain plans, PL/SQL procedure execution, and PL/SQL debugging. Irodori should keep Oracle first-class instead of treating it as an enterprise afterthought.
  • Deep SQL completion: The official site calls out table/column completion driven by SQL parsing, including CTEs and subqueries. This is a good bar for deterministic completion depth.
  • Execution plan workflows: A5 exposes access plans for Oracle, DB2, SQL Server, MySQL, and PostgreSQL. Irodori should normalize explain entry points while letting each dialect keep its plan-specific details.
  • SQL designer and analysis: GUI query design is useful for users who inspect or explain joins visually. Irodori can support this later through schema-aware query builders without copying A5’s layout.
  • Multi-statement execution: A5 handles semicolon, slash-only lines, and GO-style delimiters, with run-current, run-after-caret, and run-all modes. Irodori should keep statement parsing explicit and dialect-aware.
  • Result comparison: Running the same SQL twice and comparing multiple result sets is useful for integration/regression evidence. This maps to Irodori’s result diff and saved-run history model.
  • Excel evidence workflows: A5 can export multiple result sets to Excel and use that output as test evidence. Irodori should offer structured evidence export, but avoid making Excel the only durable artifact.
  • Comment pseudo-instructions: A5 can read directives from SQL comments to set result titles and behavior. Irodori already has Query Magics; the useful idea is an auditable, visible command layer, not hidden magic that changes execution unexpectedly.
  • Table editor + spreadsheet bridge: Excel output and paste-back editing are strong operator workflows. Irodori should keep clipboard and tabular paste semantics first-class while preserving safe transaction review.
  • Bulk import/export and dummy data: CSV-compatible import/export, whole-table transfer, and dummy test-data generation are worth tracking under IO and QA.
  • ERD and definition documents: A5 can reverse-generate ER diagrams, emit DDL, print/export diagrams, and produce table definition documents. Irodori should connect ERD, DDL generation, and docs export as one schema-comprehension flow.
  • Control panels: Oracle, PostgreSQL, and MySQL management panels are useful as source-specific admin plugins, not as hardwired core UI.
  • Read-only / AI-disabled editions: A5’s separate read-only and AI-disabled downloads are a strong product signal. Irodori should expose workspace policies for read-only mode, production guardrails, and AI-disabled environments.

Not Directly Portable

  • A5 is Windows-first, while Irodori must stay cross-platform.
  • A5’s interface density and workflow shape are not the target; use the feature coverage, not the UI expression.
  • Excel-centric evidence is valuable, but Irodori should also support neutral artifacts such as CSV/JSON/Markdown/Parquet and signed run metadata.
  • Oracle/DB2/SQL Server-specific features should remain dialect plugins or adapter-owned panels, not cross-engine assumptions.
  • Comment directives must be visible, testable, and safe. Hidden behavior changes inside ordinary SQL comments can surprise users.

Candidate Backlog Follow-Ups

  • CMPL-003/004: add CTE, subquery, function/procedure, and overload completion tests that match A5’s advertised SQL input-assistance depth.
  • CMPL-007: build a dialect-normalized explain-plan view, then add dialect-specific details for Oracle, DB2, SQL Server, MySQL, and PostgreSQL.
  • EXEC/QA: add saved-run comparison and evidence export for repeated query validation, including multiple result sets.
  • AI-005: keep Query Magics visible and auditable; consider comment directives only when they render as explicit commands before execution.
  • IO: add spreadsheet paste/import previews, Excel-compatible export, and dummy-data generation without bypassing transaction review.
  • ADV-003/004: connect table designer, ERD reverse generation, DDL generation, diagram export, and table-definition documents.
  • SEC/AI: add workspace-level read-only mode, production DML/DDL confirmation, and AI-disabled policy switches.

RSQL Reference Notes

Last reviewed: 2026-06-26 JST.

Source: https://github.com/rust-dd/rsql

License Boundary

The public README currently links to LICENSE, but the LICENSE path returned 404 during review. Treat RSQL as a public README / behavior reference only until compatible license terms are verified. Do not copy source code, UI wording, assets, screenshots, or exact implementation from the repository.

Use this note to translate visible behavior and public performance claims into Irodori-owned requirements, benchmarks, and tests.

Useful Reference Points

  • Large-result rendering: RSQL positions a canvas/WebGL grid as the primary reason it can keep DOM cost flat on very large result sets. This belongs in Irodori’s PERF-001 renderer spike as a benchmark lane against the current DOM virtual grid and possible WebGPU/canvas paths.
  • Server-side cursor pagination: RSQL describes PostgreSQL cursors, fixed-size pages, a small frontend page cache, and LRU eviction. Irodori already has bounded row virtualization and disk offload; the useful next comparison is a PostgreSQL cursor-backed result window for uncapped interactive browsing.
  • Packed IPC: RSQL avoids nested JSON for large result pages by packing rows into separator-delimited strings. Irodori should benchmark this idea against current typed JSON, Arrow IPC, and any future binary page format before changing the command boundary.
  • Dual query/metadata pools: RSQL separates user query traffic from metadata traffic. Irodori should evaluate the same separation for engines where schema refresh, completion, and object-browser calls can currently compete with a long user query.
  • Rust-side result diffing: Pinned result comparison is useful for repeated query workflows. Irodori can implement its own diff model over current result pages or persisted spill handles.
  • Record view for wide rows: A form-style row drawer/modal is a strong fit for Irodori’s wide-table UX and pairs with the existing history-detail drawer idea.
  • PostgreSQL-specific admin views: pg_stat_activity, table/index stats, locks, bloat, extensions, roles, ENUMs, settings, RLS policy editing, and LISTEN/NOTIFY are good PostgreSQL plugin/admin targets, not core cross-engine UI primitives.
  • EXPLAIN JSON visualization: Irodori already tracks explain entry points; the next useful bar is an interactive plan tree with cost, estimate-vs-actual, and timing emphasis.
  • PostGIS map view: Geometry/geography detection plus a map renderer is a useful source-specific visualizer. Keep it behind result visualizer/plugin contracts so non-spatial engines do not inherit PostgreSQL-only assumptions.
  • Production guard: Color-coded environments, read-only production mode, and explicit DML/DDL confirmation are worth bringing into Irodori’s safety model.

Not Directly Portable

  • RSQL is PostgreSQL-first. Irodori must keep the adapter model broad enough for SQLite, MySQL/MariaDB, SQL Server, Oracle, warehouses, graph, document, KV, and time-series sources.
  • RSQL’s simple-query text path is attractive for speed but loses typed values at the boundary. Irodori should preserve exact typed decoding where editing, filtering, import/export, or dialect behavior requires it.
  • A separator-delimited IPC format must handle escaping, nulls, binary data, column metadata, backpressure, cancellation, and versioning before adoption.
  • Canvas/WebGL grid adoption must include accessibility, selection/copy/editing, IME behavior, high-DPI behavior, print/export parity, and software fallback.

Candidate Backlog Follow-Ups

  • PERF-001: add an RSQL-inspired canvas/WebGL grid lane and compare scroll latency, CPU, memory, selection cost, and copy/edit behavior against DOM virtualization.
  • EXEC-010/EXEC-008: prototype PostgreSQL cursor-backed result windows and run-to-file export over a shared page contract.
  • TB/API: benchmark typed JSON vs packed text vs Arrow IPC for large result pages before changing generated command payloads.
  • CMPL/BROWSE: split metadata traffic away from user query traffic where a connector supports independent pools or workers.
  • ADV-004D: implement query-result graph/map visualizers through the same extension/result visualizer contract, not one-off PostgreSQL UI branches.
  • EXEC-007: add a record-view drawer for wide rows before expanding inline edit complexity.

Support

Irodori Table is a development-preview, open-source database workbench. Use this page as the public support text for app stores, package managers, release pages, and the project site.

Public support channels

For suspected vulnerabilities, do not open a public issue with exploit details, credentials, database dumps, connection strings, customer data, or private logs. Use the private disclosure path described in SECURITY.md.

What to include in a bug report

Include enough detail to reproduce the behavior without exposing secrets:

  • Irodori Table version and install channel.
  • Operating system and architecture.
  • Database engine and version, if relevant.
  • The smallest query, schema shape, file, or workflow that reproduces the issue.
  • A screenshot or redacted error message.
  • Whether the issue happens in a clean workspace.

Do not include passwords, access tokens, private keys, database dumps, customer records, or unredacted production connection URLs.

Support scope

Supported project areas:

  • install and startup problems;
  • connection profile setup;
  • SQL editor behavior, Vim mode, shortcuts, formatting, and execution;
  • result grid display, export, and row inspection;
  • schema metadata, completion, ERD, query plan, and migration/diff workflows;
  • release artifacts, package-manager manifests, and updater behavior;
  • extension SDK and connector-extension issues.

Out of scope for public issue support:

  • recovering damaged production data;
  • tuning private database infrastructure;
  • debugging proprietary SQL or schemas that cannot be reduced to a public reproduction;
  • account, tax, certificate, and signing issues for store maintainers.

Response expectations

Irodori Table is not yet a commercial support product. Public issues are handled on a best-effort basis. Security-sensitive reports and regressions in released installers take priority over feature requests.

Store listing support text

Short support statement:

Report bugs and request help through the GitHub issue tracker. Do not share credentials or private database data in public reports.

Japanese support statement:

不具合報告とサポート依頼は GitHub Issues で受け付けます。公開 Issue に認証情報や 非公開のデータベース内容を含めないでください。

Privacy Notice

Last updated: 2026-06-28

Irodori Table is a local-first desktop database workbench. This notice is written for public store listings and package-manager submissions. It is not legal advice; review it before using it as a formal privacy policy.

Summary

  • Irodori Table does not include product analytics, advertising SDKs, or telemetry reporting in this repository.
  • The app sends data to database servers, local files, proxy/SSH endpoints, and optional AI providers only when the user configures and uses those features.
  • Connection details, editor content, query history, preferences, and generated metadata are handled locally by the desktop app unless the user explicitly exports, copies, opens, or sends them through a configured integration.
  • Password-like values must not be pasted into public support requests. The app includes redaction helpers, but users remain responsible for reviewing shared diagnostics.

Data the app may process locally

Depending on the workflow, Irodori Table may process:

  • database connection names, hostnames, ports, usernames, options, and transport settings;
  • passwords, tokens, private keys, and proxy/SSH secrets entered by the user;
  • SQL text, query parameters, query results, metadata, query plans, and edit previews;
  • local files selected for import/export or SQLite-style local database access;
  • UI preferences, themes, keybindings, result-grid settings, and query history;
  • audit entries and diagnostic reports generated by the app.

Credentials and secrets

Connection passwords entered in normal connection profiles are treated as session-only UI input. Saved transport and proxy secrets use the app’s OS keychain-backed secret store where supported. Portable connection export paths redact password fields before writing shareable connection definitions.

Users should still treat local machines, exported diagnostics, screenshots, and query files as sensitive if they contain database names, table names, SQL text, business data, or credentials.

Network behavior

Irodori Table does not need a hosted Irodori account to run. Network access is created by user actions such as:

  • connecting to configured database servers;
  • connecting through configured SSH, SOCKS, or HTTP CONNECT transport paths;
  • calling cloud database APIs or HTTP-based connectors;
  • opening project links in a browser;
  • using optional AI generation or explanation providers such as a local Ollama server or an OpenAI-compatible HTTP endpoint configured by the user;
  • downloading releases or package-manager updates from public release channels.

Third-party database providers, AI providers, proxy services, operating-system keychains, and package managers process data under their own terms.

Logs and diagnostics

The app includes privacy mode, redaction helpers, and redacted audit export support. These features are designed to reduce accidental disclosure, not to guarantee that every private value has been removed from arbitrary user content. Review any diagnostics before sharing them.

Data deletion

Users can remove local app data by deleting local Irodori Table preferences, workspaces, generated cache files, and any saved OS keychain entries for the irodori-table service. Package managers may keep their own install caches.

Contact

Use the public support channels in support.md. For suspected security issues, follow the security policy.

Disclaimer

Irodori Table is a development-preview, open-source database workbench. This page provides public disclaimer text for release pages, app stores, package manager submissions, and support references.

Development preview

The application is pre-1.0. Features, file formats, extension APIs, and supported database behavior may change. Preview builds can contain defects and incomplete connectors.

Database operations

Database clients can run destructive commands. Review generated SQL, edit previews, migration plans, import/export settings, and target connections before executing them. Use backups, transactions, read-only accounts, staging databases, and least-privilege credentials when working with important data.

Irodori Table does not guarantee recovery from accidental data loss, incorrect queries, migration mistakes, permission mistakes, network failures, or third-party service behavior.

AI-assisted features

AI-assisted SQL generation and explanation features can produce incomplete, incorrect, insecure, or inefficient output. Treat generated SQL and explanations as drafts. Verify syntax, permissions, query plans, and business impact before running generated statements.

If an external AI provider is configured, prompts and related schema/query context may be sent to that provider according to the user’s configuration and the provider’s own terms.

Third-party systems

Irodori Table connects to database engines, cloud APIs, SSH/proxy transports, OS keychains, package managers, and optional model providers. Those systems are owned and operated independently. Availability, compatibility, pricing, data handling, and access controls are governed by the relevant third party.

License and warranty

Project-authored code is licensed under MIT OR 0BSD unless a file says otherwise. The software is provided without warranty. See the license for the controlling license text.

Licensing

Irodori Table’s own code is dual-licensed under MIT OR 0BSD.

The intent is simple: anyone can use, copy, fork, sell, rewrite, embed, or compete with Irodori Table without asking for permission. Users can choose the familiar MIT license or the almost-no-conditions 0BSD license.

Why Dual License

  • MIT is familiar to companies, package managers, and open-source contributors.
  • 0BSD preserves the project’s “fully free to copy” goal.
  • Both avoid copyleft obligations for downstream users.
  • The SPDX expression MIT OR 0BSD works well for npm and Cargo metadata.
  • Official extension examples and templates can be reused under either path.

What This Does Not Mean

  • Dependencies keep their own licenses.
  • Extensions keep their own licenses unless their author chooses MIT OR 0BSD.
  • Reference repositories under .irodori-local/ref/ are not part of Irodori Table and keep their original licenses.
  • GPL/AGPL code can be studied, but it must not be copied into the permissive core unless the project intentionally creates a separate license boundary.
  • Commercial or proprietary reference code is off-limits unless we have explicit rights.
  • Third-party adapted code keeps its original obligations; the MIT OR 0BSD choice applies to Irodori-authored code.

Default Policy

  • New Irodori-authored code: MIT OR 0BSD.
  • Official examples and extension templates: MIT OR 0BSD.
  • Third-party copied/adapted code: avoid when possible; when used, record the source, license, and attribution requirements.
  • Core app, extension API, and SDK packages should remain permissive enough that downstream users can build anything on top.

Compatibility Classes

  • Allowed by default: MIT, 0BSD, BSD-2-Clause, BSD-3-Clause, ISC, Apache-2.0, Unlicense, CC0-1.0, and similarly permissive licenses.
  • Allowed with asset-specific review: MPL-2.0, EPL-2.0, CDDL, Creative Commons attribution licenses, and font licenses such as OFL-1.1.
  • Dependency-only review required: native drivers, parser generators, grammar packages, crypto/TLS libraries, and packages that bundle binary artifacts.
  • Blocked for the permissive core unless an explicit separate license boundary is approved: GPL, LGPL, AGPL, commercial-only terms, source-available licenses that restrict use or competition, no-license code, and unclear provenance.

Asset Rules

Asset typeAllowedNeeds reviewBlocked in the permissive core
Rust crates and npm packagesPermissive licenses listed above, declared in package metadataDual-licensed packages, native/binary packages, unusual patent termsGPL/AGPL/copyleft packages that would affect the core distribution
Official extension templates and examplesIrodori-authored MIT OR 0BSDThird-party sample code with attributionCode copied from incompatible products or commercial examples
ThemesOriginal themes or permissive VS Code themes with attributionMarketplace themes with mixed assets or unclear bundled licensesProprietary themes, copied product themes, GPL/AGPL themes for bundled distribution
Snippets and query templatesOriginal snippets, public-domain examples, permissive examples with attributionVendor docs examples when terms are unclearCopied paid-course, book, commercial-product, or incompatible OSS snippets
Icons, images, and logosOriginal assets, permissive icon sets, project-owned marksCC-BY assets with required attribution, trademarked database logos used only as nominative referencesCompetitor icons, proprietary screenshots, unlicensed web images
FontsSystem fonts, OFL-1.1, permissive fontsFont licenses with embedding or naming restrictionsCommercial fonts without redistribution rights
Tree-sitter grammars and language dataPermissive grammars with recorded licenseMixed-license grammars or generated artifactsGPL/AGPL grammars bundled into the core
Database drivers and native clientsPermissive pure-Rust or clearly redistributable driversVendor clients with platform packaging termsDrivers or SDKs that forbid redistribution, competition, or reverse engineering
Sample data and fixturesOriginal data, synthetic data, public-domain datasetsPublic datasets with attribution/share requirementsProduction data, personal data, unclear-license dumps

When in doubt, do not vendor or copy the asset. Link the public source, record the license question in the PR, and implement the Irodori behavior independently.

Path to 1.0

1.0 is the excellent, stable core — not every roadmap item. The strategic call is to ship a polished cross-platform SQL GUI for the core SQL engines and explicitly defer the exotic surfaces. Current version: 0.6.0.

P0 — blockers (must do for 1.0)

  1. Declare the supported-engine line. 1.0 = PostgreSQL, MySQL/MariaDB, SQLite, SQL Server, Oracle as supported; everything else (lakehouse, graph, vector, time-series, the long in-progress list) is experimental. Without this, 1.0 never closes.
  2. Stabilize the build. cargo build green on default features (the DB-connector feature refactor is mid-flight and only builds via --features legacy-connectors). (Done: the dev [patch] is removed and irodori-sql v0.3.0 is cut — the workspace now consumes it from the v0.3.0 tag.)
  3. Core data workflows (Phase 6): schema compare + migration preview (engine already exists in irodori-sql/schema.rs — mostly wiring), editable results + table designer + index/constraint UI, data compare + safe bulk edit.
  4. UI / theme completeness: finish THEME-001b (no hardcoded colors — all theme variables) + THEME-002 (theme import/save/switch). (In progress: the AI dialog + terminal were converted to theme variables.)
  5. Release mechanics: code signing/notarization (macOS/Windows), the Tauri in-app updater (see distribution.md), and green CI (typegen drift, unit, browser smoke, Rust tests, security).

P1 — strongly wanted

  • PERF-001: settle the result-grid rendering path; prove 1M-row smoothness.
  • Workspace basics polish (tab CRUD, history search, saved queries, per-tab connection binding).
  • Git graph hardening (commit actions, branch ops).
  • AI generation stays opt-in + audited (it’s optional, never required).

Defer to post-1.0

Lakehouse/Iceberg, Snowflake full-auth, Neo4j/graph, InfluxDB/time-series, vector DBs, the headless API as a shipped product, the extension registry (local SDK is enough), team/workspace sync — and keep the new integrated terminal and local AI generation behind feature flags so they don’t gate 1.0.

Schema compare + migration preview (highest functional leverage; the diff/migration engine already exists in irodori-sql), then build-stabilization + theme completeness.

Appendix: Internal Runbooks

These pages are useful for maintainers and release planning, but they are not part of the first-read user path.

Engine Cheatsheets

One page per database that answers, fast: how do I connect from Irodori, what is the query model, and what are the per-engine quirks I will trip on. These are the human-facing, copy-pasteable companion to the deeper docs/engine-syntax-reference.md (driver/decoding internals) and docs/data-source-support-status.md (coverage).

Each cheatsheet is meant to be generated from the local knowledge inputs in irodori-table (knowledge/cheatsheets/*.json plus the knowledge DB). Until a page is generated, it may be hand-seeded and marked <!-- seed -->. Generator ownership and drift rules are tracked in repository-boundaries.md.

Index

CheatsheetEngine(s) coveredStatus
neo4j.mdNeo4j (graph, Bolt/Cypher); Memgraph notesSeed (flagship)
postgres.mdPostgreSQL (+ Cockroach/Yugabyte/Redshift/Timescale/Neon)Generated (knowledge/cheatsheets/postgres.json)
questdb.mdQuestDB (Postgres wire + time-series SQL extensions)Seed
mysql.mdMySQL / MariaDB / TiDBPlanned
sqlite.mdSQLitePlanned
sqlserver.mdSQL ServerPlanned
duckdb.mdDuckDBPlanned
mongodb.mdMongoDBPlanned

New cheatsheets are added only for engines that are at least Wired in docs/data-source-support-status.md. An engine that is “Recognized, no connector” or “Not registered” gets a row in the support-status doc, not a cheatsheet, until it can actually connect.

Page format (the template every cheatsheet follows)

Keep sections in this order so the generator can produce them deterministically and so readers build muscle memory:

  1. At a glance — wire/driver, default port, query language, Irodori support status, and a one-line “what makes this engine different”.
  2. Connect — Irodori connection fields and the raw URL/DSN form, with a minimal working example.
  3. Query model — what you type, what comes back, row cap behavior.
  4. Essential statements — a tight, runnable set of the 80%-case queries.
  5. Introspection — how to list objects the way Irodori’s object browser does.
  6. Irodori-specific behavior — mapping/quirks unique to how this app handles the engine (decoding, object-browser mapping, what’s not implemented yet).
  7. Gotchas — the small number of things that actually bite people.
  8. Sources — the knowledge/sources.json ids the page was generated from.

The Sources footer is load-bearing: it ties each page back to the official docs in the knowledge registry so a refresh can detect when a page is stale.

PostgreSQL Cheatsheet

At a glance

Wire / driverPostgres / sqlx PgPool
Adapterapps/desktop/src-tauri/src/db/postgres.rs
Default port5432
Query languageSQL (PostgreSQL dialect)
Irodori statusVerified — see docs/data-source-support-status.md
What’s differentThe wire-compatible engines (CockroachDB, YugabyteDB, Redshift, TimescaleDB, Neon) reuse this exact path; only the default port and a few catalog quirks differ.

Connect

Irodori accepts either a raw url/DSN or structured fields.

FieldExample
urlpostgres://user:pass@host:5432/db
host / port127.0.0.1 / 5432
user / passwordpostgres / postgres
databasepostgres

Wire-compatible variants change only the port: CockroachDB 26257 (?sslmode=disable for insecure), YugabyteDB YSQL 5433, Redshift 5439.

Query model

  • You type SQL; rows come back as a table.
  • Rows are capped at max_rows (default 10,000) and flagged truncated: true when more remain. Add LIMIT for large scans.
  • Version: select version().
  • Multiple statements return multiple result sets.

Essential statements

-- Read with paging
SELECT id, name FROM customers ORDER BY id LIMIT 50 OFFSET 0;

-- Parameters use $1, $2 (positional)
SELECT * FROM orders WHERE tier = $1 AND created_at >= $2;

-- Upsert
INSERT INTO customers (id, name) VALUES (1, 'Ada')
  ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name;

-- JSON / JSONB
SELECT data->>'email' AS email FROM events WHERE data @> '{"tier":"gold"}';

-- CTE
WITH recent AS (SELECT * FROM orders WHERE created_at > now() - interval '7 days')
SELECT tier, count(*) FROM recent GROUP BY tier;
  • Identifier quoting: ANSI double quotes — "My Col"; escape " by doubling.
  • Paging: LIMIT n OFFSET m.

Introspection

-- Schemas, tables, columns via the standard catalog
SELECT table_schema, table_name FROM information_schema.tables
  WHERE table_schema NOT IN ('pg_catalog','information_schema');
SELECT column_name, data_type FROM information_schema.columns
  WHERE table_name = 'customers';

-- psql-style shortcuts (also work through Irodori's editor)
SELECT version();

Irodori’s object browser reads schemas, tables, views, columns, indexes, and constraints through the Postgres information-schema metamodel.

Irodori-specific behavior

  • Decoding: NUMERIC→string (exact, via BigDecimal), TIMESTAMPTZ→RFC3339, TIMESTAMP/DATE/TIME→string, JSON/JSONB→object, UUID→string, BYTEA\xHEX, bool/ints/floats native.
  • Arrays decode best-effort to text today (rich array decode is a follow-up).
  • CockroachDB omits some OID-typed system columns that tools expect.
  • The deep driver/decoding reference is docs/engine-syntax-reference.md.

Gotchas

  • Use positional parameters ($1) — not string interpolation.
  • now() is transaction-time; use clock_timestamp() for wall-clock.
  • Wire-compatible engines share this adapter, so a Postgres-only feature may behave differently on CockroachDB/Yugabyte/Redshift — check the per-engine quirks.

Sources

Generated from knowledge/sources.json:

  • postgres-docs-current — https://www.postgresql.org/docs/current/
  • postgres-release-notes-current — https://www.postgresql.org/docs/current/release.html

Neo4j Cheatsheet

At a glance

Wire / driverBolt / neo4rs (pure Rust)
Adapterapps/desktop/src-tauri/src/db/neo4j.rs
Default port7687 (Bolt)
Query languageCypher (not SQL)
Irodori statusWired (graph) — see docs/data-source-support-status.md
What’s differentData is nodes and relationships, not rows. You query patterns with Cypher and get back records of nodes/relationships/scalars.

Connect

Irodori accepts either a raw url or structured fields. Defaults match the adapter: host 127.0.0.1, port 7687, user neo4j, database neo4j, empty password.

FieldExampleNotes
urlbolt://127.0.0.1:7687Overrides host/port. Use neo4j:// for routing/cluster, bolt+s:// / neo4j+s:// for TLS.
host / port127.0.0.1 / 7687Used only when url is empty; adapter builds bolt://host:port.
userneo4j
passwordpassword
databaseneo4jThe Neo4j database name (4.x+ multi-db).

Minimal raw form: bolt://neo4j:password@127.0.0.1:7687 (database selected separately, defaults to neo4j).

Query model

  • You type Cypher; Irodori runs it through Graph::execute.
  • Results become a table: columns = the ordered union of RETURN keys across records; each record fills known columns, missing keys → null.
  • Node / relationship / path values render as JSON cells (their properties).
  • Rows are capped at max_rows (default 10,000); the result is flagged truncated: true when more remain. Add an explicit LIMIT for large graphs.
  • Version: Irodori reads it via CALL dbms.components().

Essential statements

-- Read a pattern
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE m.released >= 2000
RETURN p.name AS actor, m.title AS movie
ORDER BY m.released DESC
LIMIT 25;

-- Parameters (preferred over string interpolation)
MATCH (p:Person {name: $name}) RETURN p;

-- Create / upsert
CREATE (p:Person {name: 'Ada', born: 1815});
MERGE (p:Person {name: 'Ada'})            -- match-or-create on the key
  ON CREATE SET p.born = 1815
  ON MATCH  SET p.seen = timestamp();

-- Relate
MATCH (a:Person {name:'Ada'}), (m:Movie {title:'Analytics'})
MERGE (a)-[r:ACTED_IN {role:'self'}]->(m);

-- Aggregate / collect
MATCH (p:Person)-[:ACTED_IN]->(m)
RETURN p.name, count(m) AS films, collect(m.title) AS titles
ORDER BY films DESC;

-- Pipe with WITH, expand lists with UNWIND
UNWIND [1,2,3] AS n RETURN n*n AS square;

-- Variable-length path
MATCH path = (a:Person {name:'Ada'})-[:KNOWS*1..3]-(b:Person)
RETURN b.name, length(path) AS hops;

-- Delete safely (detach removes attached relationships)
MATCH (p:Person {name:'Ada'}) DETACH DELETE p;

Clause order to memorize: MATCHWHEREWITHRETURNORDER BYSKIPLIMIT. WITH is the pipe between query parts (it also gates WHERE after aggregation).

Introspection

This is exactly what Irodori’s object browser runs against Neo4j:

CALL db.labels()              YIELD label              RETURN label;            -- node labels
CALL db.relationshipTypes()   YIELD relationshipType   RETURN relationshipType; -- rel types
CALL db.propertyKeys()        YIELD propertyKey        RETURN propertyKey;      -- property keys
SHOW INDEXES;                 -- indexes (4.x+)
SHOW CONSTRAINTS;             -- constraints (4.x+)
CALL dbms.components();       -- server name / version / edition

Indexes & constraints you will create often:

CREATE INDEX person_name IF NOT EXISTS FOR (p:Person) ON (p.name);
CREATE CONSTRAINT person_name_unique IF NOT EXISTS
  FOR (p:Person) REQUIRE p.name IS UNIQUE;

Irodori-specific behavior

  • Object browser mapping (neo4j.rs::metadata): node labels are shown as “tables” and relationship types as “views”. Their “columns” are the property keys, sampled with MATCH (n:\Label`) UNWIND keys(n) AS key RETURN DISTINCT key LIMIT 100`. This is a sampling pass, so a property that only appears on a few nodes can be missed — treat the column list as representative, not exhaustive.
  • Results are tabular today. Node/relationship records show as JSON property cells. A query-result graph visualization is a planned shared capability (P1, see docs/data-source-coverage-strategy.md → Graph), not yet in the UI.
  • No SQL. Engines like advanced filters / inline editing that assume relational semantics do not apply; use Cypher SET / MERGE / DELETE.

Gotchas

  • Use parameters ($name) instead of string-building queries — safer and lets the planner cache the plan.
  • MATCH ... DELETE fails if the node still has relationships; use DETACH DELETE.
  • Backtick labels/types with special characters: :`Order Line`.
  • MERGE matches on the whole patternMERGE (p:Person {name:'Ada', born:1815}) with a different born creates a second node. Merge on the key, then SET.
  • Bolt over TLS needs the +s scheme (bolt+s:// / neo4j+s://).

Memgraph speaks Bolt + Cypher and is in the DbEngine enum (memgraph) but is currently “recognized, no connector” — most of this page applies once it is routed through the Neo4j/Bolt adapter and verified.

Sources

Generated from knowledge/sources.json:

  • neo4j-cypher-manual — https://neo4j.com/docs/cypher-manual/current/
  • neo4j-browser-docs — https://neo4j.com/docs/browser/

QuestDB Cheatsheet

At a glance

Wire / driverPostgreSQL wire protocol / sqlx PgPool
Adapterapps/desktop/src-tauri/src/db/postgres.rs
Default port8812
Query languageSQL with QuestDB time-series extensions
Irodori statusWired through the Postgres-compatible path - see data-source-support-status
What’s differentQuerying is SQL-like, but the high-value features are designated timestamps, SAMPLE BY, LATEST ON, ASOF JOIN, TOLERANCE, and metadata functions such as tables().

Connect

Irodori accepts either a raw url/DSN or structured fields. QuestDB’s PostgreSQL wire endpoint defaults to port 8812.

FieldExampleNotes
urlpostgres://admin:quest@127.0.0.1:8812/qdbUses the Postgres-compatible driver path.
host / port127.0.0.1 / 8812Used only when url is empty.
user / passwordadmin / questAdjust for your QuestDB deployment.
databaseqdbCommon default for QuestDB PGWire connections.

Query model

  • You type SQL and Irodori renders rows as a normal result table.
  • Multiple statements use the same execution path as Postgres-compatible engines.
  • For time-series queries, prefer tables with a designated timestamp. It is the timestamp QuestDB uses for SAMPLE BY, LATEST ON, and time-series joins.
  • Add explicit time predicates early. Common QuestDB examples use timestamp IN '2026-01-01', BETWEEN, or bounded FROM ... TO inside SAMPLE BY.

Essential statements

-- Recent rows
SELECT *
FROM trades
WHERE timestamp IN '2026-01-01'
ORDER BY timestamp DESC
LIMIT 100;

-- Downsample by a fixed interval
SELECT timestamp, symbol, avg(price) AS avg_price, count() AS rows
FROM trades
WHERE timestamp IN '2026-01-01'
SAMPLE BY 1h;

-- Downsample with explicit output range and fill
SELECT timestamp, avg(price) AS avg_price
FROM trades
WHERE timestamp BETWEEN '2026-01-01T00:00:00Z' AND '2026-01-02T00:00:00Z'
SAMPLE BY 5m
  FROM '2026-01-01T00:00:00Z' TO '2026-01-02T00:00:00Z'
  FILL(PREV)
  ALIGN TO CALENDAR TIME ZONE 'UTC';

-- Latest row per series key
SELECT symbol, timestamp, price
FROM trades
WHERE timestamp IN '2026-01-01'
LATEST ON timestamp PARTITION BY symbol;

-- As-of join: each left row gets the nearest right row at or before its time
SELECT t.timestamp, t.symbol, t.price, q.bid_price, q.ask_price
FROM trades t
ASOF JOIN quotes q ON (symbol);

-- As-of join with a bounded lookback window
SELECT t.timestamp, t.symbol, t.price, q.bid_price
FROM trades t
ASOF JOIN quotes q ON (symbol) TOLERANCE 50T
WHERE t.timestamp IN '2026-01-01';

SAMPLE BY

Shape to memorize:

SELECT timestamp_column, aggregate(...)
FROM table_name
[WHERE ...]
SAMPLE BY 5m
  [FROM 'start' TO 'end']
  [FILL(NULL | PREV | LINEAR | constant)]
  [ALIGN TO CALENDAR [TIME ZONE 'UTC'] [WITH OFFSET '...']
   | ALIGN TO FIRST OBSERVATION];

Operational notes:

  • SAMPLE BY needs a designated timestamp column.
  • Use FROM ... TO when you want missing intervals to appear in the result.
  • FILL decides how empty buckets are represented. Common choices are NULL, PREV, LINEAR, or a constant.
  • FILL comes before ALIGN.
  • Calendar alignment is the default mental model for reporting buckets; first observation alignment is useful when the first row should define bucket edges.

ASOF JOIN

Common forms:

-- Keyless: nearest right timestamp at or before the left timestamp
SELECT *
FROM left_table
ASOF JOIN right_table;

-- Keyed: match both time and one or more series keys
SELECT *
FROM left_table l
ASOF JOIN right_table r ON (symbol);

-- Limit stale matches
SELECT *
FROM left_table l
ASOF JOIN right_table r ON (symbol) TOLERANCE 1s;

Operational notes:

  • The time comparison is implicit: QuestDB uses designated timestamps from the left and right inputs.
  • Add ON (...) when a join must match by series key, such as symbol.
  • TOLERANCE limits how far back QuestDB can look in the right-side input.
  • If a subquery should join on a non-designated timestamp, order it by that timestamp so QuestDB can use it as the time axis.

LATEST ON

SELECT *
FROM balances
LATEST ON ts PARTITION BY cust_id;

SELECT cust_id, balance_ccy, balance
FROM balances
LATEST ON ts PARTITION BY cust_id, balance_ccy;

Use LATEST ON for “most recent row per key”. The order of WHERE and LATEST ON matters. Without parentheses, a WHERE before LATEST ON filters first and then picks the latest row. Wrap a LATEST ON query in parentheses when you want to pick latest first and filter afterward.

Introspection

-- Table inventory, row counts, timestamp range, WAL state
SELECT table_name,
       table_row_count,
       table_min_timestamp,
       table_max_timestamp,
       walEnabled,
       wal_pending_row_count
FROM tables()
ORDER BY table_max_timestamp DESC
LIMIT 20;

-- Partitions and disk footprint
SELECT *
FROM table_partitions('trades')
ORDER BY minTimestamp DESC;

SELECT size_pretty(sum(diskSize)) AS disk_size
FROM table_partitions('trades');

-- Active queries
SELECT *
FROM query_activity();

-- Materialized views
SELECT *
FROM materialized_views();

Irodori-specific behavior

  • QuestDB is exposed as the questdb engine but uses the same Postgres-wire execution path as postgres.rs.
  • The SQL editor completion is QuestDB-aware for SAMPLE BY, LATEST ON, ASOF JOIN, TOLERANCE, FILL, and metadata helpers.
  • Default snippets include sampleby, samplebyfill, lateston, asofjoin, asofjointol, and qdbmeta.
  • Generic result handling still applies: rows render as tables, and large result sets should use explicit LIMIT or bounded time predicates.

Gotchas

  • Do not treat QuestDB as “just Postgres”. Query syntax is compatible enough for the wire protocol, but time-series SQL extensions are QuestDB-specific.
  • SAMPLE BY and LATEST ON work best with a designated timestamp. If you are querying imported or derived data without one, use subqueries carefully.
  • FILL(NONE) is exclusive; do not mix it with other fill strategies.
  • For ASOF JOIN, forgetting ON (symbol) can silently join across different series because only time is considered.
  • Use TOLERANCE when stale right-side values would be dangerous.
  • Prefer partition/time-bounded reads in production. Unbounded scans over hot time-series tables can be very large.

Sources

  • QuestDB SAMPLE BY docs - https://questdb.com/docs/query/sql/sample-by/
  • QuestDB ASOF JOIN docs - https://questdb.com/docs/query/sql/asof-join/
  • QuestDB LATEST ON docs - https://questdb.com/docs/query/sql/latest-on/
  • QuestDB metadata functions - https://questdb.com/docs/query/functions/meta/
  • Irodori support matrix - data-source-support-status

0000 - ADR Process

Status: accepted

Context

Irodori docs include architecture decisions that explain durable product and implementation choices. The series started at 0001, but contributors need a stable place that explains how new decisions are named, scoped, and updated.

Decision

Use Architecture Decision Records under src/adr/ for decisions that affect public architecture, repo boundaries, extension contracts, release policy, or long-lived developer workflow.

ADR files follow this naming convention:

NNNN-short-title.md

Use four digits, keep titles lowercase with hyphens, and never renumber existing ADRs. Each ADR should include:

  • status: proposed, accepted, superseded, or rejected;
  • context: the problem or constraint that forced a decision;
  • decision: the chosen rule or direction;
  • consequences: tradeoffs and follow-up work.

When a decision changes, add a new ADR and mark the old one as superseded with a link to the replacement. Small clarifications can be edited in place when they do not change the decision.

Consequences

  • Contributors have a documented process before adding 0002 and later ADRs.
  • Historical decisions stay stable and linkable.
  • Public docs can explain why the implementation is shaped a certain way without mixing durable decisions into transient roadmap pages.

ADR 0001 — SQL Editor Stack (engine · highlighting · formatter)

  • Status: Accepted implementation direction; last reconciled 2026-06-25 JST.
  • Backlog: decides EDIT-001; gates EDIT-002 (highlighting) and EDIT-008 (formatter). Related: THEME-001, EDIT-005 (Vim), EDIT-007 (SQL-aware selection), completion-and-ai-strategy.md.
  • Current implementation snapshot: CM6 editor exists, sql-formatter is wired, and desktop schema-aware completion is smoke-tested from live metadata. The shared completion contract for local API and future hosts remains open.

Context

At the time this ADR was opened, the query editor was a plain <textarea> in apps/desktop/src/App.tsx with hand-rolled SQL helpers (statementDelimiters, dollarTagAt, compactSql). The first CM6 implementation now exists. The ADR records the path to production-quality dialect-aware syntax highlighting and SQL formatting, while preserving room for deterministic completion, Vim, multi-cursor, and large-file performance.

Constraints from existing docs:

  • Multi-dialect, multi-language mandate. Not just SQL: Cypher, time-series SQL/native, document/KV, search DSLs, warehouse dialects (completion-and-ai-strategy.md).
  • Host portability. A future move off the Tauri WebView to a native Rust GUI must not be foreclosed; keep the semantic layer and completion contracts portable across hosts.
  • License. Core is MIT OR 0BSD; every editor dependency must be permissive and compatible.
  • “Tree-sitter where strong, dialect fallback.” EDIT-002 already commits to this wording; SQL tree-sitter grammar quality varies by dialect.

Decision

  1. Editor host → CodeMirror 6 (MIT).
  2. Highlighting → two layers: CM6 Lezer (@codemirror/lang-sql) as the paint layer now; web-tree-sitter (WASM) as the semantic/parse layer (completion context, outline, SQL-aware selection, folding).
  3. Formatter → sql-formatter v15 (MIT), pluggable and dialect-mapped; a CST-based formatter is a later v2.

Reference-project evidence (verified 2026-06-22)

ClientEditorLicense postureTakeaway
Beekeeper StudioCodeMirror 6 (incl. CM6 Vim keymap)GPL → behavior onlyThe OSS daily-driver baseline runs CM6 + Vim.
Outerbase / LibSQL StudioCodeMirror 6 + @codemirror/lang-sql (dialect select, function-hint, autocomplete)AGPL → behavior onlyClosest web-DB-GUI peer; CM6 carries dialect highlighting + completion.
ZequelMonacoElastic → behavior onlyMonaco is viable but heavier; the outlier.
DBeaver / DataGripNative per-dialect parsers (Eclipse / IntelliJ PSI)DBeaver Apache-2.0 (adaptable)Informs the per-engine SQLDialect concept, not the web host.

→ Among web/Electron DB clients — the closest architectural peers to a Tauri WebView — CodeMirror 6 is the dominant, proven choice.

Why CodeMirror 6 over Monaco / native

CriterionCodeMirror 6MonacoNative + tree-sitter only
Bundle / startup in a WebViewSmall, modular, tree-shakeableLarge (~MBs), worker-basedn/a (no web)
Deep completion customizationFirst-class extension APIPossible but opinionatedFull control, all DIY
Vim quality@codemirror/vim, proven by BeekeeperAvailable, less idiomaticDIY
Dialect highlighting out of the box@codemirror/lang-sql: PG/MySQL/MariaDB/MSSQL/SQLiteTextMate/Monarch, more wiringDIY
“Perfect quickly”BestMediumWorst
Future native-host moveRe-host neededRe-host neededAlready native

Net: CM6 is the fastest route to a production-quality editor now, and the semantic layer (below) is what we carry across a possible native move — not the host.

Why tree-sitter as the semantic layer

Yes — but as parse-context, not the day-1 paint layer.

  • The CM6 Lezer grammar paints instantly and is themeable today. The SQL tree-sitter ecosystem is fragmented per dialect (DerekStride/tree-sitter-sql is MySQL-leaning; tree-sitter-postgres is generated from the PG18 Bison grammar; BigQuery is a separate grammar; Oracle PL/SQL coverage is weak). There is no single solid multi-dialect SQL tree-sitter grammar, so painting from tree-sitter now would be slower to perfect and patchy — contradicting “perfect quickly.”
  • Tree-sitter earns its place for incremental CST → semantic features: completion scope (aliases, CTEs, recursive CTEs, derived tables, subqueries, lateral joins), document outline, SQL-aware selection expansion (token → expression → clause → statement, EDIT-007), and folding. Its uniform grammar interface also extends to Cypher and other non-SQL languages the strategy targets.
  • Portability dividend: web-tree-sitter runs in the WebView today and the same grammars run in native Rust later → this is the layer that survives a host move.
  • Integration boundary: keep tree-sitter decoupled from CM painting initially. Only bridge tree-sitter captures into CM highlighting later if it clearly beats Lezer for a given dialect (this realizes EDIT-002’s “Tree-sitter where the grammar is solid, with a dialect fallback”).

Formatter detail (EDIT-008)

  • sql-formatter v15.8.1 (MIT, ~500 dependents) covers Postgres, MySQL, MariaDB, TiDB, SQLite (sql), T-SQL, PL/SQL, Redshift, DuckDB, ClickHouse, Snowflake, Spark, Trino, BigQuery, DB2 — a near-exact match to Irodori’s engine list. Map DbEngine → sql-formatter language.
  • Wire a format hook + “Format SQL” command; formatter choice is configurable (EDIT-008 requires this). Comment-toggle and bracket-matching ride CM6 built-ins.
  • v2: a CST/tree-sitter-driven formatter for dialect-perfect output where sql-formatter falls short (e.g. exotic PL/SQL, vendor extensions).

Consequences / risks

  • Frontend deps: CM6 packages and sql-formatter are part of the current editor path; web-tree-sitter + per-dialect grammar .wasm remain pending for the semantic layer. Benchmark bundle size and large-file responsiveness (EDIT-001 done-when).
  • THEME-001 is the real prerequisite for EDIT-002: map Lezer highlight tags (and, later, tree-sitter captures) into one normalized theme model — do not assume TextMate-only scopes.
  • Verify each per-dialect tree-sitter grammar’s license individually before bundling (keep the core MIT OR 0BSD-clean).

Rejected

  • Monaco — heavier bundle, worker model, harder deep-customization of completion/Vim; better suited to full IDEs.
  • Native / tree-sitter-only editor now — rebuilding text editing, IME, accessibility, and selection from scratch is too slow to “perfect quickly”; revisit only if/when the host goes native.
  • Keep <textarea> + highlight overlay — does not scale to highlighting + completion + Vim + multi-cursor.

First steps (prototype = EDIT-001 done-when)

  1. Done — CM6 editor in apps/desktop (src/SqlEditor.tsx), @codemirror/lang-sql dialect bound to the active DbEngine via a Compartment; basicSetup brings line numbers, bracket matching, history, active-line, and keyword autocomplete. tsc clean, vite build green.

  2. Donesql-formatter wired behind the toolbar “Format SQL” action, dialect-mapped per engine.

  3. Partial — Vim mode smoke is covered in browser Playwright (@replit/codemirror-vim toggle + CM Vim panel). Remaining: large-file responsiveness (5–20k lines) and a focused Vim behavior suite for motions/operators/registers; full Tauri runtime smoke still needs a Tauri runner.

  4. Pendingweb-tree-sitter + one grammar (Postgres) as a non-painting outline/selection spike to de-risk the semantic layer.

  5. Open / revalidate — schema-aware completion: product status should be treated as keyword/basic autocomplete only until active DatabaseMetadata produces user-facing schema/table/column suggestions through a tested shared completion contract. That contract should feed CM6 now and future hosts later; tree-sitter scope resolution layers on after the schema/table/column baseline is proven.

Bundle note: CM6 adds ~ to the client bundle (961 kB raw / 293 kB gzip total with React + lucide). Acceptable for a desktop shell; revisit code-splitting if it grows.