Workspace Layout
memoria/ # Cargo workspace root
├── crates/
│ ├── memoria-core/ # Shared types: MemoriaError, Memory, MemoryType
│ ├── memoria-storage/ # SqlMemoryStore — all SQL queries, migrations
│ ├── memoria-service/ # Business logic
│ │ └── src/
│ │ ├── service.rs # MemoryService — main entry point
│ │ ├── scheduler.rs # GovernanceScheduler — periodic tasks + leader election
│ │ ├── config.rs # Config struct (reads env vars)
│ │ ├── distributed.rs # DistributedLock, AsyncTaskStore traits + SQL impls
│ │ ├── governance/ # GovernanceStrategy trait, DefaultGovernanceStrategy
│ │ └── plugin/ # Plugin system (manifest, repository, rhai, grpc)
│ ├── memoria-api/ # REST API (axum)
│ │ └── src/
│ │ ├── lib.rs # build_router(), route registration
│ │ ├── state.rs # AppState (service, task_store, instance_id)
│ │ └── routes/ # memory, admin, governance, plugins, sessions
│ ├── memoria-mcp/ # MCP server (stdio/SSE transport)
│ ├── memoria-cli/ # CLI binary: init, mcp, serve, plugin, benchmark
│ ├── memoria-embedding/ # Embedding + LLM client
│ └── memoria-git/ # Git-for-Data: snapshots, branches, merge
├── Cargo.toml # Workspace deps
└── build.rs # Proto compilation (tonic)
Key Traits
trait GovernanceStrategy: Send + Sync {
fn strategy_key(&self) -> &str;
async fn plan(&self, store, task) -> Result<GovernancePlan>;
async fn execute(&self, store, task, plan) -> Result<GovernanceExecution>;
}
trait GovernanceStore: Send + Sync {
async fn list_active_users(&self) -> Result<Vec<String>>;
async fn quarantine_low_confidence(&self, user) -> Result<i64>;
// ... cleanup/maintenance methods
}
trait DistributedLock: Send + Sync {
async fn try_acquire(&self, key, holder, ttl) -> Result<bool>;
async fn renew(&self, key, holder, ttl) -> Result<bool>;
async fn release(&self, key, holder) -> Result<()>;
}
trait AsyncTaskStore: Send + Sync {
async fn create_task(&self, task) -> Result<()>;
async fn complete_task(&self, task_id, result) -> Result<()>;
async fn fail_task(&self, task_id, error) -> Result<()>;
async fn get_task(&self, task_id) -> Result<Option<AsyncTask>>;
}
Database Tables
| Group |
Tables |
| Core |
mem_memories, mem_user_state, mem_branches, mem_snapshots |
| Graph |
memory_graph_nodes, memory_graph_edges, mem_entities, mem_memory_entity_links, mem_entity_links |
| Audit |
mem_edit_log, mem_retrieval_feedback, mem_memories_stats |
| Governance |
mem_governance_cooldown, mem_governance_runtime_state, mem_user_retrieval_params |
| Auth |
mem_api_keys |
| Plugin |
mem_plugin_packages, mem_plugin_signers, mem_plugin_bindings, mem_plugin_binding_rules, mem_plugin_reviews, mem_plugin_audit_events |
| Distributed |
mem_distributed_locks, mem_async_tasks |
Config
Config struct in config.rs reads from env vars:
| Field |
Env Var |
Default |
db_url |
DATABASE_URL |
mysql://root:111@localhost:6001/memoria |
embedding_provider |
EMBEDDING_PROVIDER |
local |
instance_id |
MEMORIA_INSTANCE_ID |
Random UUID |
lock_ttl_secs |
MEMORIA_LOCK_TTL_SECS |
120 |
governance_plugin_dir |
MEMORIA_GOVERNANCE_PLUGIN_DIR |
None |
governance_plugin_binding |
MEMORIA_GOVERNANCE_PLUGIN_BINDING |
default |
governance_plugin_subject |
MEMORIA_GOVERNANCE_PLUGIN_SUBJECT |
system |
When adding a Config field: update ALL test Config { .. } constructors — check scheduler.rs tests AND tests/plugin_repository.rs.
REST API Structure
| Prefix |
Module |
Auth |
Purpose |
/v1/memories |
routes/memory.rs |
Bearer |
CRUD, search, retrieve |
/v1/snapshots, /v1/branches |
routes/memory.rs |
Bearer |
Git-for-Data |
/v1/sessions |
routes/sessions.rs |
Bearer |
Episodic memory, async tasks |
/v1/governance |
routes/governance.rs |
Bearer |
Trigger governance |
/admin/* |
routes/admin.rs |
Master |
Admin ops |
/admin/plugins/* |
routes/plugins.rs |
Master |
Plugin repository |
/health |
routes/admin.rs |
None |
Liveness probe |
/health/instance |
routes/memory.rs |
None |
Readiness probe (returns instance_id) |
Adding a New REST Endpoint
- Add handler in
memoria-api/src/routes/
- Register route in
memoria-api/src/lib.rs build_router()
- Add e2e test in
memoria-api/tests/api_e2e.rs using spawn_server()
Plugin System Files
plugin/
├── mod.rs # Re-exports
├── manifest.rs # PluginManifest, PluginPackage, signing verification
├── repository.rs # Publish, review, score, binding rules, audit
├── rhai_runtime.rs # RhaiGovernanceStrategy (sandboxed Rhai)
├── grpc_runtime.rs # GrpcGovernanceStrategy (remote gRPC)
├── governance_hook.rs # Contract testing harness
└── templates/ # Rhai governance template
Distributed Components
| Component |
File |
Purpose |
DistributedLock trait |
distributed.rs |
Lock abstraction |
NoopDistributedLock |
distributed.rs |
Single-instance no-op |
SqlMemoryStore lock impl |
distributed.rs |
INSERT-based DB lock with TTL |
AsyncTaskStore trait |
distributed.rs |
Cross-instance task visibility |
GovernanceScheduler |
scheduler.rs |
Leader election + heartbeat |
AppState |
state.rs |
Holds instance_id + DB task store |
Testing
make check # cargo check + clippy -D warnings (MUST pass)
make test-unit # Unit tests (no DB): memoria-core, memoria-service, memoria-mcp
make test # All tests (needs MatrixOne, --test-threads=1)
make test-e2e # API e2e tests only
Patterns:
- E2e:
spawn_server() → random port, shared DB, reqwest client
- Distributed e2e:
spawn_server_with_instance() → custom instance ID
- Plugin:
build_signed_plugin_files() helper for base64 file maps
- Unique names:
uuid::Uuid::new_v4().simple() for test isolation
- DB tests need
DATABASE_URL env var
Common Pitfalls
- Adding Config fields → must update ALL test constructors
- Clippy
-D warnings → any warning = build failure
PathBuf::from("x") in comparisons → use Path::new("x")
format!() inside println!() → inline args directly
- Plugin exports → re-export chain:
repository.rs → plugin/mod.rs → lib.rs
- Shared DB in tests → use unique names for isolation
GovernanceScheduler constructors → #[allow(clippy::too_many_arguments)]
- MatrixOne DATETIME columns → use
chrono::NaiveDateTime, not String
1---2name: architecture3description: Memoria codebase structure, workspace layout, key traits, database tables, config patterns, and testing conventions. Use when navigating or modifying Memoria code.4---56## Workspace Layout78```9memoria/ # Cargo workspace root10├── crates/11│ ├── memoria-core/ # Shared types: MemoriaError, Memory, MemoryType12│ ├── memoria-storage/ # SqlMemoryStore — all SQL queries, migrations13│ ├── memoria-service/ # Business logic14│ │ └── src/15│ │ ├── service.rs # MemoryService — main entry point16│ │ ├── scheduler.rs # GovernanceScheduler — periodic tasks + leader election17│ │ ├── config.rs # Config struct (reads env vars)18│ │ ├── distributed.rs # DistributedLock, AsyncTaskStore traits + SQL impls19│ │ ├── governance/ # GovernanceStrategy trait, DefaultGovernanceStrategy20│ │ └── plugin/ # Plugin system (manifest, repository, rhai, grpc)21│ ├── memoria-api/ # REST API (axum)22│ │ └── src/23│ │ ├── lib.rs # build_router(), route registration24│ │ ├── state.rs # AppState (service, task_store, instance_id)25│ │ └── routes/ # memory, admin, governance, plugins, sessions26│ ├── memoria-mcp/ # MCP server (stdio/SSE transport)27│ ├── memoria-cli/ # CLI binary: init, mcp, serve, plugin, benchmark28│ ├── memoria-embedding/ # Embedding + LLM client29│ └── memoria-git/ # Git-for-Data: snapshots, branches, merge30├── Cargo.toml # Workspace deps31└── build.rs # Proto compilation (tonic)32```3334## Key Traits3536```rust37trait GovernanceStrategy: Send + Sync {38 fn strategy_key(&self) -> &str;39 async fn plan(&self, store, task) -> Result<GovernancePlan>;40 async fn execute(&self, store, task, plan) -> Result<GovernanceExecution>;41}4243trait GovernanceStore: Send + Sync {44 async fn list_active_users(&self) -> Result<Vec<String>>;45 async fn quarantine_low_confidence(&self, user) -> Result<i64>;46 // ... cleanup/maintenance methods47}4849trait DistributedLock: Send + Sync {50 async fn try_acquire(&self, key, holder, ttl) -> Result<bool>;51 async fn renew(&self, key, holder, ttl) -> Result<bool>;52 async fn release(&self, key, holder) -> Result<()>;53}5455trait AsyncTaskStore: Send + Sync {56 async fn create_task(&self, task) -> Result<()>;57 async fn complete_task(&self, task_id, result) -> Result<()>;58 async fn fail_task(&self, task_id, error) -> Result<()>;59 async fn get_task(&self, task_id) -> Result<Option<AsyncTask>>;60}61```6263## Database Tables6465| Group | Tables |66|-------|--------|67| Core | `mem_memories`, `mem_user_state`, `mem_branches`, `mem_snapshots` |68| Graph | `memory_graph_nodes`, `memory_graph_edges`, `mem_entities`, `mem_memory_entity_links`, `mem_entity_links` |69| Audit | `mem_edit_log`, `mem_retrieval_feedback`, `mem_memories_stats` |70| Governance | `mem_governance_cooldown`, `mem_governance_runtime_state`, `mem_user_retrieval_params` |71| Auth | `mem_api_keys` |72| Plugin | `mem_plugin_packages`, `mem_plugin_signers`, `mem_plugin_bindings`, `mem_plugin_binding_rules`, `mem_plugin_reviews`, `mem_plugin_audit_events` |73| Distributed | `mem_distributed_locks`, `mem_async_tasks` |7475## Config7677`Config` struct in `config.rs` reads from env vars:7879| Field | Env Var | Default |80|-------|---------|---------|81| `db_url` | `DATABASE_URL` | `mysql://root:111@localhost:6001/memoria` |82| `embedding_provider` | `EMBEDDING_PROVIDER` | `local` |83| `instance_id` | `MEMORIA_INSTANCE_ID` | Random UUID |84| `lock_ttl_secs` | `MEMORIA_LOCK_TTL_SECS` | `120` |85| `governance_plugin_dir` | `MEMORIA_GOVERNANCE_PLUGIN_DIR` | None |86| `governance_plugin_binding` | `MEMORIA_GOVERNANCE_PLUGIN_BINDING` | `default` |87| `governance_plugin_subject` | `MEMORIA_GOVERNANCE_PLUGIN_SUBJECT` | `system` |8889**When adding a Config field:** update ALL test `Config { .. }` constructors — check `scheduler.rs` tests AND `tests/plugin_repository.rs`.9091## REST API Structure9293| Prefix | Module | Auth | Purpose |94|--------|--------|------|---------|95| `/v1/memories` | `routes/memory.rs` | Bearer | CRUD, search, retrieve |96| `/v1/snapshots`, `/v1/branches` | `routes/memory.rs` | Bearer | Git-for-Data |97| `/v1/sessions` | `routes/sessions.rs` | Bearer | Episodic memory, async tasks |98| `/v1/governance` | `routes/governance.rs` | Bearer | Trigger governance |99| `/admin/*` | `routes/admin.rs` | Master | Admin ops |100| `/admin/plugins/*` | `routes/plugins.rs` | Master | Plugin repository |101| `/health` | `routes/admin.rs` | None | Liveness probe |102| `/health/instance` | `routes/memory.rs` | None | Readiness probe (returns instance_id) |103104## Adding a New REST Endpoint1051061. Add handler in `memoria-api/src/routes/`1072. Register route in `memoria-api/src/lib.rs` `build_router()`1083. Add e2e test in `memoria-api/tests/api_e2e.rs` using `spawn_server()`109110## Plugin System Files111112```113plugin/114├── mod.rs # Re-exports115├── manifest.rs # PluginManifest, PluginPackage, signing verification116├── repository.rs # Publish, review, score, binding rules, audit117├── rhai_runtime.rs # RhaiGovernanceStrategy (sandboxed Rhai)118├── grpc_runtime.rs # GrpcGovernanceStrategy (remote gRPC)119├── governance_hook.rs # Contract testing harness120└── templates/ # Rhai governance template121```122123## Distributed Components124125| Component | File | Purpose |126|-----------|------|---------|127| `DistributedLock` trait | `distributed.rs` | Lock abstraction |128| `NoopDistributedLock` | `distributed.rs` | Single-instance no-op |129| `SqlMemoryStore` lock impl | `distributed.rs` | INSERT-based DB lock with TTL |130| `AsyncTaskStore` trait | `distributed.rs` | Cross-instance task visibility |131| `GovernanceScheduler` | `scheduler.rs` | Leader election + heartbeat |132| `AppState` | `state.rs` | Holds instance_id + DB task store |133134## Testing135136```bash137make check # cargo check + clippy -D warnings (MUST pass)138make test-unit # Unit tests (no DB): memoria-core, memoria-service, memoria-mcp139make test # All tests (needs MatrixOne, --test-threads=1)140make test-e2e # API e2e tests only141```142143Patterns:144- E2e: `spawn_server()` → random port, shared DB, reqwest client145- Distributed e2e: `spawn_server_with_instance()` → custom instance ID146- Plugin: `build_signed_plugin_files()` helper for base64 file maps147- Unique names: `uuid::Uuid::new_v4().simple()` for test isolation148- DB tests need `DATABASE_URL` env var149150## Common Pitfalls1511521. Adding Config fields → must update ALL test constructors1532. Clippy `-D warnings` → any warning = build failure1543. `PathBuf::from("x")` in comparisons → use `Path::new("x")`1554. `format!()` inside `println!()` → inline args directly1565. Plugin exports → re-export chain: `repository.rs` → `plugin/mod.rs` → `lib.rs`1576. Shared DB in tests → use unique names for isolation1587. `GovernanceScheduler` constructors → `#[allow(clippy::too_many_arguments)]`1598. MatrixOne DATETIME columns → use `chrono::NaiveDateTime`, not `String`