# sol a virtual librarian for Matrix. sol lives in your chat rooms, archives conversations in OpenSearch, and responds with the help of Mistral AI — with end-to-end encryption, tool use, and per-user memory. sol is built by [sunbeam studios](https://sunbeam.pt) as part of our self-hosted collaboration stack. ## what sol does - **Matrix presence** — joins rooms, reads the vibe, decides when to speak. direct messages always get a response; in group rooms, sol evaluates relevance before jumping in. - **message archive** — every message is indexed in OpenSearch with full-text and semantic search. sol can search its own archive via tools. - **tool use** — mistral calls tools mid-conversation: archive search, room context retrieval, and a sandboxed TypeScript/JavaScript runtime (deno_core) for computation. - **per-user memory** — sol remembers things about the people it talks to. memories are extracted automatically after conversations (via ministral-3b), injected into the system prompt before responding, and accessible from scripts via `sol.memory.get/set`. user isolation is enforced at the rust level. - **reactions** — sol can react to messages with emoji when it has something to express but not enough to say. - **E2EE** — full end-to-end encryption via matrix-sdk with sqlite state store. ## architecture ``` src/ ├── main.rs entrypoint, Matrix client setup, backfill ├── sync.rs event loop — messages, reactions, redactions, invites ├── config.rs TOML config with serde defaults ├── context.rs ResponseContext — per-message sender identity ├── matrix_utils.rs message extraction, reply detection, room info ├── archive/ │ ├── schema.rs ArchiveDocument, OpenSearch index mapping │ └── indexer.rs batched indexing, reactions, edits, redactions ├── brain/ │ ├── conversation.rs sliding-window context per room │ ├── evaluator.rs engagement decision (must/maybe/react/ignore) │ ├── personality.rs system prompt templating │ └── responder.rs Mistral chat loop with tool iterations + memory ├── memory/ │ ├── schema.rs MemoryDocument, index mapping │ ├── store.rs query, get_recent, set — OpenSearch operations │ └── extractor.rs post-response fact extraction via ministral-3b └── tools/ ├── mod.rs ToolRegistry, tool definitions, dispatch ├── search.rs archive search (keyword + semantic) ├── room_history.rs context around a timestamp or event ├── room_info.rs room listing, member queries └── script.rs deno_core sandbox with sol.* API ``` ## dependencies sol talks to three external services: - **Matrix homeserver** — [tuwunel](https://github.com/tulir/tuwunel) (or any Matrix server) - **OpenSearch** — message archive + user memory indices - **Mistral AI** — response generation, engagement evaluation, memory extraction ## configuration sol reads config from `SOL_CONFIG` (default: `/etc/sol/sol.toml`) and the system prompt from `SOL_SYSTEM_PROMPT` (default: `/etc/sol/system_prompt.md`). secrets via environment: | Variable | Description | |----------|-------------| | `SOL_MATRIX_ACCESS_TOKEN` | Matrix access token | | `SOL_MATRIX_DEVICE_ID` | Matrix device ID (for E2EE) | | `SOL_MISTRAL_API_KEY` | Mistral API key | see `config/sol.toml` for the full config reference with defaults. ## building ```sh cargo build --release ``` docker (cross-compile to x86_64 linux): ```sh docker build -t sol . ``` ## running ```sh export SOL_MATRIX_ACCESS_TOKEN="..." export SOL_MATRIX_DEVICE_ID="..." export SOL_MISTRAL_API_KEY="..." export SOL_CONFIG="config/sol.toml" export SOL_SYSTEM_PROMPT="config/system_prompt.md" cargo run --release ``` ## tests ```sh cargo test ``` 80 unit tests covering config parsing, conversation windowing, engagement rules, personality templating, memory schema/store/extraction, search query building, TypeScript transpilation, and sandbox path isolation. ## license [MIT](LICENSE)