# Jan Administrator & Developer Guide

This guide is the in-depth technical reference for **Jan** — its architecture, configuration, security model, operations, and internals. It is written for administrators deploying Jan on managed machines, developers building against or contributing to Jan, and architects evaluating how it works. For end-user instructions (installing, chatting, managing models), see the companion **User Guide**.

Everything here is derived directly from the Jan source code at the documented commit. Where the source contradicts common assumptions, this guide follows the source.

---

## Table of Contents

1. [Executive Summary](#1-executive-summary)
2. [System Architecture](#2-system-architecture)
3. [Component Inventory](#3-component-inventory)
4. [Repository Structure](#4-repository-structure)
5. [System Requirements](#5-system-requirements)
6. [Build & Packaging](#6-build--packaging)
7. [Configuration Reference](#7-configuration-reference)
8. [The Tauri Command (IPC) Surface](#8-the-tauri-command-ipc-surface)
9. [Inference Engines & Plugins](#9-inference-engines--plugins)
10. [The Extension System](#10-the-extension-system)
11. [The Local API Server](#11-the-local-api-server)
12. [MCP Subsystem](#12-mcp-subsystem)
13. [Remote Provider Integration](#13-remote-provider-integration)
14. [RAG & Vector Database](#14-rag--vector-database)
15. [Security Analysis](#15-security-analysis)
16. [Operations](#16-operations)
17. [Data Model & Storage](#17-data-model--storage)
18. [Code Quality & Conventions](#18-code-quality--conventions)
19. [Potential Improvements](#19-potential-improvements)
20. [Quick Reference](#20-quick-reference)

---

## 1. Executive Summary

**Jan** is a cross-platform desktop (and mobile) application for running large language models locally and connecting to cloud providers, built by **Menlo Research**. It is a privacy-first, open-source alternative to cloud chat applications.

### Architecture at a Glance

Jan is a **Tauri 2** application: a **Rust backend** (`src-tauri/`) hosting a **React 19 frontend** (`web-app/`) in a system WebView. Inference is provided by:

- **llama.cpp** — for GGUF models on all platforms, managed by an in-process **router** (a single `llama-server` process that loads/unloads models on demand), driven by the Rust `tauri-plugin-llamacpp` plus the TypeScript `@janhq/llamacpp-extension`.
- **MLX** — for Apple Silicon, via a bundled Swift `mlx-server` binary, the `tauri-plugin-mlx` Rust plugin, and `@janhq/mlx-extension`.
- **Cloud providers** — OpenAI, Anthropic, Google, Mistral, Groq, xAI, OpenRouter, and more, via the Vercel **AI SDK** in the frontend.

It additionally exposes an **OpenAI-compatible HTTP server** (default `127.0.0.1:1337`), a **`jan` CLI** binary (a separate Cargo build target), an **MCP host** for tool use, and a built-in **RAG** pipeline (document parsing + a SQLite vector store).

### Key Facts

| Property | Value |
|----------|-------|
| Vendor | Menlo Research |
| App version (Cargo) | `0.8.2` (`src-tauri/Cargo.toml`) |
| Web-app version | `0.7.6` |
| `@janhq/core` version | `0.1.10` |
| Primary language mix | TypeScript (frontend/extensions), Rust (backend/plugins), Swift (MLX server), Python (AutoQA) |
| Desktop framework | Tauri `2.8.5` |
| Frontend | React `19.0.0`, TanStack Router, Vite, Tailwind CSS 4, Zustand |
| Local engine | llama.cpp (GGUF) + MLX (Apple Silicon) |
| Default local API | `http://127.0.0.1:1337/v1` |
| CLI server | `http://localhost:6767/v1` |
| License | Apache-2.0 (root `LICENSE`/README); component crates MIT; `@janhq/core` AGPL-3.0 — see [§15](#15-security-analysis) |
| Platforms | Windows, macOS (universal), Linux (deb/AppImage/Flatpak); Android + iOS (Tauri mobile) |
| Rust toolchain | `1.77.2` (MSRV) |
| Node / Yarn | Node ≥ 20.0.0, Yarn `4.5.3` |

> [!NOTE]
> Version numbers diverge by component because Jan is a monorepo of independently versioned packages. The user-facing "app version" tracks the `src-tauri/Cargo.toml` package (`Jan 0.8.2`). The CLI was introduced in 0.7.8, router mode in 0.8.0, and the in-app OpenClaw agent was removed in 0.7.9.

---

## 2. System Architecture

### High-Level Diagram

```text
┌─────────────────────────────────────────────────────────────────────┐
│  Jan Desktop (Tauri 2 application)                                    │
│                                                                       │
│  ┌───────────────────────────────┐    Tauri IPC    ┌───────────────┐ │
│  │  Frontend  (web-app/, React)  │ <────invoke────> │ Rust backend  │ │
│  │  - TanStack Router routes     │                  │ (src-tauri/)  │ │
│  │  - Zustand stores             │                  │  core/{app,   │ │
│  │  - services/ (typed IPC)      │                  │  server,mcp,  │ │
│  │  - AI SDK (cloud providers)   │                  │  threads,...} │ │
│  │  - Extensions (@janhq/*)      │                  └──────┬────────┘ │
│  └───────────────────────────────┘                         │          │
│                                                  Tauri plugins (Rust): │
│                                          llamacpp │ mlx │ hardware │   │
│                                          rag │ vector-db              │
└──────────────────────────────────────────────────┼───────────────────┘
                                                    │ spawns / HTTP
                  ┌─────────────────────────────────┼──────────────────┐
                  ▼                    ▼             ▼                  ▼
        llama-server (router)   mlx-server     MCP servers       Cloud provider
        GGUF models             (Swift, macOS) (npx/python)       APIs (HTTPS)
        loads/unloads on demand                                  OpenAI/Anthropic/…

  Local OpenAI-compatible API server  ──►  127.0.0.1:1337/v1
  `jan` CLI binary (separate build)   ──►  localhost:6767/v1
```

### Process Model

Jan is a **single-instance** application (`tauri-plugin-single-instance`); launching a second time focuses the existing window rather than starting a new process (`src-tauri/src/lib.rs`).

The Rust backend (`app_lib::run()` in `src-tauri/src/lib.rs`) builds the Tauri app, registers plugins, manages a shared `AppState`, and wires lifecycle handlers. Local inference runs in **child processes**:

- **llama.cpp router** — a single `llama-server` process spawned and supervised by `tauri-plugin-llamacpp`. It serves all GGUF models, loading/unloading on demand.
- **mlx-server** — a Swift binary (macOS only) spawned by `tauri-plugin-mlx`.
- **MCP servers** — external `npx`/`python`/`node`/`bun` processes spawned and monitored by the MCP subsystem with exponential-backoff restart.

### Graceful Shutdown

The backend installs careful lifecycle handlers in `app.run(...)` (`src-tauri/src/lib.rs`):

- On `CloseRequested`/`ExitRequested`, if the llamacpp router is running and models are busy, Jan **prevents close**, emits `llamacpp-busy-on-exit`, and runs `handle_graceful_exit()` which repeatedly calls `try_graceful_stop_router()` until no model is busy.
- On the final `Exit` event, it runs `background_cleanup_mcp_servers()` (10s timeout), `cleanup_llama_processes()`, and (macOS) `cleanup_mlx_processes()` synchronously so no child process is orphaned.

> [!NOTE]
> This busy-aware shutdown is why closing Jan during active generation can pause — the app is draining in-flight model work and stopping the router cleanly before exit.

### Frontend ↔ Backend Boundary

The React frontend never touches the filesystem or spawns processes directly. It calls Rust **commands** via Tauri's `invoke('command_name', args)`. The frontend's `web-app/src/services/` layer provides typed wrappers around these commands; `web-app/src/lib/` holds the heavier logic (model factory, MCP orchestrator, custom chat transport). The complete command surface is enumerated in [§8](#8-the-tauri-command-ipc-surface).

---

## 3. Component Inventory

| Component | Path | Language | Role |
|-----------|------|----------|------|
| **Rust backend** | `src-tauri/src/` | Rust | Tauri app, IPC commands, server, MCP, threads, downloads, updater |
| **`jan-cli` binary** | `src-tauri/src/bin/jan-cli.rs` + `src/core/cli/` | Rust | Standalone CLI (built with `cli` feature) |
| **Frontend** | `web-app/src/` | TS/React | UI, routing, state, cloud providers |
| **Core SDK** | `core/src/` (`@janhq/core` 0.1.10) | TS | Extension API, type definitions, browser/event abstractions |
| **llamacpp plugin** | `src-tauri/plugins/tauri-plugin-llamacpp` | Rust | Spawn/supervise llama-server router; model load |
| **mlx plugin** | `src-tauri/plugins/tauri-plugin-mlx` | Rust | MLX server management (macOS) |
| **hardware plugin** | `src-tauri/plugins/tauri-plugin-hardware` | Rust | GPU/CPU/RAM info, NVML + Vulkan probing |
| **rag plugin** | `src-tauri/plugins/tauri-plugin-rag` | Rust | Document parsing (PDF/DOCX/XLSX/CSV/HTML) |
| **vector-db plugin** | `src-tauri/plugins/tauri-plugin-vector-db` | Rust | SQLite vector store (`rusqlite` + sqlite-vec) |
| **llamacpp extension** | `extensions/llamacpp-extension` (1.0.1) | TS | Engine logic: model load/unload, chat completion |
| **mlx extension** | `extensions/mlx-extension` (1.0.0) | TS | MLX inference orchestration |
| **assistant extension** | `extensions/assistant-extension` (1.0.2) | TS | Default AI assistant + tools |
| **conversational extension** | `extensions/conversational-extension` (1.0.0) | TS | Thread/state persistence on the filesystem |
| **download extension** | `extensions/download-extension` (1.0.0) | TS | File/model download management |
| **rag extension** | `extensions/rag-extension` (0.1.0) | TS | RAG orchestration across parser/embeddings/vector DB |
| **vector-db extension** | `extensions/vector-db-extension` (0.1.0) | TS | Vector store integration (sqlite-vec w/ linear fallback) |
| **MLX server** | `mlx-server/` (Swift) | Swift | Apple-Silicon inference server binary |
| **jan-utils crate** | `src-tauri/utils/` | Rust | Shared helpers: crypto, network, paths, CUDA discovery |
| **Docs site** | `docs/` (Next.js/Nextra) | TS | Public documentation at jan.ai |
| **AutoQA** | `autoqa/` (Python) | Python | End-to-end UI test harness (CUA agent + ReportPortal) |

---

## 4. Repository Structure

```text
jan/
├── core/                       # @janhq/core — extension SDK + type defs
│   └── src/
│       ├── browser/            # extension base classes, engines, models
│       └── types/              # api, assistant, config, engine, inference,
│                               #   mcp, message, model, setting, thread
├── web-app/                    # React 19 frontend (@janhq/web-app)
│   └── src/
│       ├── routes/             # TanStack Router pages (settings, hub, threads)
│       ├── services/           # typed IPC wrappers (app, core, models, mcp, …)
│       ├── lib/                # model-factory, mcp-orchestrator, transport
│       ├── providers/          # React context providers
│       ├── constants/          # providers, models, routes, localStorage keys
│       ├── stores/             # Zustand stores
│       └── locales/            # i18n (18 languages)
├── extensions/                 # @janhq/* engine + feature extensions
│   ├── llamacpp-extension/
│   ├── mlx-extension/
│   ├── assistant-extension/
│   ├── conversational-extension/
│   ├── download-extension/
│   ├── rag-extension/
│   └── vector-db-extension/
├── src-tauri/                  # Rust backend (Tauri app + CLI)
│   ├── src/
│   │   ├── lib.rs              # app_lib::run() — Tauri builder, invoke_handler!
│   │   ├── main.rs             # binary entry; fix_path_env; deps-analyzer hook
│   │   ├── bin/jan-cli.rs      # CLI entry (feature = "cli")
│   │   └── core/               # app, server, mcp, threads, downloads,
│   │                           #   extensions, filesystem, system, updater, cli
│   ├── plugins/                # tauri-plugin-{llamacpp,mlx,hardware,rag,vector-db}
│   ├── utils/                  # jan-utils crate (crypto, network, path, system)
│   ├── capabilities/           # Tauri capability/ACL definitions
│   └── gen/android/            # Android (Tauri mobile) project
├── mlx-server/                 # Swift MLX inference server
├── docs/                       # Next.js/Nextra documentation site
├── autoqa/                     # Python E2E UI test harness
└── flatpak/                    # Flatpak packaging
```

### Triage Notes (for source navigators)

- The `web-app/src/lib/` directory (~216 KB across 27 files) holds the heaviest frontend logic: `model-factory.ts`, `custom-chat-transport.ts`, `remoteModelCatalog.ts`, `ai-model.ts`, and the `mcp-orchestrator/`.
- `web-app/src/routes/settings/` (~154 KB) contains the settings UI, including the local-api-server page.
- The authoritative backend command list is the `tauri::generate_handler!` macro in `src-tauri/src/lib.rs` — **not** any generated index. Tauri commands are neither CLI nor HTTP routes, so static analysis under-reports them.
- The user-facing docs live as MDX under `docs/src/pages/docs/desktop/` and are the canonical source for user workflows.

---

## 5. System Requirements

### Runtime (End User)

| Platform | Minimum | GPU acceleration |
|----------|---------|------------------|
| **macOS** | 13.6+ | Metal (automatic on Apple Silicon) |
| **Windows** | 10+ | CUDA (NVIDIA), Vulkan (AMD/Intel Arc) |
| **Linux** | Most modern distros | CUDA, Vulkan |

**Memory guidance:** ~8 GB RAM for 3B models, 16 GB for 7B, 32 GB for 13B. A GPU backend is only preferred when a GPU reports **≥ 6 GB VRAM**; below that Jan runs on CPU. llama.cpp builds require **AVX2** (use a `noavx` build on older CPUs).

**GPU prerequisites (NVIDIA):** driver **470.63.01+**, CUDA Toolkit **11.7+**. On Linux additionally `gcc-11/g++-11/cpp-11` and `LD_LIBRARY_PATH` configured.

### Build (Developer)

| Tool | Version |
|------|---------|
| Node.js | ≥ 20.0.0 |
| Yarn | ≥ 4.5.3 (`packageManager: yarn@4.5.3`) |
| Make | ≥ 3.81 |
| Rust | 1.77.2+ (Tauri) |
| Metal Toolchain | macOS Apple Silicon only: `xcodebuild -downloadComponent MetalToolchain` |

---

## 6. Build & Packaging

### Quick Build

```bash
git clone https://github.com/janhq/jan
cd jan
make dev          # installs deps, builds core, launches the app
```

**Make targets:** `make dev` (full setup + launch), `make build` (production), `make test` (tests + lint), `make clean` (reset).

### Yarn Workspaces

The root `package.json` (`jan-app`, private) defines workspaces `core` and `web-app`, with `extensions/` and `src-tauri/plugins/` as nested workspace sets. Package manager is **Yarn 4.5.3** with `hoistingLimits: workspaces`.

Key scripts (`package.json`):

| Script | Purpose |
|--------|---------|
| `dev` / `dev:tauri` | Build icon, copy assets, `tauri dev` (with `IS_CLEAN=true`) |
| `build` | `build:web` then `build:tauri` |
| `build:tauri:win32` | `download:bin` → `build:cli` → `tauri build` |
| `build:tauri:linux` | `download:bin` → `build:cli` → `tauri build` (no-strip) → build AppImage |
| `build:tauri:darwin` | `download:bin` → `build:mlx-server` → `build:cli` → `tauri build --target universal-apple-darwin` |
| `build:extensions` | Build core, then each extension, packing `.tgz` into `pre-install/` |
| `build:cli` | `make build-cli` (the `jan-cli` Rust binary) |
| `build:mlx-server` | `make build-mlx-server` (Swift) |
| `ios` / `android` | `tauri {ios,android} dev` |

> [!NOTE]
> Extensions are built into `.tgz` tarballs placed in `pre-install/`, then copied into `src-tauri/resources/pre-install/` and installed into the data folder at first run (see [§10](#10-the-extension-system)). The frontend depends on `@janhq/core` as `workspace:*`, while extensions depend on a packed `../../core/package.tgz`.

### Cargo Build Targets & Features

`src-tauri/Cargo.toml` defines two binaries:

- **`Jan`** (default) — the desktop application (`crate-type = ["staticlib", "cdylib", "rlib"]`, lib name `app_lib`).
- **`jan-cli`** (`src/bin/jan-cli.rs`) — requires `--features cli`.

Cargo features:

| Feature | Effect |
|---------|--------|
| `default` | `desktop` + Tauri features (wry, tray-icon, macos-private-api, custom-protocol, …) |
| `desktop` | enables `deep-link` + `hardware` |
| `hardware` | `tauri-plugin-hardware` (GPU/CPU info) |
| `deep-link` | `tauri-plugin-deep-link` (custom URL scheme) |
| `mobile` | mobile build, adds `sqlx` (SQLite on Android/iOS) |
| `cli` | builds `jan-cli` with `env_logger`, `dialoguer`, `indicatif`, `console` |

> [!IMPORTANT]
> The `cli` feature and the default desktop feature are mutually exclusive at the source level: large parts of `lib.rs` are gated `#[cfg(not(feature = "cli"))]`. The CLI is a thin adapter (`src/core/cli/mod.rs`) that calls core logic **without** an `AppHandle`, so it can run headless.

### Release Profile

The release profile is tuned for **minimal binary size**: `opt-level = "z"`, `lto = "fat"`, `strip = "symbols"`, `codegen-units = 1`, `panic = "unwind"` (allows catching library panics), `overflow-checks = false`.

### Platform-Gated Dependencies

- **MLX** (`tauri-plugin-mlx`) is a macOS-only dependency (`cfg(target_os = "macos")`), not a Cargo feature, since it's Apple-Silicon-only.
- **Desktop-only:** `tauri-plugin-updater`, `tauri-plugin-single-instance`, `once_cell`.
- **Linux:** `zbus`, `gtk`.
- **Windows:** `windows-sys` (FileSystem).
- **HTTP TLS:** `tauri-plugin-http` is configured with `native-tls` (OS trust store) instead of the bundled rustls roots, plus `http2`, `cookies`, `unsafe-headers`, and `macos-system-configuration` (system proxy).
- **MCP transport:** the `rmcp` crate (`0.8.5`) provides MCP client transports (SSE, streamable-HTTP, child-process).

---

## 7. Configuration Reference

Jan's configuration surface spans (a) **app settings** stored in the data folder, (b) **per-model inference settings** in the router preset, (c) **environment variables**, and (d) **Tauri capabilities/ACLs**. The AST config-key scrape only catches `env::var`/`process.env` reads, so this section is assembled from the actual config-bearing source files.

### App Settings & Data Folder

The active data folder is resolved by `get_jan_data_folder_path()` (`src-tauri/src/core/app/commands.rs`) and can be relocated via the `change_app_data_folder` command. Per-platform defaults:

| Platform | Data folder | Per-user config |
|----------|-------------|-----------------|
| Windows | `%APPDATA%\Jan\data` | `%APPDATA%\Jan\settings.json` |
| macOS | `~/Library/Application Support/Jan/data` | (app support dir) |
| Linux | `~/.local/share/Jan/data` (`XDG_DATA_HOME`) | `~/.config/Jan` |

App-level state is persisted to `store.json` in the data folder via `tauri-plugin-store` (it tracks the installed `version` for migration). On Windows the data-folder location is editable directly in `settings.json` (`data_folder` field, double-backslash paths).

### Environment Variables

Read by the Rust backend / utils (`src-tauri/utils/src/`, `src/core/`):

| Variable | Read in | Purpose |
|----------|---------|---------|
| `HF_TOKEN` | CLI HuggingFace download | Bearer token for gated HF repos |
| `ENABLE_SYSTEM_TRAY_ICON` | `lib.rs` (`option_env!`) | Build-time toggle for the tray icon |
| `IS_CLEAN` | `core/setup.rs` | Dev clean-launch flag |
| `LLAMA_ARG_*` | llamacpp router | Forwarded to `llama-server` at startup (e.g. `LLAMA_ARG_HOST`) |
| `CUDA_PATH`, `CUDA_HOME`, `CUDA_PATH_V*` | `utils/system.rs` | CUDA toolkit discovery (Win/Linux) |
| `ProgramFiles` | `utils/system.rs` | Windows CUDA toolkit base path |
| `LD_LIBRARY_PATH`, `DYLD_LIBRARY_PATH`, `PATH` | `utils/system.rs` | Library path injection for inference child processes |
| `TEMP` | `utils/path.rs` | Temp dir resolution |
| `HOME`, `USERPROFILE`, `APP_NAME`, `CI` | `app/commands.rs` | Path/app-name resolution |
| `XDG_CURRENT_DESKTOP` | `core/setup.rs` | Linux desktop detection |

Frontend/build-time (`vite.config.ts`): `TAURI_DEV_HOST`, `IS_TAURI`, `IS_DEV`, `TAURI_ENV_PLATFORM`.

> [!NOTE]
> There is **no large runtime env-var config surface** like a server app. Jan is a client app: configuration is overwhelmingly the in-app settings (persisted to JSON) and the per-model router preset, not environment variables. The env vars above are mostly OS-integration and inference-process plumbing.

### llama.cpp Router Preset (`router.preset.ini`)

Per-model inference settings live in `<data-folder>/llamacpp/router.preset.ini`, generated by the desktop UI. The router applies engine-wide settings as `[*]` defaults, with per-model `[<model>]` entries overriding them. Since v0.8.0 the router is launched as:

```text
llama-server --models-preset <router.preset.ini> --models-max <N> --no-webui
```

and loads/unloads models via `POST /models/load` and `POST /models/unload`.

**Engine-wide settings** (apply to every loaded model):

| Setting | Flag | Default | Notes |
|---------|------|---------|-------|
| Continuous Batching | — | Disabled | Process multiple requests at once |
| Parallel Sequences | — | 1 | 1 enables `-kvu` single-request optimization |
| Threads | `--threads` | -1 (all cores) | Generation threads |
| Threads (Batch) | — | -1 (= Threads) | Prompt/batch threads |
| uBatch Size | `-ub` | 512 | Physical max batch size |
| Devices for Offload | `--device` | empty (auto) | e.g. `CUDA0,CUDA1` |
| GPU Split Mode | — | Layer | Multi-GPU distribution |
| Main GPU Index | — | 0 | Primary GPU |
| Flash Attention | — | Auto | Force on/off only on kernel issues |
| Disable mmap | `--no-mmap` | Disabled | Enable on crashes/pageouts |
| MLock | `--mlock` | Disabled | Pin model in RAM |
| Context Shift | — | Disabled | Trim prompt start when context fills |
| KV Cache K/V Type | `--cache-type-{k,v}` | f16 | `q8_0`/`q4_0` to save VRAM |
| KV Cache Defrag Threshold | — | 0.1 | < 0 disables (deprecated upstream) |
| Max Concurrent Models | `--models-max` | — | 1 = strict single-model; 0 = unlimited |
| Max Tokens to Predict | `-n` | -1 (infinite) | Cap generation |
| Prompt Cache RAM | `--cache-ram` | -1 (unlimited) | 0 disables |
| Cache Reuse | `--cache-reuse` | 0 (disabled) | Min matching-prefix chunk to reuse |
| Full SWA Cache | `--swa-full` | Disabled | Gemma/Mistral SWA full-context |
| Keep First N Tokens | `--keep` | 0 | Tokens kept on context shift (-1 = all) |
| RoPE Scaling Method/Factor/Freq | RoPE flags | None/1/0 | Extended-context tuning |
| Mirostat Mode/LR/Entropy | mirostat flags | Off/0.1/5 | Alternative sampling |
| Grammar File / JSON Schema File | `--grammar`/schema | empty | Constrained output |

**Per-model settings** (override engine defaults): Context Size (`ctx_size`), GPU Layers (`n_gpu_layers`), Batch Size (`batch_size`), Disable KV Offload (`no_kv_offload`), Override Tensor Buffer Type, Chat Template (custom Jinja), and sampling parameters.

> [!WARNING]
> The default KV cache type was temporarily `q8_0` in a prior release and reverted to `f16`. This is **not auto-migrated** — if models fail to load after updating to 0.8.0, manually set **KV Cache K/V Type** back to `f16`.

### Assistant Configuration (`assistants/jan/assistant.json`)

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `id` | string | `jan` | Assistant identifier |
| `name` | string | `Jan` | Display name |
| `model` | string | `*` | Allowed models (`*` = all) |
| `instructions` | string | — | Default thread system prompt |
| `tools` | array | retrieval | Available tools (currently retrieval only) |
| `tools[].settings.top_k` | number | 2 | Max retrieval results |
| `tools[].settings.chunk_size` | number | 1024 | Retrieval chunk size |
| `tools[].settings.chunk_overlap` | number | 64 | Chunk overlap |
| `file_ids` | array | — | OpenAI-compatibility field |

### Cloud Provider Defaults

The built-in provider catalog (`web-app/src/constants/providers.ts`) ships default base URLs and auth settings for: openai, azure, anthropic, openrouter, mistral, groq, xai, gemini, minimax, huggingface, nvidia — plus generic `openai-compatible`. Model capability tables (`web-app/src/constants/models.ts`) declare per-provider `supportsImages`/`supportsToolCalls`/`supportsStreaming`/`supportsJSON`. Anthropic ships custom headers (`anthropic-version: 2023-06-01`, `anthropic-dangerous-direct-browser-access: true`).

### Tauri Capabilities (ACLs)

`src-tauri/capabilities/` defines the permission set granted to the WebView and which plugin commands are reachable. Each custom plugin ships an autogenerated permission set under `plugins/<plugin>/permissions/autogenerated/commands/` (one capability per command). This is the trust boundary between the frontend and the Rust backend.

---

## 8. The Tauri Command (IPC) Surface

The complete backend API is the `tauri::generate_handler!` block in `src-tauri/src/lib.rs` (macro `invoke_commands_with_extras!`). Commands are dispatched from the frontend via `invoke('<name>', args)` and grouped by module. This is the authoritative, version-stamped list.

### Filesystem (`core::filesystem::commands`)
`join_path`, `mkdir`, `exists_sync`, `readdir_sync`, `read_file_sync`, `rm`, `mv`, `file_stat`, `write_file_sync`, `write_yaml`, `read_yaml`, `decompress`, `open_dialog`, `save_dialog`

> [!NOTE]
> The filesystem commands are annotated "Deprecate soon" in source — newer extensions are expected to move off the raw FS bridge.

### App Configuration (`core::app::commands`)
`get_app_configurations`, `get_user_home_path`, `update_app_configuration`, `get_jan_data_folder_path`, `get_configuration_file_path`, `default_data_folder_path`, `change_app_data_folder`, `app_token`

### Extensions (`core::extensions::commands`)
`get_jan_extensions_path`, `install_extensions`, `get_active_extensions`

### System (`core::system::commands`)
`relaunch`, `open_app_directory`, `open_file_explorer`, `factory_reset`, `read_logs`, `is_library_available`, `launch_claude_code_with_config`, `check_jan_cli_installed`, `install_jan_cli`, `uninstall_jan_cli`, `clear_claude_code_env`

### Server (`core::server::commands`)
`start_server`, `stop_server`, `get_server_status`

### Remote Providers (`core::server::remote_provider_commands`)
`register_provider_config`, `unregister_provider_config`, `get_provider_config`, `list_provider_configs` (+ `abort_remote_stream` on mobile)

### MCP (`core::mcp::commands`)
`get_tools`, `get_tools_for_servers`, `get_server_summaries`, `call_tool`, `cancel_tool_call`, `restart_mcp_servers`, `get_connected_servers`, `save_mcp_configs`, `get_mcp_configs`, `activate_mcp_server`, `deactivate_mcp_server`, `check_jan_browser_extension_connected`

### Threads & Messages (`core::threads::commands`)
`list_threads`, `create_thread`, `modify_thread`, `delete_thread`, `list_messages`, `create_message`, `modify_message`, `delete_message`, `get_thread_assistant`, `create_thread_assistant`, `modify_thread_assistant`

### Downloads (`core::downloads::commands`)
`download_files`, `cancel_download_task`, `pause_download_task`

### Lifecycle, Theme, Updater
`confirm_exit`; `core::setup::get_system_theme`, `set_gtk_prefer_dark`; (desktop) `core::updater::commands::check_for_app_updates`, `is_update_available`

> [!IMPORTANT]
> The `download_files` / `cancel_download_task` / `pause_download_task` commands back the model-download UI. `install_extensions` runs the extension migration on startup. `factory_reset` is the destructive reset; `change_app_data_folder` relocates the data folder. The `launch_claude_code_with_config` / `clear_claude_code_env` commands implement the Claude Code integration; `install_jan_cli` / `uninstall_jan_cli` manage the CLI binary.

---

## 9. Inference Engines & Plugins

Jan's inference is split between **Rust plugins** (process supervision, OS integration) and **TypeScript extensions** (engine logic, OpenAI-shaped chat completion). The two communicate via the guest-JS API each plugin exposes.

### llama.cpp (`tauri-plugin-llamacpp` + `@janhq/llamacpp-extension`)

The Rust plugin (`0.8.2`) manages the `llama-server` router process. Notable internals:

- **`LlamacppState`** holds the router PID (`router_pid`) and process registry. `try_graceful_stop_router()`, `cleanup_llama_processes()`, and `run_deps_analyzer_if_requested()` are exported for the host.
- **Out-of-process dependency analyzer:** `main.rs` calls `tauri_plugin_llamacpp::deps_analyzer::run_deps_analyzer_if_requested()` *before* `app_lib::run()`. The binary re-invokes itself as an lddtree helper to analyze a backend's shared-library dependencies (CUDA/Vulkan/cuDNN) out-of-process — `lddtree` is excluded on macOS because its Mach-O handling has crashed in the field.
- **Binary discovery** (`src/core/cli/mod.rs::discover_llamacpp_binary`) walks `<data>/llamacpp/backends/<version>/<backend>/`, checking `build/bin/llama-server[.exe]` then `<backend>/llama-server[.exe]`, preferring the latest version.
- **HuggingFace download** (`fetch_hf_gguf_files`, `download_hf_model`) hits `https://huggingface.co/api/models/{repo}?blobs=true&files_metadata=true`, filters `.gguf` siblings, sorts by size (smallest quant first), streams the file with SHA-256 from the LFS pointer, and writes a `model.yml`. `HF_TOKEN` is sent as a bearer token; 401/403 surface a "gated repo — set HF_TOKEN" hint.
- Crypto: `tauri-plugin-llamacpp` uses `hmac`/`sha2`/`base64` for API-key generation and `byteorder` for GGUF parsing (`src/gguf/`).

The TS extension (`extensions/llamacpp-extension`, 1.0.1) implements model load/unload and streaming chat completion. It depends on `@janhq/tauri-plugin-llamacpp-api`, `@janhq/tauri-plugin-hardware-api`, `@tauri-apps/plugin-http`, and `fetch-retry`. Error surface includes backend archive validation, model-exists checks, "Model already loaded," SSE chunk parsing ("Malformed chunk"), and `model.yml not found`.

### MLX (`tauri-plugin-mlx` + `@janhq/mlx-extension`, macOS only)

The Rust plugin (`0.8.2`) supervises the Swift `mlx-server` binary. `MlxState`/`SessionInfo`/`MlxConfig` and `load_mlx_model_impl`/`cleanup_mlx_processes` are exported. Binary discovery (`discover_mlx_binary`, macOS) checks `/Applications/Jan.app/Contents/Resources/{resources/,}bin/mlx-server`, then next to the running executable. The TS extension orchestrates port allocation, model load, and chat completion, with crash detection ("MLX model has crashed! Please reload!"). The Swift server lives in `mlx-server/Sources/MLXServer/`.

### Hardware (`tauri-plugin-hardware`)

GPU/CPU/RAM enumeration using `sysinfo`, plus desktop GPU probing: `nvml-wrapper` (NVIDIA), `vulkano`/`ash` (Vulkan), and `libloading` on Windows. Used by the Hub fit-pill calculation, GPU acceleration selection (highest-VRAM GPU), and the System Monitor. CUDA path discovery lives in `jan-utils` (`system.rs`): it scans `CUDA_PATH`/`CUDA_HOME`/`CUDA_PATH_V*`, the NVIDIA toolkit base, and common Linux paths (including Flatpak GL extension paths), then injects `LD_LIBRARY_PATH`/`PATH`/`DYLD_LIBRARY_PATH` per-platform into the inference child process.

### `jan-utils` Crate

Shared Rust helpers re-exported flat (`src-tauri/utils/src/lib.rs`):

| Module | Highlights |
|--------|-----------|
| `crypto` | `generate_app_token()` (32-char alphanumeric), `generate_api_key()` (HMAC-SHA256), `compute_file_sha256_with_cancellation()` (64 KB chunks, cancellable) |
| `network` | `ProxyConfig`, proxy validation (http/https/socks4/socks5), `is_port_available`, `generate_random_port` (3000–4000), `find_process_using_port` (lsof/netstat per OS), `is_orphaned_mcp_process` |
| `http` | `is_valid_host` (trusted-hosts allowlist incl. localhost/127.0.0.1/0.0.0.0/host.docker.internal), CORS header detection, origin host extraction |
| `path` | `normalize_path` (Windows verbatim-prefix handling), `get_short_path` (Win 8.3) |
| `system` | CUDA discovery, `setup_library_path`, `can_override_npx`/`can_override_uvx` (AVX2 check for bun), Windows `CREATE_NO_WINDOW` flags, `is_flatpak` |
| `math` | MCP exponential-backoff delay (base 1s, cap 30s, ×2, ±25% jitter) |

---

## 10. The Extension System

Jan's features are delivered as **extensions** built on `@janhq/core` (the extension SDK). Extensions are TypeScript packages that the app installs into `<data>/extensions/@janhq/` at first run.

### Install / Migration Flow

On startup (`lib.rs::setup`), `setup::install_extensions(app_handle, version_changed)` runs. It compares the stored `version` (in `store.json`) against the current app version; when they differ it (re)installs the bundled extension tarballs from `src-tauri/resources/pre-install/` (packed during `build:extensions`). MCP server configs are migrated via `setup::migrate_mcp_servers`.

### Bundled Extensions

| Extension | Product name | License | Role |
|-----------|--------------|---------|------|
| `llamacpp-extension` | llama.cpp Inference Engine | AGPL-3.0 | GGUF inference |
| `mlx-extension` | MLX Inference Engine | AGPL-3.0 | Apple-Silicon inference |
| `assistant-extension` | Jan Assistant | AGPL-3.0 | Default assistant + tools |
| `conversational-extension` | Conversational | MIT | Thread/state persistence on FS |
| `download-extension` | Download Manager | AGPL-3.0 | File/model downloads |
| `rag-extension` | RAG Tools | AGPL-3.0 | Retrieval orchestration |
| `vector-db-extension` | Vector DB | AGPL-3.0 | Vector store (sqlite-vec) |

Each extension declares `main: dist/index.js` (and some a `module:`/`node:` build), depends on the packed `@janhq/core` tarball, and is built with **rolldown** (`1.0.0-beta.1`).

### Core SDK (`@janhq/core`)

`core/src/` provides the extension API and the canonical type system (`core/src/types/`: api, assistant, config, engine, inference, mcp, message, model, setting, thread). `core/src/browser/` holds extension base classes and engine/model abstractions. Runtime deps are minimal: `rxjs` and `ulidx` (ULID generation). The SDK is published under **AGPL-3.0** and peers on React 19.

> [!NOTE]
> The engine extensions and the Rust plugins are two halves of one system. The plugin owns the OS process and exports a guest-JS API (`@janhq/tauri-plugin-*-api`, linked locally); the extension calls that API to drive inference and shapes results into OpenAI-compatible responses for the frontend.

---

## 11. The Local API Server

Implemented in `src-tauri/src/core/server/` (controlled by the `start_server`/`stop_server`/`get_server_status` commands), Jan exposes an **OpenAI-compatible HTTP API** powered by the llama.cpp router.

### Defaults & Configuration

| Setting | Default | Source-of-truth |
|---------|---------|-----------------|
| Host | `127.0.0.1` | Server config (UI) |
| Port | `1337` | Server config; **no auto-fallback** if taken |
| API prefix | `/v1` | Server config |
| API key | empty (auth off) | `Authorization: Bearer` |
| Trusted hosts | — | `jan_utils::http::is_valid_host` allowlist |
| Request timeout | — | Seconds to wait for the local model |
| Execute tools on server | Disabled | Server-side MCP tool dispatch |
| CORS | Enabled | Browser cross-origin |
| Verbose logs | Enabled | Server-log view |

The server is "ready" when logs show `JAN API listening at http://127.0.0.1:1337`. The frontend route `web-app/src/routes/local-api-server/` and `routes/settings/local-api-server.tsx` provide the UI; server logs open in a separate window (`logs-window-local-api-server`).

### Endpoints

OpenAI-shaped, under the configured prefix:

```text
POST {prefix}/v1/chat/completions     # chat (streaming + non-streaming)
```

A bundled **Swagger UI** is served from `src-tauri/static/swagger-ui/` and the OpenAPI spec lives at `docs/public/openapi/`.

### Security Posture

- **Default `127.0.0.1`** binding keeps the server local-only.
- Setting host to `0.0.0.0` exposes models to the LAN — the **API key should be set** and **Trusted Hosts** restricted. `is_valid_host` enforces an allowlist (localhost, 127.0.0.1, 0.0.0.0, host.docker.internal, plus configured hosts; `*` allows all).
- The proxy/server uses an HMAC-SHA256 API-key scheme (`jan-utils::crypto::generate_api_key(model_id, api_secret)`) and a per-session random `app_token` (`generate_app_token`, 32 chars).

> [!WARNING]
> Known issue ([#8272](https://github.com/janhq/jan/issues/8272)): on some systems (Fedora Silverblue reported) the API Server proxy can hang on local requests (a `reqwest` deadlock) while the UI chat works. If automated clients hang against `1337` but the UI is fine, suspect this path.

---

## 12. MCP Subsystem

Jan is an **MCP host**. The Rust side (`src-tauri/src/core/mcp/`) uses the `rmcp` crate to connect to MCP servers over SSE, streamable-HTTP, and child-process transports. The frontend's `web-app/src/lib/mcp-orchestrator/` coordinates tool discovery, approval, and dispatch.

### Lifecycle

- Servers are configured via `save_mcp_configs`/`get_mcp_configs` and activated/deactivated via `activate_mcp_server`/`deactivate_mcp_server`.
- `get_tools`/`get_tools_for_servers`/`get_server_summaries`/`get_connected_servers` enumerate capabilities; `call_tool`/`cancel_tool_call` execute.
- `AppState` tracks `mcp_servers`, `mcp_active_servers`, `mcp_server_pids`, `mcp_monitoring_tasks`, `mcp_settings`, `mcp_shutdown_in_progress`, and a `mcp_reconnect_notify`.
- Crashed/disconnected servers are restarted with **exponential backoff** (`jan_utils::math::calculate_exponential_backoff_delay`: 1s base, ×2, 30s cap, ±25% jitter).
- Orphan detection: `is_orphaned_mcp_process` flags stray `node`/`npx`/`bun` MCP processes for cleanup on exit.

### Runtime Dependencies

Most MCP servers are external programs spawned via `npx`/`uvx`/`node`/`python`/`bun`, so **Node.js and/or Python must be installed** on the host. Jan can override `npx` with a bundled `bun` and `uvx` with `uv` (gated on AVX2 on Intel macOS — `can_override_npx`).

### Security

Tool calls require **per-call user approval** by default (inline in the chat tool card, showing arguments) unless **Allow All MCP Tool Permissions** is enabled. Permissions are persisted under `mcpGlobalPermissions`/`toolApproval`/`toolAvailability` (localStorage keys). The threat model explicitly calls out **prompt injection** and compromised-tool risk.

> [!CAUTION]
> Enabling "Execute Tools on Server" (Local API Server advanced setting) plus "Allow All MCP Tool Permissions" removes the human-in-the-loop for tool execution over the HTTP API. For multi-user or networked deployments, keep both off unless every client and tool is trusted.

---

## 13. Remote Provider Integration

Cloud providers are integrated in the **frontend** via the Vercel **AI SDK** (`ai` v6, with `@ai-sdk/{openai,openai-compatible,anthropic,google,mistral,xai,react}`). The heavy logic lives in `web-app/src/lib/`:

- **`model-factory.ts`** — builds provider clients, handles API-key rotation ("API key rotation exhausted" when all keys 401/403), and session resolution. Errors like "No running session found for model" originate here.
- **`remoteModelCatalog.ts`** — fetches a provider's model list from its `/models` endpoint (requires `base_url`; "Catalog not supported for {provider}" / "Provider must have base_url configured").
- **`custom-chat-transport.ts`** — the streaming transport bridging AI SDK to Jan's `ServiceHub`.
- **`ai-model.ts`** — provider config validation.

The **Rust** side persists provider configs (`register_provider_config`/`get_provider_config`/`list_provider_configs`) and, on mobile, can abort remote streams (`abort_remote_stream`). Built-in provider defaults and capability tables are in `web-app/src/constants/{providers,models}.ts` ([§7](#7-configuration-reference)).

> [!NOTE]
> Anthropic is reached directly from the WebView using `anthropic-dangerous-direct-browser-access: true` and a pinned `anthropic-version` header. Provider model capabilities (tools/vision/json/streaming) are read automatically after a key is added, driving which UI affordances appear.

---

## 14. RAG & Vector Database

Jan ships a built-in retrieval pipeline so Project files and the assistant's `retrieval` tool can ground answers in user documents.

### Document Parsing (`tauri-plugin-rag`)

The Rust plugin parses many formats natively:

| Format | Crate |
|--------|-------|
| PDF | `pdf-extract` |
| DOCX / OOXML | `zip` + `quick-xml` |
| XLSX / spreadsheets | `calamine` |
| CSV | `csv` |
| HTML | `html2text` |
| Encoding detection | `chardetng` + `encoding_rs` |
| MIME sniffing | `infer` |

### Vector Store (`tauri-plugin-vector-db`)

A SQLite-backed vector store using **`rusqlite`** (`bundled`, `load_extension`) — it loads the **sqlite-vec** extension when available and falls back to **linear** similarity search otherwise (`@janhq/vector-db-extension` description). UUIDs via `uuid`, store path via `dirs`.

### Orchestration (`rag-extension`, `vector-db-extension`)

`@janhq/rag-extension` registers RAG tools and orchestrates retrieval across the parser, embeddings, and vector DB. Embeddings are produced by the llamacpp engine (an embedding model). Chunking defaults come from the assistant config (`chunk_size 1024`, `chunk_overlap 64`, `top_k 2`). Error surface includes "Vector DB extension not available," "Embedding dimension not available," and "llamacpp extension not available" (the embedding backend must be present).

> [!NOTE]
> Embedding models can be loaded from the desktop app; the CLI's `--embedding` flag is **not yet supported with llamacpp**. RAG requires both the vector-db extension and an embedding-capable llamacpp model to be available.

---

## 15. Security Analysis

### Trust Boundaries

1. **Frontend ↔ Rust backend** — the WebView can only call commands listed in `generate_handler!`, gated by Tauri **capabilities/ACLs** (`src-tauri/capabilities/`, per-plugin autogenerated permissions). The raw filesystem bridge (`core::filesystem::commands`) is broad and marked for deprecation — a notable surface.
2. **Local API server ↔ network** — default `127.0.0.1` only; LAN exposure requires explicit `0.0.0.0` + (ideally) API key + trusted-hosts allowlist (`is_valid_host`).
3. **Model ↔ MCP tools** — per-call approval by default; "Allow All" removes the human-in-the-loop. Prompt injection is the primary risk.
4. **App ↔ cloud providers** — API keys are stored in app settings and sent to provider endpoints; files/prompts leave the device for cloud models.

### Secrets & Crypto

- **App token:** 32-char alphanumeric per-session token (`generate_app_token`), held in `AppState.app_token`, used to authenticate frontend↔backend where required.
- **API keys:** HMAC-SHA256 derivation (`generate_api_key(model_id, api_secret)`).
- **File integrity:** streaming SHA-256 with cancellation for downloaded models; the GGUF `model_sha256` is recorded in `model.yml`.
- **TLS:** `tauri-plugin-http` uses **native-tls** (OS trust store) on desktop, rustls on mobile. HTTPS proxy support includes an **Ignore SSL Certificates** option (accepts self-signed certs) — a deliberate weakening for corporate proxies; document as risk.
- Provider API keys render as password fields with unobscure/copy actions in the UI.

### Network Posture

- Outbound: HuggingFace (model metadata + downloads), cloud provider APIs, MCP servers, the updater endpoint, optional analytics (PostHog). Model downloads can route through the configured HTTPS proxy.
- Inbound: only the optional local API server (`1337`) and the optional CLI server (`6767`), both loopback by default.
- Proxy validation supports http/https/socks4/socks5 and a `no_proxy` allowlist with wildcard matching.

### Update Channel

Desktop builds use `tauri-plugin-updater` (signed updates via Tauri's update mechanism, `ed25519-dalek` for signature verification is present among deps). Update reliability issues have been reported on Ubuntu ([#7429](https://github.com/janhq/jan/issues/7429)).

### Licensing (Compliance)

| Component | License |
|-----------|---------|
| Project (root `LICENSE`, README) | **Apache-2.0** (Menlo Research; attribution requested) |
| `Jan` app crate + plugins (`Cargo.toml`) | MIT (per-crate `license` field) |
| `@janhq/core` and most extensions | **AGPL-3.0** |
| `conversational-extension` | MIT |

> [!IMPORTANT]
> The license picture is **mixed**. The repository's headline license is Apache-2.0 (root `LICENSE` and README), but the `@janhq/core` SDK and most TypeScript extensions are **AGPL-3.0**, while the Rust crates carry MIT in their `Cargo.toml`. Organizations redistributing or building on Jan's extension SDK must account for AGPL-3.0 obligations. Verify the license of each component you depend on.

### Hardening Checklist

- [ ] Keep the Local API Server bound to `127.0.0.1` unless LAN access is required.
- [ ] If binding to `0.0.0.0`, set a strong **API key** and restrict **Trusted Hosts**.
- [ ] Leave **Allow All MCP Tool Permissions** off; approve tool calls individually.
- [ ] Leave **Execute Tools on Server** off for networked deployments.
- [ ] Only enable **Ignore SSL Certificates** on trusted networks.
- [ ] Vet MCP servers before adding them; they run as arbitrary local processes.
- [ ] Use local models for sensitive data; understand that cloud models transmit prompts/files.
- [ ] Keep the app updated for upstream llama.cpp security/stability fixes.

---

## 16. Operations

### Logs

All logs go to `<data>/logs/app.log` via `tauri-plugin-log` (configured in `lib.rs` at `LevelFilter::Debug`, with Stdout + Webview + Folder targets, file name `app`). Tags: `[APP]`, `[SERVER]`, `[SPECS]`, and `[llamacpp-router]` / `[llamacpp-router stdout]` for engine output. The `read_logs` command exposes logs to the UI; logs are retained 24h when submitted to support.

```bash
# Tail logs
tail -n 100 ~/Library/Application\ Support/Jan/data/logs/app.log    # macOS
```

### Port Management

| Service | Default | Notes |
|---------|---------|-------|
| Local API Server | `1337` | No auto-fallback; loopback by default |
| CLI server | `6767` | `jan serve`/`launch` |
| Docs dev server | `3001` | Development only |

Find/free a stuck port:

```bash
netstat -an | grep 1337            # macOS
netstat -ano | find "1337"         # Windows
netstat -anpe | grep "1337"        # Linux
```

The backend itself uses `find_process_using_port` (lsof/netstat) and `generate_random_port` (3000–4000) for internal model-server port allocation.

### Factory Reset & Data Relocation

- **Factory reset:** `factory_reset` command (UI: Settings → General). Deletes all models, chats, settings. Requires typing `RESET`.
- **Relocate data:** `change_app_data_folder` duplicates data to a new empty directory and restarts. On Windows, `settings.json` `data_folder` can be edited manually if the UI fails.

### Updates

Desktop auto-update via `tauri-plugin-updater` (`check_for_app_updates`, `is_update_available`). The llama.cpp **engine** updates separately (backend downloads under `<data>/llamacpp/backends/`), with optional **Auto Update Engine**.

### Recovery Runbook

| Symptom | Action |
|---------|--------|
| Won't start | Delete/rename `store.json`; check single-instance; check port 1337 |
| Model won't load | Check `[llamacpp-router]` logs; reduce context/GPU layers; update backend; re-download GGUF |
| GPU unused | Verify driver/CUDA; Settings → Hardware → GPU Acceleration; restart to re-probe (esp. after Linux sleep) |
| API server hangs (clients) | Suspect the reqwest deadlock ([#8272](https://github.com/janhq/jan/issues/8272)); restart server |
| Corrupt build | Uninstall + delete data folder + reinstall latest |
| MCP server won't connect | Ensure Node/Python installed; verify command/args; restart Jan |

### Telemetry

Optional product analytics via **PostHog** (`posthog-js`), gated by `productAnalytic`/`productAnalyticPrompt` (localStorage) and **Settings → Privacy**. The docs site has separate GTM/PostHog env vars (`GTM_ID`, `POSTHOG_KEY`, `POSTHOG_HOST`).

---

## 17. Data Model & Storage

### On-Disk Layout

See the User Guide for the full tree. Key entities:

- **Threads** — `<data>/threads/<thread_id>/` with `messages.jsonl` (one message per line) and `thread.json` (metadata: assistant clone, model settings, title, timestamps). Message IDs are ULIDs.
- **Models** — `<data>/{llamacpp,mlx}/models/<id>/model.yml` (+ the GGUF file for llamacpp). `ModelYml` fields: `model_path` (absolute or relative-to-data-folder), `name`, `size_bytes`, `embedding`, `mmproj_path`, `capabilities[]`, `model_sha256`.
- **Assistants** — `<data>/assistants/<id>/assistant.json`.
- **Extensions** — `<data>/extensions/extensions.json` + `@janhq/<ext>/`.
- **Backends** — `<data>/llamacpp/backends/<version>/<backend>/` (downloaded llama.cpp builds).
- **Router preset** — `<data>/llamacpp/router.preset.ini`.
- **App store** — `store.json` (version + migration state).

### Mobile Storage

On Android/iOS (`mobile` feature), threads use **SQLite** via `sqlx` (`crate::core::threads::db::init_database`) instead of the JSONL files. Desktop uses the filesystem.

### Model Resolution

`resolve_model_engine(model_id)` probes `llamacpp` then `mlx` for a matching `model.yml`. `resolve_model_by_id` resolves the absolute model + mmproj paths (absolute paths used verbatim; relative paths joined to the data folder). `list_models(engine)` recursively scans `<data>/<engine>/models/` for `model.yml` files.

---

## 18. Code Quality & Conventions

### Languages & Tooling

- **Frontend:** TypeScript + React 19, Vite 6, Tailwind 4, ESLint 9 + typescript-eslint, Vitest (+ Testing Library), TanStack Router with the router plugin, Zustand for state, i18next for 18 locales.
- **Backend:** Rust 2021 edition, MSRV 1.77.2, `thiserror` for errors, `tokio` (full) for async, `serde`/`serde_json`/`serde_yaml` for serialization, `clap` (derive) for the CLI.
- **Build:** rolldown for extensions/core, Make for CLI + MLX server, run-script-os for per-platform scripts.
- **Testing:** Vitest projects (`@janhq/core`, `@janhq/web-app`, `@janhq/llamacpp-extension`) with `--max-old-space-size=4096`. Rust unit tests are colocated (`#[cfg(test)]`), with strong coverage in `jan-utils` (crypto, network, path, string, math). The Python **AutoQA** harness (`autoqa/`) drives full UI E2E tests via a CUA computer-use agent with ReportPortal reporting.

### Patterns

- **Plugin + extension split** — each engine is a Rust process supervisor plus a TS engine module communicating over a generated guest-JS API.
- **Command registry** — backend API is a single `generate_handler!` block; frontend `services/` mirror it as typed wrappers.
- **Capability-gated IPC** — per-command Tauri permissions enforce least privilege.
- **Graceful, busy-aware shutdown** — lifecycle handlers drain inference before exit.
- **Cancellable long operations** — downloads and hashing accept `CancellationToken`s.
- **Exponential backoff with jitter** — MCP reconnection.
- **Out-of-process risky work** — the lddtree dependency analyzer runs as a self-reinvoked helper to isolate crashes.

### Observations

- The `core::filesystem::commands` set is marked "Deprecate soon" — a broad FS bridge is a maintenance and security smell the team intends to retire.
- `web-app/src/lib/` concentrates a lot of logic in a few large files (`model-factory.ts`, `custom-chat-transport.ts`) — candidates for decomposition.
- Version skew across the monorepo (app 0.8.2, web-app 0.7.6, core 0.1.10) is intentional but can confuse "what version am I running" questions.

---

## 19. Potential Improvements

Derived from the source, error surfaces, and community issues:

- **API server reqwest deadlock** ([#8272](https://github.com/janhq/jan/issues/8272)) — proxy hangs on local requests on some Linux setups while UI works; investigate the blocking reqwest path in `core::server::proxy`.
- **SSE parser robustness** ([#8280](https://github.com/janhq/jan/issues/8280)) — the SSE parser fails on custom event types from OpenAI-compatible servers; the "Malformed chunk" path in `llamacpp-extension` and the remote transport should tolerate non-standard events.
- **Interrupted-download persistence** ([#8000](https://github.com/janhq/jan/issues/8000)) — model metadata isn't saved if a download is interrupted, forcing full re-downloads; persist partial state and support resume.
- **Multimodal/projector mismatches** ([#8273](https://github.com/janhq/jan/issues/8273), [#8278](https://github.com/janhq/jan/issues/8278)) — "unknown projector type" failures (gemma) should surface a clearer, actionable error.
- **Retire the filesystem IPC bridge** — replace `core::filesystem::commands` (flagged for deprecation) with narrower, capability-scoped operations to shrink the frontend's privilege.
- **Attachment size-limit enforcement** ([#8247](https://github.com/janhq/jan/issues/8247)) — configured limits not consistently applied.
- **CLI router-mode flag clarity** — `--ctx-size`/`--n-gpu-layers`/`--threads`/`--fit` are accepted but ignored in router mode; either honor them or warn at parse time.
- **Decompose large frontend modules** — split `model-factory.ts` and `custom-chat-transport.ts` along provider/transport seams for testability.
- **Update reliability on Linux** ([#7429](https://github.com/janhq/jan/issues/7429)) — investigate updater hangs on Ubuntu.

---

## 20. Quick Reference

### Versions

| Component | Version |
|-----------|---------|
| App (`src-tauri/Cargo.toml`) | 0.8.2 |
| web-app | 0.7.6 |
| @janhq/core | 0.1.10 |
| llamacpp-extension | 1.0.1 |
| Tauri | 2.8.5 |
| React | 19.0.0 |

### Key Files

| File | Purpose |
|------|---------|
| `src-tauri/src/lib.rs` | Tauri builder, `generate_handler!` (command registry), lifecycle |
| `src-tauri/src/main.rs` | Binary entry; `fix_path_env`; deps-analyzer hook |
| `src-tauri/src/core/cli/mod.rs` | CLI adapter (headless core logic, model discovery, HF download) |
| `src-tauri/Cargo.toml` | Crate config, features, binaries, deps |
| `web-app/src/lib/model-factory.ts` | Cloud provider clients + key rotation |
| `web-app/src/constants/providers.ts` | Built-in provider catalog |
| `web-app/src/constants/routes.ts` | Frontend route map |
| `<data>/llamacpp/router.preset.ini` | Per-model inference settings |

### Default Ports

| Service | Port |
|---------|------|
| Local API Server | `1337` |
| CLI server | `6767` |
| Docs dev | `3001` |

### Data Folders

| OS | Path |
|----|------|
| Windows | `%APPDATA%\Jan\data` |
| macOS | `~/Library/Application Support/Jan/data` |
| Linux | `~/.local/share/Jan/data` |

### Build Commands

```bash
make dev      # full dev setup + launch
make build    # production build
make test     # tests + lint
yarn build:tauri:darwin   # universal macOS build (incl. mlx-server + cli)
yarn build:cli            # build the jan-cli binary
```

### Links

- Repository: [github.com/janhq/jan](https://github.com/janhq/jan)
- Documentation: [jan.ai/docs](https://jan.ai/docs)
- API reference: [jan.ai/api-reference](https://jan.ai/api-reference)
- Community: [Discord](https://discord.gg/FTk2MvZwJH)
- Contact: [email removed]

---

_Generated June 2026 from janhq/jan at 15e372f. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
