Tauri 2 replaced the old allowlist with capabilities and permissions, and agents adding a feature tend to reach for the widest grant that makes it work: fs:default for every window, shell execute, http without a URL scope, CSP set to null. This loadout follows Tauri's security docs: the frontend is untrusted, capabilities name their windows, file and HTTP permissions carry scopes, commands validate their arguments. A small Node script checks capability files and tauri.conf.json, and the verifier runs it on a clean and a permissive project.
What's in it
- Capabilities for named windows, never "*" with fs, shell or http
- Every fs and http permission carries an allow scope
- No shell execute or spawn; shell:allow-open or the opener plugin instead
- CSP kept in app.security.csp
- Checker script with clear findings, tested by the verifier
You'll need
- Tools:
node,cargo
Verification
| Check | Result | What we found |
|---|---|---|
| Listinglisting.yaml matches the schema and declares every file in the folder. | Pass | Schema valid; 5 files declared, none missing or extra
|
| Files parseJSON parses; SKILL.md and .mdc front matter is valid YAML with the fields each agent needs; code fences are closed. | Pass | All 5 files parse; front matter valid |
| AdaptersCLAUDE.md imports @AGENTS.md, GEMINI.md imports @./AGENTS.md, and both match the generator. | Pass | CLAUDE.md and GEMINI.md import AGENTS.md and are up to date
|
| Install pathsEvery file lands where each agent documents it, with no collisions, and the laid-out project is re-discovered correctly. | Pass | Correct paths for all 6 agents; layouts re-discovered
|
| MCP configmcp.json is structurally valid, converts to each agent's format, declares no literal secrets, and referenced packages exist on npm/PyPI. | Not applicable | No MCP servers in this loadout |
| Safety scanThe intake scanner finds no credentials, risky shell, hidden Unicode or prompt-injection wording, and the listing's network flag is honest. | Pass | Clean: no credentials, risky shell or hidden text
|
| ReferencesLinks and file paths between the loadout's files resolve, including for each agent's installed layout. | Pass | 1 reference between files resolves for every agent |
| Size limitsFile count and sizes are within the submission limits; SKILL.md bodies stay under 500 lines. | Pass | 5 files, 6.5 KB total; largest skills/tauri-permissions/scripts/check-capabilities.mjs 2.6 KB
|
| LicenceA recognised SPDX licence, with the licence text available. | Pass | MIT, by Agent Loadout; text in the repository LICENSE |
| Starter buildA minimal fixture project with the loadout applied installs and builds or type-checks, and the loadout's own code examples compile. | Pass | The capability check passes a least-privilege Tauri 2 project and reports every problem in a permissive one: null CSP, wildcard windows, unscoped fs and http, shell execute and remote URLs with permissions
|
What this means: a script checked the files themselves: they parse, they install where each agent looks, any MCP config is sound, the safety scan is clean, references between files resolve, and, where a free and repeatable fixture exists, a small starter project builds with the loadout applied. No AI agent was run. Per-agent results ("tested with agent") are separate and stay untested until an agent actually runs.
Install
Choose your agent. Run the commands from your project root, or download a zip already laid out in the right folders.
Claude Code reads CLAUDE.md (imports AGENTS.md with @AGENTS.md) and skills from .claude/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/AGENTS.md curl -fsSL --create-dirs -o CLAUDE.md https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/CLAUDE.md curl -fsSL --create-dirs -o .claude/skills/tauri-permissions/SKILL.md https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/skills/tauri-permissions/SKILL.md curl -fsSL --create-dirs -o .claude/skills/tauri-permissions/scripts/check-capabilities.mjs https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/skills/tauri-permissions/scripts/check-capabilities.mjs
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
CLAUDE.md | CLAUDE.md |
skills/tauri-permissions/SKILL.md | .claude/skills/tauri-permissions/SKILL.md |
skills/tauri-permissions/scripts/check-capabilities.mjs | .claude/skills/tauri-permissions/scripts/check-capabilities.mjs |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Codex CLI reads AGENTS.md and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/AGENTS.md curl -fsSL --create-dirs -o .agents/skills/tauri-permissions/SKILL.md https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/skills/tauri-permissions/SKILL.md curl -fsSL --create-dirs -o .agents/skills/tauri-permissions/scripts/check-capabilities.mjs https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/skills/tauri-permissions/scripts/check-capabilities.mjs
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
skills/tauri-permissions/SKILL.md | .agents/skills/tauri-permissions/SKILL.md |
skills/tauri-permissions/scripts/check-capabilities.mjs | .agents/skills/tauri-permissions/scripts/check-capabilities.mjs |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Cursor reads AGENTS.md + .cursor/rules/*.mdc and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/AGENTS.md curl -fsSL --create-dirs -o .agents/skills/tauri-permissions/SKILL.md https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/skills/tauri-permissions/SKILL.md curl -fsSL --create-dirs -o .agents/skills/tauri-permissions/scripts/check-capabilities.mjs https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/skills/tauri-permissions/scripts/check-capabilities.mjs
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
skills/tauri-permissions/SKILL.md | .agents/skills/tauri-permissions/SKILL.md |
skills/tauri-permissions/scripts/check-capabilities.mjs | .agents/skills/tauri-permissions/scripts/check-capabilities.mjs |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Gemini CLI reads GEMINI.md (imports AGENTS.md with @./AGENTS.md) and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/AGENTS.md curl -fsSL --create-dirs -o GEMINI.md https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/GEMINI.md curl -fsSL --create-dirs -o .agents/skills/tauri-permissions/SKILL.md https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/skills/tauri-permissions/SKILL.md curl -fsSL --create-dirs -o .agents/skills/tauri-permissions/scripts/check-capabilities.mjs https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/skills/tauri-permissions/scripts/check-capabilities.mjs
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
GEMINI.md | GEMINI.md |
skills/tauri-permissions/SKILL.md | .agents/skills/tauri-permissions/SKILL.md |
skills/tauri-permissions/scripts/check-capabilities.mjs | .agents/skills/tauri-permissions/scripts/check-capabilities.mjs |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Antigravity CLI reads AGENTS.md (read natively; GEMINI.md is also read) and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/AGENTS.md curl -fsSL --create-dirs -o .agents/skills/tauri-permissions/SKILL.md https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/skills/tauri-permissions/SKILL.md curl -fsSL --create-dirs -o .agents/skills/tauri-permissions/scripts/check-capabilities.mjs https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/skills/tauri-permissions/scripts/check-capabilities.mjs
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
skills/tauri-permissions/SKILL.md | .agents/skills/tauri-permissions/SKILL.md |
skills/tauri-permissions/scripts/check-capabilities.mjs | .agents/skills/tauri-permissions/scripts/check-capabilities.mjs |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
GitHub Copilot reads AGENTS.md and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/AGENTS.md curl -fsSL --create-dirs -o .agents/skills/tauri-permissions/SKILL.md https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/skills/tauri-permissions/SKILL.md curl -fsSL --create-dirs -o .agents/skills/tauri-permissions/scripts/check-capabilities.mjs https://loadout.jnw-it.com/raw/tauri-desktop-app/1.0.0/skills/tauri-permissions/scripts/check-capabilities.mjs
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
skills/tauri-permissions/SKILL.md | .agents/skills/tauri-permissions/SKILL.md |
skills/tauri-permissions/scripts/check-capabilities.mjs | .agents/skills/tauri-permissions/scripts/check-capabilities.mjs |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Files
Rules AGENTS.md Tauri 2 security model, Rust command rules and checks. 31 lines
# AGENTS.md: Tauri 2 desktop app
Tauri 2 app: a web frontend (in `src/`) and a Rust core (in `src-tauri/`). The frontend is untrusted; the Rust side
decides what it may do.
## Commands
- Dev: `npm run tauri dev` (or the project's package manager). Build: `npm run tauri build`.
- Rust checks, from `src-tauri/`: `cargo fmt --check`, `cargo clippy -- -D warnings`, `cargo test`.
- Frontend checks: the project's lint, type-check and test scripts.
- Permissions check after any change to capabilities or `tauri.conf.json`:
`node <skills dir>/tauri-permissions/scripts/check-capabilities.mjs src-tauri`.
## Security model
- Capabilities in `src-tauri/capabilities/` grant permissions to named windows. Grant the minimum; use the
`tauri-permissions` skill for any change.
- Never `"windows": ["*"]` on a capability that grants file system, shell or HTTP access. Name the windows.
- File system and HTTP permissions always carry a scope (`allow` paths such as `$APPDATA/**`, or exact URLs).
- No `shell:allow-execute` or `shell:allow-spawn` unless the human asks; prefer `shell:allow-open` or the opener plugin.
- Keep a Content Security Policy in `app.security.csp`. Never set it to null to make something load.
- Remote URLs (`remote.urls`) get no command access unless the human approves each one.
## Commands (Rust)
- `#[tauri::command]` functions validate every argument. Paths from the frontend are resolved and checked against an
allowed base directory before use. Return `Result<T, String>` or a serialisable error type, never panic.
- Long work runs async or on a background thread; the UI must not freeze.
- Secrets live in the Rust side or the OS keychain, never in frontend code or `localStorage`.
## Changes
- Adding a plugin: `npm run tauri add <plugin>`, then grant only the permissions you use and justify each in the report.
- Do not upgrade Tauri, plugins or the webview target as part of another change. Adapter CLAUDE.md Imports AGENTS.md for Claude Code. 10 lines
# CLAUDE.md
@AGENTS.md
## Claude Code notes
- Skills from this loadout live in `.claude/skills/`: `tauri-permissions`. Use them when their description matches the task.
- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.
<!-- From the Agent Loadout "Tauri 2 desktop app, least privilege" v1.0.0 — https://loadout.jnw-it.com/loadouts/tauri-desktop-app/ --> Adapter GEMINI.md Imports AGENTS.md for Gemini CLI. 10 lines
# GEMINI.md
@./AGENTS.md
## Gemini CLI notes
- Skills from this loadout live in `.agents/skills/`: `tauri-permissions`. Use them when their description matches the task.
- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.
<!-- From the Agent Loadout "Tauri 2 desktop app, least privilege" v1.0.0 — https://loadout.jnw-it.com/loadouts/tauri-desktop-app/ --> Skill skills/tauri-permissions/SKILL.md Grant the narrowest permission, then check. 20 lines
---
name: tauri-permissions
description: Add or change Tauri 2 capabilities with least privilege (named windows, scoped fs and http permissions, no broad shell access, CSP kept) and run the capability check. Use when a feature needs a new plugin or permission.
---
# Tauri permissions
1. Name the exact API the feature needs (for example "read one JSON file in the app data folder").
2. Find the narrowest permission for it in the plugin's permission list (in `src-tauri/gen/schemas/` after a build, or
the plugin docs). Prefer a single `allow-<command>` permission to a plugin's `default` set.
3. Add it to a capability file for the named window, with a scope:
{ "identifier": "fs:allow-read-text-file", "allow": [{ "path": "$APPDATA/settings.json" }] }
4. Do not widen an existing capability's windows. If another window needs it, add a second capability.
5. Run the check: `node scripts/check-capabilities.mjs src-tauri`. It fails on wildcard windows with sensitive
permissions, unscoped fs or http permissions, shell execute or spawn, remote URLs with commands, and a missing or
null CSP. Fix every finding, or explain to the human why it is needed and get approval.
6. Run the app and use the feature once. Report the permissions added and why each is needed. Skill file skills/tauri-permissions/scripts/check-capabilities.mjs Flags wide or unscoped grants and a missing CSP. 48 lines
#!/usr/bin/env node
// Least-privilege check for Tauri 2 capabilities. Usage: node check-capabilities.mjs <src-tauri dir>
// Exit 1 with one line per finding; exit 0 and "ok" when clean. Reads JSON capability files and tauri.conf.json only.
import fs from "node:fs";
import path from "node:path";
const root = process.argv[2] ?? "src-tauri";
const findings = [];
const SENSITIVE = /^(fs|shell|http|process|sql|store|websocket|upload):/;
const NEEDS_SCOPE = /^(fs|http):/;
const SHELL_EXEC = /^shell:(allow-execute|allow-spawn|allow-stdin-write|default)$/;
const readJson = (p) => {
try { return JSON.parse(fs.readFileSync(p, "utf8")); } catch (e) { findings.push(`${p}: not valid JSON (${e.message})`); return null; }
};
const conf = readJson(path.join(root, "tauri.conf.json"));
const inline = conf?.app?.security?.capabilities?.filter((c) => typeof c === "object") ?? [];
if (conf && !conf.app?.security?.csp) findings.push("tauri.conf.json: app.security.csp is missing or null; keep a Content Security Policy");
const capDir = path.join(root, "capabilities");
const files = fs.existsSync(capDir) ? fs.readdirSync(capDir).filter((f) => f.endsWith(".json")) : [];
const caps = [
...files.map((f) => ({ where: `capabilities/${f}`, cap: readJson(path.join(capDir, f)) })),
...inline.map((c, i) => ({ where: `tauri.conf.json capability #${i + 1}`, cap: c })),
].filter((x) => x.cap);
for (const { where, cap } of caps) {
const perms = (cap.permissions ?? []).map((p) => (typeof p === "string" ? { identifier: p } : p));
const wildcard = (cap.windows ?? []).includes("*") || (cap.webviews ?? []).includes("*");
for (const p of perms) {
const id = p.identifier ?? "";
if (wildcard && SENSITIVE.test(id)) findings.push(`${where}: ${id} granted to every window ("*"); name the windows`);
if (SHELL_EXEC.test(id)) findings.push(`${where}: ${id} lets the frontend run programs; use shell:allow-open or the opener plugin`);
const scoped = (p.allow ?? []).length > 0;
const isScopeOnly = /:scope/.test(id);
if (NEEDS_SCOPE.test(id) && !scoped && !isScopeOnly) findings.push(`${where}: ${id} has no "allow" scope; limit it to the paths or URLs you need`);
}
if (cap.remote?.urls?.length && perms.length) findings.push(`${where}: remote URLs ${cap.remote.urls.join(", ")} get ${perms.length} permission(s); needs explicit approval`);
}
if (findings.length) {
for (const f of findings) console.log(`finding: ${f}`);
console.log(`${findings.length} finding(s) in ${caps.length} capability set(s)`);
process.exit(1);
}
console.log(`ok: ${caps.length} capability set(s), least privilege`); All files: source zip · Licence MIT · By Agent Loadout · First listed
Credits
Public work that inspired this loadout. The wording here is our own; follow the links for the originals.
Installing and testing
How do I install Tauri 2 desktop app, least privilege in Claude Code?
From your project root, put AGENTS.md, CLAUDE.md, skills/tauri-permissions/SKILL.md at .claude/skills/tauri-permissions/SKILL.md, skills/tauri-permissions/scripts/check-capabilities.mjs at .claude/skills/tauri-permissions/scripts/check-capabilities.mjs. The "Claude Code" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/tauri-desktop-app/claude-code.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Tauri 2 desktop app, least privilege in Codex CLI?
From your project root, put AGENTS.md, skills/tauri-permissions/SKILL.md at .agents/skills/tauri-permissions/SKILL.md, skills/tauri-permissions/scripts/check-capabilities.mjs at .agents/skills/tauri-permissions/scripts/check-capabilities.mjs. The "Codex CLI" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/tauri-desktop-app/codex.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Tauri 2 desktop app, least privilege in Cursor?
From your project root, put AGENTS.md, skills/tauri-permissions/SKILL.md at .agents/skills/tauri-permissions/SKILL.md, skills/tauri-permissions/scripts/check-capabilities.mjs at .agents/skills/tauri-permissions/scripts/check-capabilities.mjs. The "Cursor" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/tauri-desktop-app/cursor.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Tauri 2 desktop app, least privilege in Gemini CLI?
From your project root, put AGENTS.md, GEMINI.md, skills/tauri-permissions/SKILL.md at .agents/skills/tauri-permissions/SKILL.md, skills/tauri-permissions/scripts/check-capabilities.mjs at .agents/skills/tauri-permissions/scripts/check-capabilities.mjs. The "Gemini CLI" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/tauri-desktop-app/gemini-cli.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Tauri 2 desktop app, least privilege in Antigravity CLI?
From your project root, put AGENTS.md, skills/tauri-permissions/SKILL.md at .agents/skills/tauri-permissions/SKILL.md, skills/tauri-permissions/scripts/check-capabilities.mjs at .agents/skills/tauri-permissions/scripts/check-capabilities.mjs. The "Antigravity CLI" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/tauri-desktop-app/antigravity-cli.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Tauri 2 desktop app, least privilege in GitHub Copilot?
From your project root, put AGENTS.md, skills/tauri-permissions/SKILL.md at .agents/skills/tauri-permissions/SKILL.md, skills/tauri-permissions/scripts/check-capabilities.mjs at .agents/skills/tauri-permissions/scripts/check-capabilities.mjs. The "GitHub Copilot" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/tauri-desktop-app/copilot.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
Has Tauri 2 desktop app, least privilege been tested?
It passed the Level 1 automated install checks on 2026-10-17: the files parse, install to each agent's documented paths, and pass the safety scan. No AI agent has run it yet, so no agent pass or fail is claimed.
More for Claude Code, Codex, Cursor, Gemini CLI, Antigravity CLI and GitHub Copilot.