KRINT desktop app
KRINT ships two ways from one codebase:
| Distribution | Database | Auth | Use case |
|---|---|---|---|
Docker image (docs/self-host.md) | PostgreSQL | Keycloak (OIDC) | Servers, multi-user, always-on |
| Desktop app (this doc) | SQLite | bundled mock OIDC | Single user on their own machine |
The desktop build is a Tauri v2 window wrapped around the same KRINT.API binary the Docker image runs. The API serves the SPA + its OIDC config itself (Production MapFallbackToFile), so the webview just points at the local backend - no separate frontend build, no API changes.
How it works
On launch the desktop shell (src-tauri/src/lib.rs):
- Creates an app-data dir and a stable
vault.key(AES-256, generated once, reused). - Starts a tiny in-process OIDC issuer (
src-tauri/src/oidc.rs) that auto-issues tokens (no login screen) - zero-config local sign-in, no Docker or Java needed. - Spawns
KRINT.APIas a sidecar withDatabase__Provider=Sqliteand the SQLite file in the app-data dir. - Waits for the API to log
Application started, then navigates the window tohttp://127.0.0.1:5111/. - On exit, kills the API child; the in-process OIDC issuer stops with the app.
Why Docker is still required
KRINT provisions database instances as sibling Docker containers, so the desktop app - like the server - needs a Docker engine (Docker Desktop on Windows/macOS) running on the host. Auth, however, no longer needs Docker: it's the in-process issuer above.
Prerequisites
- Rust + the Tauri system deps for your OS (see https://v2.tauri.app/start/prerequisites/).
- The .NET 10 SDK.
- Bun (frontend build) and Node.js (runs the sidecar publish script).
- Docker Desktop / a running Docker engine (for provisioning database instances).
Install JS deps and the Tauri CLI once:
bun installBuild & run
The sidecar (KRINT.API) is published self-contained and copied into src-tauri/binaries/ with the target-triple suffix Tauri expects. This runs automatically via beforeBuildCommand, or manually:
bun run publish:sidecar # publishes for the host triple
# cross-publish a specific runtime:
RID=osx-arm64 bun run publish:sidecarThen:
bun run desktop:dev # dev window
bun run desktop:build # installers under src-tauri/target/release/bundle/Portable single exe (Windows)
For a no-install build, compile with the portable Cargo feature. It embeds the API sidecar + resources into krint-desktop.exe and self-extracts them to %APPDATA%/app.krint.desktop/runtime-<version>/ on first launch - one ~150 MB double-clickable file:
bun run publish:sidecar # produce the sidecar + wwwroot to embed
cargo build --release --features portable --manifest-path src-tauri/Cargo.toml
# -> src-tauri/target/release/krint-desktop.exe (rename to KRINT.exe)The release workflow builds and uploads this as KRINT-portable-win-x64.exe alongside the NSIS installer.
First run also needs app icons. Generate them once with
bun run tauri icon path/to/icon.png(writes intosrc-tauri/icons/, which is gitignored).
.NET RID ↔ Tauri target triple
| Platform | .NET RID | Tauri triple suffix |
|---|---|---|
| Windows x64 | win-x64 | x86_64-pc-windows-msvc |
| macOS Apple Silicon | osx-arm64 | aarch64-apple-darwin |
| macOS Intel | osx-x64 | x86_64-apple-darwin |
| Linux x64 | linux-x64 | x86_64-unknown-linux-gnu |
Find the host triple with rustc --print host-tuple.
Auto-updates
The app checks for updates on launch (tauri-plugin-updater). If a newer signed release is available it downloads, installs, and restarts - replacing the whole bundle (API sidecar + resources included), so one update covers everything.
How it fits together:
tauri.conf.json→plugins.updater.endpointspoints athttps://github.com/PianoNic/KRINT/releases/latest/download/latest.json, andpubkeyholds the public signing key.bundle.createUpdaterArtifacts: truemakestauri buildemit signed updater bundles +.sig.- The release workflow signs them and uploads
latest.json(the update manifest). - Windows ships the NSIS
.exeinstaller only (per-user, x64 + arm64, best updater support); MSI is intentionally not built. - Updatable formats: AppImage (Linux), NSIS
.exe(Windows), .app (macOS)..deb/.rpmare install-only (no self-update) - that's a Tauri limitation, not ours.
One-time setup (required before releases self-update)
- Generate a signing keypair:bash
bunx tauri signer generate -w ~/.tauri/krint-updater.key - Put the public key into
tauri.conf.json→plugins.updater.pubkey(replaces theREPLACE_WITH_...placeholder). The public key is safe to commit. - Add the private key + its password as GitHub Actions secrets (never commit them):
TAURI_SIGNING_PRIVATE_KEYTAURI_SIGNING_PRIVATE_KEY_PASSWORD
Until the real pubkey + secrets are in place, signed builds (tauri build) and auto-update won't work - the rest of the desktop build still does.
Notes / TODO
- Keep ICU: the sidecar is published without
InvariantGlobalizationbecause the MSSQL client needs ICU. Single-file self-contained bundles ICU by default. - Ports (
5111API,18080OIDC) are currently fixed insrc-tauri/src/lib.rs. Binding the API to port0and parsing the chosen port from stdout would avoid collisions - a good follow-up. - The desktop SQLite database lives in the OS app-data dir (e.g.
%APPDATA%/app.krint.desktop,~/Library/Application Support/app.krint.desktop,~/.local/share/app.krint.desktop).