Skip to content
Sponsor

Security

Boltjar is built for one person running it on their own machine. This page says what that protects, and what it does not.

The server listens on 127.0.0.1 by default, and a request has to pass three checks:

  • Host. The Host header must name this machine (127.0.0.1, localhost, [::1], plus any names in BOLTJAR_ALLOWED_HOSTS). This stops DNS rebinding, where a web page points its own domain at your loopback address.
  • Origin. WebSockets and every request that changes something must come from a page served at the address the request went to (same host name and port), or from a client that is not a browser. A page that another local server serves on its own port is refused.
  • Token. /api, /ws, /stream and /audio need the install’s token, kept in user/data/token and created on the first start. A browser holds it as an HttpOnly, SameSite=Strict cookie. Other local clients (the MCP server, your own scripts) send Authorization: Bearer <token>. To rotate it, stop the server, delete the file, and start again.

The server hands the cookie only to a browser on this machine that talks to it directly: from a loopback address, to 127.0.0.1, localhost or [::1], with no proxy headers. A browser anywhere else opens http://<host>:<port>/?token=<token> once; the server sets the cookie and sends it on to the editor.

Together these keep the web pages you visit, and other machines, away from the API. They do not keep out other accounts on the same machine: anyone who can connect to 127.0.0.1 can ask for the cookie. Do not run Boltjar on a machine you share with people you do not trust.

Browsers send a cookie to every port of the host name that set it, so every other local web server you open under the editor’s host name (127.0.0.1 or localhost, whichever you use) receives the Boltjar cookie. A server that keeps it can call the API with the editor’s full access.

Running a graph is running a program, with your permissions. A graph someone else made can:

  • read the files its nodes can reach: the files folder (user/data/files), the shipped examples/, and the databases and stores under user/data;
  • capture your screen and your windows (Screen Capture, Window Capture, Foreground Window);
  • call any URL with HTTP Request, including localhost, your network and cloud metadata addresses;
  • send your keys anywhere, since {{secret.NAME}} resolves inside every field that takes a secret, HTTP Request’s URL, headers and body included.

Read a graph before you press On. A graph keeps running after you close the browser tab; only turning it Off, or stopping the server, stops it.

Treat webhook bodies, chat messages, HTTP responses and model output as untrusted. The DB node binds a wired {tag} as a value, never as SQL, but a URL built from a tag goes wherever the tag says. A model wired to HTTP, DB or file tools can be steered by instructions hidden in the text it reads.

Third-party node packs in packs/ are Python code that runs with your permissions: install only packs you trust. The MCP server gives the connected AI the same control of the API that the editor has.

Keep it on loopback. Boltjar has no user accounts, and the token is not a login: whoever holds it has full control, and plain HTTP shows it to anyone on the network path.

If other machines must reach the editor, put the server behind a reverse proxy that terminates TLS and authenticates every user itself, pass the original Host header through, and add every name clients use to BOLTJAR_ALLOWED_HOSTS (python -m boltjar serve --host <address> --allow-remote adds the bind address). A proxy that rewrites the Host to 127.0.0.1 and adds no X-Forwarded-For makes every client it serves look like a browser on this machine, and each one gets the cookie. Never port-forward or tunnel the whole server.

To receive webhooks from outside, expose only /hook/*. Those routes skip the token and host checks because outside services call them, so give every Webhook node a secret; callers send it in the X-Webhook-Secret header. The secret and the other credential headers never reach the graph. A Webhook whose secret is a {{secret.NAME}} that is not defined refuses every call, and so does one whose secret, converted to an input, is wired to a node that has no value yet.

Keys you add in the editor are stored in user/data/secrets.json, and keys you put in .env stay in that file. Both are plain text and both are ignored by git, so anyone who can read your user account’s files can read your keys.

A {{secret.NAME}} token reads the keys added in the editor and the provider keys (ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY, XAI_API_KEY, FISH_API_KEY, ELEVENLABS_API_KEY), never any other .env or environment variable. A graph that uses a secret nobody defined does not turn On, and validation names the secret.

A graph saved with {{secret.NAME}} tokens is safe to share. A key typed straight into a field is saved as plain text in the graph file, in its snapshots under user/autosave/ and in the browser’s local draft. Live values shown in the editor display a known secret (8 characters or longer) as its {{secret.NAME}} token.

Please report privately through GitHub’s private vulnerability reporting. Do not open a public issue for a vulnerability.