Skip to the document

The app data folder

Where Plicine keeps everything it generates: caches, compiled blocks, trust decisions, the shared library and block checkouts. Nothing here is part of any vault, and the vault only ever receives one file back (blocks.lock).

Location

Platform Default
macOS ~/Library/Application Support/plicine
Windows %APPDATA%\plicine (or <home>\AppData\Roaming\plicine if APPDATA isn't set)

Setting PLICINE_DATA_DIR overrides both. It's how a CI run gets a disposable data folder of its own.

Layout

Path Contents
trust.json Every approve/deny decision made on this machine, keyed by block content hash
agents.json Optional, written by hand: agents for the palette besides the built-in ones. See below
agent-settings.json The settings last picked for each agent in the palette, such as its model, so a new conversation starts where the last one left off
agent-palette.json Whether the note pointing out the agent palette has been dismissed, so it shows once rather than on every launch
agent-cwd/ An empty folder every agent process starts in. The vault reaches the agent as its session's folder instead, so nothing a vault holds can decide which program npx runs
library/ .block and .theme files installed with plicine install (without --community), available to every vault
community/ .block and .theme files installed with plicine install --community
packages/<hash>/ One extracted, compiled block per content hash: the package's own files at the root, plus dist/block.js and, if the block emits CSS, dist/block.css
deps/<hash>/ One node_modules install per exact (package.json, bun.lock) pair a block declared, symlinked into that block's packages/<hash>/node_modules
inline/<hash>/ One compiled module per distinct ts-block body, keyed by sha256("ts-block\0" + code)
chrome/<version>/ The chrome-headless-shell downloaded for PDF export, one folder per pinned release, holding the archive's own chrome-headless-shell-<platform>/ folder. Written by plicine chrome download or by the first export on a machine with no browser
vaults/<vault-hash>/pdf/<key>.pdf and .json Cached PDF renders, keyed by a hash of the document, theme, page size, block trust and hashes, and a renderer version; the .json file alongside holds { pages, warnings }
vaults/<vault-hash>/storage/<block-id-hash>.json A flat JSON object of key/value pairs for one block's globalThis.plicine.storage, one file per block id, shared by every document in that vault
vaults/<vault-hash>/sidebar.json The folders open in that vault's sidebar, as { "openFolders": [...] } with vault-relative paths, and the widths the sidebar and the block panel were last dragged to. It lives here rather than in the page's localStorage because the desktop app's server takes a new port every launch, which gives the page a new origin and empty storage
vaults/<vault-hash>/index/documents.json The vault's search index: one entry per document, holding its title, headings, text, block names and props, and the documents it links to, each with the source line it came from. Rebuilt on start for whatever has changed and updated a document at a time from the folder watcher. See Search and backlinks below
dev/<vault-hash>/<alias>/ A checkout made by plicine block checkout/edit: the block's own files, plus a .base/ snapshot and .plicine-dev.json sidecar plicine block push/status/diff use to find the vault and detect drift. See Editing an installed block

A download that doesn't finish never looks like an install: the archive unpacks into a .download-* folder under chrome/ and is renamed into chrome/<version>/ only once the executable is there, so an interrupted download leaves nothing for the next run to trip over. An archive whose SHA-256 isn't the one pinned for the platform is thrown away the same way, before any of it is unpacked.

<vault-hash> is the first 16 characters of the SHA-256 of the vault's absolute, resolved path, so the same vault always maps to the same cache folder regardless of which command opened it, and two differently-located vaults never collide even if their contents are identical. <block-id-hash> is the first 16 characters of the SHA-256 of the block's id (its permanent @scope/name, not its content hash), so a block's stored data survives the block being updated to a new version and a new content hash.

A real data folder after installing and approving the two reference blocks and exporting one PDF looks like this:

plicine/
  trust.json
  library/
    quote-table.block
  packages/
    223ba97aa0b7.../          (full 64-character hash)
      manifest.json  BLOCK.md  package.json  src/...
      dist/block.js
    229eec57928d.../
      manifest.json  BLOCK.md  package.json  src/...
      dist/block.js
  vaults/
    20c6593f1ded5d4d/
      index/
        documents.json
      pdf/
        67cf17d7edf6....json
        67cf17d7edf6....pdf
      sidebar.json

trust.json records each decision by the block's full content hash:

{
  "version": 1,
  "approved": {
    "223ba97aa0b710a0fb893f9c5699ca782b8649a1e8b39e499d27e0f1e05eed93": {
      "id": "@solunify/iso",
      "version": "1.0.0",
      "capabilities": { "network": false, "storage": false },
      "decidedAt": "2026-09-12T20:48:14.665Z"
    }
  },
  "denied": {}
}

decide() always rewrites both the approved and denied entries for a hash together, so a hash only ever appears in one of the two objects at a time. Approving or denying an unrecognised hash isn't possible through the CLI or the API; both first look the hash up against the vault's currently resolved blocks (or, for plicine trust, every block visible to the vault at all).

vaults/<vault-hash>/index/documents.json is what GET /api/search and GET /api/backlinks answer from. It holds a version, a short hash of the vault's file list, the vault's title, and one entry per document:

{
  "version": 3,
  "files": "9f2b1c0d4e5a6b7c",
  "vaultTitle": "Lorem notes",
  "documents": [
    {
      "path": "welcome.md",
      "title": "Welcome",
      "size": 2481,
      "mtimeMs": 1758013245123.4,
      "fields": [
        { "kind": "title", "text": "Welcome", "line": 0 },
        { "kind": "heading", "text": "Blocks", "line": 24, "label": "blocks" },
        { "kind": "text", "text": "A block is a fenced code block whose name matches an installed block.", "line": 26 },
        { "kind": "block", "text": "Lorem rates", "line": 31, "label": "table.caption" }
      ],
      "links": [{ "path": "pricing.md", "line": 26, "kind": "wikilink" }],
      "unresolved": 0
    }
  ]
}

A field is one searchable piece of the document with the source line it starts on, which is how a result can scroll to the element it matched in. kind says where it came from, and how much a match in it counts for when results are ranked: the document's title, a heading (whose label is its id, so a result can link straight to it), a paragraph or other element of text, or a block, which covers both a block's name and each of its props flattened to a path such as table.rows.0.1. links holds the documents this one links to, whether the link was written as a [[wikilink]], as a markdown link to a .md file, or inside a block's markdown; unresolved counts the [[links]] that matched no file, which is how the server knows to read a document again when the vault's files change.

size and mtimeMs are the file as it was when it was indexed. A document whose file still matches both isn't read again on start, so opening a vault only parses what changed since last time. The one exception is the vault's own INDEX.md, which goes by the vault's title while it has none of its own: vaultTitle is the title it was indexed under, and it's read again when the vault has been given another. Deleting the folder costs nothing but the next rebuild.

agents.json

The agent palette finds Claude Code, Kiro, Gemini CLI and Codex by itself. Any other agent that speaks the Agent Client Protocol goes in this file:

{
  "agents": [
    { "id": "my-agent", "name": "My agent", "command": "my-agent", "args": ["acp"], "env": { "MY_AGENT_PROFILE": "work" } }
  ]
}

id is lower-case letters, digits and hyphens, and reusing a built-in id (claude, kiro, gemini, codex) replaces that agent. command is either a name looked up on PATH, as your terminal sees it, or an absolute path. args and env are optional. If the file doesn't parse, the palette says why and the built-in agents carry on working.

The list lives here and not in a vault on purpose. A vault arrives through a shared folder, and nobody you share one with should be able to pick a command that runs on your machine.

What's safe to delete

Most of the folder is cache. packages/, deps/, inline/ and vaults/*/pdf can be deleted at any time; they're rebuilt (recompiled, reinstalled, re-rendered) the next time they're needed. chrome/ is cache too, and the biggest thing in the folder at around 250 MB unpacked: deleting it (or running plicine chrome remove) costs a re-download on the next export, unless a browser is installed or PLICINE_CHROME is set, in which case it costs nothing at all. Deleting vaults/*/sidebar.json closes every folder in that vault's sidebar, and nothing else, and deleting vaults/*/index costs one rebuild the next time the vault is opened. Two things aren't cache. Deleting trust.json resets every approval, so every block goes back to pending and needs re-approving. Deleting vaults/*/storage deletes any data blocks have saved through globalThis.plicine.storage, which is real user data, not a cache, and won't come back.

Deleting library/ or community/ removes those installed blocks and themes from every vault that relies on them (rather than carrying its own copy in its .vault/blocks//.vault/themes/ folder), which will show up as "Unknown block" or a missing theme until they're reinstalled.

Deleting agents.json removes the agents you added; the built-in ones are found again. Deleting agent-settings.json forgets the models picked in the palette, so each agent goes back to starting a session on its own default. Deleting agent-palette.json brings the palette's note back. agent-cwd/ is made again whenever an agent starts.

Deleting dev/ discards every checkout, along with any local edits in them that haven't been pushed; nothing pushed is lost, since pushing writes straight into the vault. plicine block discard deletes one checkout the same way.

What the vault receives

Nothing generated lives in the vault. The single exception is blocks.lock, at .vault/blocks/blocks.lock, and even that is only written when resolving the vault's blocks actually produces a different lock than what's on disk (How blocks work covers when that happens). No cache, compiled output, or trust decision is ever written back into a vault folder.