Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ coverage
storage/*.sqlite*
storage/prompt-bank/*
storage
!storage/README.md
!storage/README.md

# Tauri / Rust
src-tauri/target
src-tauri/gen
src-tauri/Cargo.lock
1 change: 1 addition & 0 deletions .secretlintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.next/**
node_modules/**
pnpm-lock.yaml
src-tauri/target/**
29 changes: 17 additions & 12 deletions docs/phases/PHASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ App Shell, App Sidebar, Artifact Link, Auth Controls, Border Glow, Chart Area In
- Comprehensive demo mode

**What is notably absent:**
- Desktop application (Tauri) — zero implementation
- Desktop application (Tauri) — scaffold complete, distributable builds not yet produced
- Test coverage — 1 test file with 23 assertions out of 34 modules
- Real embedding models — current vectors are deterministic char-code-based
- Structured logging / observability
Expand Down Expand Up @@ -438,32 +438,37 @@ Current state: basic health signals exist (stale pipelines, review debt, weak pr

---

## Phase 16 — Desktop application `NOT STARTED`
## Phase 16 — Desktop application `SUBSTANTIALLY COMPLETE`

**Goal:** Wrap OpenTrust in a native desktop application using Tauri v2.

**Note:** This was originally Phase 8.5 (parallel with memory work). It has been renumbered to reflect that it has zero implementation and is no longer an upstream prerequisite — the memory layer was built without it.
**Note:** This was originally Phase 8.5 (parallel with memory work). Renumbered to reflect actual priority. The Tier 1 scaffold and Tier 2 native integrations are now implemented.

Primary design doc: `docs/DESKTOP-APPLICATION-PLAN.md`

### Tier 1 — Minimal desktop app `[required]`
- [ ] `[required]` Tauri v2 project scaffold (`src-tauri/`)
- [ ] `[required]` Next.js static export configuration (`output: 'export'`)
- [ ] `[required]` Tauri window configuration (title, size, platform targets)
- [ ] `[required]` Sidecar Node process for the memory runtime
- [ ] `[required]` Desktop-appropriate database path resolution (`$APPDATA` / `~/Library/Application Support`)
- [x] `[required]` Tauri v2 project scaffold (`src-tauri/`) — Cargo.toml, build.rs, tauri.conf.json, capabilities, icons
- [x] `[required]` Next.js standalone output configuration (`output: 'standalone'` for sidecar-compatible server.js)
- [x] `[required]` Tauri window configuration (1280×860 default, 900×600 min, centered, resizable)
- [x] `[required]` Sidecar Node process for the memory runtime (port auto-selection, readiness polling, env-based config)
- [x] `[required]` Desktop-appropriate database path resolution via `OPENTRUST_DB_PATH` env + Tauri `app_data_dir()`
- [ ] `[required]` Basic macOS `.dmg` build
- [ ] `[required]` Basic Windows `.msi` / `.exe` build
- [ ] `[required]` Basic Linux `.AppImage` / `.deb` build

### Tier 2 — Native integrations `[optional]`
- [ ] `[optional]` System tray icon with health status indicator
- [ ] `[optional]` Tray menu: open dashboard, run ingestion, check health, quit
- [x] `[optional]` System tray icon with health status indicator
- [x] `[optional]` Tray menu: open dashboard, memory health, quit
- [ ] `[optional]` Native file dialog for database selection / backup export
- [ ] `[optional]` OS notifications for ingestion completion and health alerts
- [x] `[optional]` OS notifications plugin wired (tauri-plugin-notification on Rust + JS)
- [ ] `[optional]` Global keyboard shortcut to open OpenTrust
- [ ] `[optional]` Deep link support (`opentrust://` protocol)

### Cross-cutting desktop support
- [x] Desktop detection utility (`lib/opentrust/desktop.ts` — `isDesktop()` / `isWeb()`)
- [x] Secretlint ignore for Rust build artifacts (`src-tauri/target/**`)
- [x] Gitignore for Tauri build artifacts (target, gen, Cargo.lock)

### Tier 3 — Distribution `[stretch]`
- [ ] `[stretch]` Auto-update via `tauri-plugin-updater`
- [ ] `[stretch]` Code signing for macOS (Developer ID) and Windows (Authenticode)
Expand Down Expand Up @@ -534,7 +539,7 @@ The core local-first evidence platform is complete end-to-end: ingest, normalize

1. **Phase 9 was built before prerequisites were met.** The execution pipeline recommended deferring memory-layer expansion until Tauri, persistence, and reliable run completion were stable. Phase 9 was implemented anyway. The result is solid, and the gating advice should be considered superseded.

2. **Desktop application (originally Phase 8.5) has zero implementation.** It was framed as a parallel workstream that "can proceed now" but no work was done. Renumbered to Phase 16 to reflect its actual priority position.
2. **Desktop application (originally Phase 8.5) now has a working scaffold.** The Tauri v2 project, sidecar architecture, system tray, desktop DB path resolution, and notification plugin are implemented. Distributable builds remain. Renumbered to Phase 16.

3. **Memory table migrations are runtime-based, not file-based.** The Phase 9 implementation plan specified `db/` migration files. The actual implementation uses `ensureMigrated()` in `db.ts`. This works but diverges from the documented approach.

Expand Down
23 changes: 21 additions & 2 deletions lib/opentrust/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,27 @@ import { existsSync, mkdirSync, readFileSync } from "node:fs";
import path from "node:path";

const repoRoot = process.cwd();
const storageDir = path.join(repoRoot, "storage");
const dbPath = path.join(storageDir, "opentrust.sqlite");

/**
* Resolve the database path.
*
* When running as a Tauri desktop app (OPENTRUST_DB_PATH is set by the
* Rust sidecar launcher), use the platform-appropriate Application Support
* / AppData path. Otherwise fall back to the local `storage/` directory
* for development and CLI usage.
*/
function resolveDbPath(): string {
const envPath = process.env.OPENTRUST_DB_PATH;
if (envPath) return envPath;
return path.join(repoRoot, "storage", "opentrust.sqlite");
}

function resolveStorageDir(): string {
return path.dirname(resolveDbPath());
}

const storageDir = resolveStorageDir();
const dbPath = resolveDbPath();
const migrationPath = path.join(repoRoot, "db", "0001_init.sql");

const globalForDb = globalThis as unknown as {
Expand Down
21 changes: 21 additions & 0 deletions lib/opentrust/desktop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Desktop environment detection and Tauri IPC helpers.
*
* These are safe to import from both server and client code — they guard
* against `window` / `__TAURI_INTERNALS__` not existing.
*/

/** Returns true when running inside the Tauri webview shell. */
export function isDesktop(): boolean {
if (typeof window === "undefined") {
// Server-side: check the env flag set by the sidecar launcher
return process.env.OPENTRUST_DESKTOP === "1";
}
// Client-side: Tauri injects this global
return "__TAURI_INTERNALS__" in window;
}

/** Returns true when running as a normal browser-served web app. */
export function isWeb(): boolean {
return !isDesktop();
}
3 changes: 3 additions & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import type { NextConfig } from "next";

const nextConfig: NextConfig = {
serverExternalPackages: ["better-sqlite3", "sqlite-vec"],
// Standalone output produces a self-contained server.js suitable for
// the Tauri sidecar. In development this has no effect on `next dev`.
output: "standalone",
};

export default nextConfig;
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
"ingest:cron": "tsx scripts/ingest-cron.ts",
"index:semantic": "tsx scripts/index-semantic.ts",
"secrets:check": "secretlint --secretlintrc .secretlintrc.json \"**/*\"",
"tauri": "tauri",
"tauri:dev": "tauri dev",
"tauri:build": "tauri build",
"prepare": "husky"
},
"dependencies": {
Expand All @@ -30,6 +33,10 @@
"@radix-ui/react-dialog": "^1.1.15",
"@tabler/icons-react": "^3.40.0",
"@tanstack/react-table": "^8.21.3",
"@tauri-apps/api": "^2.10.1",
"@tauri-apps/plugin-notification": "^2.3.3",
"@tauri-apps/plugin-process": "^2.3.1",
"@tauri-apps/plugin-shell": "^2.3.5",
"better-sqlite3": "^12.8.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
Expand All @@ -53,6 +60,7 @@
"@secretlint/core": "^11.4.0",
"@secretlint/secretlint-rule-preset-recommend": "^11.4.0",
"@tailwindcss/postcss": "^4.2.2",
"@tauri-apps/cli": "^2.10.1",
"@types/better-sqlite3": "^7.6.13",
"@types/node": "^22.19.15",
"@types/react": "^19.2.14",
Expand Down
164 changes: 164 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[package]
name = "opentrust"
version = "0.1.0"
description = "OpenTrust — Local-first OpenClaw memory layer"
authors = ["OpenTrust"]
edition = "2021"

[lib]
name = "opentrust_lib"
crate-type = ["staticlib", "cdylib", "rlib"]

[build-dependencies]
tauri-build = { version = "2", features = [] }

[dependencies]
tauri = { version = "2", features = ["tray-icon"] }
tauri-plugin-shell = "2"
tauri-plugin-process = "2"
tauri-plugin-notification = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
reqwest = { version = "0.12", features = ["blocking"] }
tokio = { version = "1", features = ["full"] }
portpicker = "0.1"

[profile.release]
panic = "abort"
codegen-units = 1
lto = true
opt-level = "s"
strip = true
Loading
Loading