Security

Who can reach the server

The OAuth flow is deliberately public: it exists so that Claude Code’s discovery handshake completes, and there are no real credentials behind it. Anyone who can reach the server’s address can use every tool it exposes, so access control has to come from the network layer. Do not expose the server to the public internet. Run it on a private network, or an overlay network such as Tailscale, so that only devices you trust can reach it.

Treat the server as having the same reach as its most permissive vault: whatever the process user can read and write in a configured vault, a connected client can too.

Limiting what a client can touch

The config file’s denyPaths blocks specific folders, globally or per vault, and is enforced in the server before a tool touches any file. It is a coarse guard, not a substitute for network access control, and it has one exception: query-graph answers from a graphify knowledge graph without consulting the deny list, so a graph built over denied folders can reveal their content. Build the graph from a vault without that content, or don’t use the tool.

denyBinaryPaths is a narrower, read-only guard for binary files. It stops read-binary-file returning a protected file’s bytes, and stops move-binary-file (and move-note, for a non-markdown file) moving a protected file out of protection, so the agent can’t relocate a file to read it. .trash is protected along with it, and read-note refuses a protected non-markdown file. It doesn’t affect markdown notes, and it doesn’t hide names: list-notes with extension still lists a protected file, and its modification time.

Symbolic links are handled differently by different tools. read-binary-file resolves a link and checks the real location: it refuses a link that points outside the vault, and applies denyPaths and denyBinaryPaths to the target. The other tools, including read-note, judge a path as the client gives it and read through a link. If the process user can read the target, a connected client can read it with read-note by way of a symlink in the vault, whatever the deny lists say. Don’t put symlinks to sensitive files inside a served vault.

Other protections that apply to every request:

fetch-binary-file

This tool makes the server perform an outbound request to a URL chosen by the caller. On any deployment, the host it runs on may be able to reach private network services that a remote client should never be able to reach. Treating that as the main risk, the tool:

read-binary-file

This tool is read-only and makes no network request or subprocess call. It checks the path against denyPaths and denyBinaryPaths before any filesystem access, and checks the file’s size against readMaxBytes using stat before reading it, so an oversized or protected file is never loaded into memory. Symbolic links are resolved first, and only regular files are returned. The denyBinaryPaths matcher runs in time bounded by the pattern and path lengths, so a client can’t slow the server with a crafted path. The size limit matters because the whole file is base64-encoded into one response, which is a third larger than the file itself.

upload-binary-file

This tool reserves a one-time URL, and a file sent to it ends up in the vault, so the URL has to be hard to misuse. It is built like this:

What it doesn’t do: it is not authentication. The server has no real credentials (see Who can reach the server), so anyone who can call its tools can reserve a URL. The token limits one upload to one destination; it doesn’t decide who may upload. The URL also appears in the tool’s result, so it is visible to the model and in the conversation transcript, which is why it is short-lived and works only once.

The address check (the upload must come from the address that reserved it) adds a little: callers behind the same NAT share an address, and behind a reverse proxy the check does nothing unless trustedProxies is set. Set it only to proxies you run, because the X-Forwarded-For header from anywhere else is ignored; if the server can be reached directly as well as through the proxy, a forged header would otherwise defeat the check.

Reporting a problem

If you think you have found a security problem, please raise it through GitHub issues. Describe the problem without including a working exploit.