Skip to content

feat(flake): add reproducible Nix devShell and Linux package#785

Open
antono wants to merge 26 commits into
refactoringhq:mainfrom
antono:nix-flake
Open

feat(flake): add reproducible Nix devShell and Linux package#785
antono wants to merge 26 commits into
refactoringhq:mainfrom
antono:nix-flake

Conversation

@antono

@antono antono commented May 31, 2026

Copy link
Copy Markdown

What this adds

Five user-facing capabilities, all opt-in — no existing source files modified.

For Linux end users — desktop app

nix run github:refactoringhq/tolaria              # run once
nix profile install github:refactoringhq/tolaria  # install permanently

Produces a wrapped binary with WebKitGTK 4.1, libsoup3, and the bundled MCP sidecar baked into the closure. Works on x86_64-linux and aarch64-linux. The runtime PATH includes xdg-utils, desktop-file-utils, git, and nodejs_24 so the deep-link plugin's URI-scheme registration and the MCP ws-bridge sidecar both spawn correctly.

For everyone — standalone MCP server

nix run github:refactoringhq/tolaria#tolaria-mcp              # stdio MCP server
nix profile install github:refactoringhq/tolaria#tolaria-mcp  # adds tolaria-mcp + tolaria-mcp-bridge to PATH

Cross-platform (Linux + macOS, x86_64/aarch64). Exposes the same 7 vault tools the desktop app's sidecar provides — search_notes, get_vault_context, list_vaults, get_note, open_note, highlight_editor, refresh_vault — over stdio, ready to drop into a Claude Desktop / Codex / Gemini MCP config:

{
  "mcpServers": {
    "tolaria": {
      "command": "tolaria-mcp",
      "env": { "VAULT_PATH": "/home/you/Vault" }
    }
  }
}

A second binary tolaria-mcp-bridge exposes the WebSocket bridge (ports 9710/9711) for advanced users running the bridge standalone.

For contributors

direnv allow      # automatic on cd into the repo
# or
nix develop       # one-off shell

Per-OS dev shells on x86_64-linux, aarch64-linux, aarch64-darwin. Pins Node 24, pnpm 11 (via corepack), the Rust toolchain (fenix), cargo-tauri, and on Linux the full WebKitGTK / libsoup3 / GTK3 / Wayland / X11 / fonts / themes stack. shellHook sets GIO_MODULE_DIR, XDG_DATA_DIRS, WEBKIT_DISABLE_DMABUF_RENDERER=1, and GDK_BACKEND=wayland,x11 so pnpm tauri dev works on both Wayland and X11 sessions without extra config.

For distros and CI parity

nix build .#tolaria              # reproducible Linux binary
nix build .#tolaria-mcp          # reproducible MCP server bundle
nix build .#tolaria-node-modules # pnpm dep closure
nix flake check                  # treefmt + eval
nix fmt                          # nixpkgs-fmt + rustfmt + prettier (md/yaml)

Reproduces every output against pinned nixpkgs, honouring every pnpm.overrides entry and all 5 patchedDependencies from package.json.

Commits

feat(flake) — flake.nix + nix/ modules

Modern 2026 flake authoring:

  • flake-parts as the framework
  • fenix for the Rust toolchain, pinned through flake.lock
  • crane for the Rust build with cargoArtifacts caching
  • fetchPnpmDeps fetcherVersion = 3 for the pnpm store (lockfileVersion 9)
  • treefmt-nix for unified formatting

The Linux package output runs pnpm build && pnpm bundle-mcp && pnpm agent-docs (mirroring tauri.conf.json's beforeBuildCommand) so dist/ and src-tauri/resources/{mcp-server,agent-docs}/ are populated before cargo runs, then builds with --features tauri/custom-protocol so the binary runs in production mode — Tauri's is_dev() is gated on this cargo feature, not on cfg!(debug_assertions), so raw cargo build --release alone isn't enough.

A small Python helper (nix/merge-pnpm-config.nix) mirrors package.json#pnpm into pnpm-workspace.yaml inside the sandbox only — pnpm 11's strict frozen-install reads workspace config from pnpm-workspace.yaml, while this repo keeps overrides + patched deps in package.json#pnpm. The project source is never modified.

chore(direnv) — .envrc + .gitignore

  • .envrc bootstraps nix-direnv 3.0.4 via use flake
  • .gitignore excludes result, result-*, .direnv/

docs(readme) — README install + dev hints

  • New "### Nix (Linux)" install subsection with nix run / nix profile install commands
  • One-liner under "Linux system dependencies" pointing NixOS contributors to direnv allow instead of the distro packages

feat(flake) — tolaria-mcp output

  • nix/mcp-server.nix bundles mcp-server/index.js + ws-bridge.js with esbuild (reusing the existing tolaria-node-modules derivation) and installs bin/tolaria-mcp + bin/tolaria-mcp-bridge wrappers around node
  • Cross-platform: Linux + macOS, x86_64 + aarch64 — pure node, no native deps
  • tolaria-node-modules lifted out of the Linux-only branch since it's just files

Why Nix

Tauri 2 on Linux has a finicky native surface: WebKitGTK 4.1, libsoup3, GTK 3, GLib, Cairo, Pango, the GdkPixbuf loader set, openssl, the Wayland/X11 stacks, plus shell-outs to xdg-mime, update-desktop-database, and a node runtime for the MCP sidecar. The README's per-distro apt/dnf/pacman lists drift, NixOS contributors can't follow them at all, end users on Linux have no easy install path between Homebrew (macOS only) and downloading raw binaries from releases, and MCP-server users want the vault tools usable outside the desktop app too. The flake closes all those gaps with a single declarative pin.

CI continues to own AppImage / deb / dmg bundling — this PR does not replace that pipeline.

Scope and non-goals

  • Linux only for the desktop app. The Darwin devShell exists for contributors; Nix-built .app bundles are out of scope. tolaria-mcp does build on Darwin since it's pure node.
  • No AppImage/deb/dmg bundling via Nix. The Tauri AppImage bundler downloads at build time and fights the Nix sandbox; CI keeps producing release artifacts.
  • No CI changes. GitHub Actions still ships releases the way it does today.

Verification

  • nix flake check passes
  • nix develop enters a working shell on Linux (pnpm tauri dev works inside it)
  • nix build .#tolaria produces bin/tolaria (~26 MB) with the MCP sidecar resources embedded
  • nix run .#tolaria launches the production webview from tauri://localhost
  • nix build .#tolaria-mcp produces bin/tolaria-mcp + bin/tolaria-mcp-bridge
  • nix run .#tolaria-mcp initializes the stdio MCP server and tools/list returns the full 7-tool spec
  • nix fmt --fail-on-change is clean on all new Nix files

Test plan

  • nix develop on NixOS — verify shell entry and pnpm tauri dev
  • nix build .#tolaria from a clean checkout — confirms hash pins still resolve
  • nix run .#tolaria — confirms the webview loads bundled UI (no "Could not connect to localhost")
  • nix run .#tolaria-mcp registered in a Claude Desktop / Codex / Gemini MCP config — confirms vault tools are reachable
  • Existing GitHub Actions CI passes (no JS/Rust changes, should be a no-op)

antono and others added 26 commits June 1, 2026 02:07
- flake-parts root with fenix Rust, crane Tauri build, treefmt
- per-OS devShell on x86_64-linux, aarch64-linux, aarch64-darwin
  (Linux pulls WebKitGTK 4.1 + libsoup3 + GTK3 + Wayland/X11 stack
  with GIO_MODULE_DIR / XDG_DATA_DIRS / WEBKIT_DISABLE_DMABUF_RENDERER
  in shellHook)
- Linux package output: fetchPnpmDeps v3 honours pnpm overrides and
  the 5 patchedDependencies; crane builds src-tauri with custom-protocol
  feature so the binary runs in production mode; wrapped with
  webkitgtk_4_1/libsoup3/openssl runtime libs and xdg-utils +
  desktop-file-utils + git + nodejs on PATH for sidecar spawning
- nix/merge-pnpm-config.nix mirrors package.json#pnpm into
  pnpm-workspace.yaml inside the sandbox so pnpm 11 frozen-install
  validates against the lockfile without touching repo source
- treefmt module wires nixpkgs-fmt + rustfmt + prettier (markdown/yaml
  only; Biome keeps owning JS/TS)
- .envrc bootstraps nix-direnv 3.0.4 via `use flake`
- .gitignore excludes `result`, `result-*`, `.direnv/`
- Nix install subsection: nix run / nix profile install from the flake
- Linux deps section: NixOS contributors can use direnv allow / nix develop
- nix/mcp-server.nix bundles mcp-server/index.js and ws-bridge.js via
  esbuild (reusing the existing tolaria-node-modules derivation), then
  installs wrapper scripts at bin/tolaria-mcp and bin/tolaria-mcp-bridge
- Cross-platform: tolaria-mcp builds on every supported system (pure
  node, no native deps), so Darwin contributors can `nix run .#tolaria-mcp`
  too. tolaria (the desktop app) stays Linux-only.
- Drops the isLinux gate on tolaria-node-modules since it's pure files
Crane now uses a buildToolchain without rustfmt or rust-analyzer, so
nix build .#tolaria no longer waits on the IDE-only fenix substitutes.
The devShell keeps the full toolchain.
cargo build --release doesn't need clippy, rustfmt, rust-analyzer, or
rust-src — move them to the devShell toolchain only. Shrinks the
.#tolaria build graph; the runtime closure was already clean.
- Add makeDesktopItem-generated club.refactoring.tolaria.desktop so
  launchers discover Tolaria after nix profile install / home-manager.
- Install icons at five hicolor sizes (32-512) under apps/ matching the
  desktop file's Icon= name.
- Drop the silent || true fallback for the icon install.
.#tolaria and .#default now ship the desktop app together with the
tolaria-mcp stdio server and ws-bridge, so installing the app always
provides the MCP binaries for Claude Desktop / Codex / Gemini configs.
.#tolaria-mcp stays exposed for headless server installs.
- Install src-tauri/resources/{mcp-server,agent-docs} into
  $out/share/tolaria/resources/ after the cargo build.
- Set RESOURCEPATH=$out/share/tolaria in the wrapper so
  mcp::mcp_server_dir finds index.js + ws-bridge.js at runtime.

Fixes "mcp-server not found" when launching tolaria from nix profile
install / home-manager (exe-dir search never reached share/).
nixpkgs-fmt brings flake.nix in line with the formatter so
`nix flake check` no longer fails on the treefmt derivation.
Disable rustfmt + prettier in the treefmt programs set until
a dedicated pass cleans up the existing markdown/yaml/rust
files; `nix fmt` continues to format Nix sources.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant