Configuration

Memory Service is configured through CLI flags or environment variables. Use the toggle below to switch between formats.

Config format

Tip: Your format preference is saved across visits. Every CLI flag has a corresponding environment variable.

Server Configuration

FlagValuesDefaultDescription
--tls-cert-fileMEMORY_SERVICE_TLS_CERT_FILEpath(none)TLS certificate file (self-signed if omitted)
--tls-key-fileMEMORY_SERVICE_TLS_KEY_FILEpath(none)TLS private key file (self-signed if omitted)
--advertised-addressMEMORY_SERVICE_ADVERTISED_ADDRESShost:portauto-detectedAdvertised address for client redirects
--read-header-timeout-secondsMEMORY_SERVICE_READ_HEADER_TIMEOUT_SECONDSinteger5HTTP read header timeout in seconds
--temp-dirMEMORY_SERVICE_TEMP_DIRpathsystem temp dirDirectory for temporary files
--management-access-logMEMORY_SERVICE_MANAGEMENT_ACCESS_LOGtrue, falsefalseEnable HTTP access logging for /health, /ready, /metrics
MEMORY_SERVICE_LOG_LEVELdebug, info, warn, error, fatalinfoServer 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.

FlagValuesDefaultDescription
--portMEMORY_SERVICE_PORTinteger8080HTTP/gRPC server port
--unix-socketMEMORY_SERVICE_UNIX_SOCKETpath(unset)Absolute path to a Unix socket for the HTTP/gRPC server
--plain-textMEMORY_SERVICE_PLAIN_TEXTtrue, falsetrueEnable plaintext HTTP/1.1 + h2c + gRPC
--tlsMEMORY_SERVICE_TLStrue, falsetrueEnable 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.

FlagValuesDefaultDescription
--management-portMEMORY_SERVICE_MANAGEMENT_PORTinteger(unset)Dedicated port for health and metrics. When unset, served on the main port.
--management-unix-socketMEMORY_SERVICE_MANAGEMENT_UNIX_SOCKETpath(unset)Absolute path to a Unix socket for the management server
--management-plain-textMEMORY_SERVICE_MANAGEMENT_PLAIN_TEXTtrue, falsetrueEnable plaintext HTTP for management server
--management-tlsMEMORY_SERVICE_MANAGEMENT_TLStrue, falsetrueEnable 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.sock

DB Configuration

Memory Service supports PostgreSQL, SQLite, and MongoDB as database backends. The database URL uses standard connection string formats (not JDBC).

FlagValuesDefaultDescription
--db-kindMEMORY_SERVICE_DB_KINDpostgres, sqlite, mongopostgresDatabase backend
--db-urlMEMORY_SERVICE_DB_URLURL(required)Database connection URL
--db-max-open-connsMEMORY_SERVICE_DB_MAX_OPEN_CONNSinteger25Maximum number of open database connections (see backend notes below)
--db-max-idle-connsMEMORY_SERVICE_DB_MAX_IDLE_CONNSinteger5Idle/minimum connections (see backend notes below)
MEMORY_SERVICE_DB_MIGRATE_AT_STARTtrue, falsetrueRun database migrations at startup

DB: PostgreSQL Configuration

PostgreSQL is the recommended database backend for Memory Service.

  • --db-max-open-connsMEMORY_SERVICE_DB_MAX_OPEN_CONNS sets the maximum number of open connections.
  • --db-max-idle-connsMEMORY_SERVICE_DB_MAX_IDLE_CONNS sets 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/memoryservice

DB: 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_URL can be a plain file path or a file: SQLite URI.
  • SQLite uses a shared database handle with conservative internal pooling and write serialization. The --db-max-open-conns and --db-max-idle-conns settings are not used for SQLite.
  • When --attachments-kindMEMORY_SERVICE_ATTACHMENTS_KIND is omitted in SQLite mode, it defaults to fs.
  • When MEMORY_SERVICE_ATTACHMENTS_FS_DIR is 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-attachments

DB: MongoDB Configuration

  • --db-max-open-connsMEMORY_SERVICE_DB_MAX_OPEN_CONNS sets the maximum connection pool size.
  • --db-max-idle-connsMEMORY_SERVICE_DB_MAX_IDLE_CONNS sets 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/memoryservice

Cache 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.

FlagValuesDefaultDescription
--cache-kindMEMORY_SERVICE_CACHE_KINDnone, local, redis, infinispannoneCache backend for context entries and response recording
MEMORY_SERVICE_CACHE_EPOCH_TTLdurationPT10MTTL 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 VarValuesDefaultDescription
MEMORY_SERVICE_RESPONSE_RESUMER_TEMP_FILE_RETENTIONdurationPT30MHow 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.

FlagValuesDefaultDescription
--redis-hostsMEMORY_SERVICE_REDIS_HOSTSRedis URLredis://localhost:6379Redis connection URL
MEMORY_SERVICE_CACHE_REDIS_CLIENTclient namedefaultOptional: 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:6379

Cache: 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 VarValuesDefaultDescription
--cache-local-max-bytesMEMORY_SERVICE_CACHE_LOCAL_MAX_BYTESmemory size64MTotal process-local memory budget for cached entries
--cache-local-num-countersMEMORY_SERVICE_CACHE_LOCAL_NUM_COUNTERSinteger100000Higher values improve eviction/admission decisions for larger working sets, but use more memory
--cache-local-buffer-itemsMEMORY_SERVICE_CACHE_LOCAL_BUFFER_ITEMSinteger64Internal 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=64M

Cache: Infinispan Configuration

Infinispan provides a distributed cache with configurable cache names for context entries and response recordings.

FlagValuesDefaultDescription
--infinispan-hostMEMORY_SERVICE_INFINISPAN_HOSThost:portlocalhost:11222Infinispan server host
--infinispan-usernameMEMORY_SERVICE_INFINISPAN_USERNAMEstring(none)Infinispan username
--infinispan-passwordMEMORY_SERVICE_INFINISPAN_PASSWORDstring(none)Infinispan password
MEMORY_SERVICE_CACHE_INFINISPAN_STARTUP_TIMEOUTdurationPT30SStartup timeout for Infinispan connection
MEMORY_SERVICE_CACHE_INFINISPAN_MEMORY_ENTRIES_CACHE_NAMEstringmemory-entriesInfinispan cache name for context entries
MEMORY_SERVICE_CACHE_INFINISPAN_RESPONSE_RECORDINGS_CACHE_NAMEstringresponse-recordingsInfinispan 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=password

Event 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 VarValuesDefaultDescription
--eventbus-kindMEMORY_SERVICE_EVENTBUS_KINDlocal, redis, infinispan, postgreslocalEvent bus backend
--eventbus-outbound-bufferMEMORY_SERVICE_EVENTBUS_OUTBOUND_BUFFERinteger200Outbound channel capacity for cross-node publish pipeline
--eventbus-batch-sizeMEMORY_SERVICE_EVENTBUS_BATCH_SIZEinteger100Max events per cross-node publish batch

SSE Stream Settings

Flag / Env VarValuesDefaultDescription
--sse-keepalive-intervalMEMORY_SERVICE_SSE_KEEPALIVE_INTERVALduration30sInterval between SSE keepalive comments
--sse-membership-cache-ttlMEMORY_SERVICE_SSE_MEMBERSHIP_CACHE_TTLduration5mTTL for local conversation-group membership cache
--sse-max-connections-per-userMEMORY_SERVICE_SSE_MAX_CONNECTIONS_PER_USERinteger5Max concurrent SSE connections per user; older local streams are evicted when exceeded
--sse-subscriber-buffer-sizeMEMORY_SERVICE_SSE_SUBSCRIBER_BUFFER_SIZEinteger64Per-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=local

Event 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:6379

Event 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=password

Event 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=postgres

Attachment Storage

Configure file attachment storage, size limits, and lifecycle.

Flag / Env VarValuesDefaultDescription
--attachments-kindMEMORY_SERVICE_ATTACHMENTS_KINDdb, fs, s3dbStorage backend for uploaded files
MEMORY_SERVICE_ATTACHMENTS_FS_DIRpath(auto)Root directory for the fs attachment backend. In SQLite mode this defaults to <db-path>.attachments.
MEMORY_SERVICE_ATTACHMENTS_MAX_SIZEmemory size10MMaximum file size per upload (e.g., 10M, 512K, 1G)
MEMORY_SERVICE_ATTACHMENTS_DEFAULT_EXPIRES_INdurationPT1HDefault TTL for unlinked attachments
MEMORY_SERVICE_ATTACHMENTS_MAX_EXPIRES_INdurationPT24HMaximum allowed TTL clients can request
MEMORY_SERVICE_ATTACHMENTS_CLEANUP_INTERVALdurationPT5MHow often the cleanup job runs
MEMORY_SERVICE_ATTACHMENTS_DOWNLOAD_URL_EXPIRES_INdurationPT5MSigned 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=db

Attachment Storage: Filesystem Configuration

The fs backend stores attachment bytes on the local filesystem while keeping metadata in the primary datastore.

  • SQLite uses fs by default.
  • If MEMORY_SERVICE_ATTACHMENTS_FS_DIR is unset in SQLite mode, memory-service derives the directory from the SQLite DB path and creates it automatically on startup.
  • fs also 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/attachments

Attachment Storage: S3 Configuration

S3 storage offloads attachments to any S3-compatible object store. Use this for large files or high-throughput workloads.

Flag / Env VarValuesDefaultDescription
--attachments-s3-bucketMEMORY_SERVICE_ATTACHMENTS_S3_BUCKETstringmemory-service-attachmentsS3 bucket name
--attachments-s3-use-path-styleMEMORY_SERVICE_ATTACHMENTS_S3_USE_PATH_STYLEtrue, falsefalseUse path-style S3 addressing (required for LocalStack/MinIO)
MEMORY_SERVICE_ATTACHMENTS_S3_PREFIXstring(empty)Optional key prefix for all objects
MEMORY_SERVICE_ATTACHMENTS_S3_DIRECT_DOWNLOADtrue, falsefalseRedirect clients directly to S3 via presigned URLs
MEMORY_SERVICE_ATTACHMENTS_S3_EXTERNAL_ENDPOINTURL(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-attachments

Direct 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:

  1. Proxy mode (default) — downloads stream through memory-service; no client-reachable S3 endpoint needed.
  2. Direct download with external endpoint — set MEMORY_SERVICE_ATTACHMENTS_S3_DIRECT_DOWNLOAD=true and MEMORY_SERVICE_ATTACHMENTS_S3_EXTERNAL_ENDPOINT to 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 VarValuesDefaultDescription
--encryption-kindMEMORY_SERVICE_ENCRYPTION_KINDcomma-separated IDsplainOrdered list of providers (plain, dek, vault, kms); first provider encrypts new data
--encryption-db-disabledMEMORY_SERVICE_ENCRYPTION_DB_DISABLEDtrue, falsefalseDisable at-rest encryption for the database even when an encryption provider is active
--encryption-attachments-disabledMEMORY_SERVICE_ENCRYPTION_ATTACHMENTS_DISABLEDtrue, falsefalseDisable 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 VarValuesDefaultDescription
--encryption-dek-keyMEMORY_SERVICE_ENCRYPTION_DEK_KEYhex 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 VarValuesDefaultDescription
--encryption-vault-transit-keyMEMORY_SERVICE_ENCRYPTION_VAULT_TRANSIT_KEYstring(required)Vault Transit key name

Standard HashiCorp Vault environment variables are used for authentication:

Env VarDescription
VAULT_ADDRVault server URL (e.g. https://vault.example.com)
VAULT_TOKENVault 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 VarValuesDefaultDescription
--encryption-kms-key-idMEMORY_SERVICE_ENCRYPTION_KMS_KEY_IDstring(required)AWS KMS key ID or ARN

Standard AWS SDK environment variables are used for authentication:

Env VarDescription
AWS_REGIONAWS region (e.g. us-east-1)
AWS_ACCESS_KEY_IDAWS access key ID
AWS_SECRET_ACCESS_KEYAWS 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_KIND to a value other than none. See Embedding Configuration.

Flag / Env VarValuesDefaultDescription
--vector-kindMEMORY_SERVICE_VECTOR_KINDnone, pgvector, qdrant, sqlitenoneVector store backend
MEMORY_SERVICE_VECTOR_MIGRATE_AT_STARTtrue, falsetrueRun vector store migrations at startup
MEMORY_SERVICE_SEARCH_SEMANTIC_ENABLEDtrue, falsetrueEnable semantic (vector) search
MEMORY_SERVICE_SEARCH_FULLTEXT_ENABLEDtrue, falsetrueEnable 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=pgvector

Vector 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 VarDefaultDescription
--vector-qdrant-hostMEMORY_SERVICE_VECTOR_QDRANT_HOSTlocalhostQdrant server hostname or host:port
MEMORY_SERVICE_VECTOR_QDRANT_PORT6334Qdrant gRPC port
MEMORY_SERVICE_VECTOR_QDRANT_COLLECTION_PREFIXmemory-servicePrefix 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_TLSfalseEnable TLS for gRPC connection
MEMORY_SERVICE_VECTOR_QDRANT_STARTUP_TIMEOUTPT30SStartup 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:6334

By 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=sqlite requires MEMORY_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=local

Embedding Configuration

The embedding provider controls how text is converted to vectors for semantic search. Embedding requires a vector store to be configured.

Flag / Env VarValuesDefaultDescription
--embedding-kindMEMORY_SERVICE_EMBEDDING_KINDnone, local, openailocalEmbedding 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=local

Embedding: OpenAI Configuration

The openai provider uses the OpenAI Embeddings API for higher-quality embeddings.

Flag / Env VarDefaultDescription
--embedding-openai-api-keyMEMORY_SERVICE_EMBEDDING_OPENAI_API_KEY(required)OpenAI API key
MEMORY_SERVICE_EMBEDDING_OPENAI_MODEL_NAMEtext-embedding-3-smallOpenAI model name
MEMORY_SERVICE_EMBEDDING_OPENAI_BASE_URLhttps://api.openai.com/v1API 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.

FlagValuesDefaultDescription
--policy-dirMEMORY_SERVICE_POLICY_DIRpathbuilt-in policiesDirectory 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-1

Clients 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-service

Admin 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

RoleAccessDescription
adminRead + WriteFull administrative access across all users. Implies auditor and indexer.
auditorRead-onlyView any user’s conversations and search system-wide. Cannot modify data.
indexerIndex onlyIndex 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.

FlagDefaultDescription
--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-indexer

User-Based Assignment

Assign roles directly to user IDs (matched against the OIDC token principal name):

FlagDefaultDescription
--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-user
MEMORY_SERVICE_ROLES_ADMIN_USERS=alice,bob
MEMORY_SERVICE_ROLES_AUDITOR_USERS=charlie,dave
MEMORY_SERVICE_ROLES_INDEXER_USERS=indexer-user

Client-Based Assignment (API Key)

Assign roles to API key client IDs, allowing agents or services to call admin APIs.

FlagDefaultDescription
--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-agent
MEMORY_SERVICE_ROLES_ADMIN_CLIENTS=admin-agent
MEMORY_SERVICE_ROLES_AUDITOR_CLIENTS=monitoring-agent,audit-agent
MEMORY_SERVICE_ROLES_INDEXER_CLIENTS=indexer-service,summarizer-agent

Audit Logging

All admin API calls are logged. Each request can include a justification field explaining why the admin action was taken.

FlagValuesDefaultDescription
--admin-require-justificationMEMORY_SERVICE_ADMIN_REQUIRE_JUSTIFICATIONtrue, falsefalseRequire justification for all admin API calls

CORS Configuration

Env VarValuesDefaultDescription
MEMORY_SERVICE_CORS_ENABLEDtrue, falsefalseEnable CORS
MEMORY_SERVICE_CORS_ORIGINScomma-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:3000

Monitoring

Memory Service exposes Prometheus metrics and provides admin stats endpoints that query Prometheus for aggregated metrics across all service replicas.

Management Endpoints

EndpointDescription
GET /healthLiveness — returns 200 {"status":"ok"} as soon as the process is up
GET /readyReadiness — returns 200 {"status":"ready"} once all initialization (migrations, store connections, network listeners) has completed; returns 503 {"status":"starting"} before that
GET /metricsPrometheus 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

FlagValuesDefaultDescription
--prometheus-urlMEMORY_SERVICE_PROMETHEUS_URLURL(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

EndpointDescription
/v1/admin/stats/request-rateHTTP request rate (requests/sec)
/v1/admin/stats/error-rate5xx error rate (percent)
/v1/admin/stats/latency-p95P95 response latency (seconds)
/v1/admin/stats/cache-hit-rateCache hit rate (percent)
/v1/admin/stats/db-pool-utilizationDB connection pool usage (percent)
/v1/admin/stats/store-latency-p95Store operation P95 latency by type
/v1/admin/stats/store-throughputStore 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