one server. no resistance.
A persistent LSP process manager daemon for Neovim. Neovim starts a fresh server per session — ohm replaces that with one shared server per {root_dir, language} pair, fixing memory bloat, stuck diagnostics, and monorepo duplication at the daemon layer.
Neovim instances (any number)
↕ stdio (LSP JSON-RPC via ohm --client bridge)
ohm daemon — fan-out multiplexer, request ID rewriting
↕ stdio (LSP JSON-RPC)
LSP servers (gopls, rust-analyzer, tsserver, ...)
{root_dir, language} pair, shared across all Neovim sessions.textDocument/didClose before detach to prevent stuck diagnostics.shutdown/exit so individual session closes don't kill the shared server.Step 1 — download the binary for your platform:
# Linux x86_64
curl -L https://github.com/ryan-WORK/ohm/releases/latest/download/ohm-linux-amd64 -o ~/.local/bin/ohm
# Linux arm64
curl -L https://github.com/ryan-WORK/ohm/releases/latest/download/ohm-linux-arm64 -o ~/.local/bin/ohm
# macOS Apple Silicon
curl -L https://github.com/ryan-WORK/ohm/releases/latest/download/ohm-darwin-arm64 -o ~/.local/bin/ohm
# macOS Intel
curl -L https://github.com/ryan-WORK/ohm/releases/latest/download/ohm-darwin-amd64 -o ~/.local/bin/ohm
chmod +x ~/.local/bin/ohm
Make sure ~/.local/bin is on your PATH. Verify with ohm --help.
Step 2 — add the plugin (no build hook needed):
{
"ryan-WORK/ohm",
config = function()
require("ohm").setup()
end,
}
Requires Go 1.21+.
{
"ryan-WORK/ohm",
build = "./build.sh",
config = function()
require("ohm").setup()
end,
}
build.sh compiles the binary into bin/ohm inside the plugin directory on install and update.
mason registry submission in progress — project needs more stars.
# download
curl -L https://github.com/ryan-WORK/ohm/releases/latest/download/ohm-linux-amd64 -o ohm
chmod +x ohm
# verify checksum (replace hash with value from checksums.txt on the release page)
echo "<sha256> ohm" | sha256sum -c
Pass the path explicitly if not on PATH:
require("ohm").setup({ binary = "/path/to/ohm" })
require("ohm").setup({
-- Path to ohm binary. Auto-detected from bin/ohm in plugin dir or PATH.
binary = nil,
-- Unix socket path for the control channel.
socket = vim.fn.stdpath("data") .. "/ohm.sock",
-- Enable verbose daemon logging (useful for debugging LSP issues).
debug = false,
})
| Command | Description |
|---|---|
:OhmStatus |
Show active servers: PID, language, memory, refs, last response |
:OhmRestart |
Stop and restart the daemon |
# build
go build -o bin/ohm .
# run tests
go test ./...
# run daemon (silent by default — only warns/errors surface)
mkdir -p tmp && go run . tmp/ohm.sock
# run daemon with verbose logging
mkdir -p tmp && go run . --debug tmp/ohm.sock
See docs/architecture.md for a deep dive: two-socket design, request flow, ID rewriting, initialize caching, respawn, and the concurrency model.
MIT