@bastra-recall/daemon

中文文档
1.1.8发布于 2026/10/31MITES Module构建来源已验证

MCP server over a markdown vault: recall, load_memory, save_memory, edit_memory, archive_memory and document tools — plus the REST gateway, the quiet hook lanes and the bastra CLI.

原始使用文档
文档目录12 节
  1. What it does
  2. Surfaces
  3. Tools exposed (MCP + REST symmetric)
  4. Install + register
  5. Daemon process check
  6. Daemon startup
  7. Dev workflow
  8. Configuration (environment variables)
  9. Telemetry
  10. Search ranking
  11. Schema reference
  12. Limitations

@bastra-recall/daemon

The MCP server + HTTP gateway behind bastra-recall. Watches a markdown vault, indexes it with BM25 + optional embeddings, and exposes a stable tool surface over both stdio MCP and HTTP REST.

For project-level docs (vision, install, REST API, roadmap), see the top-level README and PLAN.md.

What it does

  • Watches a directory of .md files with YAML frontmatter (schema in ../../docs/memory-schema.md).
  • Indexes via minisearch BM25; recall_when is the highest-weighted field because it's authored for triggering.
  • Hybrid recall: optional embeddings (Ollama or OpenAI) via Reciprocal Rank Fusion on top of BM25 — see BASTRA_EMBEDDING_PROVIDER.
  • Save path: writes .md files, validates frontmatter (zod), force-reindexes so save+recall in the same turn are consistent.
  • Auto-related enricher: for each new save, fills related_via with cosine ≥0.7 neighbors.
  • Memory graph: multi-hop recall (expand_hops: 1) returns 1-hop neighbors via related_via.
  • Sensitivity filter: private memories aren't visible to external MCP/REST callers — and since #464 not writable by them either. The permission is bound to the transport, not passed as a tool argument, and a refused read and a refused write answer alike, so neither is an existence oracle.
  • Staleness re-ranking: memories with valid_until / expires_after_days / last_reviewed_at get demoted (or excluded if expired) at recall time.

Surfaces

SurfaceEntry pointUse case
MCP (stdio, standalone)dist/index.jsSingle-client setup; each session spawns its own embedded daemon
MCP forwarder (stdio → loopback HTTP)dist/mcp-forwarder.jsDefault for multi-client setups. Starts a missing daemon on demand, first giving an installed managed macOS service up to ten seconds. All sessions share one vault state, one index, one telemetry stream
HTTP RESThttp://127.0.0.1:6723/api/v1/{tool}Non-MCP clients (web apps, scripts, legacy Custom GPT Actions). Bearer auth + CORS supported
MCP over HTTPPOST /api/v1/mcp (Streamable HTTP, stateless)Remote clients such as ChatGPT web/phone and claude.ai, through your own HTTPS tunnel. Bearer token or the daemon's own OAuth 2.1 login. Remote access
Hooksdist/hook.js (PreToolUse), dist/session-hook.js (SessionStart)Both POST to the daemon's /hook/recall
CLIdist/cli.js (bastra bin)Install / uninstall / doctor across every supported AI client

Tools exposed (MCP + REST symmetric)

ToolPurpose
recall(query, k?, scope?, type?, expand_hops?)Search the vault; hybrid BM25 + embeddings when enabled
load_memory(id)Fetch full frontmatter + body
save_memory({title, type, body, …})Write a new memory with schema validation + force-reindex
edit_memory({id, str_replace?, append?, frontmatter?, expected_revision?})Patch one existing memory through the same save path (#519)
find_document(query, k?)Search documents (PDFs, photos, contracts)
read_document(id)Load extracted text + metadata for a document
open_document(id)macOS-only: open in the system handler
find_code(query, mode?, repo?, depth?)Locate a symbol or file in the code graph; one hop of dependents (#576)
save_document / recategorize_document / move_documentDocument write path (Pro Mac-app uses this; OSS callers may need BASTRA_DOCUMENT_WRITE=1)

Install + register

See the top-level README. Paths, in order of friction:

  1. curl -fsSL https://bastra.io/install | bash — installs Homebrew + tap + binary + runs the guided setup. The recommended non-developer path.
  2. Install Bastra.command doubleclick — same steps, but a browser download is quarantined and mode 644, so it needs right-click → Open (#320).
  3. bastra install all — single CLI call that registers MCP + Skill + the default quiet Hooks across Claude Code, Claude Desktop, Codex/ChatGPT Desktop, and Cursor. On a first run with no vault configured, an interactive install offers to create ~/BastraVault for you (non-interactive/--yes/--dry-run runs keep the deterministic error).
  4. Fully manual JSON snippets — fallback.

All paths end with the client configs patched. Whether the daemon is already running differs: the interactive Map wizard starts it, and the MCP forwarder starts it on the first tool call — bastra install all only registers, it neither starts nor health-checks the daemon. bastra doctor is what probes the endpoint (#525). Use bastra doctor --fix to repair stale paths, missing required hooks, or a stale Skill copy after an update. The quiet Stop save-eval hook is enabled by default by the CLI. Opt out with bastra install claude-code --no-stop-hook; deliberately disabling this optional hook does not make Doctor fail.

Daemon process check

Don't grep for daemon/dist/index.js — the daemon is often launched with a relative path (node dist/index.js) and won't match. Use the port instead:

lsof -i :6723 -P -n      # who owns the daemon port
curl -sS http://127.0.0.1:6723/health

Exactly one PID should be listed. Two means a stale daemon is running in parallel — the HTTP port goes to whichever bound first, and the loser exits silently (see the EADDRINUSE handler in http-listen.ts).

Daemon startup

The MCP forwarder uses one shared daemon, giving an installed managed macOS service up to ten seconds before fallback spawn. For REST clients that need the daemon before an MCP client connects, start it explicitly:

bastra-recall &

Dev workflow

npm run dev                # ts-watch via tsx (no compile step)
npm run build              # tsc + chmod +x all dist binaries
npm run check:types        # type-check only
npm run smoke              # smoke-test recall against fixtures/sample-vault
npm run smoke:telemetry    # smoke-test telemetry append
npm run backfill:related   # populate related_via on legacy memories

Configuration (environment variables)

Every on/off switch reads 0, false, off or no as off and 1, true, on or yes as on, in any case; the table shows the canonical spelling.

env varrequireddefaultmeaning
BASTRA_VAULT_PATHyes—absolute path to the vault root (memories are auto-discovered)
BASTRA_HTTP_PORTno6723loopback HTTP port for REST + hooks; read only when neither URL var below is set
BASTRA_HTTP_URLnoderivedfull URL override (for non-loopback testing); read only when BASTRA_DAEMON_URL is unset
BASTRA_API_TOKENnounsetthe bearer REST /api/v1/* requires from every caller that is not a direct local one; unset/empty does not open the API — such callers then get a 401 nothing can satisfy until a token is minted (#526)
BASTRA_AUTH_LOOPBACK_SKIPno1the token-free path needs a loopback peer and a present, loopback Host header — a missing header counts as foreign (#526); set to 0 to require the bearer even for direct 127.0.0.1 callers
BASTRA_CORS_ORIGINnounset (deny all)comma-separated browser-origin allowlist; unset = no browser origin allowed; * = explicit permissive opt-in (tunnel/dev)
BASTRA_EMBEDDING_PROVIDERnounsetollama or openai; without it the daemon stays BM25-only
BASTRA_EMBEDDING_MODELnoprovider defaulte.g. embeddinggemma (ollama) or text-embedding-3-small (openai)
BASTRA_OLLAMA_URLnohttp://localhost:11434ollama provider endpoint
BASTRA_ALLOW_REMOTE_OLLAMAnounsetallow a non-loopback BASTRA_OLLAMA_URL; without it the daemon refuses a remote endpoint, so a mistyped URL never sends memory text off-box (guards both the embedding provider and the reranker)
BASTRA_OLLAMA_KEEP_ALIVEno10mper-request keep_alive window — how long Ollama keeps the embedding model in RAM after each embed
BASTRA_OLLAMA_IDLE_UNLOAD_MSno600000 (10 min)unload the embedding model from Ollama RAM after this long without an embed (battery saver); 0 disables
BASTRA_BATTERY_SAVERnounset (→ battery.saver, default off)battery mode on macOS (#632): 1 / 0 override the settings file. On battery, doc2query waits for AC, warm-ups are skipped and the idle unload fires after 60 s — see USAGE
OPENAI_API_KEYnounsetrequired when BASTRA_EMBEDDING_PROVIDER=openai
BASTRA_FORWARDER_SPAWNno1when 0, the MCP forwarder will not auto-spawn the daemon. This remains a workaround for service-only starts, with BASTRA_DAEMON_URL naming that owner. macOS managed services now get a ten-second chance automatically; a headless service behind a forwarder waits for its idle exit (#758). Linux managed setup follows in #566
BASTRA_DAEMON_URLnohttp://127.0.0.1:6723which daemon this machine means — highest-precedence input to the single endpoint resolver every surface uses since #531 (CLI, doctor, map, hooks, forwarder, bridge, and the daemon's own bind), and what bastra install writes into a client registration
BASTRA_HOOK_TIMEOUT_MSnoper laneoverrides the lane's wall-clock budget before fail-silent — the per-lane defaults are in docs/hooks.md (#305)
BASTRA_VECTOR_DEADLINE_MSno150hook path only: how long a recall waits for the dense arm before serving BM25-only. Bounds that stage, not the call — measured warm the arm costs 87–96ms (total 106–113ms), cold 668ms (total 694ms), so 150ms passes every warm call and caps a cold one near 180ms. The embed is abandoned, not cancelled, so the model still finishes loading and the next call is warm. Degradations are visible as degraded: "vector-arm-timeout"; 0 disables (kill switch)
BASTRA_HOOK_MAX_SHOWno1how often the same memory may appear in <recall-hints> per session (4h window); a load_memory of that id resets the counter
BASTRA_HOOK_CONTENT_RECALLnooffset to 1 to run the opt-in edit-content recall arm (#282)
BASTRA_DOCUMENT_WRITEnounsetset to 1 to expose document write tools
BASTRA_LOG_PATHno~/.bastra/logstelemetry JSONL output directory (out-of-vault on purpose)
BASTRA_TELEMETRYnoonset to off to disable telemetry writes entirely

Telemetry

Every recall, load_memory, save_memory and hook call appends one JSON line to events-YYYY-MM-DD.jsonl in the log dir. Each daemon process gets a fresh session_id; recalls get a recall_id and any load_memory / save_memory within 5 minutes references that id as follows_recall.

That's enough to compute:

  • recalls per session, hit counts, top-score distribution, latency p50/p95
  • recall→load_memory follow-through rate (proxy for "was the hint useful?")
  • saves per session, type/scope distribution, % overwrite vs. new
  • save→follows_recall rate (was a duplicate-check done?)
  • per-hook latency and hit-quality (where do PreToolUse / SessionStart actually help?)

Logs live outside the vault on purpose so the file watcher doesn't index them. Tail one to watch live: tail -f ~/.bastra/logs/events-$(date +%F).jsonl.

Search ranking

Field boosts (in src/search.ts):

recall_when_flat:          5    ← authored for triggering, highest weight
title:                     4
tags_flat:                 3
recall_when_expanded_flat: 2    ← doc2query paraphrases (#117): machine-generated, below the author's words
topic_path_flat:           2
summary:                   2
body:                      1

Fuzzy distance 0.2, prefix matching enabled, combineWith: "OR". Hybrid mode combines BM25 with embedding cosine via Reciprocal Rank Fusion (RRF); the embedding query goes to the provider once per recall and is cached in-memory.

Schema reference

See ../../docs/memory-schema.md for the full memory frontmatter spec. The daemon enforces required fields via zod on load — files that fail validation are skipped with a warning to stderr.

Limitations

  • Shared process. The forwarder starts the daemon on demand. On macOS, bastra autostart on manages an optional LaunchAgent for keeping it running; use bastra autostart off to return to on-demand operation.
  • Cloud-storage mounts (Google Drive, iCloud, Dropbox) need polling-mode in chokidar — enabled by default, but watcher latency can be a few seconds.
  • Cursor rules are per project. bastra install cursor registers MCP. Run bastra rules cursor inside each project to install the shared memory guidance.

版本与发布记录

@bastra-recall/daemon 共发布 27 个版本,首次发布于 2026/06/23;过去 12 个月发布了 27 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.1.8latest2026/10/312 天前
1.1.12026/10/1717 天前
1.1.02026/10/1123 天前
1.0.12026/10/0429 天前
1.0.02026/09/141 个月前
0.9.22026/08/272 个月前

关于 @bastra-recall/daemon 的常见问题

@bastra-recall/daemon 是什么?

MCP服务器在markdown vault上:recall, load_memory, save_memory, edit_memory, archive_memory和文档工具-加上REST网关,安静的钩子通道和基础命令行。

如何安装 @bastra-recall/daemon?

在项目目录运行 npm install @bastra-recall/daemon,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 bastra、bastra-recall-mcp、bastra-recall-hook、bastra-recall-bridge、bastra-recall-daemon、bastra-recall-gate-hook、bastra-recall-stop-hook、bastra-recall-todo-hook、bastra-recall-prompt-hook、bastra-recall-session-hook、bastra-recall-bash-pre-hook、bastra-recall-bash-fail-hook,可以用 npx bastra 直接运行。

@bastra-recall/daemon 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/bastra-recall__daemon 是否存在。

@bastra-recall/daemon 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@bastra-recall/daemon 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@bastra-recall/daemon 可以商用吗?

@bastra-recall/daemon 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@bastra-recall/daemon 还在维护吗?

最新版本 1.1.8 发布于 2026/10/31(2 天前);至今共发布 27 个版本,其中过去 12 个月发布了 27 个;首次发布于 2026/06/23。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。