Repository navigation
Architecture Overview
This page is the 10-minute tour. For the exhaustive design documents, see docs/Musubi/ in the repo — it's an Obsidian vault with one markdown file per spec.
┌─────────────────────────────────────────────────────────────┐
│ MUSUBI CORE │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
write ─►│ EPISODIC │──┬──►│ CONCEPT │──┬──►│ CURATED │─► Obsidian │
│ └──────────┘ │ └──────────┘ │ └──────────┘ vault │
│ ▲ │ ▲ │ ▲ │
│ │ mature│ synth │ gate │ │ │
│ └────────┘ └────────┘ │ │
│ │ │
│ ┌──────────┐ ┌─────────┴─────────┐ │
│ │ ARTIFACT │ │ THOUGHTS (pub/sub)│ │
│ └──────────┘ └───────────────────┘ │
│ blobs agent ↔ agent msgs │
└─────────────────────────────────────────────────────────────┘
Purpose: the firehose. Every observation, quote, or short thought an agent ingests lands here.
State machine: provisional → matured → demoted.
- A new row starts
provisional. An LLM scores its importance; if the score is low and the row isn't reinforced, it TTL's out. - After a dwell window (hours), and if nothing contradicts it, it flips to
matured. - The retriever only returns
maturedrows by default — so the firehose doesn't pollute queries while it's still settling.
Storage: Qdrant collection musubi_episodic. Named vectors: dense (BGE-M3), sparse (SPLADE-v3), rerank (BGE-reranker).
Purpose: distilled themes. A daily pass clusters recent matured episodics by shared topic + semantic similarity, then asks an LLM to summarise each cluster into a SynthesizedConcept.
Key property: reinforcement, not duplication. If a new cluster is close to an existing concept (cosine ≥ 0.85), the existing concept's reinforcement_count increments and its last_reinforced_at updates — no new row. Over time, the rows that reach high reinforcement counts are the ones your fleet has "learned."
State machine: synthesized → matured → promoted → demoted.
Contradiction detection runs after synthesis: pairs at 0.75 ≤ similarity < 0.85 get asked "consistent or contradictory?" by the LLM; contradictory pairs are blocked from promotion until resolved.
Purpose: the human-readable knowledge base. Concepts that pass the promotion gate (reinforcement ≥ 3 AND importance ≥ 6 AND age ≥ 48h AND no active contradiction) get rendered as markdown and written to an Obsidian vault.
The vault is the store of record for curated content. A watcher observes vault edits; changes flow back into Musubi through the vault-sync pipeline. An edit to curated/eric/aoi/api/retrieval.md becomes an update on the corresponding musubi_curated row.
Binary blobs — images, PDFs, audio clips — that episodic or curated rows reference. Stored content-addressed on disk; the database holds a content_sha + metadata only.
Not a persistent memory plane; a pub/sub channel. Agents send(thought) to a named channel with namespace, from_presence, to_presence, content. Subscribers receive over server-sent events. It's how the fleet talks to itself without a coordinator.
Five sweeps run on cron:
| Job | Cadence (UTC) | What it does |
|---|---|---|
maturation |
hourly at :13 | Scores episodic importance; flips provisional → matured after dwell; TTL's unreinforced provisionals. |
synthesis |
daily 03:00 | Clusters matured episodics per namespace, generates concepts, runs contradiction checks. |
promotion |
daily 04:00 | Promotes eligible concepts → curated markdown files in the Obsidian vault. |
demotion |
daily 05:00 | Soft-deletes episodic/concept rows past their decay window. |
reflection |
daily 06:00 | Writes a daily digest markdown back to the vault (what was captured, promoted, demoted, what themes emerged). |
Each sweep:
- Runs per-namespace (so a noisy tenant can't block a quiet one).
- Is file-locked (
/var/lib/musubi/locks/<job>.lock) so two workers don't duplicate work. - Emits structured
LifecycleEventrows to a SQLite journal. - Is idempotent — re-running on the same window is a no-op.
Implementation lives in src/musubi/lifecycle/.
Three paths:
-
Fast — dense-only kNN on the named
dense_bge_m3_v1vector. Low latency, good for autocomplete-style use. -
Hybrid — dense + sparse (SPLADE) RRF-blended, optional rerank pass. The default for
retrieve. - Deep — hybrid + rerank + query expansion via the LLM. Higher latency, better recall on hard queries.
All three honor state filters (default: matured), namespace scoping, and recency decay.
-
HTTP — FastAPI, OpenAPI 3.1 spec at
src/musubi/api/openapi.yaml. Routes under/v1/. -
gRPC — partial coverage generated from
proto/. Same port; protocol negotiated on the connection. -
SSE —
/v1/thoughts/streamfor the pub/sub subscription side.
-
SDK (
src/musubi/sdk/) — the Python client. Depends ontypes/only; no server-side code. -
Adapters (
src/musubi/adapters/) — each downstream interface (MCP, LiveKit, OpenClaw) gets its own subdirectory. Adapters import SDK + types only; they do not reach into plane or API code.
The split keeps the core stable while adapters iterate. An adapter change is never a core change.
- It's not a chat history store. Thoughts are ephemeral; curated notes are the persistent layer.
- It's not a multi-tenant SaaS. Namespacing is there (
<tenant>/<presence>) but there's no billing, no per-tenant quotas, no admin UI. - It's not a distributed system. Today's releases run single-node; multi-node HA is a post-1.0 design space and will require a real ADR.
- It's not a vector store — it's a memory server that uses a vector store.
- Getting Started — install + run locally
- The Lifecycle (coming soon) — walks through what happens to one capture over 48 hours
- Adapters (coming soon) — MCP, LiveKit, OpenClaw integration
-
docs/Musubi/13-decisions/— every load-bearing architecture decision, as an ADR
Musubi • Apache 2.0 licensed • Report a vulnerability • 結び — to tie, to join, to bind
- Deployment Guide (TBD)
- The Lifecycle (TBD)
- Supply Chain & Verification (TBD)
- SDK Guide (TBD)
- Adapters (TBD)
- Writing a new plane (TBD)