David Kendig16 downloadsChat with local models (Ollama or LM Studio) inside your vault, with attachments, web search, and note integration.
Chat with your AI in Obsidian.
0.8.270 — BETA. Usable daily, but the interface and the stored data format may still change between releases. Requires Obsidian 1.13.0 or later.
Hephaestus, stripped down to run as an Obsidian plugin: a chat pane inside your vault, talking directly to a local model server. No backend process, no Electron shell of its own — the plugin runs entirely inside Obsidian.
/api/chat) and any OpenAI-compatible
server — LM Studio, llama.cpp, vLLM, LocalAI, Jan — selected in
settings. Both streaming and non-streaming paths are supported.| Hephaestus component | Here |
|---|---|
| Electron + React shell | Dropped — Obsidian is the host |
| Python FastAPI backend | Dropped — the plugin calls the model server directly from TypeScript |
| Ollama chat w/ streaming | Ported |
| Conversation history (SQLite) | Ported — plugin data (JSON), with images as files alongside it |
| Model picker | Ported — fed from /api/tags or /v1/models |
| Markdown rendering | Ported — Obsidian's own renderer |
| Accounts / login / encryption | Dropped — Obsidian is single-user |
| Web search | Ported |
| Thinking mode | Ported — Ollama only; no OpenAI-compatible equivalent |
| Image input | Ported — attachments to vision models |
| Code agent / working directory | Not yet — a vault-scoped agent is the natural sequel |
| Image generation | Dropped |
npm install
npm run build # type-checks and bundles to main.js
npm test # runs the unit tests
npm run dev # rebuild on save
Then copy (or symlink) this folder into your vault at
<vault>/.obsidian/plugins/hephaestus/ and enable it in
Settings → Community plugins.
Settings → Hephaestus → Server:
http://localhost:11434, the default.http://localhost:1234. Start the server from LM
Studio's Developer tab. Enter the base URL without /v1.
The Test button checks reachability and lists the models it finds.
Switching servers restores that server's default URL and clears the
remembered model, since names differ between them.A third option — a hosted "Cloud API key" provider — is built but not exposed; see CLAUDE.md.
Neither Ollama nor LM Studio has authentication, so never expose one
directly to the internet. Use Tailscale/WireGuard (simplest — nothing in
the plugin changes) or an SSH tunnel. A reverse proxy with bearer-token
auth will not work yet: the plugin has no setting for an
Authorization header.
Obsidian pages run from the app://obsidian.md origin, and model
servers only accept browser requests from origins they trust. For
streaming to work:
OLLAMA_ORIGINS=app://obsidian.md ollama serve
For LM Studio, enable CORS in the Developer tab. Without this the plugin
falls back to Obsidian's requestUrl, which bypasses CORS but cannot
stream — you get the full reply at once instead of token by token. It
still works; it just stops feeling live.
Windows, macOS, and Linux. The plugin is desktop-only (isDesktopOnly)
because it uses Node APIs for hardware detection — that excludes
Obsidian mobile, not any desktop OS.
GPU detection differs by platform, and everything degrades to "VRAM unknown" rather than failing:
| Platform | GPU name | VRAM |
|---|---|---|
| Windows | nvidia-smi, else WebGL renderer |
NVIDIA only |
| Linux | nvidia-smi, else WebGL renderer |
NVIDIA, plus AMD via sysfs |
| macOS (Apple silicon) | system_profiler |
Unified — shares system RAM |
| macOS (Intel) | system_profiler |
Discrete card VRAM |
On Apple silicon the GPU addresses system memory, so the fit check compares against ~75% of total RAM instead of looking for dedicated VRAM — a 30B model genuinely does fit on a 64 GB Mac, and reporting "no GPU detected" there would be wrong rather than merely unhelpful.
The context length is read from the model itself — Ollama reports it via
/api/show, LM Studio via its native API — and refreshed whenever you
switch models. Turn off Detect context window automatically to set
the number by hand. Detected values are capped at 131,072 tokens: some
models advertise far more than the machine can actually serve, and a
gauge scaled to a million tokens would read 0% forever.
That number drives three things: the gauge in the composer, trimming, and the window the server itself uses. When a request would overflow, the oldest messages are dropped and a notice says how many — a model that has quietly forgotten the start of a thread just looks like it got worse, so this is deliberately loud.
It is sent to Ollama as num_ctx. That matters more than it sounds:
without it the server runs at its own default no matter what this setting
says, so a request carefully trimmed to fit 20k tokens arrives and is
truncated to the default anyway — silently, with the dropped tokens never
reaching the model. It also means the setting costs video memory,
because the KV cache scales with the window. Lowering it is one of the
few levers that can make a large model fit on the GPU.
There is no equivalent on the OpenAI-compatible path: LM Studio and friends take the context length from how the model was loaded, not from the request.
Leave this on automatic. Ollama decides how much of a model to place on the GPU and is usually right, and the field is empty by default.
It exists for the case where it is not. On Windows the NVIDIA driver
will let a process overcommit video memory and page the excess through
system RAM, which thrashes over PCIe and is far slower than a clean CPU
offload — the difference between "slow" and "apparently frozen". The
symptom is a model performing much worse than the GPU share in the
context pane suggests it should. Capping the layers below what fits
sidesteps it. 0 runs entirely on the CPU. Ollama only.
Token counts are estimated at ~4 characters per token, and images are counted at a flat 800. Both are approximations meant to drive a gauge, not to match your model's tokenizer exactly.
The same pane reports video memory underneath the token table, because a
full context window is only one of the two reasons a reply crawls. It
shows card-wide VRAM in use (NVIDIA only — read live from nvidia-smi,
never cached) and, for Ollama, how much of each loaded model actually
sits on the GPU. The reading requires an external tool; see
what this plugin can reach for the exact
list and the setting that turns it off.
That last number is the one worth watching. When a model does not fit, Ollama splits it with the CPU rather than failing, and every token then has to cross the CPU-resident layers — so a model 80% on the GPU does not run at 80% speed, it runs at a small fraction of it. That is what a "hang" on a large model almost always is. The fix is a smaller model or quantisation, a lower context window (the KV cache grows with it), or freeing the card and reloading.
Obsidian marks Hephaestus as able to run shell commands and read files outside the vault, which is true and worth being precise about. Both capabilities exist for one feature — reporting your GPU and its memory — and both are behind Detect GPU and video memory in settings. Turn that off and the plugin spawns no process and opens no file outside the vault.
The complete list of what it runs, with the exact arguments:
nvidia-smi |
--query-gpu=name,memory.total,memory.used --format=csv,noheader |
system_profiler |
SPDisplaysDataType -json (macOS only) |
Both go through execFile with a fixed argument list, so no shell is
involved and nothing you type is ever part of a command. Both have
timeouts, and a failure — including the tool not being installed — is
swallowed and reported as "unknown" rather than raised.
The one file read outside the vault is
/sys/class/drm/card{0..3}/device/mem_info_vram_total, a Linux kernel
file that reports total video memory for AMD cards. The path is fixed,
it is read-only, and nothing is ever written outside the vault.
Two more things Obsidian reports, for completeness. CPU model, core
count and RAM come from Node's own os module — in-process, no
subprocess. And the plugin lists vault files (getFiles,
getMarkdownFiles) to populate the attach-from-vault picker and to run
the search_vault tool; it reads a file's contents only through
Obsidian's own cachedRead.
The model can call write_to_note to append to your open note. Untrusted
text reaches the model through web search results and attached files,
and either can contain instructions aimed at it. Every write therefore
shows a confirmation with the exact text first. The toggle in settings
can turn that off; leave it on.
assets/ Logo, inlined into the bundle at build time
manifest.json Obsidian plugin manifest
src/main.ts Plugin, chat view, settings tab, API clients
src/lib.ts Pure helpers (tokens, protocol translation) — tested
tests/ node --test suites over src/lib.ts
styles.css Chat styling on Obsidian CSS variables
esbuild.config.mjs Bundler config
esbuild.lib.mjs Builds src/lib.ts for the tests
Hephaestus is Apache-2.0 licensed (see LICENSE). It stands on the following work:
| Project | Used for | License |
|---|---|---|
| Obsidian API | Plugin, view, modal, and settings APIs the whole plugin is built on | MIT |
| obsidian-sample-plugin | esbuild.config.mjs is adapted from its build configuration |
0BSD |
| Lucide | Every icon in the UI, referenced by name through Obsidian's setIcon |
ISC |
| Ollama | The /api/chat and /api/tags protocol this plugin speaks |
MIT |
| llmfit | Inspiration for the System panel — hardware detection and model fit scoring | MIT |
| esbuild | Bundling | MIT |
| TypeScript | Type checking | Apache-2.0 |
| builtin-modules | Marking Node builtins external in the bundle | MIT |
Notes on what is and is not borrowed:
html.duckduckgo.com. That is a service, not a
dependency: no DuckDuckGo code is bundled, and use is subject to their
terms.