mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
docs: truth-up architecture.md + 3 new domain pages + Linux/Arch caveats in localized READMEs (#2634)
This commit is contained in:
@@ -60,6 +60,12 @@ curl -fsSL https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts
|
||||
irm https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.ps1 | iex
|
||||
```
|
||||
|
||||
<!-- TODO: translate (de) — English source mirrored from README.md so non-EN readers get the same install caveats. Please translate. -->
|
||||
> **Linux:** the AppImage can crash on launch under Wayland (and on Arch-based distros with `sharun: Interpreter not found!`) — see [#2463](https://github.com/tinyhumansai/openhuman/issues/2463) for the cause and env-var workarounds.
|
||||
Arch Linux package maintainers can use the [`openhuman-bin` AUR recipe](./packages/arch/openhuman-bin/);
|
||||
once published, Arch users can install it with `yay -S openhuman-bin`.
|
||||
<!-- /TODO -->
|
||||
|
||||
# Was ist OpenHuman?
|
||||
|
||||
OpenHuman ist ein quelloffener, agentenbasierter Assistent, der sich in deinen Alltag einfügt. Jeder Punkt verlinkt auf die ausführliche Beschreibung in der [Doku](https://tinyhumans.gitbook.io/openhuman/).
|
||||
|
||||
@@ -60,6 +60,12 @@ curl -fsSL https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts
|
||||
irm https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.ps1 | iex
|
||||
```
|
||||
|
||||
<!-- TODO: translate (ja-JP) — English source mirrored from README.md so non-EN readers get the same install caveats. Please translate. -->
|
||||
> **Linux:** the AppImage can crash on launch under Wayland (and on Arch-based distros with `sharun: Interpreter not found!`) — see [#2463](https://github.com/tinyhumansai/openhuman/issues/2463) for the cause and env-var workarounds.
|
||||
Arch Linux package maintainers can use the [`openhuman-bin` AUR recipe](./packages/arch/openhuman-bin/);
|
||||
once published, Arch users can install it with `yay -S openhuman-bin`.
|
||||
<!-- /TODO -->
|
||||
|
||||
# OpenHuman とは?
|
||||
|
||||
OpenHuman は、あなたの日常生活に統合されるよう設計されたオープンソースのエージェント型アシスタントです。各項目は[ドキュメント](https://tinyhumans.gitbook.io/openhuman/)内の詳細な解説にリンクしています。
|
||||
|
||||
@@ -61,6 +61,12 @@ curl -fsSL https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts
|
||||
irm https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.ps1 | iex
|
||||
```
|
||||
|
||||
<!-- TODO: translate (ko) — English source mirrored from README.md so non-EN readers get the same install caveats. Please translate. -->
|
||||
> **Linux:** the AppImage can crash on launch under Wayland (and on Arch-based distros with `sharun: Interpreter not found!`) — see [#2463](https://github.com/tinyhumansai/openhuman/issues/2463) for the cause and env-var workarounds.
|
||||
Arch Linux package maintainers can use the [`openhuman-bin` AUR recipe](./packages/arch/openhuman-bin/);
|
||||
once published, Arch users can install it with `yay -S openhuman-bin`.
|
||||
<!-- /TODO -->
|
||||
|
||||
# OpenHuman이란 무엇인가요?
|
||||
|
||||
OpenHuman은 일상 생활에 통합되도록 설계된 오픈 소스 에이전트 어시스턴트입니다. 각 글머리 기호는 [문서](https://tinyhumans.gitbook.io/openhuman/)의 더 깊은 설명으로 연결됩니다.
|
||||
|
||||
@@ -59,6 +59,12 @@ curl -fsSL https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts
|
||||
irm https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.ps1 | iex
|
||||
```
|
||||
|
||||
<!-- TODO: translate (zh-CN) — English source mirrored from README.md so non-EN readers get the same install caveats. Please translate. -->
|
||||
> **Linux:** the AppImage can crash on launch under Wayland (and on Arch-based distros with `sharun: Interpreter not found!`) — see [#2463](https://github.com/tinyhumansai/openhuman/issues/2463) for the cause and env-var workarounds.
|
||||
Arch Linux package maintainers can use the [`openhuman-bin` AUR recipe](./packages/arch/openhuman-bin/);
|
||||
once published, Arch users can install it with `yay -S openhuman-bin`.
|
||||
<!-- /TODO -->
|
||||
|
||||
# 什么是 OpenHuman?
|
||||
|
||||
OpenHuman 是一个开源智能助手,旨在融入你的日常生活。以下每条链接都指向[文档](https://tinyhumans.gitbook.io/openhuman/)中更详细的说明。
|
||||
|
||||
@@ -50,6 +50,9 @@
|
||||
* [Agent Observability](developing/agent-observability.md)
|
||||
* [Architecture](developing/architecture/README.md)
|
||||
* [Agent Harness](developing/architecture/agent-harness.md)
|
||||
* [Memory Tree (`src/openhuman/memory_tree/`)](developing/architecture/memory-tree.md)
|
||||
* [MCP Registry (`src/openhuman/mcp_registry/`)](developing/architecture/mcp-registry.md)
|
||||
* [Security (`src/openhuman/security/`)](developing/architecture/security.md)
|
||||
* [Frontend (app/src/)](developing/architecture/frontend.md)
|
||||
* [Tauri Shell (app/src-tauri/)](developing/architecture/tauri-shell.md)
|
||||
|
||||
|
||||
@@ -15,10 +15,10 @@ OpenHuman is a cross-platform communication and automation platform purpose-buil
|
||||
|
||||
| Path | Contents |
|
||||
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **`app/`** | Yarn workspace **`openhuman-app`**: Vite/React UI (`app/src/`), Tauri shell (`app/src-tauri/`), Vitest tests |
|
||||
| **`app/`** | pnpm workspace **`openhuman-app`**: Vite/React UI (`app/src/`), Tauri shell (`app/src-tauri/`), Vitest tests |
|
||||
| **Repo root `src/`** | Rust **`openhuman_core`** library + **`openhuman-core`** CLI binary - core server, JSON-RPC, first-class JavaScript runtime (`src/openhuman/javascript/`) backed by a managed Node.js implementation, channels, memory, etc. |
|
||||
| **`Cargo.toml`** (root) | Builds the `openhuman-core` binary (`cargo build --bin openhuman-core`) staged into `app/src-tauri/binaries/` for the desktop bundle |
|
||||
| **`skills/`** | Skill packages consumed by the runtime |
|
||||
| **`src/openhuman/skills/`** | **Metadata-only** skill helpers (`ops_create`, `ops_discover`, `ops_install`, `ops_parse`, `inject`, `schemas`, `types`). The legacy QuickJS / `rquickjs` skill execution runtime was removed; skills now contribute metadata + tool descriptors that get injected into agent prompts, while tool execution flows through native Rust handlers and Node-backed helpers via `runtime_node`. |
|
||||
| **`docs/`** | This book + per-tree guides (`docs/src/`, `docs/src-tauri/`) |
|
||||
|
||||
The desktop app **WebView** loads the UI from `app/`; heavy RPC and skills run in the **`openhuman-core`** process, reachable over HTTP from the Tauri host (`core_rpc_relay`).
|
||||
@@ -57,13 +57,13 @@ Tauri v2 compiles the Rust core into native binaries per platform, embedding the
|
||||
| Rust Core Engine |
|
||||
| |
|
||||
| +------------------+ +------------------+ +-----------------+ |
|
||||
| | QuickJS Skills | | Socket Manager | | AI Encryption | |
|
||||
| | Runtime Engine | | (Persistent WS) | | & Memory Store | |
|
||||
| | Tool Runtime | | Socket Manager | | AI Encryption | |
|
||||
| | (native + Node) | | (Persistent WS) | | & Memory Store | |
|
||||
| +------------------+ +------------------+ +-----------------+ |
|
||||
| |
|
||||
| +------------------+ +------------------+ +-----------------+ |
|
||||
| | Skill Registry | | Cron Scheduler | | Session & Auth | |
|
||||
| | & Bridge APIs | | (5s tick loop) | | Management | |
|
||||
| | Skill Metadata | | Cron Scheduler | | Session & Auth | |
|
||||
| | & Tool Registry | | (5s tick loop) | | Management | |
|
||||
| +------------------+ +------------------+ +-----------------+ |
|
||||
| |
|
||||
| +------------------+ +------------------+ +-----------------+ |
|
||||
@@ -78,7 +78,7 @@ Tauri v2 compiles the Rust core into native binaries per platform, embedding the
|
||||
(Socket.io Server) (Telegram, etc.)
|
||||
```
|
||||
|
||||
The frontend communicates with the **openhuman** Rust core in two ways: **Tauri IPC** for a small set of shell commands (windows, AI file helpers, **`core_rpc_relay`**) and **HTTP JSON-RPC** to the core process for business logic and skills. The core owns persistent connections where applicable, cryptographic work for memory/features, and **QuickJS** sandboxed skill execution.
|
||||
The frontend communicates with the **openhuman** Rust core in two ways: **Tauri IPC** for a small set of shell commands (windows, AI file helpers, **`core_rpc_relay`**) and **HTTP JSON-RPC** to the core process for business logic and tools. The core owns persistent connections where applicable, cryptographic work for memory/features, and tool execution — native Rust handlers plus Node-backed helpers via `runtime_node`, gated by the `security/` sandbox policy. Skills no longer execute in-process; the `src/openhuman/skills/` domain contributes metadata + tool descriptors that get injected into agent prompts.
|
||||
|
||||
---
|
||||
|
||||
@@ -88,8 +88,8 @@ OpenHuman chose Tauri + Rust over Electron for fundamental performance and secur
|
||||
|
||||
| Metric | OpenHuman (Tauri + Rust) | Typical Electron App |
|
||||
| ------------------------- | -------------------------------------------------------- | ---------------------------- |
|
||||
| Binary size | Feature-dependent (CEF runtime + skills bundle dominate) | ~150 MB+ |
|
||||
| Memory per skill context | ~1-2 MB (QuickJS) | ~150 MB+ (Chromium renderer) |
|
||||
| Binary size | Feature-dependent (CEF runtime dominates) | ~150 MB+ |
|
||||
| Memory per tool execution | Native Rust (no per-tool VM); shared managed Node runtime for helper calls | ~150 MB+ (Chromium renderer per process) |
|
||||
| Cold startup | Sub-500ms | 2-5 seconds |
|
||||
| Garbage collection pauses | None (Rust ownership model) | V8 GC pauses |
|
||||
| Memory safety | Compile-time guaranteed | Runtime exceptions |
|
||||
@@ -217,16 +217,16 @@ AI Model (Backend)
|
||||
|
|
||||
| 2. Decides which tool to call
|
||||
|
|
||||
| 3. mcp:toolCall { skillId__toolName, arguments }
|
||||
| 3. mcp:toolCall { tool_name, arguments }
|
||||
| |
|
||||
| v
|
||||
| Socket Manager routes to Skill Registry
|
||||
| Socket Manager routes to the unified Tool Registry
|
||||
| |
|
||||
| v
|
||||
| QuickJS Skill Instance executes tool
|
||||
| Native Rust handler (or Node helper via `runtime_node`) executes
|
||||
| |
|
||||
| v
|
||||
| Bridge API call (HTTP, DB, etc.)
|
||||
| External call (HTTP via reqwest, SQLite, etc.) — gated by SecurityPolicy
|
||||
| |
|
||||
| <-- mcp:toolCallResponse { result }
|
||||
|
|
||||
@@ -259,9 +259,9 @@ Memory encryption keys derive from user credentials via Argon2id, ensuring memor
|
||||
| Security Layers |
|
||||
| |
|
||||
| +------------------+ +------------------+ +------------------+ |
|
||||
| | OS Keychain | | AES-256-GCM | | Sandboxed | |
|
||||
| | (macOS/Win/Lin) | | Memory Encrypt | | QuickJS per | |
|
||||
| | for credentials | | + Argon2id KDF | | skill (64 MB) | |
|
||||
| | OS Keychain | | AES-256-GCM | | Tool sandbox | |
|
||||
| | (macOS/Win/Lin) | | Memory Encrypt | | (Docker / bwrap | |
|
||||
| | for credentials | | + Argon2id KDF | | firejail / etc) | |
|
||||
| +------------------+ +------------------+ +------------------+ |
|
||||
| |
|
||||
| +------------------+ +------------------+ +------------------+ |
|
||||
@@ -274,7 +274,7 @@ Memory encryption keys derive from user credentials via Argon2id, ensuring memor
|
||||
|
||||
- **Credential storage**: OS keychain integration via the `keyring` crate (macOS Keychain, Windows Credential Manager, Linux Secret Service), desktop only
|
||||
- **Memory encryption**: AES-256-GCM with Argon2id key derivation. All AI memory is encrypted at rest
|
||||
- **Skill sandboxing**: Each QuickJS instance has enforced memory limits (64 MB default) and stack limits (512 KB). No cross-skill memory access
|
||||
- **Tool sandboxing**: Executable tools run through `SecurityPolicy` (`src/openhuman/security/policy.rs`) and a host-appropriate sandbox backend selected at runtime — Docker, Bubblewrap, Firejail, Landlock, or Noop (`src/openhuman/security/{docker,bubblewrap,firejail,landlock}.rs`, `detect.rs`). The legacy per-skill QuickJS memory/stack limit model is gone
|
||||
- **Auth handoff**: Web-to-desktop authentication uses single-use login tokens with 5-minute TTL, exchanged via Rust HTTP client (bypasses CORS)
|
||||
- **Network TLS**: All WebSocket and HTTP connections use rustls, no dependency on platform OpenSSL
|
||||
- **State management**: Sensitive data lives in Redux (memory) and OS keychain (persistent). No localStorage for credentials or tokens
|
||||
@@ -299,25 +299,25 @@ AI model receives prompt + tool catalog (via tool:sync)
|
||||
AI decides to invoke a skill tool (e.g., send Telegram message)
|
||||
|
|
||||
v
|
||||
mcp:toolCall event sent over Socket.io
|
||||
mcp:toolCall event sent over Socket.io (or local invocation)
|
||||
|
|
||||
v
|
||||
Socket Manager (Rust) receives event, parses skillId__toolName
|
||||
Socket Manager (Rust) receives event, parses the tool name
|
||||
|
|
||||
v
|
||||
Skill Registry routes message to correct QuickJS instance via MPSC channel
|
||||
Tool Registry routes to the registered handler (native Rust or Node helper via `runtime_node`)
|
||||
|
|
||||
v
|
||||
QuickJS skill executes tool handler
|
||||
Handler executes through `SecurityPolicy` + the active sandbox backend
|
||||
|
|
||||
v
|
||||
Bridge API: net.rs makes HTTP request via reqwest (CORS-free, rustls TLS)
|
||||
External call: reqwest HTTP request via rustls (no browser CORS), SQLite, OS keychain, etc.
|
||||
|
|
||||
v
|
||||
External service responds (e.g., Telegram API)
|
||||
External service responds
|
||||
|
|
||||
v
|
||||
Result flows back: Bridge -> QuickJS -> Registry -> Socket -> MCP -> AI -> UI
|
||||
Result flows back: Handler -> Registry -> Socket -> MCP -> AI -> UI
|
||||
|
|
||||
v
|
||||
User sees the result in the chat interface
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
---
|
||||
description: >-
|
||||
The dynamic, user-facing side of MCP-client support — discover servers on
|
||||
Smithery.ai, persist installs to SQLite, supervise local-spawn subprocess
|
||||
lifecycle, surface their tools to agents via the unified tool registry.
|
||||
icon: plug
|
||||
---
|
||||
|
||||
# MCP Registry (`src/openhuman/mcp_registry/`)
|
||||
|
||||
`src/openhuman/mcp_registry/` is the **dynamic, user-facing** half of OpenHuman's Model Context Protocol client support. It lets a user browse the Smithery.ai MCP registry, install a chosen server, persist that choice to SQLite, and (for servers launched as local subprocesses) supervise the subprocess lifecycle. Installed servers' tools are surfaced to agents via the unified tool registry (`crate::openhuman::tool_registry`).
|
||||
|
||||
> **Naming note**: the Rust module path is `mcp_registry`, but the RPC namespace and on-disk SQLite filename are still `mcp_clients` for backward compatibility with existing frontend code and stored user state. Grep both names when chasing call sites.
|
||||
|
||||
This module is paired with `src/openhuman/mcp_client/` — the **transport library** (HTTP + stdio primitives) plus the _static, config-declared_ server set read from `[[mcp_client.servers]]` in `config.toml`. Agents reach that static set through generic bridge tools. The static set is intentionally separate from this dynamic registry; both kinds will eventually share the transport primitives from `mcp_client`.
|
||||
|
||||
```text
|
||||
┌───────────────────────────────────────────────┐
|
||||
Smithery.ai ──► registries/ + registry.rs (10-min SQLite cache)│
|
||||
└────────────────────┬──────────────────────────┘
|
||||
│ browse / install
|
||||
▼
|
||||
┌──────────────────────┐
|
||||
Frontend (Skills UI) ─►│ ops.rs / schemas.rs │ RPC controllers
|
||||
└──────────┬───────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────┐
|
||||
│ store.rs │ mcp_clients.db (SQLite)
|
||||
│ InstalledServer rows│
|
||||
└──────────┬───────────┘
|
||||
│ at boot
|
||||
▼
|
||||
┌──────────────────────┐
|
||||
│ boot.rs │ spawn_installed_servers
|
||||
└──────────┬───────────┘
|
||||
│ for each local-spawn
|
||||
▼
|
||||
┌──────────────────────┐
|
||||
│ connections.rs │ wraps mcp_client::
|
||||
│ (global registry) │ McpStdioClient
|
||||
└──────────┬───────────┘
|
||||
│ surfaces tools to
|
||||
▼
|
||||
tool_registry (agents)
|
||||
```
|
||||
|
||||
## Server transport model
|
||||
|
||||
Today every `InstalledServer` is a **local subprocess** launched by `npx`, `uvx`, or a direct binary (see `types::CommandKind`). The connection is **stdio JSON-RPC**, owned by `connections.rs`.
|
||||
|
||||
HTTP-remote MCP servers (the majority of what Smithery actually lists) are **not yet modelled** as an `InstalledServer` variant. Adding a remote transport variant is planned follow-up work; after that, the registry will hold both kinds and `connections.rs` will dispatch by transport.
|
||||
|
||||
## Boot-time spawn
|
||||
|
||||
`boot::spawn_installed_servers` is called from `bootstrap_core_runtime` so every local-spawn server is connected as soon as the core comes up. Errors are logged per-server and **never block boot** — a broken MCP install should not gate the desktop app starting.
|
||||
|
||||
## Layout
|
||||
|
||||
| Path | Role |
|
||||
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `types.rs` | Data structures: `InstalledServer`, `McpTool`, `ConnStatus`, Smithery DTOs, etc. |
|
||||
| `store.rs` | SQLite persistence — `mcp_clients.db`, CRUD over `InstalledServer` rows. |
|
||||
| `registry.rs` | Smithery HTTP client with a 10-minute SQLite cache so re-browsing doesn't hammer the upstream registry. |
|
||||
| `registries/` | Adapters for the upstream registries this code can browse (currently Smithery). |
|
||||
| `connections.rs` | Global in-process connection registry. Wraps `crate::openhuman::mcp_client::McpStdioClient` — there is no separate stdio client implementation here. |
|
||||
| `boot.rs` | Boot-time spawn (`spawn_installed_servers`) called from `bootstrap_core_runtime`. |
|
||||
| `setup.rs` / `setup_ops.rs` | "Setup agent" support — the small agent that walks a user through configuring a freshly installed server (env vars, secrets, first connect). |
|
||||
| `ops.rs` | RPC handler implementations (install, uninstall, list, browse, enable / disable, etc.). |
|
||||
| `schemas.rs` | Controller schemas + handler dispatch. Re-exported from `mod.rs` as `all_mcp_registry_controller_schemas` / `all_mcp_registry_registered_controllers`. |
|
||||
| `bus.rs` | `DomainEvent` subscriber for lifecycle logging. |
|
||||
|
||||
## Public surface
|
||||
|
||||
The exports from `mod.rs` are intentionally narrow:
|
||||
|
||||
```rust
|
||||
pub use schemas::{
|
||||
all_controller_schemas as all_mcp_registry_controller_schemas,
|
||||
all_registered_controllers as all_mcp_registry_registered_controllers,
|
||||
schemas as mcp_registry_schemas,
|
||||
};
|
||||
|
||||
pub use types::{ConnStatus, InstalledServer, McpTool};
|
||||
```
|
||||
|
||||
Everything else — `boot`, `bus`, `connections`, `store`, `setup`, `setup_ops` — is `pub mod` for in-crate callers but not re-exported.
|
||||
|
||||
## Calls into
|
||||
|
||||
- `crate::openhuman::mcp_client::McpStdioClient` — the actual stdio transport.
|
||||
- `crate::openhuman::tool_registry` — installed servers' tools land here so agents see them alongside native tools.
|
||||
- `memory_store` / workspace SQLite — for `mcp_clients.db` persistence.
|
||||
- Smithery.ai HTTP — registry browsing.
|
||||
|
||||
## Called by
|
||||
|
||||
- `bootstrap_core_runtime` (via `boot::spawn_installed_servers`).
|
||||
- Frontend Skills UI — the (currently stubbed) MCP servers panel will dispatch through `ops.rs` over the `openhuman.mcp_clients_*` RPC namespace.
|
||||
- The setup agent in `setup_ops.rs` — for first-connect onboarding.
|
||||
|
||||
## Tests
|
||||
|
||||
Unit tests are co-located inline under `#[cfg(test)]` blocks in `store.rs`, `connections.rs`, and `setup.rs`. There is no dedicated `*_tests.rs` sibling per file (the convention in this domain is inline).
|
||||
|
||||
## Related
|
||||
|
||||
- [`mcp_registry/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/mcp_registry/mod.rs) — the authoritative rustdoc this page mirrors.
|
||||
- `src/openhuman/mcp_client/` — the transport library + static config-declared server set.
|
||||
- [Agent Harness](agent-harness.md) — how the agent ends up calling MCP tools through `tool_registry`.
|
||||
- [Architecture overview](../architecture.md) — where this fits in the wider system.
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
description: >-
|
||||
The generic summary-tree engine under the Memory Tree feature - bucket-seal
|
||||
cascades, scoring, embedding, entity extraction, retrieval, summarisation.
|
||||
Kind-agnostic mechanics that Source / Global / Topic trees all share.
|
||||
icon: diagram-project
|
||||
---
|
||||
|
||||
# Memory Tree (`src/openhuman/memory_tree/`)
|
||||
|
||||
`src/openhuman/memory_tree/` is the **generic tree engine** sitting under the user-facing [Memory Tree feature](../../features/obsidian-wiki/memory-tree.md). It owns the kind-agnostic mechanics — appending leaves, cascading bucket seals, summarising one level to the next, scoring and embedding, retrieving for agents — that every concrete tree flavour (`Source`, `Global`, `Topic`) shares. It is deliberately **unaware** of which flavour a tree belongs to.
|
||||
|
||||
Kind-specific policy — when to spawn a topic tree, what scope a global tree covers, how digests are written — lives in `src/openhuman/memory/tree_global` and `src/openhuman/memory/tree_topic`. Persistence (the single `Tree` table and its schema) lives one layer down in `memory_store::trees`. This module sits between them.
|
||||
|
||||
```text
|
||||
memory (orchestrator) ──┐
|
||||
│ writes leaves via TreeWriteRequest
|
||||
▼
|
||||
memory_tree (this module — generic mechanics)
|
||||
├── tree/ append + cascade seal + flush
|
||||
├── summarise.rs L_n -> L_{n+1} text via the chat model
|
||||
├── retrieval/ agent-facing read tools (walk, drill, fetch)
|
||||
├── score/ scoring, embedding, entity extraction
|
||||
├── tools.rs re-exports from memory::query
|
||||
└── io.rs canonical Tree{Write,Read}{Request,Outcome,Result}
|
||||
│
|
||||
▼
|
||||
memory_store::trees (persistence: one Tree table, one schema)
|
||||
```
|
||||
|
||||
## Layout
|
||||
|
||||
| Path | Role |
|
||||
| ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| [`mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/memory_tree/mod.rs) | Re-exports `io::*` and the controller-schema registries hosted in `memory`. Also re-exports `memory::tree_global` + `memory::tree_topic` under the legacy `memory_tree::tree_{global,topic}` paths for backward compatibility. |
|
||||
| `io.rs` | Canonical contract types: `TreeWriteRequest` / `TreeWriteOutcome`, `TreeReadRequest` / `TreeReadHit` / `TreeReadResult`, `TreeLeafPayload`, `TreeLabelStrategy`. Pure types, no IO. |
|
||||
| `tree/` | `bucket_seal` (append leaf + cascade seal), `flush` (time-based partial seal), `registry` (kind-parameterized `get_or_create_tree` with UNIQUE-race recovery), `mod.rs` (re-exports + `memory_store::trees` shims for legacy paths). |
|
||||
| `summarise.rs` | One function: produce the next-level summary text for a bucket. Wraps the chat model with a fixed prompt and token budget. |
|
||||
| `retrieval/` | Agent-facing tools. Read: `walk` (agentic), `drill_down`, `fetch_leaves`, `query_{source,global,topic}`, `search_entities`. Write: `ingest_document` (orchestrator-facing). |
|
||||
| `score/` | Scoring signals, embedding (cloud / Ollama / inert), entity extraction (regex / LLM), canonical resolver, entity index store. |
|
||||
| `tools.rs` | Re-exports from `memory::query` for backward compatibility. |
|
||||
| `tree_runtime/` | Tree-summarizer controller registry — exposed through `all_tree_summarizer_controller_schemas` / `all_tree_summarizer_registered_controllers` re-exports in `mod.rs`. |
|
||||
|
||||
## Layer rules
|
||||
|
||||
These are load-bearing invariants — break one and the engine stops being kind-agnostic:
|
||||
|
||||
- **No tree-kind branching here.** `bucket_seal`, `flush`, `registry`, and `summarise` all take `TreeKind` as a parameter or treat it as opaque. Conditionals on "is this a Source tree?" belong in the orchestrator (`src/openhuman/memory/`), not here.
|
||||
- **No persistence here.** Reads and writes go through `memory_store::trees::{store, registry, hotness}`. This module does not open SQLite handles directly.
|
||||
- **No policy here.** Curator gates (hotness thresholds), digest cadence, global scope sentinels all live in `memory::tree_{global,topic}`. This module reacts to policy decisions, it does not make them.
|
||||
|
||||
## How a write flows in
|
||||
|
||||
1. The orchestrator (`memory::*`) constructs a `TreeWriteRequest` with a `TreeKind` and a `TreeLeafPayload`.
|
||||
2. `tree::bucket_seal` appends the leaf to the open bucket at L0. If the bucket fills, it seals — `summarise.rs` produces the L1 summary, which becomes a leaf in the L1 bucket, and the cascade continues upward until a non-full bucket is hit.
|
||||
3. `score/` runs in the background: embeddings (cloud / Ollama / inert backend), entity extraction (regex first, LLM optional), hotness signals. None of this blocks the write path.
|
||||
4. The outcome (`TreeWriteOutcome`) is returned synchronously to the orchestrator; scoring catches up asynchronously.
|
||||
|
||||
`tree::flush` exists for the time-bounded case — if a bucket hasn't filled within its TTL, it gets sealed partially so the next level always has something fresh to summarise.
|
||||
|
||||
## How a read flows out
|
||||
|
||||
Agents reach this module through the tools in `retrieval/`:
|
||||
|
||||
- `walk` — agentic exploration; the agent picks summary nodes to drill into.
|
||||
- `drill_down` — deterministic traversal from a known starting summary.
|
||||
- `fetch_leaves` — pull raw leaves for a sealed bucket.
|
||||
- `query_{source,global,topic}` — kind-scoped retrieval; the orchestrator's tree-kind policy decides which one the agent sees.
|
||||
- `search_entities` — entity-index lookup backed by `score/`.
|
||||
|
||||
All retrieval handlers consult `memory_store::trees::hotness` so warm content surfaces first.
|
||||
|
||||
## Controller registry
|
||||
|
||||
`memory_tree::mod.rs` re-exports two controller registries that get wired into the global registry in `src/core/all.rs`:
|
||||
|
||||
- `all_memory_tree_controller_schemas` / `all_memory_tree_registered_controllers` — sourced from `memory::schema` (the orchestrator hosts them; this module just surfaces them under the `memory_tree` path).
|
||||
- `all_retrieval_controller_schemas` / `all_retrieval_registered_controllers` — the agent-facing read tools listed above.
|
||||
- `all_tree_summarizer_controller_schemas` / `all_tree_summarizer_registered_controllers` — from `tree_runtime`, for summariser admin / inspection.
|
||||
|
||||
## Related
|
||||
|
||||
- [`memory_tree/README.md`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/memory_tree/README.md) — authoritative internal-audience overview this page mirrors.
|
||||
- [Memory Tree feature](../../features/obsidian-wiki/memory-tree.md) — what end users see.
|
||||
- [Architecture overview](../architecture.md) — where this fits in the wider system.
|
||||
@@ -0,0 +1,121 @@
|
||||
---
|
||||
description: >-
|
||||
Trust boundary for the autonomous core - autonomy / risk policy, pluggable
|
||||
sandbox backends (Docker, Bubblewrap, Firejail, Landlock, Noop), audit log,
|
||||
encrypted secret store, public-bind / pairing guard, and the redact() helper.
|
||||
icon: shield-halved
|
||||
---
|
||||
|
||||
# Security (`src/openhuman/security/`)
|
||||
|
||||
`src/openhuman/security/` is the **trust boundary for the autonomous core**. It owns the autonomy / risk policy that decides whether a given tool call is allowed, the pluggable sandbox backends that confine those calls when the host supports it, the append-only audit log of every agent action, the encrypted secret store, the pairing guard that gates public binding of the RPC server, and the `redact()` helper every other domain uses to keep logs free of plaintext credentials.
|
||||
|
||||
It does **not** own:
|
||||
|
||||
- The cross-domain `EncryptionEngine` — that lives in `src/openhuman/encryption/`.
|
||||
- Per-channel credential storage — that lives in `src/openhuman/credentials/`.
|
||||
|
||||
This module is the place to look first when asking "is this agent action allowed, and if so, how is it confined?"
|
||||
|
||||
## Public surface
|
||||
|
||||
| Item | File | Purpose |
|
||||
| ----------------------------------------------------------------------------------------------------------------------------- | ------------ | ----------------------------------------------------------------------- |
|
||||
| `SecurityPolicy` | `policy.rs` | Assembles runtime policy from `AutonomyConfig` + workspace dir. |
|
||||
| `AutonomyLevel` (`Supervised` / `SemiAutonomous` / `Autonomous`) | `policy.rs` | Three-step autonomy ladder. |
|
||||
| `CommandRiskLevel`, `ToolOperation`, `ActionTracker` | `policy.rs` | Risk classification + per-session counting. |
|
||||
| `Sandbox` trait, `NoopSandbox` | `traits.rs` | The pluggable sandbox abstraction; every backend implements `Sandbox`. |
|
||||
| `create_sandbox(&SecurityConfig) -> Arc<dyn Sandbox>` | `detect.rs` | Picks the best backend available on the host at runtime. |
|
||||
| `pub mod docker / bubblewrap / firejail / landlock` | (siblings) | Per-backend implementations of `Sandbox`. |
|
||||
| `SecretStore` | `secrets.rs` | XOR / OS-keychain encrypted secret persistence with round-trip helpers. |
|
||||
| `AuditLogger`, `AuditEventType`, `AuditEvent`, `Actor`, `Action`, `ExecutionResult`, `SecurityContext`, `CommandExecutionLog` | `audit.rs` | Append-only audit trail. |
|
||||
| `PairingGuard`, `constant_time_eq`, `is_public_bind` | `pairing.rs` | Pairing-token check before binding the RPC server publicly. |
|
||||
| `redact(value: &str) -> String` | `core.rs` | Uniform 4-char-prefix redaction for logs. |
|
||||
| `security_policy_info() -> RpcOutcome<serde_json::Value>` | `ops.rs` | RPC handler for the doctor / settings UI. |
|
||||
|
||||
## Sandbox backend selection
|
||||
|
||||
`detect::create_sandbox` walks a preference list and returns the **first available** backend on the host. The exact order is encoded in `detect.rs`; in practice it favours the strongest available isolation:
|
||||
|
||||
```text
|
||||
┌──────────────┐
|
||||
SecurityConfig ─►│ create_sandbox│
|
||||
└──────┬───────┘
|
||||
│ probes
|
||||
├─► Docker (best isolation; needs daemon)
|
||||
├─► Bubblewrap (Linux user-namespace sandbox)
|
||||
├─► Firejail (Linux setuid sandbox)
|
||||
├─► Landlock (Linux LSM; in-process)
|
||||
└─► Noop (last resort; logs only)
|
||||
```
|
||||
|
||||
The agent never sees the choice — it just calls into `Sandbox::run(...)` and the active backend handles the rest. Every backend lives in a sibling file (`docker.rs`, `bubblewrap.rs`, `firejail.rs`, `landlock.rs`); the noop fallback is in `traits.rs`.
|
||||
|
||||
## Autonomy ladder
|
||||
|
||||
`AutonomyLevel` is a three-step ladder that controls how aggressively the policy gates tool calls:
|
||||
|
||||
- **Supervised** — every higher-risk tool call requires an explicit approval round-trip.
|
||||
- **SemiAutonomous** — low / medium-risk tool calls flow through; higher-risk ones still approval-gate.
|
||||
- **Autonomous** — the policy lets the agent run unattended within budget and risk caps.
|
||||
|
||||
`CommandRiskLevel` + `ToolOperation` classify a given tool call; `ActionTracker` keeps the per-session counts that the policy compares against caps. The agent harness asks `SecurityPolicy` for a decision before every executable tool dispatch.
|
||||
|
||||
## Audit log
|
||||
|
||||
`audit.rs` writes an append-only stream of `AuditEvent`s under the workspace dir. Every executable tool call lands here with its `Actor` (agent / user), `Action`, `ExecutionResult`, and the `SecurityContext` (autonomy level, sandbox backend, etc.) it ran under. The log is the post-hoc story of what the agent did and why it was allowed.
|
||||
|
||||
## Pairing guard
|
||||
|
||||
`PairingGuard` (in `pairing.rs`) stands between the RPC server and any attempt to bind to a non-loopback address. `is_public_bind` detects the dangerous case; `PairingGuard` requires a constant-time-compared pairing token (`constant_time_eq`) before such a bind is permitted. This is the iOS / LAN-companion pairing flow's defence against an unpaired peer attaching to the desktop core.
|
||||
|
||||
## Secret store
|
||||
|
||||
`SecretStore` (in `secrets.rs`) persists per-key secrets with at-rest encryption. On supported platforms the encryption key comes from the OS keychain; otherwise it falls back to a workspace-local XOR scheme (which is **obfuscation, not security**, and is documented as such in the source).
|
||||
|
||||
## `redact()`
|
||||
|
||||
`redact(value)` returns a uniform 4-char-prefix string (e.g. `"sk-a"` -> `"sk-a…"`) for use in logs and error messages. Use it whenever a secret, credential, token, or PII string is about to be formatted into a `log::` / `tracing::` call. Other domains call it directly — `credentials/`, `webhooks/`, `composio/`, the integration adapters.
|
||||
|
||||
## Layout
|
||||
|
||||
| Path | Role |
|
||||
| ------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
||||
| `policy.rs`, `policy_tests.rs` | `SecurityPolicy`, `AutonomyLevel`, risk classification, action tracking. |
|
||||
| `traits.rs` | `Sandbox` trait + `NoopSandbox` fallback. |
|
||||
| `detect.rs` | `create_sandbox` — best-available-backend selection. |
|
||||
| `docker.rs` / `bubblewrap.rs` / `firejail.rs` / `landlock.rs` | Per-backend `Sandbox` implementations. |
|
||||
| `core.rs` | `redact()` + small shared helpers (has its own `#[cfg(test)] mod tests`). |
|
||||
| `audit.rs` | Append-only audit log types. |
|
||||
| `secrets.rs`, `secrets_tests.rs` | `SecretStore` + round-trip tests. |
|
||||
| `pairing.rs`, `pairing_tests.rs` | `PairingGuard` + constant-time helpers. |
|
||||
| `ops.rs` | RPC handler (`security_policy_info`). |
|
||||
| `schemas.rs` | Controller schemas + handler dispatch. |
|
||||
| `mod.rs` | Re-exports of the public surface above. |
|
||||
|
||||
## Calls into
|
||||
|
||||
- `src/openhuman/config/` — `SecurityConfig`, `AutonomyConfig` for policy + sandbox selection.
|
||||
- OS-level sandbox tools — `docker`, `bwrap`, `firejail`, Landlock syscalls (per backend).
|
||||
- Workspace filesystem — for the audit log and secret store.
|
||||
|
||||
## Called by
|
||||
|
||||
- `src/openhuman/cron/scheduler.rs` — wraps shell jobs in `SecurityPolicy::from_config`.
|
||||
- `src/openhuman/tools/local_cli.rs`, `tools/ops.rs`, and most `tools/impl/{system,network,memory,agent}/*.rs` — every executable tool consults `SecurityPolicy`.
|
||||
- `src/openhuman/tools/impl/network/{curl,http_request,composio}.rs` — risk-classify outbound calls.
|
||||
- `src/openhuman/tools/impl/memory/{store,forget}.rs` — sensitive-write tracking.
|
||||
- `src/openhuman/tools/impl/agent/delegate.rs` — sub-agent dispatch goes through the autonomy gate.
|
||||
- `src/openhuman/credentials/` — uses `SecretStore` and `redact`.
|
||||
|
||||
## Tests
|
||||
|
||||
- Unit: `pairing_tests.rs`, `policy_tests.rs`, `secrets_tests.rs`.
|
||||
- `core.rs` has its own `#[cfg(test)] mod tests` — round-trips `SecretStore` encrypt / decrypt, `redact()` cases, `PairingGuard` defaults.
|
||||
- Sandbox-backend smoke tests: each backend file has its own `#[cfg(test)]` blocks where the binary is available on the host.
|
||||
|
||||
## Related
|
||||
|
||||
- [`security/README.md`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/README.md) — authoritative internal-audience overview this page mirrors.
|
||||
- [Architecture overview](../architecture.md) — wider system context.
|
||||
- [Agent Harness](agent-harness.md) — where `SecurityPolicy` is consulted on every tool dispatch.
|
||||
Reference in New Issue
Block a user