PacketBenchDocs

Where data lives#

PacketBench writes to one hidden directory under your home folder, one platform log directory, the OS credential store, the webview's localStorage, and a handful of files inside your own projects. Nothing else. This page is the canonical list: every path, what owns it, and whether deleting it costs you anything.

It supersedes the four partial tables that used to live on Install & first run, Core concepts, Issues & git hosts and Architecture internals. Where those disagreed, the correction is recorded at the end of this page.

The data directory is the same on every platform#

There is no per-platform application-data folder. DATA_DIR_NAME is .packetbench (src-tauri/src/core/brand.rs:15) and it is joined onto the user's home directory, resolved from USERPROFILE and then HOME (src-tauri/src/core/shared.rs:16).

Platform Data directory
Windows %USERPROFILE%\.packetbench\
macOS ~/.packetbench/
Linux ~/.packetbench/

That means a Windows install puts application state in your user profile root, not in %APPDATA%. Only logs use a platform convention — see below.

One resolver can still adopt the pre-rename directory#

Most call sites join home/.packetbench directly. Four do not: the unified state file, the provider-settings file, PTY transcripts and the PTY pid registry resolve through core::storage::data_dir() (src-tauri/src/core/storage.rs:309), which returns ~/.packetbench when it exists and otherwise falls back to ~/.packetade — but only when that legacy directory classifies as Ours (src-tauri/src/core/migration.rs:82). The classifier exists because a sibling packetcode TUI claimed the older name after the rename, and writing our state into another product's home would scribble on someone else's data.

In normal operation the two resolvers agree, because startup migration creates ~/.packetbench before anything reads. They diverge only if the migration failed and a legacy Ours directory survives: state and transcripts would land in ~/.packetade while conversations and dictation land in ~/.packetbench. See Upgrades & migration.

Everything in the data directory#

Source order is by subsystem. "Safe to delete" means with the app closed, and describes what you lose.

Path Owned by Safe to delete
state.v1.json src-tauri/src/core/storage.rs:94 — flights, issues, workspaces, servers, agent configs, settings, CLI accounts, memory events and patterns No. This is the backend authority for almost everything.
state.v1.json.bak write_with_backup (src-tauri/src/core/storage.rs:343) Yes, but it is the only recovery copy if the primary is corrupted.
state.v1.json.tmp Crash residue between the fsync and the rename-in (src-tauri/src/core/storage.rs:352) Yes, once the app has started cleanly.
state.v1.json.corrupt-<unix> quarantine_corrupt (src-tauri/src/core/storage.rs:371) Yes, once you no longer need it for forensics.
provider-settings.v1.json save_provider_runtime_settings (src-tauri/src/core/storage.rs:856) — provider base URLs and runtime overrides Yes. Providers revert to their defaults.
conversations/<id>.json src-tauri/src/commands/conversations.rs:20 — one file per API-agent conversation, frontend-defined JSON that Rust treats as opaque Yes, per file. You lose that transcript.
conversations/<id>/checkpoints/ Nothing. The checkpoint panel was Removed in 0.10.0 and upgraded installs still carry the leftovers (src-tauri/src/core/brand.rs:12) Yes. Nothing reads or writes them.
pty-transcripts/<uuid>.log src-tauri/src/core/pty.rs:113. A sibling <uuid>.log.truncated marks a transcript that hit its cap Yes. Terminal history only.
pty-active-pids src-tauri/src/core/pty.rs:130 — a flat file, one <pid>\t<command> line per PTY child spawned this run Yes when the app is not running. Deleting it while children are alive loses the orphan-reaping safety net for the next launch.
dictation.db src-tauri/src/commands/dictation/history.rs:30 — dictation history and speaking analytics Yes.
dictation.json src-tauri/src/commands/dictation/config.rs:106 — dictation settings, including the system-wide paste opt-in Yes. Settings revert to defaults, which means clipboard-only delivery.
models/ggml-<size>.bin src-tauri/src/commands/dictation/models.rs:151 — Whisper weights, 75 MB to 3 GB each Yes. Re-downloadable from Settings → Dictation.
models/ggml-<size>.bin.sha256 src-tauri/src/commands/dictation/models.rs:155 — the verified-checksum marker Only alongside its .bin. An orphaned marker forces a re-verify, which is the safe direction.
usage.jsonl src-tauri/src/commands/usage.rs:47 — append-only token and cost ledger Yes, but the budget guardrails read it, so spend caps reset.
provider-launches.json src-tauri/src/commands/provider_stats.rs:48 Yes. Launch counters only.
sidecar-stats.json src-tauri/src/commands/agent_sidecar/status.rs:152 — lifetime sidecar restart and crash counters Yes.
git-hosts.json src-tauri/src/commands/github.rs:186 — Gitea/Forgejo connection metadata, base URL and label only Yes. Connections must be re-added; the tokens stay in the keyring.
github-token Nothing writes it. Legacy plaintext token read once at startup, copied to the keyring, then deleted (src-tauri/src/commands/github.rs:232) Yes.
ssh/known_hosts src-tauri/src/commands/pty.rs:1381, path from src-tauri/src/core/execution.rs:12. Directory created mode 0700 on Unix No. Deleting it drops every pinned host key, and unpinned hosts fall back to trust-on-first-use.
ssh-cm/ src-tauri/src/core/execution.rs:38 — SSH ControlMaster sockets, mode 0700, Unix only Yes when no SSH session is live. Windows OpenSSH has no ControlMaster, so this directory never appears there.
commands/*.md You. PacketBench only reads it (src-tauri/src/commands/slash_commands.rs:119) — global slash commands Yours to manage.
<command>-bin You. A pin file naming an absolute path for one CLI, read at spawn (src-tauri/src/commands/pty.rs:90) Yes. Resolution falls back to PATH.
scratch/ src-tauri/src/commands/pty.rs:212 — the neutral working directory for a pane opened with no project Yes, recreated on demand.
crashes/crash-<unix>.log write_crash_log (src-tauri/src/commands/crashes.rs:102) — panic message, location and backtrace Yes. Also deletable from Settings → Security & Diagnostics → Crash Reports.
Note

pty-active-pids is a file, not a directory. Earlier documentation drew it with a trailing slash. It holds one tab-separated line per child so the next launch can verify a pid's command basename still matches before signalling it — a recycled pid is never killed.

known_hosts lives under ssh/, not at the root#

Two pages disagreed about this, and it matters: the wrong path sends you looking for a file that is not there when you are trying to work out why a host is unpinned.

The Rust source settles it. app_known_hosts_path() joins the data directory, then ssh, then known_hosts:

pub fn app_known_hosts_path() -> PathBuf {
    let home = home_dir().unwrap_or_else(|| ".".to_string());
    PathBuf::from(home)
        .join(crate::core::brand::DATA_DIR_NAME)
        .join("ssh")
        .join("known_hosts")
}

That is src-tauri/src/core/execution.rs:12-18, and it is the single definition — ensure_known_hosts_dir (line 22), the ssh_pin_host write path (src-tauri/src/commands/pty.rs:1381), the get_app_known_hosts_path command returned to the frontend (src-tauri/src/commands/pty.rs:1412) and the UserKnownHostsFile argument (src-tauri/src/core/execution.rs:113) all resolve through it. The true path is:

~/.packetbench/ssh/known_hosts

install.md placed it at ~/.packetbench/known_hosts. That was wrong. SSH remote workspaces and Settings were correct.

The bare name known_hosts does appear once outside that directory, in the migration marker list (src-tauri/src/core/migration.rs:30), where it is matched as a directory entry name to identify a pre-rename data directory. It is not a path.

Logs are the one platform-specific location#

Logs do not live in the data directory. dirs_log_dir() (src-tauri/src/lib.rs:40) uses the platform convention, under LOG_DIR_NAME = PacketBench (src-tauri/src/core/brand.rs:23).

Platform Log directory
Windows %LOCALAPPDATA%\PacketBench\logs\, falling back to %APPDATA% then C:\ProgramData
macOS ~/Library/Application Support/PacketBench/logs/
Linux $XDG_DATA_HOME/PacketBench/logs/, defaulting to ~/.local/share/PacketBench/logs/

The appender rotates daily, writing packetbench.log.<YYYY-MM-DD> (src-tauri/src/lib.rs:24). There is no retention limit and no pruning: the directory grows for as long as the app is used. Deleting old files is safe and is currently the only way to bound it. Old installs may also hold a PacketADE directory under the same parent (LEGACY_LOG_DIR_NAME, src-tauri/src/core/brand.rs:26); nothing reads it.

Three kinds of state, and how to tell them apart#

Conflating these is the most common source of confusion, because a setting you changed in the UI may live in any of the three.

The OS keyring holds every secret#

Service packetbench. API keys, git-host tokens, SSH passwords and the Packet Agent token, and nothing else. It is not a file, it is not in the data directory, and it is not included in a backup of your home folder. A keyring write failure is a hard error rather than a fallback to disk. The full entry table is on Security & credentials.

localStorage holds UI state and two authoritative slices#

The webview's localStorage, keys prefixed packetbench: (src/lib/brand.ts:28). It lives in the Tauri webview's profile directory, which is managed by the OS webview runtime — WebView2 on Windows, WKWebView on macOS, WebKitGTK on Linux — and is not a path PacketBench controls.

Most of it is presentational and losing it costs nothing: mosaic layouts, right-dock widths, sidebar preferences, the last-opened tab, onboarding dismissal, project history. Some of it is not:

Key Holds Losing it
packetbench:issues The authoritative cold-start cache for the issue board; every mutation is mirrored into state.v1.json so the Fixes #N close loop resolves the same set (src/stores/issueStore.ts:234) Issues reload from the mirror in state.v1.json.
packetbench:mcp-hub-trust-v1 Per-server MCP trust profiles (src/stores/mcpTrustStore.ts:42) Every server reverts to the default profile: reads on, writes off.
packetbench:provenance-audit-v1 The bounded trust audit, 7 or 30 days and 200 entries (src/stores/provenanceAuditStore.ts:49) The audit history, which is the point of the card.
packetbench:agent-profiles Agent profiles and their permission modes (src/stores/profileStore.ts:12) Custom profiles; the built-ins are re-seeded.
packetbench:workspaces-cache A paint-fast copy so Welcome renders before Rust answers Nothing. state.v1.json is the authority.
Important

packetbench:issues and state.v1.json are two halves of one record, on purpose. localStorage is the cold-start authority so the board paints instantly; the mirror exists so Rust-side consumers see the same issues. Clearing site data without also clearing state.v1.json leaves the mirror as the surviving copy.

On-disk state files hold everything durable#

The table above. state.v1.json is one PersistedState struct written through a two-tier lock so a stale full-state save cannot overwrite a slice save that landed in between — see Architecture internals.

Outside the data directory#

These are the paths PacketBench reads or writes that are not its own.

Path What it is Owned by
<project>/.agents/memory/*.md Durable project notes, ordinary Markdown, committed with your repo You and the agent. See Memory.
<project>/.pkt-worktrees/<id> Git worktrees for Flight attempts and conversation branches, locally or on the remote host (src-tauri/src/commands/git.rs:385) PacketBench. The dirty-root check excludes it deliberately.
<project>/AGENTS.md, <project>/CLAUDE.md Project rules, written as a matched pair by the Project Rules editor You, through Settings.
<project>/.mcp.json Project-scoped MCP server definitions You or another tool. PacketBench reads it.
<project>/.packetbench/commands/*.md Project-scoped slash commands (src-tauri/src/commands/slash_commands.rs:130) You. Read-only to PacketBench.
~/.claude/settings.json Global MCP server definitions, and the Claude status-line hook Claude Code. PacketBench reads and writes the hook.
~/.claude/statusline-state/ State the PacketBench status-line helper writes for claude panes (src-tauri/src/core/claude_statusline.rs:194) PacketBench.
~/.claude, ~/.codex Watched for CLI login changes so account badges stay accurate (src-tauri/src/commands/auth_watcher.rs:319) The CLIs.
A CLI account config directory Any path you choose, pointed at by CLAUDE_CONFIG_DIR or CODEX_HOME You. PacketBench seeds settings.json and config.toml into a new one and never copies .credentials.json, credentials or auth.json (src-tauri/src/commands/cli_account.rs:29).
$TMPDIR/packetbench-askpass-<uuid>/secret An SSH password, mode 0600 in a mode-0700 directory, for the duration of one ssh invocation. Unix only (src-tauri/src/core/ssh_askpass.rs:58) PacketBench. A Drop guard removes file and directory.

What is safe to delete#

Three useful resets, in increasing order of loss. Close the app first.

  • Reclaim disk. Delete models/, pty-transcripts/, crashes/ and the log directory. Together these are almost all of the footprint; the models alone can be several gigabytes. Nothing durable is lost.
  • Reset the UI without losing work. Clear the packetbench: keys in localStorage. Workspaces, flights and servers survive in state.v1.json; issues reload from the mirror; MCP trust profiles revert to defaults.
  • Start over. Delete ~/.packetbench/ entirely. This also removes ssh/known_hosts, so every remote host reverts to unpinned and the next connection is trust-on-first-use. It does not clear the keyring — API keys, tokens and SSH passwords survive, and a re-created host record with a reused id will find its old password.
Warning

Deleting state.v1.json while leaving localStorage intact produces a half-restored install: the issue board repopulates from packetbench:issues while workspaces, flights and servers are gone. Delete both or neither.

Where the four older tables were wrong#

Recorded rather than quietly fixed, so anyone holding a stale copy can tell which one they have.

Source Claim Correction
install.md known_hosts at ~/.packetbench/known_hosts Wrong. It is ~/.packetbench/ssh/known_hosts (src-tauri/src/core/execution.rs:12). remote.md and settings.md were right.
install.md pty-active-pids/ drawn as a directory It is a flat file (src-tauri/src/core/pty.rs:130).
install.md Tree omitted models/, ssh/, ssh-cm/, scratch/, commands/, provider-settings.v1.json, provider-launches.json, sidecar-stats.json, git-hosts.json and github-token All are listed above.
install.md Keyring table listed six entries Ten exist: api-key-minimax-api, api-key-ollama, git-host-token-<id> and packet-agent-token were missing. See Security & credentials.
concepts.md "the app-managed known_hosts", unqualified Correct but unlocated; the path is now stated once, here.
issues.md "Gitea connections → persisted app config" Specifically ~/.packetbench/git-hosts.json (src-tauri/src/commands/github.rs:186).
dev-architecture.md "five distinct persistence surfaces" Accurate as a model, but it lists state.v1.json, conversations and usage.jsonl as the only files. Nine more files and six directories exist.
All four No mention of the log directory It is the one platform-specific location, and the only one that grows without bound.