SAF (souveraine architecture files) is now the in-repo doc set. working notes (CLAUDE.md, docs/) stay on disk, gitignored.
5.2 KiB
SAF: Server Implementation
HTTP server layer — axum on port 8484. Last updated: 2026-05-12 (Full audit — memory CRUD, auth, instance registry)
Implementation Status
| Area | Module | Status | Notes |
|---|---|---|---|
| Server struct + lifecycle | server/mod.rs (228 lines) |
✅ | SouveraineServer, new(), run(), instance registry, compaction wiring |
| Database schema | server/db.rs (77 lines) |
✅ | agents + agent_instances tables, SQLite |
| API models | api/models.rs (190 lines) |
✅ | All request/response types |
| Session manager | server/session_manager.rs (111 lines) |
✅ | Sessions with SSE broadcast, conversation store wiring |
| Agent inventory | server/agent_inventory.rs (514 lines) |
✅ | CRUD + SQLite + per-agent seed init + subconscious creation + instance heartbeat |
| HTTP handlers | api/handlers.rs (261 lines) |
✅ | Agent, conversation, memory handlers |
| API routes | api/mod.rs (65 lines) |
✅ | Public + memory + web routes |
| Auth middleware | api/auth.rs (42 lines) |
✅ | Bearer-token protection for memory routes |
| Consciousness engine | server/consciousness_engine.rs (677 lines) |
✅ | N+1 Aster (full LLM tool loop), N+25 reflection, 3-tier compaction warnings |
| CLI integration | main.rs + cli/commands.rs |
✅ | souveraine server command |
| Gitea memory | server/gitea_memory.rs (115 lines) |
⚠️ | Opt-in, gracefully disabled if unreachable |
| Server conversation | server/conversation.rs (72 lines) |
⚠️ | Simplified Bifrost call, no tool loop (tool loop lives in LocalBackend) |
What the Server Provides
Endpoints
| Method | Path | Handler | Status |
|---|---|---|---|
| GET | /health |
health_check | ✅ |
| GET | /v1/agents |
list_agents | ✅ |
| POST | /v1/agents |
create_agent | ✅ (auto-creates subconscious + seed) |
| GET | /v1/agents/:id |
get_agent | ✅ |
| PATCH | /v1/agents/:id |
update_agent | ✅ |
| DELETE | /v1/agents/:id |
delete_agent | ✅ |
| GET | /v1/conversations |
list_conversations | ✅ |
| POST | /v1/conversations |
create_conversation | ✅ |
| GET | /v1/conversations/:id |
get_conversation | ✅ |
| POST | /v1/conversations/:id/messages |
stream_messages (SSE) | ✅ |
| GET | /v1/agents/:id/memory |
list_memory | ✅ (auth required) |
| GET | /v1/agents/:id/memory/*path |
read_memory | ✅ (auth required) |
| PUT | /v1/agents/:id/memory/*path |
write_memory | ✅ (auth required) |
| PATCH | /v1/agents/:id/memory/*path |
append_memory | ✅ (auth required) |
| DELETE | /v1/agents/:id/memory/*path |
delete_memory | ✅ (auth required) |
| GET | / |
ServeDir(web/dist/) |
✅ (SPA fallback, no UI built) |
Endpoints Not Implemented
No Letta-compatible block endpoints (/v1/agents/:id/core-memory/blocks) are planned — Souveraine committed to memfs-only memory per docs/MEMORY_BLOCKS_DECISION.md. The memory CRUD endpoints above replace Letta's block API.
Instance Registry
The server maintains an agent_instances table (server/db.rs, server/agent_inventory.rs):
register_instance()— creates one row per known agent per process, prunes stale rows (>5 min)heartbeat_instance()— bumpslast_seen_atand incrementslifetime_active_secondsin 30s ticksinstance_count()— how many running instances for a given agentlifetime_active_seconds()— total lifecycle uptime for uptime percentage
A background tokio task in SouveraineServer::new() handles the 30s heartbeat loop.
Auth
Memory routes require a bearer token (Authorization: Bearer <token>), enforced by middleware at api/auth.rs. Loopback requests (127.0.0.1 / ::1) can bypass auth when auth.allow_loopback is true (configurable in souveraine.toml [server.auth] section). Public routes (agents list, conversations, health) are unauthenticated.
How to Run
souveraine server
# Binds to 127.0.0.1:8484 (configurable: [server] bind=, port=, or SOUVERAINE_SERVER_BIND env)
# Creates ~/.souveraine/server/
# ├── agents/ # Agent directories with UUID naming
# │ └── {uuid}/
# │ ├── agent.json
# │ └── conversations/
# └── database.sqlite3 # Agent index + instance registry
# With custom bind
souveraine server --bind 0.0.0.0 --port 8484
The user-side memfs lives at ~/.souveraine/agents/{uuid}/memory/ — this is the single canonical path. Subconscious agents at ~/.souveraine/subconscious-agents/{id}-sub/.
Architecture Note
The server and LocalBackend share the same engine (SouveraineServer). The difference is the transport layer:
- Server mode: axum HTTP + SSE — client/server separation
- Local mode: in-process
Arc<SouveraineServer>with directBackendEventstreaming — no socket
Both paths run the same run_turn() tool loop (in LocalBackend), the same N+1 Aster pass, and the same N+25 reflection trigger. The server's ServerConversation (72 lines) is a simplified single-turn path used only by the SSE handler; the full tool loop with all 11 tools lives in LocalBackend::run_turn().
Debugging
There is currently no souveraine server debug output — run() uses println! not tracing. Memory CRUD endpoints are logged via auth middleware.