Setup Session Persistence¶
Learn how to configure session persistence to maintain conversation history and context across application restarts.
Overview¶
Session persistence stores conversation metadata and working memory in a database, enabling:
- Conversation resumption after server restarts
- Distributed deployments with shared session state
- Long-running conversations that survive process crashes
- Multi-agent isolation with shared infrastructure
Architecture¶
Hector uses a three-layer memory system:
┌─────────────────────────────────────────────────────┐
│ 1. SESSION STORE (NEW!) │
│ - Session metadata (created_at, agent_id) │
│ - Working memory (conversation history) │
│ - Database: SQL (SQLite, PostgreSQL, MySQL) │
│ - Survives: Process restarts ✅ │
├─────────────────────────────────────────────────────┤
│ 2. WORKING MEMORY │
│ - Active conversation context │
│ - Managed by: summary_buffer, buffer_window │
│ - Backed by: Session store │
├─────────────────────────────────────────────────────┤
│ 3. LONG-TERM MEMORY │
│ - Semantic recall across conversations │
│ - Vector database (Qdrant, Pinecone, etc.) │
│ - Isolated by: agent_id + session_id │
└─────────────────────────────────────────────────────┘
Key Point: Session persistence (layer 1) is the foundation that backs working memory, while long-term memory provides semantic search capabilities.
Quick Start¶
1. Basic SQLite Setup¶
Simplest setup for local development:
# config.yaml
session_stores:
main-db:
backend: sql
sql:
driver: sqlite
database: ./data/sessions.db
max_conns: 10
max_idle: 2
agents:
assistant:
llm: "gpt-4o"
session_store: "main-db" # Reference the global store
memory:
working:
strategy: "summary_buffer"
budget: 4000
Start server:
./hector serve --config config.yaml
Test resumption:
# First conversation
curl -X POST http://localhost:8091/v1/agents/assistant/message:send \
-H "Content-Type: application/json" \
-d '{
"message": {
"context_id": "my-session",
"role": 1,
"content": [{"text": "Remember: my project ID is ALPHA-789"}]
}
}'
# Restart server
pkill hector
./hector serve --config config.yaml &
# Resume conversation - agent remembers!
curl -X POST http://localhost:8091/v1/agents/assistant/message:send \
-H "Content-Type: application/json" \
-d '{
"message": {
"context_id": "my-session",
"role": 1,
"content": [{"text": "What is my project ID?"}]
}
}'
Configuration Reference¶
Global Session Stores¶
Define session stores once, reference them by name:
session_stores:
# Store name: can be referenced by multiple agents
production-db:
backend: sql
sql:
driver: postgres
host: postgres.example.com
port: 5432
user: hector
password: ${DB_PASSWORD} # From environment
database: hector_sessions
ssl_mode: require
max_conns: 50
max_idle: 10
conn_max_lifetime: 3600 # 1 hour
dev-db:
backend: sql
sql:
driver: sqlite
database: ./dev-sessions.db
max_conns: 5
Agent Configuration¶
Reference session stores by name:
agents:
assistant:
llm: "gpt-4o"
session_store: "production-db" # References global store
researcher:
llm: "claude-3-5-sonnet-20241022"
session_store: "production-db" # Shares DB, isolated by agent_id
dev-agent:
llm: "gpt-4o-mini"
session_store: "dev-db" # Uses separate DB
Multi-Agent Isolation: Even when sharing a database, agents are isolated by agent_id in the sessions table. Agent A's session "test" is completely separate from Agent B's session "test".
Database Setup¶
SQLite (Development)¶
Pros: - ✅ No setup required - ✅ Single file database - ✅ Perfect for local development
Cons: - ❌ Not suitable for distributed deployments - ❌ Limited concurrent writes
Configuration:
session_stores:
local:
backend: sql
sql:
driver: sqlite
database: ./data/sessions.db
max_conns: 10 # SQLite supports limited concurrency
max_idle: 2
Schema auto-created:
CREATE TABLE sessions (
id TEXT PRIMARY KEY,
agent_id TEXT NOT NULL,
metadata TEXT,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
CREATE TABLE messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
role INTEGER NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP,
FOREIGN KEY (session_id) REFERENCES sessions(id)
);
PostgreSQL (Production)¶
Pros: - ✅ Battle-tested reliability - ✅ Excellent concurrent write performance - ✅ Distributed deployment support - ✅ Full ACID guarantees
Setup:
# 1. Install PostgreSQL
brew install postgresql # macOS
apt install postgresql # Ubuntu
# 2. Create database
createdb hector_sessions
# 3. Create user
psql -c "CREATE USER hector WITH PASSWORD 'secure-password';"
psql -c "GRANT ALL PRIVILEGES ON DATABASE hector_sessions TO hector;"
Configuration:
session_stores:
postgres:
backend: sql
sql:
driver: postgres
host: localhost
port: 5432
user: hector
password: ${HECTOR_DB_PASSWORD} # From environment
database: hector_sessions
ssl_mode: disable # Use 'require' in production
max_conns: 100
max_idle: 25
conn_max_lifetime: 3600
Environment variable:
export HECTOR_DB_PASSWORD="secure-password"
./hector serve --config config.yaml
MySQL (Alternative)¶
Setup:
# 1. Install MySQL
brew install mysql # macOS
apt install mysql-server # Ubuntu
# 2. Create database
mysql -u root -p -e "CREATE DATABASE hector_sessions;"
mysql -u root -p -e "CREATE USER 'hector'@'localhost' IDENTIFIED BY 'secure-password';"
mysql -u root -p -e "GRANT ALL PRIVILEGES ON hector_sessions.* TO 'hector'@'localhost';"
Configuration:
session_stores:
mysql:
backend: sql
sql:
driver: mysql
host: localhost
port: 3306
user: hector
password: ${HECTOR_DB_PASSWORD}
database: hector_sessions
max_conns: 100
max_idle: 25
conn_max_lifetime: 3600
CLI Session Support¶
Using --session Flag¶
Resume conversations across CLI invocations:
# First call - store information
./hector call "Remember: meeting at 3pm" --agent assistant \
--config config.yaml --session=work
# Later - agent remembers!
./hector call "When is the meeting?" --agent assistant \
--config config.yaml --session=work
Interactive chat:
# With custom session ID
./hector chat --agent assistant --config config.yaml --session=my-chat
# Auto-generated session ID (displayed on start)
./hector chat --agent assistant --config config.yaml
# Output: 💾 Session ID: cli-chat-1729612345
# Resume later with: --session=cli-chat-1729612345
Session Management¶
Create session:
SESSION_ID=$(uuidgen) # Generate unique ID
./hector call "Hello" --agent assistant --config config.yaml --session=$SESSION_ID
Resume session:
./hector call "Continue" --agent assistant --config config.yaml --session=$SESSION_ID
Check session in database:
sqlite3 ./data/sessions.db "SELECT id, agent_id, created_at FROM sessions;"
Multi-Agent Deployments¶
Shared Database, Isolated Sessions¶
Multiple agents can share a single database with proper isolation:
session_stores:
shared-db:
backend: sql
sql:
driver: postgres
host: db.example.com
database: hector_sessions
user: hector
password: ${DB_PASSWORD}
max_conns: 200 # Shared across all agents
agents:
customer-support:
session_store: "shared-db"
llm: "gpt-4o"
sales-assistant:
session_store: "shared-db"
llm: "claude-3-5-sonnet-20241022"
technical-advisor:
session_store: "shared-db"
llm: "gpt-4o"
Isolation guarantees:
- ✅ Sessions are isolated by
agent_id+session_id - ✅ Agent A cannot access Agent B's sessions
- ✅ Shared connection pool for efficiency
- ✅ Each agent maintains independent conversation history
Testing Session Persistence¶
Verification Script¶
#!/bin/bash
# 1. Start server
./hector serve --config config.yaml --port 8090 &
SERVER_PID=$!
sleep 3
# 2. Store information
curl -X POST http://localhost:8091/v1/agents/assistant/message:send \
-H "Content-Type: application/json" \
-d '{
"message": {
"context_id": "test-session",
"role": 1,
"content": [{"text": "My favorite color is blue"}]
}
}'
# 3. Restart server
kill $SERVER_PID
sleep 2
./hector serve --config config.yaml --port 8090 &
sleep 3
# 4. Verify agent remembers
RESPONSE=$(curl -s -X POST http://localhost:8091/v1/agents/assistant/message:send \
-H "Content-Type: application/json" \
-d '{
"message": {
"context_id": "test-session",
"role": 1,
"content": [{"text": "What is my favorite color?"}]
}
}')
if echo "$RESPONSE" | grep -q "blue"; then
echo "✅ Session persistence works!"
else
echo "❌ Session persistence failed"
fi
Database Inspection¶
View sessions:
# SQLite
sqlite3 ./data/sessions.db "SELECT * FROM sessions;"
# PostgreSQL
psql -h localhost -U hector hector_sessions \
-c "SELECT id, agent_id, created_at FROM sessions;"
View messages:
# SQLite
sqlite3 ./data/sessions.db \
"SELECT session_id, role, substr(content, 1, 50) FROM messages;"
# PostgreSQL
psql -h localhost -U hector hector_sessions \
-c "SELECT session_id, role, left(content, 50) FROM messages;"
Performance Tuning¶
Connection Pooling¶
session_stores:
production:
backend: sql
sql:
# Connection pool settings
max_conns: 100 # Maximum connections
max_idle: 25 # Idle connections to keep
conn_max_lifetime: 3600 # Close connections after 1 hour
Guidelines:
- max_conns: Set to 2-3x expected concurrent sessions
- max_idle: Set to 25% of max_conns
- conn_max_lifetime: 1-4 hours depending on database policy
Database Optimization¶
Indexes (auto-created by Hector):
CREATE INDEX idx_sessions_agent_id ON sessions(agent_id);
CREATE INDEX idx_sessions_updated_at ON sessions(updated_at);
CREATE INDEX idx_messages_session_id ON messages(session_id);
Maintenance:
-- PostgreSQL: Vacuum regularly
VACUUM ANALYZE sessions;
VACUUM ANALYZE messages;
-- MySQL: Optimize tables
OPTIMIZE TABLE sessions;
OPTIMIZE TABLE messages;
Security Considerations¶
Connection Security¶
session_stores:
secure-db:
backend: sql
sql:
driver: postgres
host: db.prod.example.com
ssl_mode: require # ✅ Require SSL
password: ${DB_PASSWORD} # ✅ From environment, never hardcode
Access Control¶
# ✅ Good: Least privilege
GRANT SELECT, INSERT, UPDATE, DELETE ON sessions TO hector;
GRANT SELECT, INSERT, UPDATE, DELETE ON messages TO hector;
# ❌ Bad: Too broad
GRANT ALL PRIVILEGES ON DATABASE hector_sessions TO hector;
Data Encryption¶
At rest (database-level):
# PostgreSQL with encryption
initdb --data-checksums /var/lib/postgresql/data
# MySQL with encryption
[mysqld]
innodb_encrypt_tables = ON
In transit:
session_stores:
encrypted:
backend: sql
sql:
driver: postgres
ssl_mode: require
ssl_cert: /path/to/client-cert.pem
ssl_key: /path/to/client-key.pem
ssl_root_cert: /path/to/ca-cert.pem
Troubleshooting¶
Session Not Found¶
Symptom: Agent doesn't remember conversation after restart
Check:
# 1. Verify database exists
ls -la ./data/sessions.db # SQLite
# 2. Check sessions table
sqlite3 ./data/sessions.db "SELECT COUNT(*) FROM sessions;"
# 3. Verify agent_id and session_id match
sqlite3 ./data/sessions.db \
"SELECT id, agent_id FROM sessions WHERE id='your-session-id';"
Solution: Ensure you're using the same context_id in requests.
Database Connection Errors¶
Symptom: failed to connect to session store
Check:
# PostgreSQL
pg_isready -h localhost -p 5432
# MySQL
mysqladmin ping -h localhost
# SQLite
ls -la ./data/sessions.db && sqlite3 ./data/sessions.db "SELECT 1;"
Solution: Verify connection parameters and database is running.
Schema Migration Errors¶
Symptom: table already exists or column not found
Solution: Drop and recreate (⚠️ loses data):
# SQLite
rm ./data/sessions.db
# PostgreSQL
psql -h localhost -U hector hector_sessions -c "DROP TABLE messages; DROP TABLE sessions;"
Production Deployment¶
Docker Compose Example¶
version: '3.8'
services:
hector:
image: hector:latest
environment:
- HECTOR_DB_PASSWORD=secure-password
volumes:
- ./config.yaml:/app/config.yaml
ports:
- "8080:8080"
depends_on:
- postgres
postgres:
image: postgres:15
environment:
POSTGRES_DB: hector_sessions
POSTGRES_USER: hector
POSTGRES_PASSWORD: secure-password
volumes:
- postgres-data:/var/lib/postgresql/data
ports:
- "5432:5432"
volumes:
postgres-data:
Kubernetes Example¶
apiVersion: apps/v1
kind: Deployment
metadata:
name: hector
spec:
replicas: 3
template:
spec:
containers:
- name: hector
image: hector:latest
env:
- name: HECTOR_DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
volumeMounts:
- name: config
mountPath: /app/config.yaml
subPath: config.yaml
---
apiVersion: v1
kind: Service
metadata:
name: postgres
spec:
selector:
app: postgres
ports:
- port: 5432
Migration Guide¶
From In-Memory to Persistent¶
Before (no persistence):
agents:
assistant:
llm: "gpt-4o"
memory:
working:
strategy: "summary_buffer"
After (with persistence):
session_stores:
main-db:
backend: sql
sql:
driver: sqlite
database: ./data/sessions.db
agents:
assistant:
llm: "gpt-4o"
session_store: "main-db" # ← Add this line
memory:
working:
strategy: "summary_buffer"
No other changes needed! Working memory automatically uses persistent storage.
Best Practices¶
✅ Do¶
- Use PostgreSQL for production deployments
- Set appropriate connection pool sizes
- Store passwords in environment variables
- Enable SSL for database connections
- Monitor database size and performance
- Implement session cleanup (e.g., delete sessions older than 90 days)
❌ Don't¶
- Don't hardcode passwords in config files
- Don't use SQLite for distributed systems
- Don't share session IDs between users
- Don't ignore connection pool warnings
- Don't run without database backups in production
Next Steps¶
- Memory Configuration - Working & long-term memory
- Sessions & Streaming - Session lifecycle
- CLI Reference - Command-line session support
- Configuration Reference - All session_stores options
- Deploy to Production - Production deployment guide
Related Topics¶
- Architecture - How session persistence works internally
- Security - Session security best practices
- Multi-Agent Systems - Multi-agent session isolation