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
- Executive Summary
- System Architecture
- Component Inventory
- Repository Structure
- System Requirements
- Build & Packaging
- Configuration Reference
- The Tauri Command (IPC) Surface
- Inference Engines & Plugins
- The Extension System
- The Local API Server
- MCP Subsystem
- Remote Provider Integration
- RAG & Vector Database
- Security Analysis
- Operations
- Data Model & Storage
- Code Quality & Conventions
- Potential Improvements
- 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-serverprocess that loads/unloads models on demand), driven by the Rusttauri-plugin-llamacppplus the TypeScript@janhq/llamacpp-extension. - MLX — for Apple Silicon, via a bundled Swift
mlx-serverbinary, thetauri-plugin-mlxRust 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 |
| 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.tomlpackage (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
┌─────────────────────────────────────────────────────────────────────┐
│ 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-serverprocess spawned and supervised bytauri-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/bunprocesses 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, emitsllamacpp-busy-on-exit, and runshandle_graceful_exit()which repeatedly callstry_graceful_stop_router()until no model is busy. - On the final
Exitevent, it runsbackground_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.
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
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 themcp-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 insrc-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
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
.tgztarballs placed inpre-install/, then copied intosrc-tauri/resources/pre-install/and installed into the data folder at first run (see §10). The frontend depends on@janhq/coreasworkspace:*, 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 nameapp_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
clifeature and the default desktop feature are mutually exclusive at the source level: large parts oflib.rsare gated#[cfg(not(feature = "cli"))]. The CLI is a thin adapter (src/core/cli/mod.rs) that calls core logic without anAppHandle, 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-httpis configured withnative-tls(OS trust store) instead of the bundled rustls roots, plushttp2,cookies,unsafe-headers, andmacos-system-configuration(system proxy). - MCP transport: the
rmcpcrate (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:
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_0in a prior release and reverted tof16. This is not auto-migrated — if models fail to load after updating to 0.8.0, manually set KV Cache K/V Type back tof16.
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_taskcommands back the model-download UI.install_extensionsruns the extension migration on startup.factory_resetis the destructive reset;change_app_data_folderrelocates the data folder. Thelaunch_claude_code_with_config/clear_claude_code_envcommands implement the Claude Code integration;install_jan_cli/uninstall_jan_climanage 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:
LlamacppStateholds the router PID (router_pid) and process registry.try_graceful_stop_router(),cleanup_llama_processes(), andrun_deps_analyzer_if_requested()are exported for the host.- Out-of-process dependency analyzer:
main.rscallstauri_plugin_llamacpp::deps_analyzer::run_deps_analyzer_if_requested()beforeapp_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 —lddtreeis 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>/, checkingbuild/bin/llama-server[.exe]then<backend>/llama-server[.exe], preferring the latest version. - HuggingFace download (
fetch_hf_gguf_files,download_hf_model) hitshttps://huggingface.co/api/models/{repo}?blobs=true&files_metadata=true, filters.ggufsiblings, sorts by size (smallest quant first), streams the file with SHA-256 from the LFS pointer, and writes amodel.yml.HF_TOKENis sent as a bearer token; 401/403 surface a "gated repo — set HF_TOKEN" hint. - Crypto:
tauri-plugin-llamacppuseshmac/sha2/base64for API-key generation andbyteorderfor 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:
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.1binding keeps the server local-only. - Setting host to
0.0.0.0exposes models to the LAN — the API key should be set and Trusted Hosts restricted.is_valid_hostenforces 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 randomapp_token(generate_app_token, 32 chars).
Warning
Known issue (#8272): on some systems (Fedora Silverblue reported) the API Server proxy can hang on local requests (a
reqwestdeadlock) while the UI chat works. If automated clients hang against1337but 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_configsand activated/deactivated viaactivate_mcp_server/deactivate_mcp_server. get_tools/get_tools_for_servers/get_server_summaries/get_connected_serversenumerate capabilities;call_tool/cancel_tool_callexecute.AppStatetracksmcp_servers,mcp_active_servers,mcp_server_pids,mcp_monitoring_tasks,mcp_settings,mcp_shutdown_in_progress, and amcp_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_processflags straynode/npx/bunMCP 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/modelsendpoint (requiresbase_url; "Catalog not supported for {provider}" / "Provider must have base_url configured").custom-chat-transport.ts— the streaming transport bridging AI SDK to Jan'sServiceHub.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).
Note
Anthropic is reached directly from the WebView using
anthropic-dangerous-direct-browser-access: trueand a pinnedanthropic-versionheader. 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-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
--embeddingflag 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
- 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. - Local API server ↔ network — default
127.0.0.1only; LAN exposure requires explicit0.0.0.0+ (ideally) API key + trusted-hosts allowlist (is_valid_host). - Model ↔ MCP tools — per-call approval by default; "Allow All" removes the human-in-the-loop. Prompt injection is the primary risk.
- 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 inAppState.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_sha256is recorded inmodel.yml. - TLS:
tauri-plugin-httpuses 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_proxyallowlist 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).
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
LICENSEand README), but the@janhq/coreSDK and most TypeScript extensions are AGPL-3.0, while the Rust crates carry MIT in theirCargo.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.1unless 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.
# 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:
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_resetcommand (UI: Settings → General). Deletes all models, chats, settings. Requires typingRESET. - Relocate data:
change_app_data_folderduplicates data to a new empty directory and restarts. On Windows,settings.jsondata_foldercan 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); 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>/withmessages.jsonl(one message per line) andthread.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).ModelYmlfields: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,
thiserrorfor errors,tokio(full) for async,serde/serde_json/serde_yamlfor 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 injan-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; frontendservices/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
CancellationTokens. - 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::commandsset 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) — 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) — the SSE parser fails on custom event types from OpenAI-compatible servers; the "Malformed chunk" path in
llamacpp-extensionand the remote transport should tolerate non-standard events. - Interrupted-download persistence (#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, #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) — configured limits not consistently applied.
- CLI router-mode flag clarity —
--ctx-size/--n-gpu-layers/--threads/--fitare accepted but ignored in router mode; either honor them or warn at parse time. - Decompose large frontend modules — split
model-factory.tsandcustom-chat-transport.tsalong provider/transport seams for testability. - Update reliability on Linux (#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
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
- Documentation: jan.ai/docs
- API reference: jan.ai/api-reference
- Community: Discord
- Contact: [email removed]