Onyx can invoke Claude or Codex against local files, so the local HTTP boundary is deliberately narrow:
- Browser origins are allowlisted, never
*:null(file://), localhost, and127.0.0.1, plus anything listed in theallowed_originssetting. It holdsapp://obsidian.mdby default so the Obsidian plugin can reach the API; clearing it shuts the plugin out at the server. - The service binds to
127.0.0.1by default. - Host headers must be
127.0.0.1:<port>orlocalhost:<port>to block DNS rebinding. - Browser origins are limited to local origins and
file://'snullorigin. - A random per-process token is embedded in
ask.jsand required by all mutations and provider actions. - Context folders are resolved through symlinks and must remain under a trusted
root unless the explicit
--allow-anyescape hatch is used. - Claude is restricted to Read, Grep, and Glob on the folder, plus WebSearch,
WebFetch, and skills so an answer can check an outside fact before stating
it; Bash, Edit, and Write stay denied. No MCP servers load
(
--strict-mcp-config), so MCP tools your own Claude settings pre-allow never reach an answer. Codex runs headlessly with a read-only sandbox, live web search, no approvals, no user rules, and optional tool/plugin features disabled. Web access has one cost: a document's own text could try to steer a fetch, and whatever goes into its URL, to an outside site. Settings → Answers → Check outside facts on the web turns it off for both providers, Codex's cached search included, for faster answers from the folder and skills only. - Remote fetches reject credentials, non-HTML content, responses over 12 MB, and private, loopback, link-local, reserved, or multicast addresses unless private URLs are explicitly enabled.
- Local document assets use unguessable capabilities that expire after eight hours and are bounded to the 32 most recently opened documents. JSON and source-map assets are never served.
- Remote HTML scripts and inline handlers are removed. Trusted local HTML may run its authored scripts, but script connections stay restricted to the local service; plugins and base-URL changes remain blocked.
- File citations are displayed only after canonicalizing them and proving they exist inside the active context folder.
- Artifacts writes only symlinks and folders, and only inside its own folders: never through a linked folder (that would write into the tree it points at), never over an existing entry, never a link to something that contains the Artifacts folder. Moving, renaming, pinning and removing act only on an entry sitting directly in one of its own folders; removing deletes a link or an empty folder, never a real file. Link targets are never modified.
- Editing a note (⌘E) writes only the file the page was opened from. The
source is handed out against that page's own document capability, with an
edit capability for that one file; a save names no path. It writes only an
existing UTF-8 Markdown file, in place, never through a path that has
become a symlink, and never over a version newer than the one the editor
last read (the save is refused and the editor asks). A task's box on the
reading page ticks its line through that page's capability, against the
version the page shows. The editor's images come through
/_fsunder a capability of their own that lists only images the note as saved references. This is the reader's own write path: the provider runners still have no write tools.
Both executables are launched directly, not through an interactive shell. Before launch, Anthropic/OpenAI API-key and alternate-provider environment variables are removed so the verified subscription session is used.