Security
What the attack surface is, and what it is not.
Zephyr runs entirely on your machine. There is no Zephyr server, no account system, and no hosted API. That makes the surface small, and it makes it worth writing down precisely. This page describes the one network service the app runs, how it authenticates, how releases are signed, and what ends up in your profile folder.
Scope of this report
A local desktop app, not a web service.
Because Zephyr is a desktop application, the server-side attack surface is the loopback MCP endpoint and nothing else. There is no multi-user backend, no session store, no database, no file upload handler, and no auth service to compromise. Every other part of the app is client-side code running with the privileges of the user who launched it.
If you are reviewing this for a deployment that runs Zephyr as a shared service on a server, note that Zephyr is not designed for that and you would be deploying something the project does not support.
Reporting
Found something? Here is where to say it.
For anything that looks exploitable, or that would expose data belonging to another user on the same machine, use GitHub Security Advisories so the report stays private until a fix exists.
Private report
Use the security advisory flow on the repository for anything sensitive. It creates a private thread, so details are not public until a release exists.
Public issue
Crashes, install problems, hardening suggestions, and anything you are not sure about go in the issue tracker. Those are public and there is usually no need to keep a bug private.
What to include
- Version, shown in the About panel and in the window title area.
- Platform and Windows build, or Linux distribution.
- The MCP server state: on or off, port, and whether the token was present.
- Steps to reproduce, with the exact request that triggered it if one did.
- What an attacker gains, not just what breaks.
Give the maintainer a reasonable window to ship a fix before disclosing publicly. There is one person on this project, so that window is measured in weeks, not hours.
MCP server
The only network listener in the app, on port 9222.
The server speaks HTTP JSON-RPC. A CLI connects to it, sends tool calls, and gets editor state back. The controls below are the whole model; there is no TLS because the socket never leaves loopback.
Loopback only
The server binds 127.0.0.1 on port 9222. It is not reachable from another machine, and it is not listening on a public interface even if the firewall allows it.
Bearer token on every call
A token is generated when the server starts. Every request must carry it in the Authorization header. Without a valid token the server answers with a rejection and does no work.
It can be turned off
There is a setting that disables the server entirely. When it is off, nothing binds the port and the feature is not reachable at all.
editor_write stays in the buffer
The write tool mutates the in-memory editor buffer. It does not call the file system, does not save, and does not touch your files on disk. Saving is a separate, explicit act.
/health is inert
The health endpoint needs no token and returns a small status document. It performs no UI work, reads no file, and exposes nothing beyond whether the server is alive.
The honest limitation: any process on the same machine running as your user can reach the port, and loopback offers no protection from a process that already has your privileges. The token raises the cost of an accidental or casual caller. It is not a boundary against malware, and it is not meant to be.
Release signing
Signature checked before the update runs.
Release artifacts are signed with minisign. The public key is compiled into the binary, and Zephyr verifies the signature before it applies an update. A download that was modified in transit, or an artifact that was replaced, fails verification and is discarded rather than installed.
SmartScreen will probably warn you
The installer is not signed with a paid CA certificate, so Windows Defender SmartScreen may show an unknown-publisher warning on first run. That is expected and it is not a defect. A code-signing certificate is a recurring cost for a solo project with no revenue behind it, and the minisign check on updates is the mechanism that actually protects the update path.
For the same reason, the app is not notarized and may be refused by Gatekeeper on macOS. There is no macOS build, so this only matters if you are running it under a compatibility layer.
Current release: v1.1.11. Signatures and checksums are published on the releases page.
Data handling
What sits on disk, and what does not leave it.
Everything the app persists lives in one folder in your user profile: %APPDATA%\zephyr on Windows. Nothing is written anywhere else, and nothing is sent to the project or to any third party.
Settings
Theme, layout, font size, and editor preferences.
Editor history
Recently opened folders and files, so the window can reopen where you left it.
Extension list
The extensions found in the extensions folder, loaded at startup.
Logs
Local diagnostic output. These stay on the machine and are never uploaded.
Provider keys
Only if you enter one. See the note below on how it is written to disk.
Stored keys are obfuscated, not encrypted
If you paste a provider API key, it is obfuscated with XOR and BLAKE3 before it is written to disk. That is deliberately described as obfuscation: XOR is reversible by anyone who knows the key derivation, and the scheme exists to stop a key from being read out of a config file by accident. It is not cryptography and it is not a defense against anything deliberate. Any process running as your user can recover the key.
If that trade-off does not work for your setup, do not store keys in the app. Set the environment variable your CLI expects and leave the field empty.
No telemetry, no analytics, no crash reporting
- No usage counters, no unique identifiers, no device fingerprinting.
- No analytics or crash-reporting SDK anywhere in the build.
- No update pings. The updater fetches a manifest and stops there.
- No data sold, shared, or brokered, because there is no data to share.
The full breakdown is on the privacy page.