SafetyCard Request a review

Jan

Offline ChatGPT alternative desktop app with MCP tool calling

github.com/janhq/jan · 2026-10-05 · 98c3b84

Defense-in-depth score

4.7 / 10

Jan's chat shell runs in an OS sandbox with no network, its own disposable folder, no secrets and no view of your home directory, and it is not offered at all when no sandbox is available. Every MCP tool call asks you first. The dominant risk is the web access that is on by default: web search and page fetch run without approval, so text injected into a page or document can make the model send your conversation or memory notes to an outside URL. The chat loop also has no step or spend limit, and MCP servers you add run unpinned with your full environment.

Safest setup

Jan's chat shell runs in an OS sandbox with no network and no view of your home directory, and every MCP tool call asks you first. Web search and page fetch run without approval and the chat loop has no step or spend limit, so the safest setup keeps MCP approval on, turns web access off where it isn't needed and puts limits around the app yourself.

SetupHowWhat it gives you
A spend-limited provider keyUse a model-provider key with a spending limit set at the provider, or a local modelA runaway chat stops at a ceiling the provider enforces C10
Per-call MCP approvalKeep Allow All MCP Tool Permissions off, the default allowAllMCPPermissions: falseA person sees the exact input before each MCP tool call runs C2
Web access only when neededSwitch off Enable Web Search (webSearchEnabled) in settings for chats that don't need the webThe model loses Jan's search and fetch tools, the ones that run without approval C5
Reviewed, pinned MCP serversAdd only MCP servers you have read, with an exact package version in their launch argumentsEach server keeps running the code you reviewed C7
A lean launch environmentStart Jan from an environment holding only the keys your MCP servers needMCP servers, which inherit Jan's full environment, see nothing beyond those keys C1
Memory notes you trustRemember only messages you trust, and review the memory notes in settings from time to timeEvery later chat starts from background context you have checked C6
Its own OS account or VMRun Jan under a separate OS user account or in a VM once you add MCP serversAn outer boundary around MCP servers, which run on the host outside the shell sandbox C4

Approving with confidence

  • Read each prompt and approve calls one at a time; allow for this chat and always allow also cover later calls with different arguments.
  • Always-allow grants have no screen to review or revoke them, so keep them for tools you would approve every time.
  • The chat loop has no step limit; press Stop when a run keeps calling tools.

Handling data

  • Each conversation's JSONL file in the Jan data folder holds tool arguments and results; store it like the documents you chat about.
  • MCP server keys sit in plain text in the MCP config; protect that file like the services those keys unlock.

Criteria

C1 Identity & least privilege

0.55 / 1.00

Jan runs as the desktop user and has no per-request authorization layer, but it deliberately narrows what its built-in tools can reach. The chat shell starts from an emptied environment with only a short allowlist of variables, and the user's home directory and the Jan data folder (settings, keys, models) are masked from it. Web search uses a keyless hosted provider by default, so no credentials are attached to built-in tools. MCP servers the user adds are the exception: they are started with the app's full inherited environment plus whatever keys the user configured for them.

C2 Approval gates

0.57 / 1.00

Every MCP tool call asks the user first, unless they already trusted that tool or server. The prompt sits next to the full tool input and offers deny, allow once, allow for this chat, or always allow. Jan's own tools never prompt: web search and page fetches run unattended, and the shell runs without approval because it is confined to a disposable sandbox with no network. A single settings switch, off by default and clearly described, auto-approves every MCP call. 'Always allow' grants are stored in app settings with no screen to review or revoke them.

C3 Tool & action scoping

0.45 / 1.00

Jan's file tools resolve and re-check paths against the workspace before writing, but the chat surface does not use them. Its default tools are general-purpose: a shell that takes any command string, and a web fetch that takes any URL. Both are bounded by other layers (the OS sandbox and a hosted fetch provider) rather than by argument validation. MCP tool arguments are passed through as given. Web access is on by default and can be switched off; the shell cannot be disabled on its own.

C4 Code-execution isolation

Strong 0.75 / 1.00

The chat shell runs inside an OS sandbox: bubblewrap on Linux, Seatbelt on macOS and AppContainer on Windows. Writes are limited to a per-conversation workspace, the home directory and Jan data folder are hidden, network is off, and the environment is emptied. If no sandbox backend is available the shell is not offered instead of running unconfined, and the desktop has no switch that runs it on the host. Two things limit it: the rest of the filesystem outside the home directory stays readable, and there are no CPU or memory limits. MCP servers the user adds run directly on the host.

C5 Untrusted input blast radius

0.38 / 1.00

Jan does not separate untrusted content from instructions: web pages, search results, attached documents and MCP results enter the conversation as ordinary tool output. What limits a hijacked chat is that MCP actions always need a click and the shell has no network. But web search and page fetch run without approval and can request any URL, so injected text can send conversation content, attached documents or memory notes out through a fetched URL. Nothing irreversible can happen without a human in the default setup.

C6 Memory, context & configuration integrity

0.57 / 1.00

Chat has one persistent store that reaches future conversations: memory notes, injected as background context into every chat. In the stable chat surface only the user writes them, by clicking Remember on a message or editing them in settings; the model has no memory-writing tool there. There are no workspace instruction files or project configs to auto-load. The concern is what happens once a note is poisoned: it has no expiry or provenance and is presented as the user's own notes in every later conversation, where it can steer tool use.

C7 Third-party extensions

0.30 / 1.00

No third-party code runs by default: every MCP server in the shipped catalog is inactive, and adding one is an explicit user step. Once added, servers are launched as written, often through 'npx -y' with an unpinned or '@latest' package. Jan does not pin or verify them and does not prompt again when they change. They run as separate processes with the user's full environment. Model downloads are checked against a SHA-256 when one is known, and app updates are signed.

C8 Secrets & sensitive-data protection

0.50 / 1.00

Provider and web-search API keys are kept in the OS keyring, with an encrypted owner-only file as a fallback, and are excluded from the settings file. Product analytics are opt-in, with autocapture and session recording off. The shell sees none of these keys because its environment is emptied and the data folder is hidden. Gaps: MCP server keys sit in plain text in the MCP config, MCP processes inherit the full environment, and nothing masks secrets in logs, transcripts or model-bound messages.

C9 Audit & traceability

0.45 / 1.00

Every conversation is saved as a per-thread JSONL file in the Jan data folder, and each assistant message records its tool calls with their arguments and results. Denied calls are recorded as errors. Approvals and who gave them are not, and there is no audit trail separate from the editable chat history. Records are written by the app once a message completes, not as a durable per-action log.

C10 Limits & kill switch

0.20 / 1.00

The chat tool loop has no step or token limit: it keeps calling the model as long as the model keeps asking for tools, until the user presses Stop. Individual calls are bounded. MCP calls time out after 30 seconds. Shell commands are moved to the background after 30 seconds (the model can choose a longer timeout), and Stop kills the conversation's shell process groups. The step and token budgets in the code apply only to the preview Cowork surface.