Zest

Security and privacy

On this page

Zest runs on your laptop and talks to nobody unless you ask it to.

What the server accepts

Submit (the button that starts your agent)

Your team's zest.json

A zest.json arrives with every clone, and it can name a build, a check or an extension: code that would run as you. Zest runs none of it until a person approves that exact command in that exact folder (zest trust). The approval is a fingerprint; change the command, or a file of the extension, and it waits again. Approvals live in Zest's config file, never in the repository. Built-in packs ship with Zest and need no approval.

For an IT team: the policy file

One JSON file outside any repository, writable only by an administrator, turns features off for everyone on the machine. The editor and the command line both obey it, and the editor says what it fixed.

PlatformWhere
macOS/Library/Application Support/Zest/policy.json
Linux/etc/zest/policy.json
Windows%ProgramData%\Zest\policy.json
anythe path in ZEST_POLICY
{ "runner": false, "generate": false, "allowHosts": ["cdn.jsdelivr.net", "api.openverse.org"], "extensions": false, "message": "Ask IT before turning these on." }
KeyEffect
runner: falseNo Submit; the editor copies the prompt instead
generate: falseNo image generation
network: falseNo network at all: library, search, downloads, generation
allowHostsOnly these hosts (and their subdomains), checked at every redirect
extensions: falseNo team extensions from repositories (Zest's own packs still work)
updates: falseNo daily check for a newer release
messageShown next to anything the policy turned off

A policy file that is not valid JSON locks everything it could lock, rather than nothing.

What Zest can touch, on screen

More > Setup > What Zest can touch lists the folder the server serves and the address it listens on, the files it writes, whether Run is on and exactly what a run may do, where each key came from, what the repository asks to run and whether you approved it, which extensions are loaded, the policy in force, and every network call the server has made since it started: the host, why, the status and the size.

The Zest service

The service runs at login, listens on 127.0.0.1:4470 only, and is the one server Zest needs: it runs the editor for every page in its own process (Editing in place).

Keys

What goes online, and when

When youZest calls
Open the Library tabcdn.jsdelivr.net (the starter library manifest and pictures)
Searchapi.openverse.org, or api.pexels.com with your key
Generateapi.openai.com or generativelanguage.googleapis.com, with your key
Pick an image from search, or paste an image addressThe host of that image, to copy it into assets/
Run zest doctorThe library CDN and Openverse, to check they answer
Start the editor or Claude Code, at most once a dayapi.github.com, for this repo's list of releases, so Zest can say when a newer one is out. One GET with no cookies, keys or page content

Previews come through your local server, so your browser never calls those hosts directly. Before every download, and again at every redirect, Zest resolves the host and refuses it if any address is private or local (including spellings like localhost. and [::ffff:127.0.0.1]). Every download has a size cap and one deadline for the whole answer, so a slow host cannot hang the editor. There is no analytics, no telemetry and no account. The one call Zest makes without being asked is the daily release check in the last row: ZEST_UPDATE_CHECK=0 turns it off on one machine, and "updates": false in the policy file turns it off for everyone on it.

What it writes

Your HTML file (only the bytes you change), images in assets/, and its own .zest/ folder beside the page. When a round your team splits into tasks runs in a git checkout, Zest also records the checkout as snapshot commits under refs/zest/ (local refs: git push does not send them unless you name them), makes and removes worktrees in your temporary folder, and writes the source changes your notes asked for into the checkout. Evals keep their cases and runs in .zest/evals/ at the top of the main checkout, which every worktree of the repo shares. It never commits, never moves a branch and never touches your index or staging. zest gc drops old snapshots.

The ledger

Zest keeps one more file, outside every checkout: ~/.zest/ledger.jsonl (or ledger.jsonl in $ZEST_HOME). One line per event: a note a round took, the agent's reading of it, what the round changed, and what happened next (your Right or Not right, a note you left on the same spot, a hand edit or a restore that undid it, the change reaching main). It is there so the record of what your agent got right and wrong outlives the worktree a round ran in, and spans every page on this machine.

What it stores: your notes' words, where each pointed (a selector, a tag, a few words of the element's text), the page's address and, when the editor sends it, a cut-down excerpt of the page around the note (at most 8 KB), the agent's answers, file names and line counts from each round (not the files' contents), the words before and after when you hand-edit what a round wrote (cut to 200 characters), the repo's folder name, its origin address with any user name or token taken out, the branch and commit, and this machine's name. It stays on your machine, readable by you alone (mode 600). Nothing in Zest sends it anywhere. An extension your team wrote and you approved can copy each row as it is written (ledger.append, see Your team's setup); that copy goes wherever the team's code sends it.

ZEST_LEDGER=0 turns it off: nothing new is written, and no row reaches an extension. Deleting the file loses the record and nothing else.

Mailboxes and the waiting list

When Submit hands notes to an agent already working in a page's checkout, Zest writes them to ~/.zest/mailbox/<agent id>.jsonl: the notes' words, where each is pinned, a summary of what the page showed (its address, scroll and section, never its markup), the page's path and the repo's name and origin. A page whose Submit waits for that agent to go quiet is listed in ~/.zest/waiting.json. Both stay on your machine, readable by you alone (mode 600). The agent id comes from your team's extension; Zest keeps only letters, digits, ., _ and - of it, so an id can never name a file outside the mailbox folder. Nothing in Zest sends either file anywhere; the extension's deliver hook is told an entry was written and is handed that entry.

Reporting a problem

Report it privately on GitHub: Security > Report a vulnerability on the repository. Please do not open a public issue for a security problem. SECURITY.md says what happens next.

Suggest a change to this page