4.1 KiB
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 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 (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
cargo build --release
docker (cross-compile to x86_64 linux):
docker build -t sol .
running
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
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.