Fast. Lightweight. Rust-powered.
Kairo is a Postman/Insomnia-class API client — REST, GraphQL, WebSockets, SSE, mock servers, and load testing in one app — built with React + TypeScript on the frontend and Rust + Tauri 2 on the backend.
- Why this stack
- Feature guide
- Data & storage
- Project layout
- Prerequisites
- Setup
- Building installers
- CI / automated releases
- Icons
- Known limitations / roadmap
- Tauri, not Electron. Tauri uses the OS's native webview (WebView2 on Windows, WKWebView on macOS, WebKitGTK on Linux) instead of bundling a full Chromium runtime — a multi-MB installer instead of 100MB+, and tens of MB of idle RAM instead of 150MB+.
- All heavy lifting happens in Rust, not the webview's JS thread:
- HTTP execution via
reqwest/hyperwith real phase timing (TTFB, download) — not a browserfetch(), which can't expose this and is also blocked by CORS for arbitrary APIs. - SQLite (via
rusqlite+r2d2pooling, WAL mode) for collections, requests, history, environments, and settings — scales to tens of thousands of history rows with indexed lookups, unlikelocalStorage. - Pre-request/test scripts run in an embedded QuickJS sandbox (
rquickjs) — no bundled Node.js runtime required. - WebSocket, SSE, and mock-server connections are all managed as native Rust
tasks (
tokio-tungstenite,reqwest, a smallhyper-based mock listener) that stream events to the frontend over Tauri's event bus — the webview never opens a raw socket itself.
- HTTP execution via
- React is fine here because the IPC boundary means most re-renders are local
to a panel (Params table, response tree, performance gauge) — one
invoke()per action, not chatty round trips. - Release profile tuned for size/startup: LTO, single codegen unit,
opt-level = "z", stripped symbols (seesrc-tauri/Cargo.toml).
(src/components/RequestBuilder/)
The main panel for composing a request, with the standard tab set:
| Tab | What it does |
|---|---|
| Params | Key/value query-string editor (KeyValueTable.tsx); rows sync bidirectionally with the URL bar. |
| Headers | Key/value header editor, same table component. |
| Body | none, json (Monaco editor with formatting), form-data (multipart, file + text fields via FormDataTable.tsx), url-encoded, raw, binary. |
| Auth | none, bearer, basic, api-key, oauth2 — auth values are substituted into the request the same way as environment variables. |
| Scripts | Pre-request and test scripts, each in a Monaco editor, executed in an embedded QuickJS sandbox in Rust (commands/scripts.rs) — no Node.js needed, and scripts can't touch the filesystem or network directly. |
| Settings | Per-request overrides: timeout (ms), follow redirects, max redirects, SSL certificate verification toggle. |
| Code | Live-generated request snippets in cURL, JavaScript (Fetch/Axios), Node.js, Python (Requests), Go, Rust (reqwest), Java, and PHP — regenerated on every field change, with environment variables already substituted (lib/codegen.ts). |
Also in this area:
- URL bar (
UrlBar.tsx) parses pasted cURL commands automatically and can also import one explicitly via Import cURL (CurlImportModal.tsx). - Save Request (
SaveRequestModal.tsx) saves the current tab into a collection/folder.
(src/components/ResponseViewer/)
| Tab | What it does |
|---|---|
| Response | Collapsible JSON tree (JsonTree.tsx) with click-to-copy JS path (e.g. data.users[0].name), plus a Raw / Pretty toggle and configurable indent width. |
| Headers | Response headers, tab badge shows the count. |
| Cookies | Cookies set by the response. |
| Tests | Results of the request's test script (pass/fail per assertion); tab badge shows the count. |
| Raw | Unformatted response body. |
| Preview | Rendered preview for HTML/image responses. |
The status bar (StatusBar.tsx) shows status code, total time, and response
size, colored by status class (2xx/3xx/4xx/5xx).
(src/components/PerformancePanel/)
A right-hand panel (toggleable) showing:
- A circular gauge for total response time on the most recent send.
- A TTFB / download / total breakdown.
- A rolling bar chart of response times across recent requests, filterable by
window (1m/5m/15m/1h), built with
recharts. - Response metadata: status, HTTP version, content type, content length, date.
All timings come from real Rust-side instrumentation around the reqwest call
— not performance.now() around a fetch(), which can't see TTFB.
(src/components/Sidebar/, stores/collectionStore.ts)
- Collections panel — nested folder tree of saved requests, create/rename/ delete/reorder collections and folders, drag requests between them.
- History panel — every sent request is logged (capped, indexed in SQLite) with full-text search across URL/method.
- Icon rail — left-hand navigation between Collections, Environments, History, Sockets, GraphQL, SSE, Mocks, Tests, Settings, and the About modal (bottom user icon).
(src/features/environments/, stores/environmentStore.ts)
- Two-pane Environments modal: environment list on the left, key/value variable editor on the right (also reachable as its own sidebar panel).
- Active environment is switchable from the top-right selector next to the request tabs.
{{VAR}}syntax is substituted into URL, params, headers, body, and auth fields — substitution happens in Rust (commands/env.rs) right before the request is sent, so it's correct even for scripted/dynamic values.
(stores/tabStore.ts, stores/openTabsStore.ts, src/components/Tabs/)
- Multiple request tabs open at once, each with its own unsaved-changes dot.
- Tabs (and their in-progress edits) are persisted to SQLite and restored on next launch if Restore Last Session is enabled in Settings.
(src-tauri/src/commands/import.rs, postman_parser.rs, openapi_parser.rs)
- Import from URL or Import from file, supporting:
- Postman Collection v2.1 JSON — folders, requests, auth, and bodies are walked recursively into Kairo's collection tree.
- OpenAPI / Swagger specs (JSON or YAML) — every
path/operationbecomes a request, grouped by tag into folders. - Raw cURL commands, pasted directly into the URL bar or via the Import cURL modal.
(src/features/sockets/SocketsModal.tsx, src-tauri/src/commands/ws.rs, stores/socketStore.ts)
- Connect to any
ws:///wss://URL with custom headers. - Send text or binary frames; full message log with direction (sent/received),
timestamp, and payload, streamed from a native
tokio-tungsteniteconnection over Tauri events — not the browser'sWebSocketAPI, so it isn't subject to webview CSP/mixed-content restrictions. - Multiple simultaneous connections are tracked independently by connection ID.
(src/features/sse/SseModal.tsx, src-tauri/src/commands/sse.rs)
- Connect to any SSE endpoint; events stream in as they arrive, each shown with event type, ID, and payload in a scrollable log.
- Pause/resume the stream without disconnecting, and reconnect on demand.
(src/features/graphql/GraphqlModal.tsx, stores/graphqlStore.ts, lib/graphql.ts)
- Multi-operation workspace (tabs within the modal) with Query, Variables, Headers, and Schema sub-tabs.
- Schema introspection — fetches the endpoint's schema and lists queryable/ mutable fields for reference while writing operations.
- Built-in query formatter and one-click copy.
(src/features/mocks/MockServerModal.tsx, src-tauri/src/commands/mock.rs)
- Spins up a real local HTTP server (configurable port) directly from Rust — define routes (method + path), status code, response delay, response body, and custom response headers.
- Template variables in the response body, resolved per-request:
{{$uuid}},{{$timestamp}},{{$isoTimestamp}},{{$randomInt}}/{{$randomInt(min,max)}},{{$randomFloat}},{{$randomBool}},{{$randomString}},{{$randomEmail}}. - Live request log showing every hit the mock server receives, with method, path, status, and duration.
- Routes can be toggled on/off individually without deleting them.
(src/features/tests/TestsModal.tsx, src/features/runner/RunnerModal.tsx, stores/runnerStore.ts, stores/loadTestStore.ts, src-tauri/src/commands/loadtest.rs)
Two related but distinct tools:
-
Collection Runner (
RunnerModal.tsx) — runs every request in a collection sequentially, showing pass/fail/duration per request and an overall summary. Reachable from a collection's context menu. -
Tests panel (
TestsModal.tsx), with several test shapes:- Load Test — constant virtual users for a fixed duration.
- Stress Test — ramps VUs up over time to find breaking points.
- Spike Test — sudden burst of traffic, then recovery.
- Soak Test — sustained load over an extended period (presets from 1m to 2h).
- Response Assertions — validate status code, response time threshold, and body content for a single request.
- Chain / Sequence Test — runs requests from a collection in order, passing variables between steps.
Load/stress/spike/soak tests execute in Rust (
commands/loadtest.rs) using real concurrent HTTP workers, not simulated in the webview — progress (VUs active, requests/sec, error rate, latency percentiles) streams back live via Tauri events.
(src/components/Settings/SettingsDrawer.tsx, stores/settingsStore.ts)
- Appearance — Dark / Light theme toggle, Opaque Mode (solid vs. translucent panel backgrounds).
- Font Dimension Metrics — independent font-size steppers for the Sidebar, Request Panel, and Response Panel.
- Zoom Level — independent UI scale (layout, icons, and text) per panel.
- Preferences — Restore Last Session, Default HTTP Method, default JSON formatting style (Pretty/Compact), Response Word Wrap, Confirm Before Closing (unsaved-tab guard).
- Panel sizes (sidebar width, request-editor height, performance-panel width)
are independently draggable and debounce-persisted, along with every setting
above, to the SQLite
app_settingstable so they survive a restart.
(src/features/updater/)
- Checks for new releases in the background (
useAutoUpdate.ts) using Tauri's updater plugin, and shows an in-appUpdateModal.tsxwith release notes and a one-click install-and-restart, instead of sending the user to a download page.
(src/features/about/AboutDevModal.tsx)
- Opened from the user icon at the bottom of the sidebar.
- A short note from the developer plus X/LinkedIn contact links (opened via the Tauri shell plugin so they launch the system browser, since a webview has no "open in new tab" chrome of its own).
- A small interactive easter egg: a draggable mask overlay on an image, themed for both light and dark mode.
Everything below lives in a single SQLite database file (WAL mode, connection-
pooled via r2d2), versioned with SQL migrations in src-tauri/src/db/migrations/:
- Collections & folders (with ordering)
- Saved requests
- Request history (capped, indexed for search)
- Environments & variables
- Persisted open tabs (for session restore)
- App settings (theme, fonts, zoom, panel sizes, preferences)
No data is sent anywhere — Kairo is entirely local-first; the only outbound network calls are the ones you configure (the requests you send, imports you trigger, and update checks).
kairo/
├─ src/ React + TypeScript frontend
│ ├─ components/ Sidebar, Tabs, RequestBuilder, ResponseViewer,
│ │ PerformancePanel, Settings, common (shared modals)
│ ├─ features/ environments/, sockets/, sse/, graphql/, mocks/,
│ │ tests/, runner/, updater/, about/
│ ├─ stores/ zustand stores — one per domain (tabs, settings,
│ │ environments, collections, sockets, sse, graphql,
│ │ mock, runner, load test)
│ ├─ lib/ typed IPC wrapper (api.ts), codegen, graphql helpers,
│ │ factories
│ └─ types/ shared TS types, mirrored 1:1 by Rust structs
├─ src-tauri/ Rust backend
│ ├─ src/commands/ tauri commands: http, ws, sse, mock, loadtest,
│ │ scripts, storage, env, import (+ postman/openapi parsers)
│ ├─ src/storage/ SQLite access layer, one module per entity
│ ├─ src/db/migrations/ versioned SQL migrations
│ ├─ src/models/ serde structs mirroring src/types/index.ts
│ ├─ capabilities/ Tauri permission manifests
│ └─ tauri.conf.json window + bundle target config
└─ kairo-landing/ separate Next.js marketing site (not part of the app)
- Node.js 18+
- Rust (stable, via rustup)
- Platform build tools:
- Windows: Microsoft C++ Build Tools + WebView2 (usually preinstalled on Win 10/11)
- macOS: Xcode Command Line Tools (
xcode-select --install) - Linux:
libwebkit2gtk-4.1-dev,libgtk-3-dev,libayatana-appindicator3-dev,librsvg2-dev,build-essential(Debian/Ubuntu package names)
npm install
npm run tauri dev # launches the app with hot reloadNote: some Rust-side changes (backend defaults, new Tauri commands, new capability permissions) require a full restart of
tauri dev, not just a frontend hot-reload — the Rust binary itself has to recompile.
Tauri cross-compiles best when built on each target OS (or via CI runners for each OS — GitHub Actions is the common approach for a single repo that ships to all three).
npm run tauri buildThis produces, per OS it's run on:
| OS | Artifacts | Location |
|---|---|---|
| Windows | .msi, .exe (NSIS) |
src-tauri/target/release/bundle/{msi,nsis} |
| macOS | .app, .dmg |
src-tauri/target/release/bundle/{macos,dmg} |
| Linux | .AppImage, .deb, .rpm |
src-tauri/target/release/bundle/{appimage,deb,rpm} |
Target list is already set in src-tauri/tauri.conf.json → bundle.targets.
Two workflows live in .github/workflows/:
-
ci.yml— runs on every push/PR tomain: type-checks and builds the frontend so broken TypeScript never reaches a release build. -
release.yml— builds all three platforms in parallel (Windows.msi/.exe, macOS.dmgfor both Apple Silicon and Intel, Linux.AppImage/.deb/.rpm) and attaches them as a draft GitHub Release whenever you push a version tag:git tag v1.1.2 git push --tags
Go to your repo's Releases tab, review the draft, and publish it — that's the file you hand to Windows/macOS/Linux users, each downloading only the artifact for their own OS. Typical end-to-end runtime on GitHub's free runners is 15–25 minutes across the 4 parallel jobs. No local Windows/Mac/ Linux machines needed — GitHub's hosted runners do the OS-specific compiling.
Icons live in src-tauri/icons/. To regenerate a full multi-resolution set
from a new source image:
npm run tauri icon path/to/source-1024.png- Form-data multipart body construction — the UI for adding fields exists
(
FormDataTable.tsx); the multipart encode/send path is the next increment. - DNS/connect/TLS phase timing breakdown — TTFB, download, and total are
real today; a finer-grained breakdown needs a custom
hyperconnector. - Drag-and-drop reordering in the collection tree beyond folder-level reorder is still basic.
Every feature above was designed to be additive — new SQL migrations, new command modules, new feature folders — without touching existing code paths, so extending any of them shouldn't require restructuring what's already there.# Kairo
Fast. Lightweight. Rust-powered.
Kairo is a Postman/Insomnia-class API client — REST, GraphQL, WebSockets, SSE, mock servers, and load testing in one app — built with React + TypeScript on the frontend and Rust + Tauri 2 on the backend.
- Why this stack
- Feature guide
- Data & storage
- Project layout
- Prerequisites
- Setup
- Building installers
- CI / automated releases
- Icons
- Known limitations / roadmap
- Tauri, not Electron. Tauri uses the OS's native webview (WebView2 on Windows, WKWebView on macOS, WebKitGTK on Linux) instead of bundling a full Chromium runtime — a multi-MB installer instead of 100MB+, and tens of MB of idle RAM instead of 150MB+.
- All heavy lifting happens in Rust, not the webview's JS thread:
- HTTP execution via
reqwest/hyperwith real phase timing (TTFB, download) — not a browserfetch(), which can't expose this and is also blocked by CORS for arbitrary APIs. - SQLite (via
rusqlite+r2d2pooling, WAL mode) for collections, requests, history, environments, and settings — scales to tens of thousands of history rows with indexed lookups, unlikelocalStorage. - Pre-request/test scripts run in an embedded QuickJS sandbox (
rquickjs) — no bundled Node.js runtime required. - WebSocket, SSE, and mock-server connections are all managed as native Rust
tasks (
tokio-tungstenite,reqwest, a smallhyper-based mock listener) that stream events to the frontend over Tauri's event bus — the webview never opens a raw socket itself.
- HTTP execution via
- React is fine here because the IPC boundary means most re-renders are local
to a panel (Params table, response tree, performance gauge) — one
invoke()per action, not chatty round trips. - Release profile tuned for size/startup: LTO, single codegen unit,
opt-level = "z", stripped symbols (seesrc-tauri/Cargo.toml).
(src/components/RequestBuilder/)
The main panel for composing a request, with the standard tab set:
| Tab | What it does |
|---|---|
| Params | Key/value query-string editor (KeyValueTable.tsx); rows sync bidirectionally with the URL bar. |
| Headers | Key/value header editor, same table component. |
| Body | none, json (Monaco editor with formatting), form-data (multipart, file + text fields via FormDataTable.tsx), url-encoded, raw, binary. |
| Auth | none, bearer, basic, api-key, oauth2 — auth values are substituted into the request the same way as environment variables. |
| Scripts | Pre-request and test scripts, each in a Monaco editor, executed in an embedded QuickJS sandbox in Rust (commands/scripts.rs) — no Node.js needed, and scripts can't touch the filesystem or network directly. |
| Settings | Per-request overrides: timeout (ms), follow redirects, max redirects, SSL certificate verification toggle. |
| Code | Live-generated request snippets in cURL, JavaScript (Fetch/Axios), Node.js, Python (Requests), Go, Rust (reqwest), Java, and PHP — regenerated on every field change, with environment variables already substituted (lib/codegen.ts). |
Also in this area:
- URL bar (
UrlBar.tsx) parses pasted cURL commands automatically and can also import one explicitly via Import cURL (CurlImportModal.tsx). - Save Request (
SaveRequestModal.tsx) saves the current tab into a collection/folder.
(src/components/ResponseViewer/)
| Tab | What it does |
|---|---|
| Response | Collapsible JSON tree (JsonTree.tsx) with click-to-copy JS path (e.g. data.users[0].name), plus a Raw / Pretty toggle and configurable indent width. |
| Headers | Response headers, tab badge shows the count. |
| Cookies | Cookies set by the response. |
| Tests | Results of the request's test script (pass/fail per assertion); tab badge shows the count. |
| Raw | Unformatted response body. |
| Preview | Rendered preview for HTML/image responses. |
The status bar (StatusBar.tsx) shows status code, total time, and response
size, colored by status class (2xx/3xx/4xx/5xx).
(src/components/PerformancePanel/)
A right-hand panel (toggleable) showing:
- A circular gauge for total response time on the most recent send.
- A TTFB / download / total breakdown.
- A rolling bar chart of response times across recent requests, filterable by
window (1m/5m/15m/1h), built with
recharts. - Response metadata: status, HTTP version, content type, content length, date.
All timings come from real Rust-side instrumentation around the reqwest call
— not performance.now() around a fetch(), which can't see TTFB.
(src/components/Sidebar/, stores/collectionStore.ts)
- Collections panel — nested folder tree of saved requests, create/rename/ delete/reorder collections and folders, drag requests between them.
- History panel — every sent request is logged (capped, indexed in SQLite) with full-text search across URL/method.
- Icon rail — left-hand navigation between Collections, Environments, History, Sockets, GraphQL, SSE, Mocks, Tests, Settings, and the About modal (bottom user icon).
(src/features/environments/, stores/environmentStore.ts)
- Two-pane Environments modal: environment list on the left, key/value variable editor on the right (also reachable as its own sidebar panel).
- Active environment is switchable from the top-right selector next to the request tabs.
{{VAR}}syntax is substituted into URL, params, headers, body, and auth fields — substitution happens in Rust (commands/env.rs) right before the request is sent, so it's correct even for scripted/dynamic values.
(stores/tabStore.ts, stores/openTabsStore.ts, src/components/Tabs/)
- Multiple request tabs open at once, each with its own unsaved-changes dot.
- Tabs (and their in-progress edits) are persisted to SQLite and restored on next launch if Restore Last Session is enabled in Settings.
(src-tauri/src/commands/import.rs, postman_parser.rs, openapi_parser.rs)
- Import from URL or Import from file, supporting:
- Postman Collection v2.1 JSON — folders, requests, auth, and bodies are walked recursively into Kairo's collection tree.
- OpenAPI / Swagger specs (JSON or YAML) — every
path/operationbecomes a request, grouped by tag into folders. - Raw cURL commands, pasted directly into the URL bar or via the Import cURL modal.
(src/features/sockets/SocketsModal.tsx, src-tauri/src/commands/ws.rs, stores/socketStore.ts)
- Connect to any
ws:///wss://URL with custom headers. - Send text or binary frames; full message log with direction (sent/received),
timestamp, and payload, streamed from a native
tokio-tungsteniteconnection over Tauri events — not the browser'sWebSocketAPI, so it isn't subject to webview CSP/mixed-content restrictions. - Multiple simultaneous connections are tracked independently by connection ID.
(src/features/sse/SseModal.tsx, src-tauri/src/commands/sse.rs)
- Connect to any SSE endpoint; events stream in as they arrive, each shown with event type, ID, and payload in a scrollable log.
- Pause/resume the stream without disconnecting, and reconnect on demand.
(src/features/graphql/GraphqlModal.tsx, stores/graphqlStore.ts, lib/graphql.ts)
- Multi-operation workspace (tabs within the modal) with Query, Variables, Headers, and Schema sub-tabs.
- Schema introspection — fetches the endpoint's schema and lists queryable/ mutable fields for reference while writing operations.
- Built-in query formatter and one-click copy.
(src/features/mocks/MockServerModal.tsx, src-tauri/src/commands/mock.rs)
- Spins up a real local HTTP server (configurable port) directly from Rust — define routes (method + path), status code, response delay, response body, and custom response headers.
- Template variables in the response body, resolved per-request:
{{$uuid}},{{$timestamp}},{{$isoTimestamp}},{{$randomInt}}/{{$randomInt(min,max)}},{{$randomFloat}},{{$randomBool}},{{$randomString}},{{$randomEmail}}. - Live request log showing every hit the mock server receives, with method, path, status, and duration.
- Routes can be toggled on/off individually without deleting them.
(src/features/tests/TestsModal.tsx, src/features/runner/RunnerModal.tsx, stores/runnerStore.ts, stores/loadTestStore.ts, src-tauri/src/commands/loadtest.rs)
Two related but distinct tools:
-
Collection Runner (
RunnerModal.tsx) — runs every request in a collection sequentially, showing pass/fail/duration per request and an overall summary. Reachable from a collection's context menu. -
Tests panel (
TestsModal.tsx), with several test shapes:- Load Test — constant virtual users for a fixed duration.
- Stress Test — ramps VUs up over time to find breaking points.
- Spike Test — sudden burst of traffic, then recovery.
- Soak Test — sustained load over an extended period (presets from 1m to 2h).
- Response Assertions — validate status code, response time threshold, and body content for a single request.
- Chain / Sequence Test — runs requests from a collection in order, passing variables between steps.
Load/stress/spike/soak tests execute in Rust (
commands/loadtest.rs) using real concurrent HTTP workers, not simulated in the webview — progress (VUs active, requests/sec, error rate, latency percentiles) streams back live via Tauri events.
(src/components/Settings/SettingsDrawer.tsx, stores/settingsStore.ts)
- Appearance — Dark / Light theme toggle, Opaque Mode (solid vs. translucent panel backgrounds).
- Font Dimension Metrics — independent font-size steppers for the Sidebar, Request Panel, and Response Panel.
- Zoom Level — independent UI scale (layout, icons, and text) per panel.
- Preferences — Restore Last Session, Default HTTP Method, default JSON formatting style (Pretty/Compact), Response Word Wrap, Confirm Before Closing (unsaved-tab guard).
- Panel sizes (sidebar width, request-editor height, performance-panel width)
are independently draggable and debounce-persisted, along with every setting
above, to the SQLite
app_settingstable so they survive a restart.
(src/features/updater/)
- Checks for new releases in the background (
useAutoUpdate.ts) using Tauri's updater plugin, and shows an in-appUpdateModal.tsxwith release notes and a one-click install-and-restart, instead of sending the user to a download page.
(src/features/about/AboutDevModal.tsx)
- Opened from the user icon at the bottom of the sidebar.
- A short note from the developer plus X/LinkedIn contact links (opened via the Tauri shell plugin so they launch the system browser, since a webview has no "open in new tab" chrome of its own).
- A small interactive easter egg: a draggable mask overlay on an image, themed for both light and dark mode.
Everything below lives in a single SQLite database file (WAL mode, connection-
pooled via r2d2), versioned with SQL migrations in src-tauri/src/db/migrations/:
- Collections & folders (with ordering)
- Saved requests
- Request history (capped, indexed for search)
- Environments & variables
- Persisted open tabs (for session restore)
- App settings (theme, fonts, zoom, panel sizes, preferences)
No data is sent anywhere — Kairo is entirely local-first; the only outbound network calls are the ones you configure (the requests you send, imports you trigger, and update checks).
kairo/
├─ src/ React + TypeScript frontend
│ ├─ components/ Sidebar, Tabs, RequestBuilder, ResponseViewer,
│ │ PerformancePanel, Settings, common (shared modals)
│ ├─ features/ environments/, sockets/, sse/, graphql/, mocks/,
│ │ tests/, runner/, updater/, about/
│ ├─ stores/ zustand stores — one per domain (tabs, settings,
│ │ environments, collections, sockets, sse, graphql,
│ │ mock, runner, load test)
│ ├─ lib/ typed IPC wrapper (api.ts), codegen, graphql helpers,
│ │ factories
│ └─ types/ shared TS types, mirrored 1:1 by Rust structs
├─ src-tauri/ Rust backend
│ ├─ src/commands/ tauri commands: http, ws, sse, mock, loadtest,
│ │ scripts, storage, env, import (+ postman/openapi parsers)
│ ├─ src/storage/ SQLite access layer, one module per entity
│ ├─ src/db/migrations/ versioned SQL migrations
│ ├─ src/models/ serde structs mirroring src/types/index.ts
│ ├─ capabilities/ Tauri permission manifests
│ └─ tauri.conf.json window + bundle target config
└─ kairo-landing/ separate Next.js marketing site (not part of the app)
- Node.js 18+
- Rust (stable, via rustup)
- Platform build tools:
- Windows: Microsoft C++ Build Tools + WebView2 (usually preinstalled on Win 10/11)
- macOS: Xcode Command Line Tools (
xcode-select --install) - Linux:
libwebkit2gtk-4.1-dev,libgtk-3-dev,libayatana-appindicator3-dev,librsvg2-dev,build-essential(Debian/Ubuntu package names)
npm install
npm run tauri dev # launches the app with hot reloadNote: some Rust-side changes (backend defaults, new Tauri commands, new capability permissions) require a full restart of
tauri dev, not just a frontend hot-reload — the Rust binary itself has to recompile.
Tauri cross-compiles best when built on each target OS (or via CI runners for each OS — GitHub Actions is the common approach for a single repo that ships to all three).
npm run tauri buildThis produces, per OS it's run on:
| OS | Artifacts | Location |
|---|---|---|
| Windows | .msi, .exe (NSIS) |
src-tauri/target/release/bundle/{msi,nsis} |
| macOS | .app, .dmg |
src-tauri/target/release/bundle/{macos,dmg} |
| Linux | .AppImage, .deb, .rpm |
src-tauri/target/release/bundle/{appimage,deb,rpm} |
Target list is already set in src-tauri/tauri.conf.json → bundle.targets.
Two workflows live in .github/workflows/:
-
ci.yml— runs on every push/PR tomain: type-checks and builds the frontend so broken TypeScript never reaches a release build. -
release.yml— builds all three platforms in parallel (Windows.msi/.exe, macOS.dmgfor both Apple Silicon and Intel, Linux.AppImage/.deb/.rpm) and attaches them as a draft GitHub Release whenever you push a version tag:git tag v1.1.2 git push --tags
Go to your repo's Releases tab, review the draft, and publish it — that's the file you hand to Windows/macOS/Linux users, each downloading only the artifact for their own OS. Typical end-to-end runtime on GitHub's free runners is 15–25 minutes across the 4 parallel jobs. No local Windows/Mac/ Linux machines needed — GitHub's hosted runners do the OS-specific compiling.
Icons live in src-tauri/icons/. To regenerate a full multi-resolution set
from a new source image:
npm run tauri icon path/to/source-1024.png- Form-data multipart body construction — the UI for adding fields exists
(
FormDataTable.tsx); the multipart encode/send path is the next increment. - DNS/connect/TLS phase timing breakdown — TTFB, download, and total are
real today; a finer-grained breakdown needs a custom
hyperconnector. - Drag-and-drop reordering in the collection tree beyond folder-level reorder is still basic.
Every feature above was designed to be additive — new SQL migrations, new command modules, new feature folders — without touching existing code paths, so extending any of them shouldn't require restructuring what's already there.