# https://github.com/h-wata/kioku-mesh Project Manual

Generated at: 2026-06-24 06:45:13 UTC

## Table of Contents

- [Project Overview & System Architecture](#page-1)
- [Observations, Storage & Local Index](#page-2)
- [Mesh Networking, Replication & Security](#page-3)
- [CLI, MCP Server & Messaging Layer](#page-4)

<a id='page-1'></a>

## Project Overview & System Architecture

### Related Pages

Related topics: [Observations, Storage & Local Index](#page-2), [Mesh Networking, Replication & Security](#page-3), [CLI, MCP Server & Messaging Layer](#page-4)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/h-wata/kioku-mesh/blob/main/README.md)
- [src/mesh_mem/__main__.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/__main__.py)
- [src/mesh_mem/core/__init__.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/__init__.py)
- [src/mesh_mem/core/config.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/config.py)
- [src/mesh_mem/core/models.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/models.py)
- [src/mesh_mem/messaging/keyspace.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/messaging/keyspace.py)
- [src/mesh_mem/messaging/models.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/messaging/models.py)
- [src/mesh_mem/memory/purge.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/memory/purge.py)
- [src/mesh_mem/backend.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/backend.py)
</details>

# Project Overview & System Architecture

## Purpose & Scope

`kioku-mesh` (originally `mesh-mem`, renamed in v0.3.0) is a shared-memory layer for AI coding agents. It lets tools such as Claude Code, Codex CLI, and Gemini CLI persist observations, search prior context, and — in upcoming phases — exchange messages, all from a common key/value fabric replicated across hosts and processes. The README leads with the project's differentiator: "Shared memory for AI coding agents, across tools and machines." Source: [README.md:1-50]()

The product ships in three flavors selected at `kioku-mesh init --mode`:

| Mode    | Shape                                                        | Use case                              |
|---------|--------------------------------------------------------------|---------------------------------------|
| `local` | SQLite only, no `zenohd` daemon                              | Single-host persistent default        |
| `hub`   | `zenohd` + RocksDB + LAN listener                            | Always-on central peer                |
| `spoke` | `zenohd` + RocksDB, dials a hub via `--connect`              | Laptop / desktop / CI satellite peer  |

Source: [src/mesh_mem/__main__.py:200-260]() — the `init` parser documents the three modes. The `--mode localhost` variant was removed in v0.4.1 because it was a strict subset of `local` plus `mesh start`.

## High-Level Architecture

### Topology: Hub and Spokes

Each host exposes a fast SQLite read index for its agents, backed by a Zenoh router with a RocksDB volume. Peers replicate over Zenoh's pub/sub/query primitives on a transport that can be LAN, Tailscale/WireGuard VPN, or trusted WAN. The recommended layout is one hub and N spokes; spokes only dial outward, the hub only listens. Source: [README.md:60-120]()

```mermaid
flowchart TB
  subgraph Hub["⭐ Hub · always-on peer"]
    HZ["zenohd<br/>Router + RocksDB"]
    HS["SQLite index<br/>rebuild subscriber"]
  end
  subgraph SpokeA["🖥️ Spoke A · laptop"]
    AZ["zenohd<br/>Router + RocksDB"]
    AS["SQLite index"]
  end
  subgraph SpokeB["🖥️ Spoke B · desktop"]
    BZ["zenohd<br/>Router + RocksDB"]
    BS["SQLite index"]
  end
  AgentA1["Claude Code"]
  AgentA2["Codex CLI"]
  AgentB1["Gemini CLI"]
  AgentA1 --> AS
  AgentA2 --> AS
  AgentB1 --> BS
  AS <--> AZ
  BS <--> BZ
  AZ <==>|TCP 7447| HZ
  BZ <==>|TCP 7447| HZ
  HZ <--> HS
```

### Storage, Replication & Inbox

The Zenoh key expression `mem/**` is bound to a `rocksdb` volume with replication intervals tuned for hot/warm tiers (10s intervals, 6 hot, 30 warm, 250 ms propagation). Source: [src/mesh_mem/__main__.py:400-460]() — the generated `storage_manager` block in the JSON5 config. Each peer maintains a subscriber-driven SQLite index that materializes remote writes for low-latency local search; deletion is handled by tombstones that physically remove both the tomb key and the mirrored `mem/obs/...` key, plus a rolling-upgrade sweep across every canonical namespace for the affected `observation_id`. Source: [src/mesh_mem/memory/purge.py:40-90]()

## Layered Module Architecture (ADR-0023)

The package enforces a strict layering under `src/mesh_mem/`:

- `core/` — shared Zenoh session, mTLS, identity, keyspace, and config infrastructure. It depends on nothing else inside the package. Source: [src/mesh_mem/core/__init__.py:1-10]()
- `memory/` — observation storage, indexing, tombstoning, and GC. Backed by `core/` for transport.
- `messaging/` — the ADR-0022 agent-to-agent messaging namespace. By design it **does not** import `memory/`; communication happens over the shared Zenoh fabric. Source: [src/mesh_mem/messaging/keyspace.py:1-15]() and [src/mesh_mem/messaging/models.py:1-20]()
- `bridge/` — adapter layer for non-Zenoh transports (planned extension).

The legacy top-level `backend.py` is preserved as a module alias to `mesh_mem.memory.backend` so older import paths keep working through the package rename tracked in ADR-0024. Source: [src/mesh_mem/backend.py:1-10]()

## Configuration & Identity

Runtime settings live in `~/.config/kioku-mesh/config.yaml`; project-local defaults may be committed as `.kioku-mesh.yaml` and are discovered by walking upward from the working directory (like `.editorconfig`). Source: [src/mesh_mem/core/config.py:30-80]() — `find_project_config()` and the `_read_yaml` helper.

The user scope id (`user_id`) is **not** exposed as an MCP tool argument on purpose: an LLM-supplied value could pollute the `mem/user/{user_id}/**` namespace. Resolution priority is `MESH_MEM_USER_ID` env var → `~/.config/kioku-mesh/config.yaml` → empty. Source: [src/mesh_mem/core/config.py:90-120]()

The `Observation` domain model — the unit stored across the mesh — carries `memory_type`, `importance`, `visibility`, `scope_id`, `agent_family`, `client_id`, `pc_id`, `session_id`, content/subject/summary, tags, references, and a `supersedes` chain. Forward/backward compatibility is handled by clamping unknown `memory_type` values to `"note"` at DEBUG level rather than rejecting the payload. Source: [src/mesh_mem/core/models.py:60-120]()

## Visibility-Tiered Namespaces (ADR-0019)

v0.5.0 prepared every reader for **visibility-tiered replication**. Alongside the legacy `mem/obs/...` namespace, read paths now also understand:

- `mem/mesh/...` — mesh-wide broadcast
- `mem/user/{user_id}/...` — single-user scope
- `mem/team/{team_id}/...` — team scope

The Zenoh router plugin key expression remains `mem/**` so all tiers replicate, but the read-side namespace shape is now layered. Source: [src/mesh_mem/messaging/keyspace.py:1-30]() — analogous scoped-key construction for the upcoming messaging namespace (`msg/mesh/...`, `msg/team/...`, `msg/user/...`, plus `inbox/session/{session_id}` and `inbox/agent/{agent_id}` and `ack/{msg_id}/{recipient_session_id}`).

## CLI Surface

The CLI is built on `argparse` with optional `argcomplete` shell completion. Subcommands include `init`, `mesh start` / `mesh join` (ephemeral Zenoh smoke tests), `save`, `search` (with `text`, `markdown`, `json` formats), `delete`, `gc`, `mcp install`, and configuration helpers. Source: [src/mesh_mem/__main__.py:1-60]() — `main()` and the `_build_parser` dispatcher. The CLI explicitly closes the cached Zenoh session before exit so the replication subscriber thread cannot keep the interpreter alive past the command's return. Source: [src/mesh_mem/__main__.py:130-170]()

## Active Community Directions

Three follow-up issues frame the current roadmap:

- **#185 / ADR-0022** — Messaging MVP (direct 1:1 delivery, MCP `check_messages` / `ack_message` poll tools, TTL-bounded inbox spool, minimum presence schema).
- **#191** — Hard authorization, binding mTLS certificate subjects (ADR-0014) to Zenoh ACLs for `msg/team/**` and `msg/user/**` so the client-side scope filter is no longer the only gate.
- **#193** — Agent-level inbox ack aggregation: when multiple sessions share an `agent_id`, decide whether `msg/{scope}/ack/{msg_id}/{recipient_session_id}` keys are aggregated per-agent or kept session-local.

Source: GitHub issues #185, #191, #193 referenced in the community context above.

## See Also

- Multi-host mesh walkthrough — `README.md` §Power users
- MCP client recipes — `docs/mcp-clients.md`
- Multi-agent identity — `docs/multi-agent.md`
- ADR-0019 visibility tiers, ADR-0022 messaging flow, ADR-0023 layer separation, ADR-0024 package rename — `docs/adr/`

---

<a id='page-2'></a>

## Observations, Storage & Local Index

### Related Pages

Related topics: [Project Overview & System Architecture](#page-1), [Mesh Networking, Replication & Security](#page-3)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/mesh_mem/core/models.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/models.py)
- [src/mesh_mem/memory/local_index.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/memory/local_index.py)
- [src/mesh_mem/memory/purge.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/memory/purge.py)
- [src/mesh_mem/core/config.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/config.py)
- [src/mesh_mem/messaging/keyspace.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/messaging/keyspace.py)
- [src/mesh_mem/messaging/models.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/messaging/models.py)
- [src/mesh_mem/__main__.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/__main__.py)
- [README.md](https://github.com/h-wata/kioku-mesh/blob/main/README.md)
</details>

# Observations, Storage & Local Index

## Overview

kioku-mesh persists agent knowledge as **observations** — small, immutable JSON records that multiple AI coding agents (Claude Code, Codex CLI, Gemini CLI) share across machines through a Zenoh mesh. The canonical store of truth lives in a Zenoh router with a RocksDB backend, but a per-process **SQLite sidecar index** mirrors observation metadata so local reads stay sub-millisecond even when the mesh is large. v0.5.0/v0.6.0 add visibility-tiered key shapes (`mem/mesh/...`, `mem/user/{user_id}/...`, `mem/team/{team_id}/...`) alongside the legacy `mem/obs/...` namespace, so every read path understands both layouts ([README.md]()).

The two principal domain objects are the `Observation` (write-once note) and the `Tombstone` (logical delete marker). Deletion is **existence-based** — a tombstone put at `mem/tomb/...` hides the corresponding observation, and the search layer filters them out at query time.

## Observation Data Model

The `Observation` dataclass in `src/mesh_mem/core/models.py` is the single writeable unit. It carries identity, content, and visibility metadata and is serialized to a compact UTF-8 JSON string via `to_json()`. `from_json()` is intentionally tolerant: unknown fields are stashed in `_extras`, and forward-compat clamps unknown `memory_type` or `visibility` values to safe defaults so a peer running a newer schema does not break older readers ([src/mesh_mem/core/models.py]()). Key fields include:

| Field | Purpose |
|---|---|
| `observation_id` | UUID v4; updates MUST use a fresh id (immutability) |
| `memory_type` | One of `VALID_MEMORY_TYPES` (clamped to `"note"` if unknown) |
| `importance`, `subject`, `summary`, `content` | Display + search body |
| `agent_family` / `client_id` / `pc_id` / `session_id` | Identity provenance |
| `visibility` / `scope_id` | ADR-0019 tier (mesh / user / team / legacy `""`) |
| `tags`, `references`, `supersedes` | Graph edges |
| `source_files` | Optional list of repo paths |

The Zenoh key for a write is built by `obs_key(...)` in `src/mesh_mem/core/models.py`, mirroring the `tomb_key(...)` for deletions. Both key builders emit a path that encodes identity and the visibility tier, so a wildcard query on `mem/obs/*/*/*/*/{observation_id}` reaches the same row across all tiers.

## Local Index (SQLite Sidecar)

Because Zenoh-RocksDB scans slow down as the mesh grows, `src/mesh_mem/memory/local_index.py` maintains a per-process SQLite mirror that `store.search_observations` reads from (Phase 3+). The module's docstring states the contract explicitly: **"Zenoh write success is the contract; SQLite errors are logged and swallowed"** — a corrupt index must never convert a successful `put` into a failure ([src/mesh_mem/memory/local_index.py]()).

```mermaid
flowchart LR
  CLI["save / search / delete"] --> Store["memory/store.py<br/>Zenoh + RocksDB"]
  Store -->|"put observation"| Zenoh[("Zenoh router<br/>RocksDB")]
  Store -->|"upsert obs_index row"| SQLite[("Local SQLite index<br/>sidecar, fast reads")]
  SQLite -->|"search_observations"| Store
  Zenoh -->|"replication · rebuild"| SQLite
```

Identity filters (`agent_family`, `client_id`, `pc_id`, `session_id`) are read with `json_extract(payload_json, ...)` rather than dedicated columns. The TASK-134 spike at 50k rows measured a ~0.4s rebuild, ~0.04ms p99 query, and a ~49MB file. A future schema migration will lift these fields into indexable columns once workloads justify it.

**Disable switches** are documented in the module docstring:

- `MESH_MEM_DISABLE_INDEX=1` — turns the index into a no-op (falls back to Zenoh full-scan).
- `MESH_MEM_INDEX_DB=:memory:` — ephemeral in-process DB for tests.

## Tombstones and Purge

Deletion is **logical**: the writer emits a `Tombstone` JSON record under `mem/tomb/...`, and the search layer hides any observation whose tombstone key is present ([src/mesh_mem/core/models.py]()). The same `Observation` model owns `tomb_key(...)`, and `Tombstone` is its own dataclass with `observation_id`, `reason`, and `deleted_at`.

Physical purge lives in `src/mesh_mem/memory/purge.py`. The entry point enumerates **every canonical obs / tomb key carrying `observation_id`** via per-id leaf selectors across both legacy and ADR-0019 tiers, then exact-deletes each. After per-key deletes, a **best-effort wildcard `session.delete`** is broadcast for `mem/obs/*/*/*/*/{id}` and `mem/tomb/*/*/*/*/{id}` so currently-connected replicas that were missed by the live query still get the message. Wildcard delete support varies by Zenoh version, so failures are logged and swallowed — the emergency purge must not turn into a hard error on a backend that refuses the pattern ([src/mesh_mem/memory/purge.py]()).

Two safety checks are worth noting:

1. A tomb whose key suffix disagrees with its body's `observation_id` is **skipped** (defense against a corrupted / misrouted tombstone that would otherwise delete an unrelated obs).
2. A `pc_id`-level bulk sweep is exposed via `gc --by-pc-id` to clean up throwaway benchmark sessions (originally introduced in v0.2.4).

## Configuration and Paths

Runtime configuration is read from `~/.config/kioku-mesh/config.yaml` and an optional per-project `.kioku-mesh.yaml` (searched upward from cwd). The visibility tier and scope id are resolved in `src/mesh_mem/core/config.py`:

- `MESH_MEM_USER_ID` env var → global config → unset.
- `team_id`: env → project config → global config → unset.
- `default_visibility`: env → project config → global config → legacy `""` ([src/mesh_mem/core/config.py]()).

`user_id` is **deliberately not** read from the project config — a committed file must never set a personal namespace. Every `save` response echoes the effective scope, e.g. `saved: <id> (visibility=team/kioku-mesh)`.

## CLI Surface

`src/mesh_mem/__main__.py` exposes the user-facing commands. The save path calls `get_backend().put_observation(obs)` and prints the resolved visibility; the search path returns `text`, `json`, or `markdown` formatted results, with a friendly "no matching memories" exit code 0 when nothing matches ([src/mesh_mem/__main__.py]()). The retention sweep (`gc --retention-days`) re-verifies each candidate against live Zenoh state — false-shadows are upserted back to live, genuine expiries are physically deleted (added in v0.2.5). The `init` subcommand provisions `local` (SQLite only, no zenohd), `hub` (zenohd + rocksdb + LAN listener), and `spoke` (zenohd + rocksdb, dials a hub via `--connect`) deployment shapes; the legacy `--mode localhost` was removed in v0.4.1 because it was a strict subset of `--mode local` plus `mesh start`.

## See Also

- Messaging & Inbox Spool (ADR-0022)
- mTLS Peer Authentication (ADR-0014)
- Visibility-Tiered Replication (ADR-0019)
- MCP Client Integration

---

<a id='page-3'></a>

## Mesh Networking, Replication & Security

### Related Pages

Related topics: [Project Overview & System Architecture](#page-1), [Observations, Storage & Local Index](#page-2)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/h-wata/kioku-mesh/blob/main/README.md)
- [src/mesh_mem/__main__.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/__main__.py)
- [src/mesh_mem/core/__init__.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/__init__.py)
- [src/mesh_mem/core/keyspace.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/keyspace.py)
- [src/mesh_mem/core/tls.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/tls.py)
- [src/mesh_mem/core/config.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/config.py)
- [src/mesh_mem/core/models.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/models.py)
- [src/mesh_mem/messaging/keyspace.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/messaging/keyspace.py)
- [src/mesh_mem/messaging/models.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/messaging/models.py)
</details>

# Mesh Networking, Replication & Security

kioku-mesh is a shared-memory layer for AI coding agents that operates either on a single host or across multiple hosts joined into a mesh. The mesh layer is built on Zenoh: each host runs a local Zenoh router (`zenohd`) with a RocksDB-backed storage backend that serves as the source of truth, while a per-host SQLite index acts as a fast read cache rebuilt from Zenoh replication. This page documents the mesh's networking model, its replication semantics, the visibility-tiered key namespace, and the optional mutual TLS (mTLS) authentication layer that guards peer admission.

## Architecture and Topology

The mesh layer is implemented as a set of collaborating layers. The `core` package — which contains the shared Zenoh session, mTLS helpers, identity, keyspace vocabulary, and config infrastructure — is the foundation that the `memory`, `messaging`, and `bridge` layers depend on, and it has no upward dependencies on those layers ([src/mesh_mem/core/__init__.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/__init__.py)).

The recommended deployment is one always-on **hub** peer plus any number of **spoke** peers. The hub listens on a LAN / Tailscale / VPN address; spokes dial only the hub. Each host serves its local agents from a SQLite index rebuilt from the local Zenoh/RocksDB store, and hosts replicate to each other over the mesh on TCP 7447 ([README.md](https://github.com/h-wata/kioku-mesh/blob/main/README.md)). For ephemeral smoke testing without provisioning a long-lived daemon, the CLI exposes `kioku-mesh mesh start`, which launches an in-process Zenoh router in `mode=router` so the user can observe replication from a terminal session ([src/mesh_mem/__main__.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/__main__.py)).

```mermaid
flowchart LR
    subgraph HostA["Host A"]
      direction TB
      A1["Claude Code"] --> AZ["zenohd + RocksDB"]
      A2["Codex CLI"] --> AZ
      AZ --> AS[("SQLite index")]
    end
    subgraph HostB["Host B"]
      direction TB
      B1["Codex CLI"] --> BZ["zenohd + RocksDB"]
      BZ --> BS[("SQLite index")]
    end
    AZ <==>|"Zenoh mesh replication\nTCP 7447"| BZ
```

Three CLI init modes select the deployment shape: `local` (SQLite only, no daemon), `hub` (zenohd + RocksDB + listener), and `spoke` (zenohd + RocksDB + dials a hub). The previously supported `localhost` mode was removed in v0.4.1 because it was a strict subset of features already covered by `local` and `mesh start` ([src/mesh_mem/__main__.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/__main__.py)).

## Replication and the Key Namespace

Each save emits an immutable `Observation` and (for deletions) a `Tombstone` under mirrored `mem/obs/...` and `mem/tomb/...` keys. The search layer hides observations whose tombstone key is present, which is an existence-based, not timestamp-LWW, reconciliation ([src/mesh_mem/core/models.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/models.py)). The Zenoh storage backend is expected to propagate both inserts and deletes through replication so the two sides converge; physical delete is performed via `session.delete` and is handled by the `gc` command ([src/mesh_mem/__main__.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/__main__.py)).

### Visibility-tiered key namespaces (ADR-0019 Phase A)

Release v0.5.0 prepared every reader for visibility-tiered replication. Read paths now understand four key shapes simultaneously, while write paths still emit the legacy shape only ([src/mesh_mem/core/keyspace.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/keyspace.py)):

| Tier | Key shape |
|---|---|
| Legacy | `mem/{obs\|tomb}/{agent}/{client}/{pc}/{session}/{obs_id}` |
| Mesh | `mem/mesh/{obs\|tomb}/{agent}/{client}/{pc}/{session}/{obs_id}` |
| User | `mem/user/{user_id}/{obs\|tomb}/{agent}/{client}/{pc}/{session}/{obs_id}` |
| Team | `mem/team/{team_id}/{obs\|tomb}/{agent}/{client}/{pc}/{session}/{obs_id}` |

Because Zenoh's `**` matches zero or more chunks, a single `mem/**/obs/**` selector covers every shape above without enumerating tiers — verified against `zenoh-python 1.9` ([src/mesh_mem/core/keyspace.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/keyspace.py)). The corresponding constant `OBS_READ_KEY_EXPR` is the canonical read selector used by the replication subscriber, index rebuild scan, fallback search, and shadow re-verify. A parallel `msg/...` namespace is reserved for the agent-messaging layer (ADR-0022), which carries `msg/mesh/...`, `msg/team/...`, `msg/user/...`, and per-session / per-agent inbox and ack keys ([src/mesh_mem/messaging/keyspace.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/messaging/keyspace.py)).

### Configuration sources for visibility

The default visibility for new writes and the `team_id` namespace are resolved in priority order: an env var (`MESH_MEM_DEFAULT_VISIBILITY` / `MESH_MEM_TEAM_ID`), then a project-local `.kioku-mesh.yaml` discovered by walking upward from the current working directory, then the global `~/.config/kioku-mesh/config.yaml`, and finally the empty string fallback ([src/mesh_mem/core/config.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/config.py)). The project file is intentionally restricted to non-personal settings: `user_id` is never read from it, because a committed file must never set a personal namespace ([README.md](https://github.com/h-wata/kioku-mesh/blob/main/README.md)).

## mTLS Peer Authentication (ADR-0014)

Network admission (Tailscale, WireGuard, or a trusted LAN) remains the first line of defense; mTLS adds transport-level peer authentication on top for deployments that want it. The mesh's transport trust is a small private PKI the operator runs themselves:

- A single **CA** (`ca.key` + `ca.crt`) — the CA private key never leaves the host that created it and is the only secret that must be guarded long-term.
- One key pair **per peer** — the private key is generated on the peer and never travels; the peer emits a CSR (public information) that the CA signs ([src/mesh_mem/core/tls.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/tls.py)).

This CSR-based shape is deliberately chosen over "CA mints everything": it mirrors `ssh-copy-id` and ensures that only non-secret material (the CSR going to the CA, the signed cert + CA cert coming back) ever crosses hosts.

The CLI exposes a complete enrollment workflow under `kioku-mesh tls`:

| Subcommand | Purpose |
|---|---|
| `init-ca` | Create the mesh CA on the CA host (run once) |
| `request` | Generate this peer's key locally and emit a CSR blob to copy to the CA host |
| `sign` | Sign a peer's CSR on the CA host, producing a bundle block to copy back |
| `install` | Install the signed bundle into the local peer config |
| `info` | Inspect a certificate or local peer identity |
| `enroll` | One-command end-to-end enrollment when SSH to the CA host is available |

The CA validity default is `DEFAULT_CA_DAYS` days, and `init-ca --force` replaces an existing CA — which invalidates every signed peer cert ([src/mesh_mem/__main__.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/__main__.py)). When the mesh is started with `--tls`, the generated `zenohd` config includes the appropriate TLS block for listeners and connectors so that authenticated peering is enforced end to end.

### Open design questions for the messaging MVP

The v0.6.0 messaging work (ADR-0022) ships with **client-side scope filtering only** for `msg/team/...` and `msg/user/...` keys. The community has open follow-up issues #191, #192, and #193 tracking the move from soft to hard authorization: tying mTLS certificate subjects to Zenoh ACLs for team/user scope, deciding whether mesh-scope presence should default on, and aggregating per-session acks across multiple sessions that share an `agent_id`. Until those land, sensitive scopes should continue to rely on the network-admission layer as the enforcement boundary ([src/mesh_mem/core/tls.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/tls.py)).

## Failure Modes and Operational Notes

- **Ephemeral state loss**: `mesh start` is for short-lived smoke tests; for production multi-host use, install `zenohd` as a persistent service so replication state survives router restarts ([src/mesh_mem/__main__.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/__main__.py)).
- **Spoke onboarding**: a fresh spoke joining a populated mesh no longer reports `count: 0`; Zenoh replicates existing memories into the new spoke's RocksDB before the first save, after the v0.3.2 fix ([release notes](https://github.com/h-wata/kioku-mesh/releases/tag/v0.3.2)).
- **Index divergence**: `gc --retention-days` prunes long-shadowed index rows and re-verifies each candidate against live Zenoh state; false-shadows are upserted back to live, genuine expiries are physically deleted ([release notes](https://github.com/h-wata/kioku-mesh/releases/tag/v0.2.5)).
- **Bulk purge**: `mesh-mem gc --by-pc-id` (now `kioku-mesh gc --by-pc-id`) was added for cleaning up throwaway-session floods from benchmark or smoke runs; dry-run by default and `--execute` requires interactive confirmation ([release notes](https://github.com/h-wata/kioku-mesh/releases/tag/v0.2.4)).
- **Forward-compatible reads**: `Observation.from_json` and `Message.from_json` both tolerate unknown fields, stashing them in `_extras`, so older readers do not break when a future writer emits extra keys ([src/mesh_mem/core/models.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/models.py), [src/mesh_mem/messaging/models.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/messaging/models.py)).

## See Also

- [README.md](https://github.com/h-wata/kioku-mesh/blob/main/README.md) — project overview, modes, and CLI quickstart
- [docs/mcp-clients.md](https://github.com/h-wata/kioku-mesh/blob/main/docs/mcp-clients.md) — MCP client setup recipes
- [docs/multi-agent.md](https://github.com/h-wata/kioku-mesh/blob/main/docs/multi-agent.md) — multi-agent identity patterns
- ADR-0014 (mTLS), ADR-0019 (visibility-tiered replication), ADR-0022 (messaging), ADR-0023 (layered architecture), ADR-0024 (package rename) — referenced in core module docstrings and issue threads

---

<a id='page-4'></a>

## CLI, MCP Server & Messaging Layer

### Related Pages

Related topics: [Project Overview & System Architecture](#page-1), [Mesh Networking, Replication & Security](#page-3)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/mesh_mem/__main__.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/__main__.py)
- [src/mesh_mem/messaging/__init__.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/messaging/__init__.py)
- [src/mesh_mem/messaging/keyspace.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/messaging/keyspace.py)
- [src/mesh_mem/messaging/models.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/messaging/models.py)
- [src/mesh_mem/messaging/spool.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/messaging/spool.py)
- [src/mesh_mem/core/__init__.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/__init__.py)
- [src/mesh_mem/core/config.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/config.py)
- [src/mesh_mem/core/tls.py](https://github.com/h-wata/kioku-mesh/blob/main/src/mesh_mem/core/tls.py)
- [README.md](https://github.com/h-wata/kioku-mesh/blob/main/README.md)
</details>

# CLI, MCP Server & Messaging Layer

## Overview

kioku-mesh exposes its capabilities through three coordinated surfaces: a unified command-line interface (`kioku-mesh`), a stdio-based MCP server (`kioku-mesh-mcp`) that integrates with AI coding agents, and a scoped messaging layer that lets agents exchange directed messages over the same Zenoh mesh used for memory replication. All three share the core layer that owns the Zenoh session, mTLS enrollment, identity resolution, and configuration loading.

Source: [src/mesh_mem/core/__init__.py:1-3]()

The messaging layer is intentionally decoupled: it does not import the memory module, keeping ACLs, TTL semantics, and key shapes independent of long-term observation storage.

Source: [src/mesh_mem/messaging/keyspace.py:1-20]()

## CLI Subsystem

The CLI is implemented as a thin argparse wrapper in [`src/mesh_mem/__main__.py`](src/mesh_mem/__main__.py). It registers subcommands for memory operations (`save`, `search`, `get-memory`, `delete`), lifecycle (`init`, `mesh start`, `mesh join`), maintenance (`gc`), and security (`tls init-ca | request | sign | install | enroll`). Optional shell completion is loaded via `argcomplete` when the `[completion]` extra is installed.

Source: [src/mesh_mem/__main__.py:1-32]()

Deployment modes are selected at `init` time:

| Mode | Backend | Extra service |
|---|---|---|
| `local` (default) | SQLite | none |
| `hub` | Zenoh + RocksDB | `zenohd` listener |
| `spoke` | Zenoh + RocksDB | `zenohd` dialing hub |

Source: [src/mesh_mem/__main__.py:148-170](), [README.md:32-52]()

The `init` subcommand also accepts `--tls` to embed mTLS blocks into the generated zenohd JSON5 configuration, with placeholders for the listen endpoint, the hub connect endpoint, and a TLS section referencing the CA bundle and peer cert paths.

Source: [src/mesh_mem/__main__.py:354-410]()

For ephemeral multi-host trials, `kioku-mesh mesh start` launches an in-process zenoh router that other peers can join via `ZENOH_CONNECT=tcp/<host>:17447`. This path is explicitly described as a try-it/demo surface and not for production.

Source: [src/mesh_mem/__main__.py:185-230]()

## MCP Server and Client Installers

The README describes two installed commands: `kioku-mesh` (CLI) and `kioku-mesh-mcp` (stdio MCP server launched by an agent). The `kioku-mesh mcp install --client <name>` subcommand handles per-client registration and accepts `--env KEY=VALUE` (repeatable), `--name`, `--force`, and `--dry-run` flags.

Source: [src/mesh_mem/__main__.py:60-90](), [README.md:54-64]()

Supported clients listed in the README include Claude Code and Codex CLI; Claude Desktop, Gemini CLI, and ChatGPT Desktop require manual JSON/TOML edits documented under `docs/mcp-clients.md`. The installer targets a default registry key (overridable via `--name`) and prints the resulting command or config block instead of executing when `--dry-run` is set.

Source: [src/mesh_mem/__main__.py:60-90]()

## Messaging Layer

The messaging module introduces a scoped, TTL-bounded inbox system that sits beside the long-term memory store. Its Phase 1 scope, tracked under ADR-0022, includes direct 1:1 delivery, an MCP `check_messages` / `ack_message` poll path, and a short-term inbox spool separated from durable memory.

Source: [src/mesh_mem/messaging/__init__.py:1-18]()

### Key Shapes

Zenoh keys are produced by helpers in `keyspace.py`:

```text
msg/mesh/{msg_id}                                         (Phase 2 broadcast)
msg/team/{team_id}/{msg_id}                               (team body)
msg/user/{user_id}/{msg_id}                               (user body)
msg/{scope}/inbox/session/{recipient_session_id}/{msg_id} (session inbox)
msg/{scope}/inbox/agent/{recipient_agent_id}/{msg_id}     (agent inbox)
msg/{scope}/ack/{msg_id}/{recipient_session_id}           (explicit ack)
```

Source: [src/mesh_mem/messaging/keyspace.py:1-50]()

The `scope` segment carries `mesh`, `team`, or `user` and is parsed back via `parse_scope_from_key`, allowing the receiver to enforce ACLs locally until a hard mTLS-to-ACL mapping is implemented.

### Domain Models

`Message` carries `sender`, `recipient` (`agent_id` + `session_id`), a `body`, `kind`, `content_type`, optional `correlation_id`, TTL (`ttl_sec`, `expires_at`), a monotonic `sender_seq`, and a `schema_version`. JSON serialization is forward-compatible: unknown fields are preserved in a `_extras` dict on deserialization.

Source: [src/mesh_mem/messaging/models.py:1-160]()

`Ack` records the `msg_id`, the `recipient_session_id` that acknowledged, and an `acked_at` UTC timestamp. The session-scoped ack key (`msg/{scope}/ack/{msg_id}/{recipient_session_id}`) means each running session maintains its own ack record, which is the design assumption behind open question #193 (multi-session ack aggregation for the same `agent_id`).

Source: [src/mesh_mem/messaging/models.py:180-220]()

### Spool Semantics

`MessageSpool` is the in-memory TTL spool used by Phase 1:

```python
class MessageSpool:
    """Ephemeral in-memory store; put() is idempotent on msg_id."""
    def put(self, msg): ...        # silently dedup
    def get(self, msg_id): ...     # returns None if expired
    def list_active(self, scope=None): ...
    def purge_expired(self): ...
```

Source: [src/mesh_mem/messaging/spool.py:1-50]()

TTL enforcement is client-side: `get` and `list_active` filter expired messages without deleting them, and `purge_expired` reclaims memory. This separation lets the spool back onto a Zenoh subscription in Phase 2 without changing the public API.

Source: [src/mesh_mem/messaging/spool.py:25-50]()

### Layer Boundaries

```mermaid
flowchart LR
  subgraph Core["core/ (ADR-0023)"]
    Z[Zenoh session]
    M[mTLS / identity]
    C[config.yaml]
  end
  subgraph Messaging["messaging/"]
    K["keyspace.py"]
    S["MessageSpool"]
    M2["models.Message / Ack"]
  end
  Memory["memory/"] -. "no import" .- Messaging
  Messaging --> Core
  Memory --> Core
```

The messaging module is explicitly forbidden from importing `mesh_mem.memory.*`. This boundary is enforced by docstring-level convention and is the foundation for independent ACL work such as the proposed hard mTLS-cert-subject to Zenoh ACL mapping (issue #191).

Source: [src/mesh_mem/messaging/keyspace.py:9-11]()

## Configuration and Identity

Project-level defaults are read from `.kioku-mesh.yaml` via an upward directory walk (`editorconfig` style). The file may carry `default_visibility` and `team_id` but never `user_id`, since a committed file must not pin a personal namespace. The effective scope is echoed on every save response.

Source: [src/mesh_mem/core/config.py:25-95]()

User identity is resolved with strict priority: `MESH_MEM_USER_ID` env var, then `user_id:` in `~/.config/kioku-mesh/config.yaml`, then empty. Like the agent identity, it is deliberately not exposed as an MCP tool argument to prevent LLM-driven namespace pollution.

Source: [src/mesh_mem/core/config.py:80-110]()

## See Also

- [README.md](README.md) — high-level overview, modes, MCP client recipes
- `docs/mcp-clients.md` — per-client MCP registration walkthroughs
- `docs/multi-agent.md` — multi-agent identity recipes
- ADR-0022 (messaging MVP) and ADR-0023 (layer boundaries) referenced throughout
- Community: issue #185 (messaging MVP), #191 (hard authorization), #193 (multi-session ack aggregation)

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Pitfall Log

Project: h-wata/kioku-mesh

Summary: Found 23 structured pitfall item(s), including 0 high/blocking item(s). Top priority: Installation risk - Installation risk requires verification.

## 1. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: mesh-mem v0.2.4
- User impact: Upgrade or migration may change expected behavior: mesh-mem v0.2.4
- Evidence: failure_mode_cluster:github_release | https://github.com/h-wata/kioku-mesh/releases/tag/v0.2.4

## 2. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: mesh-mem v0.2.5
- User impact: Upgrade or migration may change expected behavior: mesh-mem v0.2.5
- Evidence: failure_mode_cluster:github_release | https://github.com/h-wata/kioku-mesh/releases/tag/v0.2.5

## 3. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: mesh-mem v0.3.0
- User impact: Upgrade or migration may change expected behavior: mesh-mem v0.3.0
- Evidence: failure_mode_cluster:github_release | https://github.com/h-wata/kioku-mesh/releases/tag/v0.3.0

## 4. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: v0.3.1
- User impact: Upgrade or migration may change expected behavior: v0.3.1
- Evidence: failure_mode_cluster:github_release | https://github.com/h-wata/kioku-mesh/releases/tag/v0.3.1

## 5. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: v0.3.2
- User impact: Upgrade or migration may change expected behavior: v0.3.2
- Evidence: failure_mode_cluster:github_release | https://github.com/h-wata/kioku-mesh/releases/tag/v0.3.2

## 6. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: v0.3.3
- User impact: Upgrade or migration may change expected behavior: v0.3.3
- Evidence: failure_mode_cluster:github_release | https://github.com/h-wata/kioku-mesh/releases/tag/v0.3.3

## 7. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: v0.4.0 — Mutual TLS for the mesh
- User impact: Upgrade or migration may change expected behavior: v0.4.0 — Mutual TLS for the mesh
- Evidence: failure_mode_cluster:github_release | https://github.com/h-wata/kioku-mesh/releases/tag/v0.4.0

## 8. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/h-wata/kioku-mesh/issues/167

## 9. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.host_targets | https://github.com/h-wata/kioku-mesh

## 10. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: design: hard authorization — mTLS cert subject ↔ Zenoh ACL for team/user scope (#185 follow-up)
- User impact: Developers may misconfigure credentials, environment, or host setup: design: hard authorization — mTLS cert subject ↔ Zenoh ACL for team/user scope (#185 follow-up)
- Evidence: failure_mode_cluster:github_issue | https://github.com/h-wata/kioku-mesh/issues/191

## 11. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: failure_mode_cluster:github_issue | https://github.com/h-wata/kioku-mesh/issues/185

## 12. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: v0.4.1
- User impact: Upgrade or migration may change expected behavior: v0.4.1
- Evidence: failure_mode_cluster:github_release | https://github.com/h-wata/kioku-mesh/releases/tag/v0.4.1

## 13. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/h-wata/kioku-mesh/issues/187

## 14. Capability evidence risk - Capability evidence risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: README/documentation is current enough for a first validation pass.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.assumptions | https://github.com/h-wata/kioku-mesh

## 15. Maintenance risk - Maintenance risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this migration risk before relying on the project: kioku-mesh v0.5.0
- User impact: Upgrade or migration may change expected behavior: kioku-mesh v0.5.0
- Evidence: failure_mode_cluster:github_release | https://github.com/h-wata/kioku-mesh/releases/tag/v0.5.0

## 16. Maintenance risk - Maintenance risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/h-wata/kioku-mesh

## 17. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: downstream_validation.risk_items | https://github.com/h-wata/kioku-mesh

## 18. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: risks.scoring_risks | https://github.com/h-wata/kioku-mesh

## 19. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/h-wata/kioku-mesh/issues/191

## 20. Capability evidence risk - Capability evidence risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: Developers should check this conceptual risk before relying on the project: refactor: split store.py monolith before ADR-0019 visibility work
- User impact: Developers may hit a documented source-backed failure mode: refactor: split store.py monolith before ADR-0019 visibility work
- Evidence: failure_mode_cluster:github_issue | https://github.com/h-wata/kioku-mesh/issues/167

## 21. Capability evidence risk - Capability evidence risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: Developers should check this conceptual risk before relying on the project: refactor: パッケージ mesh_mem → kioku_mesh リネーム（ADR-0024）
- User impact: Developers may hit a documented source-backed failure mode: refactor: パッケージ mesh_mem → kioku_mesh リネーム（ADR-0024）
- Evidence: failure_mode_cluster:github_issue | https://github.com/h-wata/kioku-mesh/issues/187

## 22. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: issue_or_pr_quality=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/h-wata/kioku-mesh

## 23. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: release_recency=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/h-wata/kioku-mesh

<!-- canonical_name: h-wata/kioku-mesh; human_manual_source: deepwiki_human_wiki -->
