{"id":"9814dbfe-1515-4779-a28e-278109b57d20","slug":"nanace","kind":"mcp","name":"nanace","summary":"Self-hosted team knowledge base with a desktop app, knowledge graph and semantic search — shared with Claude through MCP.","owner":"felichita","repoUrl":"https://github.com/felichita/nanace","license":"Apache-2.0","licenseTier":"permissive","trust":"community","stars":3,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-10-27T23:09:36.000Z","addedAt":"2026-10-28T09:29:22.016Z","logoUrl":"https://avatars.githubusercontent.com/u/17034704?u=3b0a1e5ca3c06bf1be7fd0c733362a649001cf9b&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["memory-knowledge","search","databases"],"tags":["claude","claude-code","desktop-app","knowledge-base","knowledge-graph","markdown","multi-tenant","note-taking"],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-28T10:36:12.677Z","sample":false,"url":"https://conduits.dev/tools/nanace","language":"Rust","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# nanace\n\nMulti-tenant markdown knowledge base with team-level RBAC and semantic search,\nexposed to Claude through MCP. See [ARCHITECTURE.md](ARCHITECTURE.md).\n\n## Quick start\n\n```bash\ncp .env.example .env          # set NANACE_JWT_SECRET (openssl rand -base64 48)\nmake run                      # starts PostgreSQL + Qdrant, then the API on :8080\n```\n\nThe first start downloads the `bge-small-en-v1.5` model (~130 MB) into\n`.fastembed_cache/`.\n\n### Google sign-in\n\n1. In Google Cloud Console create an OAuth client of type **Web application**\n   with redirect URI `http://127.0.0.1:8080/api/v1/auth/google/callback`\n   (use your `NANACE_PUBLIC_URL`).\n2. Set `NANACE_GOOGLE_CLIENT_ID`, `NANACE_GOOGLE_CLIENT_SECRET`, and\n   optionally `NANACE_GOOGLE_ALLOWED_DOMAINS` in `.env`.\n3. Open <http://127.0.0.1:8080/auth/complete>, sign in, and create a\n   personal access token (`read` scope is enough for Claude).\n\nWithout Google, `NANACE_DEV_LOGIN=true` gives a session for any email:\n\n```bash\ncurl -s -X POST localhost:8080/api/v1/auth/dev-login \\\n  -H 'Content-Type: application/json' -d '{\"email\":\"you@example.com\"}'\n```\n\n## Connect Claude (MCP)\n\nIn the desktop app, choose **Connect to Claude** in the sidebar. It\nregisters the bundled MCP server with Claude Code (all projects) and/or\nClaude Desktop, each with its own read-only token. Check **Allow Claude to\ncreate and edit pages** to let Claude save notes (\"save this to nanace\").\nTo set it up by hand instead, build the MCP server once:\n\n```bash\ncargo build --release -p nanace-mcp\n```\n\nClaude Code, with a personal access token:\n\n```bash\nclaude mcp add nanace \\\n  --env NANACE_API_URL=http://127.0.0.1:8080 \\\n  --env NANACE_TOKEN=nnc_pat_... \\\n  -- \"$PWD/target/release/nanace-mcp\"\n```\n\nClaude Desktop (`claude_desktop_config.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"nanace\": {\n      \"command\": \"/absolute/path/to/target/release/nanace-mcp\",\n      \"env\": {\n        \"NANACE_API_URL\": \"http://127.0.0.1:8080\",\n        \"NANACE_TOKEN\": \"nnc_pat_...\"\n      }\n    }\n  }\n}\n```\n\nInstead of `NANACE_TOKEN` you can set `NANACE_TOKEN_FILE` to a file that\nholds the token (re-read on every request; keep it readable only by you), so\nthe token stays out of config files.\n\nAgainst a development API you can use `NANACE_DEV_EMAIL=you@example.com`\ninstead of a token. `NANACE_ORG_ID` picks a default organization.\n`NANACE_ALLOW_WRITES=true` (with a write-scoped token) adds the\n`create_context` and `update_context` tools.\n\n### Work-log skill for Claude Code\n\n`integrations/claude-code/skills/nanace` is a Claude Code skill: `/nanace\nPROJECT-2345` records what was done in the current session on the task's page\n(appending a dated section, or creating the page). It treats each team as a\nwiki: before writing it reads the team's `Wiki: Schema` page (page types, title\nprefixes, templates, tags), and afterwards it updates the related `Topic:` page\nand `Wiki: Index`. A team without a schema is initialized from\n`schema-template.md`, and the new schema is marked as the default for review.\nIt needs the write tools (**Allow Claude to create and edit pages**).\n\nInstall or update the MCP server binary and the skill, and connect Claude Code\nto your server, with one command (run it again later to update). Point it at\nyour server with `NANACE_API_URL`; the default is `http://127.0.0.1:8080`:\n\nmacOS / Linux:\n\n```bash\n/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/felichita/nanace/HEAD/integrations/claude-code/install.sh)\"\n\n# with your server:\nNANACE_API_URL=https://nanace.example.com \\\n  /bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/felichita/nanace/HEAD/integrations/claude-code/install.sh)\"\n```\n\nWindows (PowerShell):\n\n```powershell\npowershell -ExecutionPolicy Bypass -c \"irm https://raw.githubusercontent.com/felichita/nanace/HEAD/integrations/claude-code/install.ps1 | iex\"\n\n# with your server, in a PowerShell window:\n$env:NANACE_API_URL = 'https://nanace.example.com'\nirm https://raw.githubusercontent.com/felichita/nanace/HEAD/integrations/claude-code/install.ps1 | iex\n```\n\nWhat the installer does:\n\n- **`nanace-mcp`**: downloads it from the latest GitHub release\n  (`NANACE_VERSION=v0.1.3` pins one), checks the `.sha256`, and installs it to\n  `~/.local/bin` (Windows: `%LOCALAPPDATA%\\nanace\\bin`; override with\n  `NANACE_BIN_DIR`). A `.nanace-mcp.version` file next to it records the release\n  and checksum, so a re-run downloads nothing when the binary is current,\n  updates it when a newer release exists, and repairs it when the file was\n  changed. The replacement is atomic, so a running server is not affected.\n  Published builds: macOS arm64, Linux x64/arm64 (glibc), Windows x64; on other\n  platforms (for example Intel macOS) the binary is skipped — use the desktop\n  app, which bundles it, or `cargo build --release -p nanace-mcp`.\n  `NANACE_SKIP_BINARY=1` installs only the skill.\n- **Skill**: copies it to `~/.claude/skills/nanace` (or\n  `$CLAUDE_CONFIG_DIR/skills/nanace`) from `NANACE_REF` (default `main`). An\n  older version is moved to `~/.claude/skill-backups/` first.\n- **Claude Code connection** (interactive, asks before changing anything):\n  connects Claude Code to the server at `NANACE_API_URL` (default\n  `http://127.0.0.1:8080`, for example\n  `NANACE_API_URL=https://nanace.example.com`). It opens\n  `<server>/auth/complete`, where you sign in and create a token with scope\n  `write`, reads the token with hidden input, checks it against the API, and\n  saves it to `~/.config/nanace/claude-code.token` (Windows:\n  `%LOCALAPPDATA%\\nanace\\claude-code.token`; override with\n  `NANACE_TOKEN_FILE`), readable only by you. If you belong to several\n  organizations it asks which one to use (or set `NANACE_ORG_ID`). Then it\n  runs `claude mcp add nanace --scope user` with `NANACE_TOKEN_FILE`, so the\n  token is never in a config file or a command line. A re-run reuses the saved\n  token, and does nothing when Claude Code already uses this `nanace-mcp`\n  (`NANACE_RECONNECT=1` connects again, e.g. after changing the server).\n  If Claude Code runs another copy (such as the desktop app's, which\n  **Connect to Claude** registers and the app updates), it asks before\n  switching. Without a terminal, or with `NANACE_CONNECT=0`, it only prints the\n  manual steps. New users need an account on the server and, to save pages, a\n  team role of `writer` from an admin; the installer warns when that is missing.\n\nTo install the skill from a local clone instead of GitHub:\n\n```bash\nNANACE_BASE_URL=\"file://$PWD/integrations/claude-code/skills/nanace\" integrations/claude-code/install.sh\n```\n\n## Desktop app\n\nRequires Node 20+ and the Rust toolchain. With the API running:\n\n```bash\nmake desktop-dev        # npm install + tauri dev\nmake desktop-build      # release bundle in apps/desktop/src-tauri/target/release/bundle\n```\n\nSign in with Google (opens your browser) or, against a dev server, with any\nemail. **Import Claude memory**, in the sidebar or ⌘K, turns Claude Code's\nmemory files (`~/.claude/projects/*/memory`) into linked pages. The app stores a personal access token named \"nanace desktop\" in the\nmacOS Keychain (or the OS keyring).\n\n## Docker\n\nThe `Docker` workflow publishes the backend image for `linux/amd64` and\n`linux/arm64` for every `v*` tag (pull requests only build it):\n\n```bash\ndocker pull ghcr.io/felichita/nanace-server:latest   # or :0.1.2, :0.1\n```\n\nThe image runs `nanace-server` as a non-root user on port 8080 and has the\nembedding model baked in, so it starts without internet access. It does not\ncontain `nanace-mcp`: Claude runs that on each user's machine (it ships with\nthe desktop app and the release archives) and points it at the server. Configure it with the same environment variables as\n`.env.example` (`DATABASE_URL`, `QDRANT_URL`, `NANACE_JWT_SECRET`,\n`NANACE_PUBLIC_URL`, ...); PostgreSQL must give it a non-superuser role (see\n`deploy/postgres/init.sql`). Probes: `GET /livez` (liveness: the process\nanswers, no dependencies touched) and `GET /readyz` (readiness: PostgreSQL and\nQdrant answer; `/healthz` is the same).\n\nBuild it locally with `docker build -t nanace-server .`, or run the whole\nstack (API server, PostgreSQL, Qdrant) with Docker Compose:\n\n```bash\nmake up      # docker compose --profile app up -d --build; settings from .env\nmake logs    # follow the server logs\nmake down    # stop it (data volumes are kept)\n```\n\nThe server listens on `127.0.0.1:8080`; set `NANACE_HOST_BIND=0.0.0.0` to\nshare it on your network.\n\n### Logs and metrics\n\n- **Logs:** `NANACE_LOG_FORMAT=json` writes one JSON object per line, for\n  Loki or ELK (the default is `text`). Request logs carry the path only,\n  never query strings.\n- **Metrics:** `NANACE_METRICS_BIND=0.0.0.0:9090` serves Prometheus metrics\n  on `/metrics` on that separate listener, so the public ingress never\n  exposes them. Docker Compose serves them on `127.0.0.1:9090`.\n  - `nanace_http_requests_total` and `nanace_http_request_duration_seconds`,\n    by method, route pattern and status;\n  - `nanace_embedding_duration_seconds`, by `passage` or `query`.\n\n### Upgrades\n\nBy default the server upgrades the database when it starts:\n\n- it applies the SQL migrations;\n- it runs the application upgrades written in Rust (for example backfilling\n  derived data);\n- if `NANACE_EMBEDDER` now selects a different model, it reindexes.\n\nOn Kubernetes, run that as a separate step instead (an init container, a\nJob, or a Helm pre-upgrade hook) and start the pods with\n`NANACE_RUN_MIGRATIONS=false`. They then refuse to start on a database that\nis behind or ahead of them:\n\n```bash\nnanace-server migrate --check   # report only; exit status 1 when something is pending\nnanace-server migrate           # apply, then exit\n```\n\nTo rebuild the vectors from PostgreSQL, for example after moving to a new or\nempty Qdrant or changing the embedding model, run the `reindex` command. It\nrecreates each organization's collection; search for that organization is\nincomplete until it finishes:\n\n```bash\ndocker compose --profile app run --rm server reindex            # all organizations\ndocker compose --profile app run --rm server reindex --org <id>  # one organization\ncargo run -p nanace-server -- reindex                            # from source, with .env\n``` Unverified email login stays off in Compose unless\n`NANACE_COMPOSE_DEV_LOGIN=true`.\n\n## Releases\n\nPrebuilt downloads are on the GitHub Releases page:\n\n- **Desktop app:** macOS (Apple silicon and Intel), Windows x64, and Linux\n  x64/arm64 (AppImage, deb, rpm).\n- **Server and MCP archives:** Linux x64/arm64, macOS arm64, Windows x64.\n\nTo publish a release, push a version tag:\n\n```bash\ngit tag v0.1.0 && git push origin v0.1.0\n```\n\nThe `Release` workflow builds everything on native runners, uploads it to a\ndraft release, and publishes it when every build succeeds. Builds are not\ncode-signed yet.\n\n## Development\n\n```bash\nmake check              # fmt, clippy -D warnings, unit tests\nmake test-integration   # API, RLS and MCP tests (nanace_test DB + qdrant-test)\nmake test-model         # real embedding model test\nmake desktop-check      # desktop: tsc, vitest, clippy, Rust tests\n```\n\n## License\n\nLicensed under the [Apache License, Version 2.0](LICENSE). Copyright 2026 Fedor Nikolaev.\n"}