Configuration
Memory Service is configured through CLI flags or environment variables. Use the toggle below to switch between formats.
Tip: Your format preference is saved across visits. Every CLI flag has a corresponding environment variable.
Server Configuration
| Flag | Values | Default | Description |
|---|---|---|---|
--tls-cert-fileMEMORY_SERVICE_TLS_CERT_FILE | path | (none) | TLS certificate file (self-signed if omitted) |
--tls-key-fileMEMORY_SERVICE_TLS_KEY_FILE | path | (none) | TLS private key file (self-signed if omitted) |
--advertised-addressMEMORY_SERVICE_ADVERTISED_ADDRESS | host:port | auto-detected | Advertised address for client redirects |
--read-header-timeout-secondsMEMORY_SERVICE_READ_HEADER_TIMEOUT_SECONDS | integer | 5 | HTTP read header timeout in seconds |
--temp-dirMEMORY_SERVICE_TEMP_DIR | path | system temp dir | Directory for temporary files |
--management-access-logMEMORY_SERVICE_MANAGEMENT_ACCESS_LOG | true, false | false | Enable HTTP access logging for /health, /ready, /metrics |
MEMORY_SERVICE_LOG_LEVEL | debug, info, warn, error, fatal | info | Server log level |
Network Listener
--port and --unix-socket are mutually exclusive when both are explicitly configured. If --unix-socket is set, the default port value is ignored for the main listener.
| Flag | Values | Default | Description |
|---|---|---|---|
--portMEMORY_SERVICE_PORT | integer | 8080 | HTTP/gRPC server port |
--unix-socketMEMORY_SERVICE_UNIX_SOCKET | path | (unset) | Absolute path to a Unix socket for the HTTP/gRPC server |
--plain-textMEMORY_SERVICE_PLAIN_TEXT | true, false | true | Enable plaintext HTTP/1.1 + h2c + gRPC |
--tlsMEMORY_SERVICE_TLS | true, false | true | Enable TLS HTTP/1.1 + HTTP/2 + gRPC |
When a Unix socket path points into a directory that does not exist yet, Memory Service creates the parent directory with permissions restricted to the current user.
Management Network Listener
By default, /health, /ready, and /metrics are served on the main port alongside the API. Set --management-port to move them onto a dedicated port, keeping them out of the main API’s auth middleware — and making Prometheus scrape configuration more straightforward.
--management-port and --management-unix-socket are mutually exclusive when both are explicitly configured. If --management-unix-socket is set, the default management port value is ignored for that listener.
| Flag | Values | Default | Description |
|---|---|---|---|
--management-portMEMORY_SERVICE_MANAGEMENT_PORT | integer | (unset) | Dedicated port for health and metrics. When unset, served on the main port. |
--management-unix-socketMEMORY_SERVICE_MANAGEMENT_UNIX_SOCKET | path | (unset) | Absolute path to a Unix socket for the management server |
--management-plain-textMEMORY_SERVICE_MANAGEMENT_PLAIN_TEXT | true, false | true | Enable plaintext HTTP for management server |
--management-tlsMEMORY_SERVICE_MANAGEMENT_TLS | true, false | true | Enable TLS for management server (uses same cert/key as main listener) |
Unix socket example:
# Main API over a Unix socket
--unix-socket=$HOME/.local/run/memory-service/api.sock
# Optional dedicated management socket
--management-unix-socket=$HOME/.local/run/memory-service/mgmt.sock# Main API over a Unix socket
MEMORY_SERVICE_UNIX_SOCKET=$HOME/.local/run/memory-service/api.sock
# Optional dedicated management socket
MEMORY_SERVICE_MANAGEMENT_UNIX_SOCKET=$HOME/.local/run/memory-service/mgmt.sockDB Configuration
Memory Service supports PostgreSQL, SQLite, and MongoDB as database backends. The database URL uses standard connection string formats (not JDBC).
| Flag | Values | Default | Description |
|---|---|---|---|
--db-kindMEMORY_SERVICE_DB_KIND | postgres, sqlite, mongo | postgres | Database backend |
--db-urlMEMORY_SERVICE_DB_URL | URL | (required) | Database connection URL |
--db-max-open-connsMEMORY_SERVICE_DB_MAX_OPEN_CONNS | integer | 25 | Maximum number of open database connections (see backend notes below) |
--db-max-idle-connsMEMORY_SERVICE_DB_MAX_IDLE_CONNS | integer | 5 | Idle/minimum connections (see backend notes below) |
MEMORY_SERVICE_DB_MIGRATE_AT_START | true, false | true | Run database migrations at startup |
DB: PostgreSQL Configuration
PostgreSQL is the recommended database backend for Memory Service.
--db-max-open-connsMEMORY_SERVICE_DB_MAX_OPEN_CONNSsets the maximum number of open connections.--db-max-idle-connsMEMORY_SERVICE_DB_MAX_IDLE_CONNSsets the maximum number of idle connections kept in the pool.
# Select PostgreSQL as the datastore
--db-kind=postgres
# PostgreSQL connection (standard URL format)
--db-url=postgresql://postgres:postgres@localhost:5432/memoryservice# Select PostgreSQL as the datastore
MEMORY_SERVICE_DB_KIND=postgres
# PostgreSQL connection (standard URL format)
MEMORY_SERVICE_DB_URL=postgresql://postgres:postgres@localhost:5432/memoryserviceDB: SQLite Configuration
SQLite is intended for local development, demos, single-node deployments, and CI runs that do not need a separate database container.
--db-urlMEMORY_SERVICE_DB_URLcan be a plain file path or afile:SQLite URI.- SQLite uses a shared database handle with conservative internal pooling and write serialization. The
--db-max-open-connsand--db-max-idle-connssettings are not used for SQLite. - When
--attachments-kindMEMORY_SERVICE_ATTACHMENTS_KINDis omitted in SQLite mode, it defaults tofs. - When
MEMORY_SERVICE_ATTACHMENTS_FS_DIRis omitted in SQLite mode, the attachment directory is derived from the SQLite DB path by appending.attachments.
# Select SQLite as the datastore
--db-kind=sqlite
# SQLite file path or file: URI
--db-url=/tmp/memory-service.sqlite
# Optional: explicit attachment root (otherwise /tmp/memory-service.sqlite.attachments)
--attachments-kind=fs
--attachments-fs-dir=/tmp/memory-service-attachments# Select SQLite as the datastore
MEMORY_SERVICE_DB_KIND=sqlite
# SQLite file path or file: URI
MEMORY_SERVICE_DB_URL=/tmp/memory-service.sqlite
# Optional: explicit attachment root (otherwise /tmp/memory-service.sqlite.attachments)
MEMORY_SERVICE_ATTACHMENTS_KIND=fs
MEMORY_SERVICE_ATTACHMENTS_FS_DIR=/tmp/memory-service-attachmentsDB: MongoDB Configuration
--db-max-open-connsMEMORY_SERVICE_DB_MAX_OPEN_CONNSsets the maximum connection pool size.--db-max-idle-connsMEMORY_SERVICE_DB_MAX_IDLE_CONNSsets the minimum pool size — connections MongoDB keeps open proactively. This is not an idle-connection cap.
# Select MongoDB as the datastore
--db-kind=mongo
# MongoDB connection
--db-url=mongodb://localhost:27017/memoryservice# Select MongoDB as the datastore
MEMORY_SERVICE_DB_KIND=mongo
# MongoDB connection
MEMORY_SERVICE_DB_URL=mongodb://localhost:27017/memoryserviceCache Configuration
Memory Service uses a unified cache configuration for all cache-dependent features, including the response recording manager and the context entries cache. Configure the cache backend once, and all features will use it automatically.
| Flag | Values | Default | Description |
|---|---|---|---|
--cache-kindMEMORY_SERVICE_CACHE_KIND | none, local, redis, infinispan | none | Cache backend for context entries and response recording |
MEMORY_SERVICE_CACHE_EPOCH_TTL | duration | PT10M | TTL for cached context entries |
Memory Entries Cache
When a cache backend is configured, Memory Service caches context entries to reduce database load and improve GET/sync latency. The cache stores the complete list of context entries at the latest epoch for each conversation/client pair.
Features of the context entries cache:
- Automatic population: Cache is populated on first read and updated after sync operations
- Write-refreshed TTL: TTL is refreshed when entries are written back into cache
- In-memory pagination: Cache stores complete entry list; pagination is applied in-memory
- Graceful degradation: Falls back to database queries if cache is unavailable
Response Recording Settings
Response Recording and Resumption lets clients reconnect to in-progress streaming responses after a network interruption. It automatically uses the configured cache backend and is enabled when cache is local, redis, or infinispan.
| Env Var | Values | Default | Description |
|---|---|---|---|
MEMORY_SERVICE_RESPONSE_RESUMER_TEMP_FILE_RETENTION | duration | PT30M | How long to retain temp files |
Cache: Redis Configuration
Redis provides a fast, distributed cache with full support for the context entries cache and response recording manager.
| Flag | Values | Default | Description |
|---|---|---|---|
--redis-hostsMEMORY_SERVICE_REDIS_HOSTS | Redis URL | redis://localhost:6379 | Redis connection URL |
MEMORY_SERVICE_CACHE_REDIS_CLIENT | client name | default | Optional: specify a named Redis client |
# Enable Redis cache (response recording manager will automatically use it)
--cache-kind=redis
# Redis connection
--redis-hosts=redis://localhost:6379# Enable Redis cache (response recording manager will automatically use it)
MEMORY_SERVICE_CACHE_KIND=redis
# Redis connection
MEMORY_SERVICE_REDIS_HOSTS=redis://localhost:6379Cache: Local Configuration
The local backend is intended for single-instance deployments such as local agents or embedded development setups. It is process-local, so do not use it when the service is running with replicas.
| Flag / Env Var | Values | Default | Description |
|---|---|---|---|
--cache-local-max-bytesMEMORY_SERVICE_CACHE_LOCAL_MAX_BYTES | memory size | 64M | Total process-local memory budget for cached entries |
--cache-local-num-countersMEMORY_SERVICE_CACHE_LOCAL_NUM_COUNTERS | integer | 100000 | Higher values improve eviction/admission decisions for larger working sets, but use more memory |
--cache-local-buffer-itemsMEMORY_SERVICE_CACHE_LOCAL_BUFFER_ITEMS | integer | 64 | Internal batch size for cache reads. Higher values can help under very high read contention |
# Enable process-local cache
--cache-kind=local
# Process-local cache budget
--cache-local-max-bytes=64M# Enable process-local cache
MEMORY_SERVICE_CACHE_KIND=local
# Process-local cache budget
MEMORY_SERVICE_CACHE_LOCAL_MAX_BYTES=64MCache: Infinispan Configuration
Infinispan provides a distributed cache with configurable cache names for context entries and response recordings.
| Flag | Values | Default | Description |
|---|---|---|---|
--infinispan-hostMEMORY_SERVICE_INFINISPAN_HOST | host:port | localhost:11222 | Infinispan server host |
--infinispan-usernameMEMORY_SERVICE_INFINISPAN_USERNAME | string | (none) | Infinispan username |
--infinispan-passwordMEMORY_SERVICE_INFINISPAN_PASSWORD | string | (none) | Infinispan password |
MEMORY_SERVICE_CACHE_INFINISPAN_STARTUP_TIMEOUT | duration | PT30S | Startup timeout for Infinispan connection |
MEMORY_SERVICE_CACHE_INFINISPAN_MEMORY_ENTRIES_CACHE_NAME | string | memory-entries | Infinispan cache name for context entries |
MEMORY_SERVICE_CACHE_INFINISPAN_RESPONSE_RECORDINGS_CACHE_NAME | string | response-recordings | Infinispan cache name for response recordings |
# Enable Infinispan cache (response recording manager will automatically use it)
--cache-kind=infinispan
# Infinispan connection
--infinispan-host=localhost:11222
--infinispan-username=admin
--infinispan-password=password# Enable Infinispan cache (response recording manager will automatically use it)
MEMORY_SERVICE_CACHE_KIND=infinispan
# Infinispan connection
MEMORY_SERVICE_INFINISPAN_HOST=localhost:11222
MEMORY_SERVICE_INFINISPAN_USERNAME=admin
MEMORY_SERVICE_INFINISPAN_PASSWORD=passwordEvent Bus Configuration
The event bus enables real-time Server-Sent Events (SSE) for frontend cache invalidation. Connected clients receive lightweight notifications when
conversations, entries, or memberships change. Single-node deployments use the default local bus; multi-node deployments use one of the other types for
cross-node fan-out.
For higher-scale multi-node deployments, prefer redis or infinispan. The current event routing model is user-scoped, and PostgreSQL LISTEN/NOTIFY does not scale as well with the resulting
per-user channel fan-out. PostgreSQL remains supported, but it is better suited to smaller clustered installs.
| Flag / Env Var | Values | Default | Description |
|---|---|---|---|
--eventbus-kindMEMORY_SERVICE_EVENTBUS_KIND | local, redis, infinispan, postgres | local | Event bus backend |
--eventbus-outbound-bufferMEMORY_SERVICE_EVENTBUS_OUTBOUND_BUFFER | integer | 200 | Outbound channel capacity for cross-node publish pipeline |
--eventbus-batch-sizeMEMORY_SERVICE_EVENTBUS_BATCH_SIZE | integer | 100 | Max events per cross-node publish batch |
SSE Stream Settings
| Flag / Env Var | Values | Default | Description |
|---|---|---|---|
--sse-keepalive-intervalMEMORY_SERVICE_SSE_KEEPALIVE_INTERVAL | duration | 30s | Interval between SSE keepalive comments |
--sse-membership-cache-ttlMEMORY_SERVICE_SSE_MEMBERSHIP_CACHE_TTL | duration | 5m | TTL for local conversation-group membership cache |
--sse-max-connections-per-userMEMORY_SERVICE_SSE_MAX_CONNECTIONS_PER_USER | integer | 5 | Max concurrent SSE connections per user; older local streams are evicted when exceeded |
--sse-subscriber-buffer-sizeMEMORY_SERVICE_SSE_SUBSCRIBER_BUFFER_SIZE | integer | 64 | Per-subscriber channel buffer; full buffer triggers eviction |
Event Bus: Local
The default local backend uses in-process channels. Suitable for single-node deployments and development.
# Local event bus (default, no configuration needed)
--eventbus-kind=local# Local event bus (default, no configuration needed)
MEMORY_SERVICE_EVENTBUS_KIND=localEvent Bus: Redis
The redis backend uses Redis Pub/Sub for cross-node event fan-out. It reuses the same Redis connection configured for caching and is the recommended choice for better event-stream scaling.
# Redis event bus (reuses cache Redis connection)
--eventbus-kind=redis
--cache-kind=redis
--redis-hosts=redis://localhost:6379# Redis event bus (reuses cache Redis connection)
MEMORY_SERVICE_EVENTBUS_KIND=redis
MEMORY_SERVICE_CACHE_KIND=redis
MEMORY_SERVICE_REDIS_HOSTS=redis://localhost:6379Event Bus: Infinispan
The infinispan backend uses Infinispan’s RESP-compatible Pub/Sub endpoint through the same implementation as the Redis event bus. If you already run Infinispan for caching, it is a good choice for higher-scale event delivery.
# Infinispan event bus (reuses Infinispan RESP endpoint)
--eventbus-kind=infinispan
--cache-kind=infinispan
--infinispan-host=localhost:11222
--infinispan-username=admin
--infinispan-password=password# Infinispan event bus (reuses Infinispan RESP endpoint)
MEMORY_SERVICE_EVENTBUS_KIND=infinispan
MEMORY_SERVICE_CACHE_KIND=infinispan
MEMORY_SERVICE_INFINISPAN_HOST=localhost:11222
MEMORY_SERVICE_INFINISPAN_USERNAME=admin
MEMORY_SERVICE_INFINISPAN_PASSWORD=passwordEvent Bus: PostgreSQL
The postgres backend uses PostgreSQL LISTEN/NOTIFY for cross-node fan-out. It reuses the configured database connection, but it is not the preferred choice for larger multi-node deployments because Redis/Infinispan scale better with user-scoped event routing.
# PostgreSQL event bus (reuses database connection)
--eventbus-kind=postgres# PostgreSQL event bus (reuses database connection)
MEMORY_SERVICE_EVENTBUS_KIND=postgresAttachment Storage
Configure file attachment storage, size limits, and lifecycle.
| Flag / Env Var | Values | Default | Description |
|---|---|---|---|
--attachments-kindMEMORY_SERVICE_ATTACHMENTS_KIND | db, fs, s3 | db | Storage backend for uploaded files |
MEMORY_SERVICE_ATTACHMENTS_FS_DIR | path | (auto) | Root directory for the fs attachment backend. In SQLite mode this defaults to <db-path>.attachments. |
MEMORY_SERVICE_ATTACHMENTS_MAX_SIZE | memory size | 10M | Maximum file size per upload (e.g., 10M, 512K, 1G) |
MEMORY_SERVICE_ATTACHMENTS_DEFAULT_EXPIRES_IN | duration | PT1H | Default TTL for unlinked attachments |
MEMORY_SERVICE_ATTACHMENTS_MAX_EXPIRES_IN | duration | PT24H | Maximum allowed TTL clients can request |
MEMORY_SERVICE_ATTACHMENTS_CLEANUP_INTERVAL | duration | PT5M | How often the cleanup job runs |
MEMORY_SERVICE_ATTACHMENTS_DOWNLOAD_URL_EXPIRES_IN | duration | PT5M | Signed download URL expiry |
| — | — | — | Signed download URLs are enabled automatically when --encryption-dek-key is set; the signing key is derived from it via HKDF-SHA256. |
Attachment Storage: DB Configuration
The default db backend stores attachments directly in the primary database (PostgreSQL or MongoDB). No additional configuration is required.
# Use database storage (default)
--attachments-kind=db# Use database storage (default)
MEMORY_SERVICE_ATTACHMENTS_KIND=dbAttachment Storage: Filesystem Configuration
The fs backend stores attachment bytes on the local filesystem while keeping metadata in the primary datastore.
- SQLite uses
fsby default. - If
MEMORY_SERVICE_ATTACHMENTS_FS_DIRis unset in SQLite mode, memory-service derives the directory from the SQLite DB path and creates it automatically on startup. fsalso works with non-SQLite backends when you want filesystem-backed blobs without S3.
# Store attachment bytes on local disk
--attachments-kind=fs
--attachments-fs-dir=/var/lib/memory-service/attachments# Store attachment bytes on local disk
MEMORY_SERVICE_ATTACHMENTS_KIND=fs
MEMORY_SERVICE_ATTACHMENTS_FS_DIR=/var/lib/memory-service/attachmentsAttachment Storage: S3 Configuration
S3 storage offloads attachments to any S3-compatible object store. Use this for large files or high-throughput workloads.
| Flag / Env Var | Values | Default | Description |
|---|---|---|---|
--attachments-s3-bucketMEMORY_SERVICE_ATTACHMENTS_S3_BUCKET | string | memory-service-attachments | S3 bucket name |
--attachments-s3-use-path-styleMEMORY_SERVICE_ATTACHMENTS_S3_USE_PATH_STYLE | true, false | false | Use path-style S3 addressing (required for LocalStack/MinIO) |
MEMORY_SERVICE_ATTACHMENTS_S3_PREFIX | string | (empty) | Optional key prefix for all objects |
MEMORY_SERVICE_ATTACHMENTS_S3_DIRECT_DOWNLOAD | true, false | false | Redirect clients directly to S3 via presigned URLs |
MEMORY_SERVICE_ATTACHMENTS_S3_EXTERNAL_ENDPOINT | URL | (none) | Override endpoint in presigned URLs |
# Select S3 storage
--attachments-kind=s3
# S3 bucket configuration
--attachments-s3-bucket=memory-service-attachments# Select S3 storage
MEMORY_SERVICE_ATTACHMENTS_KIND=s3
# S3 bucket configuration
MEMORY_SERVICE_ATTACHMENTS_S3_BUCKET=memory-service-attachmentsDirect download vs. proxy mode: By default, downloads are proxied through memory-service. Set MEMORY_SERVICE_ATTACHMENTS_S3_DIRECT_DOWNLOAD=true to redirect clients directly to the S3 backend via presigned URLs instead.
When using a self-hosted S3-compatible store (like MinIO) that is only reachable at an internal address, you have two options:
- Proxy mode (default) — downloads stream through memory-service; no client-reachable S3 endpoint needed.
- Direct download with external endpoint — set
MEMORY_SERVICE_ATTACHMENTS_S3_DIRECT_DOWNLOAD=trueandMEMORY_SERVICE_ATTACHMENTS_S3_EXTERNAL_ENDPOINTto a client-reachable URL (e.g.,http://minio-api.example.com).
S3 incompatibility: S3 direct download is incompatible with encryption. Keep direct download disabled (the default) when using a real encryption provider.
See Attachments for details on how attachments work.
Encryption
Memory Service supports transparent encryption of stored data using AES-256-GCM. Configure encryption by selecting one or more providers via --encryption-kind. The first provider is primary — used for all new encryptions. Additional providers are decryption-only fallbacks, enabling zero-downtime key rotation.
| Flag / Env Var | Values | Default | Description |
|---|---|---|---|
--encryption-kindMEMORY_SERVICE_ENCRYPTION_KIND | comma-separated IDs | plain | Ordered list of providers (plain, dek, vault, kms); first provider encrypts new data |
--encryption-db-disabledMEMORY_SERVICE_ENCRYPTION_DB_DISABLED | true, false | false | Disable at-rest encryption for the database even when an encryption provider is active |
--encryption-attachments-disabledMEMORY_SERVICE_ENCRYPTION_ATTACHMENTS_DISABLED | true, false | false | Disable at-rest encryption for the attachment store even when an encryption provider is active |
Encryption: DEK (AES-256-GCM)
The dek provider encrypts data locally using AES-256-GCM with a key you supply. No external service is required — all encryption happens in-process. Signed attachment download URLs are automatically derived from this key via HKDF-SHA256.
| Flag / Env Var | Values | Default | Description |
|---|---|---|---|
--encryption-dek-keyMEMORY_SERVICE_ENCRYPTION_DEK_KEY | hex or base64 string | (none) | Comma-separated AES-256 keys (16/24/32 bytes). First key encrypts new data; additional keys are legacy decryption-only (for key rotation). |
# Enable DEK encryption
--encryption-kind=dek
--encryption-dek-key=<base64-encoded-32-byte-key># Enable DEK encryption
MEMORY_SERVICE_ENCRYPTION_KIND=dek
MEMORY_SERVICE_ENCRYPTION_DEK_KEY=<base64-encoded-32-byte-key>Generate a key with:
openssl rand -base64 32
Key rotation: Supply a comma-separated list of keys — the first key encrypts new data; additional keys are tried in order for decryption only. Remove old keys once all stored data has been re-encrypted with the primary key.
MEMORY_SERVICE_ENCRYPTION_DEK_KEY=new-primary-key,old-legacy-key
Encryption: Vault (HashiCorp Vault Transit)
The vault provider uses HashiCorp Vault Transit to wrap data-encryption keys (DEKs). DEKs are loaded from the encryption_deks database table at startup — Vault is called only once at load time (never per request). A random DEK is generated on first start, wrapped via Vault Transit, and stored in the table. Loss of Vault access prevents startup but does not affect already-running instances.
| Flag / Env Var | Values | Default | Description |
|---|---|---|---|
--encryption-vault-transit-keyMEMORY_SERVICE_ENCRYPTION_VAULT_TRANSIT_KEY | string | (required) | Vault Transit key name |
Standard HashiCorp Vault environment variables are used for authentication:
| Env Var | Description |
|---|---|
VAULT_ADDR | Vault server URL (e.g. https://vault.example.com) |
VAULT_TOKEN | Vault token (or use any other Vault auth method) |
# Enable Vault encryption
--encryption-kind=vault
--encryption-vault-transit-key=memory-service# Enable Vault encryption
MEMORY_SERVICE_ENCRYPTION_KIND=vault
MEMORY_SERVICE_ENCRYPTION_VAULT_TRANSIT_KEY=memory-service
# HashiCorp Vault connection (standard env vars)
VAULT_ADDR=https://vault.example.com
VAULT_TOKEN=<token>Encryption: KMS (AWS KMS)
The kms provider uses AWS KMS to wrap data-encryption keys (DEKs). DEKs are loaded from the encryption_deks database table at startup — KMS is called only once at load time (never per request). A random DEK is generated on first start, wrapped via kms:Encrypt, and stored in the table. Loss of KMS access prevents startup but does not affect already-running instances.
| Flag / Env Var | Values | Default | Description |
|---|---|---|---|
--encryption-kms-key-idMEMORY_SERVICE_ENCRYPTION_KMS_KEY_ID | string | (required) | AWS KMS key ID or ARN |
Standard AWS SDK environment variables are used for authentication:
| Env Var | Description |
|---|---|
AWS_REGION | AWS region (e.g. us-east-1) |
AWS_ACCESS_KEY_ID | AWS access key ID |
AWS_SECRET_ACCESS_KEY | AWS secret access key |
Any other credential source supported by the AWS SDK (IAM roles, AWS_PROFILE, instance metadata, etc.) is also honoured.
# Enable KMS encryption
--encryption-kind=kms
--encryption-kms-key-id=arn:aws:kms:us-east-1:123456789012:key/mrk-...# Enable KMS encryption
MEMORY_SERVICE_ENCRYPTION_KIND=kms
MEMORY_SERVICE_ENCRYPTION_KMS_KEY_ID=arn:aws:kms:us-east-1:123456789012:key/mrk-...
# AWS authentication (standard env vars)
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...Automatic attachment encryption
When a non-plain provider is the primary provider, file attachments are encrypted automatically — no extra configuration is needed. When plain is active (the default), attachments are stored as-is with no overhead.
S3 incompatibility: Attachment encryption is incompatible with S3 direct download. Direct download is disabled by default; do not enable it when using encryption with S3.
Key rotation
To rotate between providers, list the new provider first and the old provider as a fallback in --encryption-kind. New data is always encrypted by the first (primary) provider; existing ciphertext is decrypted by trying each provider in order.
Example: migrate from dek to vault:
MEMORY_SERVICE_ENCRYPTION_KIND=vault,dek
New data is encrypted with Vault; existing dek-encrypted data is still readable. Remove dek from the list once all data has been re-encrypted.
Vector Store Configuration
For semantic search capabilities, configure a vector store backend. The vector store holds embeddings alongside the metadata needed to map them back to context entries.
Note: A vector store requires an embedding provider. When a vector store is enabled, configure
--embedding-kindMEMORY_SERVICE_EMBEDDING_KINDto a value other thannone. See Embedding Configuration.
| Flag / Env Var | Values | Default | Description |
|---|---|---|---|
--vector-kindMEMORY_SERVICE_VECTOR_KIND | none, pgvector, qdrant, sqlite | none | Vector store backend |
MEMORY_SERVICE_VECTOR_MIGRATE_AT_START | true, false | true | Run vector store migrations at startup |
MEMORY_SERVICE_SEARCH_SEMANTIC_ENABLED | true, false | true | Enable semantic (vector) search |
MEMORY_SERVICE_SEARCH_FULLTEXT_ENABLED | true, false | true | Enable full-text search |
Vector Store: pgvector Configuration
pgvector integrates directly with PostgreSQL to add vector search capabilities alongside your existing data. It is the natural choice when the datastore is already PostgreSQL.
# pgvector (auto-selected when datastore is postgres)
--vector-kind=pgvector# pgvector (auto-selected when datastore is postgres)
MEMORY_SERVICE_VECTOR_KIND=pgvectorVector Store: Qdrant Configuration
Qdrant is a dedicated vector database for semantic search. When using Qdrant, the primary datastore (PostgreSQL or MongoDB) still stores all conversation data — Qdrant only stores embeddings and metadata.
| Flag / Env Var | Default | Description |
|---|---|---|
--vector-qdrant-hostMEMORY_SERVICE_VECTOR_QDRANT_HOST | localhost | Qdrant server hostname or host:port |
MEMORY_SERVICE_VECTOR_QDRANT_PORT | 6334 | Qdrant gRPC port |
MEMORY_SERVICE_VECTOR_QDRANT_COLLECTION_PREFIX | memory-service | Prefix for derived collection name |
MEMORY_SERVICE_VECTOR_QDRANT_COLLECTION_NAME | (none) | Optional explicit collection name override |
MEMORY_SERVICE_VECTOR_QDRANT_API_KEY | (none) | API key for authentication |
MEMORY_SERVICE_VECTOR_QDRANT_USE_TLS | false | Enable TLS for gRPC connection |
MEMORY_SERVICE_VECTOR_QDRANT_STARTUP_TIMEOUT | PT30S | Startup migration timeout |
# Qdrant vector store
--vector-kind=qdrant
--vector-qdrant-host=localhost:6334# Qdrant vector store
MEMORY_SERVICE_VECTOR_KIND=qdrant
MEMORY_SERVICE_VECTOR_QDRANT_HOST=localhost:6334By default, memory-service derives the collection name as memory-service_<model>-<dimensions>, for example memory-service_openai-text-embedding-3-small-1536. Set MEMORY_SERVICE_VECTOR_QDRANT_COLLECTION_NAME to force a specific collection.
Vector Store: SQLite Configuration
The sqlite vector backend stores conversation embeddings and episodic memory vectors in the same SQLite database file by using sqlite-vec scalar functions.
MEMORY_SERVICE_VECTOR_KIND=sqliterequiresMEMORY_SERVICE_DB_KIND=sqlite.- This backend enables both conversation semantic search and episodic memory semantic search.
- You still need an embedding provider such as
MEMORY_SERVICE_EMBEDDING_KIND=local.
# SQLite vector store (requires --db-kind=sqlite)
--vector-kind=sqlite
--embedding-kind=local# SQLite vector store (requires MEMORY_SERVICE_DB_KIND=sqlite)
MEMORY_SERVICE_VECTOR_KIND=sqlite
MEMORY_SERVICE_EMBEDDING_KIND=localEmbedding Configuration
The embedding provider controls how text is converted to vectors for semantic search. Embedding requires a vector store to be configured.
| Flag / Env Var | Values | Default | Description |
|---|---|---|---|
--embedding-kindMEMORY_SERVICE_EMBEDDING_KIND | none, local, openai | local | Embedding provider selection |
Embedding: Local Configuration
The default local provider uses an in-process all-MiniLM-L6-v2 ONNX model (384 dimensions). No external API calls are required.
# Use local embedding model (default)
--embedding-kind=local# Use local embedding model (default)
MEMORY_SERVICE_EMBEDDING_KIND=localEmbedding: OpenAI Configuration
The openai provider uses the OpenAI Embeddings API for higher-quality embeddings.
| Flag / Env Var | Default | Description |
|---|---|---|
--embedding-openai-api-keyMEMORY_SERVICE_EMBEDDING_OPENAI_API_KEY | (required) | OpenAI API key |
MEMORY_SERVICE_EMBEDDING_OPENAI_MODEL_NAME | text-embedding-3-small | OpenAI model name |
MEMORY_SERVICE_EMBEDDING_OPENAI_BASE_URL | https://api.openai.com/v1 | API base URL (for Azure OpenAI or proxies) |
MEMORY_SERVICE_EMBEDDING_OPENAI_DIMENSIONS | (model default) | Optional dimension override |
# Use OpenAI embeddings
--embedding-kind=openai
--embedding-openai-api-key=sk-...# Use OpenAI embeddings
MEMORY_SERVICE_EMBEDDING_KIND=openai
MEMORY_SERVICE_EMBEDDING_OPENAI_API_KEY=sk-...Memories Configuration
Configure namespaced Memories policy loading.
| Flag | Values | Default | Description |
|---|---|---|---|
--policy-dirMEMORY_SERVICE_POLICY_DIR | path | built-in policies | Directory containing memory OPA/Rego policy files: authz.rego (data.memories.authz.decision), attributes.rego (data.memories.attributes.attributes), filter.rego (data.memories.filter) |
API Key Authentication
Memory Service supports API key authentication for trusted agents. Configure API keys by client ID using environment variables:
# Format: MEMORY_SERVICE_API_KEYS_<CLIENT_ID>=key1,key2,...
MEMORY_SERVICE_API_KEYS_AGENT_A=agent-a-key-1,agent-a-key-2
MEMORY_SERVICE_API_KEYS_AGENT_B=agent-b-key-1# Format: MEMORY_SERVICE_API_KEYS_<CLIENT_ID>=key1,key2,...
MEMORY_SERVICE_API_KEYS_AGENT_A=agent-a-key-1,agent-a-key-2
MEMORY_SERVICE_API_KEYS_AGENT_B=agent-b-key-1Clients include the API key in requests via the X-API-Key header.
OIDC Authentication
Memory Service supports OIDC authentication via Keycloak or any compliant provider.
# OIDC configuration
--oidc-issuer=http://localhost:8180/realms/memory-service# OIDC configuration
MEMORY_SERVICE_OIDC_ISSUER=http://localhost:8180/realms/memory-serviceAdmin Access Configuration
Memory Service provides /v1/admin/* APIs for platform administrators and auditors.
Access is controlled through role assignment, which can be configured via OIDC token roles,
explicit user lists, or API key client IDs. All three mechanisms are checked — if any
grants a role, the caller has that role.
Roles
| Role | Access | Description |
|---|---|---|
admin | Read + Write | Full administrative access across all users. Implies auditor and indexer. |
auditor | Read-only | View any user’s conversations and search system-wide. Cannot modify data. |
indexer | Index only | Index any conversation’s transcript for search. Cannot view or modify other data. |
Role Assignment
Roles can be assigned through three complementary mechanisms:
OIDC Role Mapping
Map OIDC token roles to internal Memory Service roles.
| Flag | Default | Description |
|---|---|---|
--roles-admin-oidc-roleMEMORY_SERVICE_ROLES_ADMIN_OIDC_ROLE | (none) | OIDC role name that maps to admin |
--roles-auditor-oidc-roleMEMORY_SERVICE_ROLES_AUDITOR_OIDC_ROLE | (none) | OIDC role name that maps to auditor |
--roles-indexer-oidc-roleMEMORY_SERVICE_ROLES_INDEXER_OIDC_ROLE | (none) | OIDC role name that maps to indexer |
# Map OIDC "administrator" role to internal "admin" role
--roles-admin-oidc-role=administrator
# Map OIDC "manager" role to internal "auditor" role
--roles-auditor-oidc-role=manager
# Map OIDC "transcript-indexer" role to internal "indexer" role
--roles-indexer-oidc-role=transcript-indexer# Map OIDC "administrator" role to internal "admin" role
MEMORY_SERVICE_ROLES_ADMIN_OIDC_ROLE=administrator
# Map OIDC "manager" role to internal "auditor" role
MEMORY_SERVICE_ROLES_AUDITOR_OIDC_ROLE=manager
# Map OIDC "transcript-indexer" role to internal "indexer" role
MEMORY_SERVICE_ROLES_INDEXER_OIDC_ROLE=transcript-indexerUser-Based Assignment
Assign roles directly to user IDs (matched against the OIDC token principal name):
| Flag | Default | Description |
|---|---|---|
--roles-admin-usersMEMORY_SERVICE_ROLES_ADMIN_USERS | (empty) | Comma-separated user IDs with admin access |
--roles-auditor-usersMEMORY_SERVICE_ROLES_AUDITOR_USERS | (empty) | Comma-separated user IDs with auditor access |
--roles-indexer-usersMEMORY_SERVICE_ROLES_INDEXER_USERS | (empty) | Comma-separated user IDs with indexer access |
--roles-admin-users=alice,bob
--roles-auditor-users=charlie,dave
--roles-indexer-users=indexer-userMEMORY_SERVICE_ROLES_ADMIN_USERS=alice,bob
MEMORY_SERVICE_ROLES_AUDITOR_USERS=charlie,dave
MEMORY_SERVICE_ROLES_INDEXER_USERS=indexer-userClient-Based Assignment (API Key)
Assign roles to API key client IDs, allowing agents or services to call admin APIs.
| Flag | Default | Description |
|---|---|---|
--roles-admin-clientsMEMORY_SERVICE_ROLES_ADMIN_CLIENTS | (empty) | Comma-separated API client IDs with admin access |
--roles-auditor-clientsMEMORY_SERVICE_ROLES_AUDITOR_CLIENTS | (empty) | Comma-separated API client IDs with auditor access |
--roles-indexer-clientsMEMORY_SERVICE_ROLES_INDEXER_CLIENTS | (empty) | Comma-separated API client IDs with indexer access |
--roles-admin-clients=admin-agent
--roles-auditor-clients=monitoring-agent,audit-agent
--roles-indexer-clients=indexer-service,summarizer-agentMEMORY_SERVICE_ROLES_ADMIN_CLIENTS=admin-agent
MEMORY_SERVICE_ROLES_AUDITOR_CLIENTS=monitoring-agent,audit-agent
MEMORY_SERVICE_ROLES_INDEXER_CLIENTS=indexer-service,summarizer-agentAudit Logging
All admin API calls are logged. Each request can include a justification field explaining why the admin action was taken.
| Flag | Values | Default | Description |
|---|---|---|---|
--admin-require-justificationMEMORY_SERVICE_ADMIN_REQUIRE_JUSTIFICATION | true, false | false | Require justification for all admin API calls |
CORS Configuration
| Env Var | Values | Default | Description |
|---|---|---|---|
MEMORY_SERVICE_CORS_ENABLED | true, false | false | Enable CORS |
MEMORY_SERVICE_CORS_ORIGINS | comma-separated origins | (none) | Allowed CORS origins |
# Enable CORS
MEMORY_SERVICE_CORS_ENABLED=true
MEMORY_SERVICE_CORS_ORIGINS=http://localhost:3000# Enable CORS
MEMORY_SERVICE_CORS_ENABLED=true
MEMORY_SERVICE_CORS_ORIGINS=http://localhost:3000Monitoring
Memory Service exposes Prometheus metrics and provides admin stats endpoints that query Prometheus for aggregated metrics across all service replicas.
Management Endpoints
| Endpoint | Description |
|---|---|
GET /health | Liveness — returns 200 {"status":"ok"} as soon as the process is up |
GET /ready | Readiness — returns 200 {"status":"ready"} once all initialization (migrations, store connections, network listeners) has completed; returns 503 {"status":"starting"} before that |
GET /metrics | Prometheus metrics |
Use /ready for Kubernetes readiness probes and Docker Compose healthcheck (so dependent services wait for full startup). Use /health for liveness probes.
Prometheus Configuration
| Flag | Values | Default | Description |
|---|---|---|---|
--prometheus-urlMEMORY_SERVICE_PROMETHEUS_URL | URL | (none) | Prometheus server URL for admin stats queries |
When --prometheus-url is not configured, admin stats endpoints return 501 Not Implemented. All other Memory Service functionality works normally.
Available Stats Endpoints
| Endpoint | Description |
|---|---|
/v1/admin/stats/request-rate | HTTP request rate (requests/sec) |
/v1/admin/stats/error-rate | 5xx error rate (percent) |
/v1/admin/stats/latency-p95 | P95 response latency (seconds) |
/v1/admin/stats/cache-hit-rate | Cache hit rate (percent) |
/v1/admin/stats/db-pool-utilization | DB connection pool usage (percent) |
/v1/admin/stats/store-latency-p95 | Store operation P95 latency by type |
/v1/admin/stats/store-throughput | Store operations/sec by type |
Prometheus Scrape Configuration
For production, use a dedicated management port so Prometheus scrapes only the metrics endpoint without going through the main API’s middleware:
# Expose health and metrics on a dedicated port
--management-port=8085# Expose health and metrics on a dedicated port
MEMORY_SERVICE_MANAGEMENT_PORT=8085# prometheus.yml — scrape the management port
scrape_configs:
- job_name: "memory-service"
scrape_interval: 15s
metrics_path: /metrics
static_configs:
- targets: ["memory-service:8085"]
Without a management port, scrape the main port instead:
# prometheus.yml — no dedicated management port
scrape_configs:
- job_name: "memory-service"
scrape_interval: 15s
metrics_path: /metrics
static_configs:
- targets: ["memory-service:8080"]
Example: Docker Compose
services:
memory-service:
image: ghcr.io/chirino/memory-service:latest
environment:
# Datastore selection
MEMORY_SERVICE_DB_KIND: postgres
# PostgreSQL connection (standard URL format)
MEMORY_SERVICE_DB_URL: postgresql://postgres:postgres@postgres:5432/memoryservice
# Cache with Redis (response recording manager automatically enabled)
MEMORY_SERVICE_CACHE_KIND: redis
MEMORY_SERVICE_REDIS_HOSTS: redis://redis:6379
# Event bus (cross-node SSE fan-out via Redis)
MEMORY_SERVICE_EVENTBUS_KIND: redis
# Authentication
MEMORY_SERVICE_OIDC_ISSUER: http://keycloak:8180/realms/memory-service
# Admin stats
MEMORY_SERVICE_PROMETHEUS_URL: http://prometheus:9090
# Management port (health + metrics on a dedicated port)
MEMORY_SERVICE_MANAGEMENT_PORT: 8085
ports:
- "8080:8080"
- "8085:8085"
depends_on:
- postgres
- redis
Next Steps
- Learn about Core Concepts
- Explore Deployment Options