diff --git a/content/develop/ai/context-engine/agent-memory/_index.md b/content/develop/ai/context-engine/agent-memory/_index.md index 8419809815..3a350dfc75 100644 --- a/content/develop/ai/context-engine/agent-memory/_index.md +++ b/content/develop/ai/context-engine/agent-memory/_index.md @@ -141,7 +141,12 @@ Get started with Redis Agent Memory on Redis Cloud, join the private preview for -tab-sep- -Contact your Redis representative or [contact sales](https://redis.io/contact/) to join the private preview on Redis Software. +Redis Agent Memory is available for self-managed deployment on Kubernetes as a private +preview. See +[Self-managed Agent Memory]({{< relref "/develop/ai/context-engine/agent-memory/self-managed" >}}). + +You need a license key to deploy. Contact your Redis representative or +[contact sales](https://redis.io/contact/). -tab-sep- diff --git a/content/develop/ai/context-engine/agent-memory/api-reference.md b/content/develop/ai/context-engine/agent-memory/api-reference.md index b4ec2a4eaa..7241107a65 100644 --- a/content/develop/ai/context-engine/agent-memory/api-reference.md +++ b/content/develop/ai/context-engine/agent-memory/api-reference.md @@ -1,9 +1,10 @@ --- -Title: LangCache REST API +Title: Redis Agent Memory API linkTitle: API reference layout: apireference type: page +weight: 20 params: sourcefile: ./openapi-agent-memory.json sortOperationsAlphabetically: false ---- \ No newline at end of file +--- diff --git a/content/develop/ai/context-engine/agent-memory/api-reference/openapi-agent-memory.json b/content/develop/ai/context-engine/agent-memory/api-reference/openapi-agent-memory.json index 1115d940b6..147d45efeb 100644 --- a/content/develop/ai/context-engine/agent-memory/api-reference/openapi-agent-memory.json +++ b/content/develop/ai/context-engine/agent-memory/api-reference/openapi-agent-memory.json @@ -1,9 +1,9 @@ { "openapi": "3.0.2", "info": { - "title": "MemoryDataPlaneServer", + "title": "Redis Agent Memory API", "version": "1.0.0", - "description": "API for managing Redis Agent Memory (RAM) data plane operations.\n\nAuthentication:\n- Cloud deployments require `Authorization: Bearer `, where the store API key is obtained from [Redis Cloud Agent Memory](https://cloud.redis.io/#/agent-memory).\n- On-prem deployments run with authentication disabled.\n" + "description": "API for storing, retrieving, and searching Redis Agent Memory session memory and long-term memory.\n\nAuthentication depends on your deployment. Use the credential issued for your Agent Memory service or store. For self-managed deployments, see the self-managed Agent Memory guide for authentication options.\n" }, "paths": { "/health": { diff --git a/content/develop/ai/context-engine/agent-memory/self-managed/_index.md b/content/develop/ai/context-engine/agent-memory/self-managed/_index.md new file mode 100644 index 0000000000..201174bfa5 --- /dev/null +++ b/content/develop/ai/context-engine/agent-memory/self-managed/_index.md @@ -0,0 +1,108 @@ +--- +Title: Self-managed Redis Agent Memory +alwaysopen: false +categories: +- docs +- develop +- ai +description: Deploy, configure, secure, and operate Redis Agent Memory on a self-managed Kubernetes cluster. +linkTitle: Self-managed Agent Memory +weight: 40 +hideListLinks: true +--- + +Redis Agent Memory provides persistent memory for AI agents and +applications. Applications write conversation events and long-term memories to +Agent Memory, then query Agent Memory for relevant context before calling an LLM. + +This guide covers deployment, configuration, security, validation, API examples, +and operations for self-managed Agent Memory. + +The [Redis Agent Memory API]({{< relref "/develop/ai/context-engine/agent-memory/api-reference" >}}) +is the shared Data Plane API for Redis Cloud and self-managed deployments. The +[Control Plane API reference]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/control-plane-api-reference" >}}) +documents the self-managed admin endpoints for stores and agent keys. + +{{< note >}} +Self-managed Redis Agent Memory is available as a private preview. You need a +license key to deploy it. Contact your Redis representative or +[contact sales](https://redis.io/contact/). +{{< /note >}} + +## What you are deploying + +A standard self-managed Agent Memory deployment contains: + +| Component | Purpose | Default service | +| --- | --- | --- | +| Agent Memory Data Plane | Store-scoped runtime memory API. | `redis-agent-memory:9000` | +| Agent Memory worker | Background promotion, summarization, and forgetting work. | No public service | +| Agent Memory Control Plane | Optional admin API for creating stores and managing agent keys. | `redis-agent-memory-controlplane:9100` | +| Store Redis | Holds session memory, long-term memory, indexes, and TTL data. | Customer-provided | +| Job Redis | Holds background work for Agent Memory workers. | Customer-provided | +| Metadata Redis | Holds Control Plane store records and agent-key records. | Required for Control Plane managed stores and agent keys | + +### How the components work together + +The Data Plane handles runtime memory requests. The optional Control Plane +handles store and agent-key administration. + +| Flow | Caller | Service | Backing Redis | +| --- | --- | --- | --- | +| Store and key administration | Platform admin | Agent Memory Control Plane | Metadata Redis | +| Runtime memory requests | Agent, app, or gateway | Agent Memory Data Plane | Store Redis | +| Background memory processing | Agent Memory worker | Agent Memory Data Plane | Job Redis and Store Redis | + +1. Platform admins use the Control Plane to create stores and agent keys. +1. The Control Plane stores store records and agent-key grants in Metadata Redis. +1. Agents and applications call the Data Plane with a store ID. +1. The Data Plane enforces the configured auth mode and reads or writes memory in Store Redis. +1. Workers use Job Redis for background work and write generated memory to Store Redis. + +### API surfaces + +All Data Plane APIs are scoped to a store. A store is the logical isolation +boundary for memory data. + +When agent-key authentication is enabled, grants stored in Metadata Redis +determine which stores and actions each key can access. + +| API surface | Endpoint prefix | Purpose | +| --- | --- | --- | +| Session memory | `/v1/stores/{storeId}/session-memory` | Ordered conversation events, session metadata, and session lifecycle operations. | +| Long-term memory | `/v1/stores/{storeId}/long-term-memory` | Searchable facts, preferences, summaries, and custom memory records. | +| Control Plane | `/v1/stores`, `/v1/api-keys` | Self-managed administration for stores and agent keys. | + +Session memory keeps conversation continuity within a session. Long-term memory +provides searchable context across sessions. + +Long-term memory `memoryType` is an open identifier. When omitted on create, +Agent Memory stores the record as `semantic`; built-in names include `semantic`, +`episodic`, `message`, and `session_summary_view`. + + +## Deployment modes + +Start with [Plan a deployment]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/plan-deployment" >}}) +to choose between static stores and Control Plane managed stores. + +| If you need to | Go to | +| --- | --- | +| Review software, Redis, network, Secret, image, and sizing requirements | [Prerequisites]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/prerequisites" >}}) | +| Prepare `memory-dataplane.config.yaml` for either deployment mode | [Data Plane configuration]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/data-plane-configuration" >}}) | +| Deploy a first-install or single-store setup without the Control Plane | [Deploy with static stores]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/deploy-static" >}}) | +| Deploy runtime store and agent-key administration | [Deploy with Control Plane managed stores]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/deploy-control-plane" >}}) | + +Do not combine static `metadata.stores` with Control Plane managed store +metadata in the same Data Plane config. Static stores do not use Metadata Redis. +Control Plane managed stores use `metadata.source: live` and require Metadata +Redis. + +{{< warning >}} +Do not expose an auth-disabled Data Plane to untrusted callers. In auth-disabled +mode, Agent Memory does not authenticate or authorize Data Plane requests; any caller that +can reach the API can read or write memory for configured stores. Use that mode +only when Kubernetes NetworkPolicy, private service exposure, ingress/gateway +policy, service mesh, or equivalent controls restrict access to trusted +components. +{{< /warning >}} diff --git a/content/develop/ai/context-engine/agent-memory/self-managed/api-examples.md b/content/develop/ai/context-engine/agent-memory/self-managed/api-examples.md new file mode 100644 index 0000000000..0cb05abd8a --- /dev/null +++ b/content/develop/ai/context-engine/agent-memory/self-managed/api-examples.md @@ -0,0 +1,201 @@ +--- +Title: Self-managed API examples +alwaysopen: false +categories: +- docs +- develop +- ai +description: Use curl examples with the Redis Agent Memory self-managed Control Plane and Data Plane APIs. +linkTitle: Self-managed API examples +weight: 70 +hideListLinks: true +--- + +These examples show self-managed Control Plane and Data Plane requests. + +They assume either an auth-disabled private Data Plane or agent-key auth +configured as described in [Authentication and authorization]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/authentication" >}}). + +For the complete shared Data Plane schema, see the +[Redis Agent Memory API]({{< relref "/develop/ai/context-engine/agent-memory/api-reference" >}}). +For the self-managed admin schema, see the +[Control Plane API reference]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/control-plane-api-reference" >}}). + +## Control Plane API examples + +Set variables: + +```bash +CP_URL="http://localhost:9100" +RAM_ADMIN_TOKEN="" +``` + +List stores: + +```bash +curl -sS "$CP_URL/v1/stores" \ + -H "Authorization: Bearer $RAM_ADMIN_TOKEN" +``` + +Create a store: + +```bash +curl -sS -X POST "$CP_URL/v1/stores" \ + -H "Authorization: Bearer $RAM_ADMIN_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "my-store" + }' +``` + +Response: + +```json +{ + "storeId": "" +} +``` + +Mint an agent key: + +```bash +curl -sS -X POST "$CP_URL/v1/api-keys" \ + -H "Authorization: Bearer $RAM_ADMIN_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "my-agent-key", + "grants": [ + { + "resourceType": "mem-store", + "resourceId": "", + "actions": ["read", "write"] + } + ] + }' +``` + +Response: + +```json +{ + "keyId": "0123456789abcdef0123456789abcdef", + "token": "", + "createdAt": 1780000000 +} +``` + +Agent-key fields: + +| Field | Notes | +| ----- | ----- | +| Endpoint | Use `/v1/api-keys`. | +| `name` | Required. | +| `grants` | Required. | +| `resourceType` | Use `mem-store`. | +| `resourceId` | Set to the store ID. | +| `actions` | Use `read`, `write`, or both. | +| Token | Returned only when you mint or rotate a key. Store it immediately. | + +## Data Plane API examples + +Set variables: + +```bash +DP_URL="http://localhost:9000" +STORE_ID="" +RAM_AGENT_KEY="" +``` + +For auth-disabled deployments, omit the `Authorization` header and rely on the +deployment's hosting controls. + +### Write a session event + +```bash +curl -sS -X POST "$DP_URL/v1/stores/$STORE_ID/session-memory/events" \ + -H "Authorization: Bearer $RAM_AGENT_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "sessionId": "session-001", + "actorId": "user-001", + "role": "USER", + "content": [ + { + "text": "What is the capital of France?" + } + ], + "createdAt": "2026-06-25T18:00:00Z" + }' +``` + +Request body fields: + +| Field | Notes | +| ----- | ----- | +| `actorId` | Required. | +| `role` | Required. Use `USER`, `ASSISTANT`, or `SYSTEM`. | +| `content` | Required. For text, use a content object such as `{"text": "..."}`. | +| `createdAt` | Required. Use an RFC 3339 timestamp, for example `2026-06-25T18:00:00Z`. | +| `sessionId` | Optional. If omitted, Agent Memory generates one. | + +### Read session memory + +```bash +curl -sS "$DP_URL/v1/stores/$STORE_ID/session-memory/session-001" \ + -H "Authorization: Bearer $RAM_AGENT_KEY" +``` + +### List sessions + +List sessions for a specific owner: + +```bash +curl -sS "$DP_URL/v1/stores/$STORE_ID/session-memory?filterOwnerId=user-001" \ + -H "Authorization: Bearer $RAM_AGENT_KEY" +``` + +List all sessions: + +```bash +curl -sS "$DP_URL/v1/stores/$STORE_ID/session-memory?includeAll=true" \ + -H "Authorization: Bearer $RAM_AGENT_KEY" +``` + +`filterOwnerId` and `includeAll` are mutually exclusive. + +### Create long-term memories directly + +```bash +curl -sS -X POST "$DP_URL/v1/stores/$STORE_ID/long-term-memory" \ + -H "Authorization: Bearer $RAM_AGENT_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "memories": [ + { + "id": "pref-email-updates", + "text": "User prefers email updates.", + "memoryType": "semantic", + "ownerId": "user-001", + "namespace": "preferences", + "topics": ["communications"] + } + ] + }' +``` + +### Search long-term memory + +```bash +curl -sS -X POST "$DP_URL/v1/stores/$STORE_ID/long-term-memory/search" \ + -H "Authorization: Bearer $RAM_AGENT_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "text": "How should we contact this user?", + "filter": { + "ownerId": { + "eq": "user-001" + } + }, + "limit": 5 + }' +``` diff --git a/content/develop/ai/context-engine/agent-memory/self-managed/authentication.md b/content/develop/ai/context-engine/agent-memory/self-managed/authentication.md new file mode 100644 index 0000000000..87823aba56 --- /dev/null +++ b/content/develop/ai/context-engine/agent-memory/self-managed/authentication.md @@ -0,0 +1,264 @@ +--- +Title: Authentication and authorization +alwaysopen: false +categories: +- docs +- develop +- ai +description: Configure Redis Agent Memory self-managed Control Plane authentication, Data Plane auth modes, worker callbacks, and gateway integration. +linkTitle: Authentication and authorization +weight: 60 +hideListLinks: true +--- + +Self-managed Agent Memory uses separate authentication models for the Control +Plane and Data Plane. + +The Control Plane uses an admin token for management endpoints. The Data Plane +can run behind infrastructure controls with Agent Memory auth disabled, or it +can validate Agent Memory agent keys and enforce store-level grants. + +## Control Plane admin token + +Control Plane management endpoints require: + +```http +Authorization: Bearer +``` + +Production deployments should read the token from a mounted Secret file: + +```yaml +profile: prod + +auth: + type: admin-token + admin_token: + token_file: /etc/controlplane-onprem/admin/token +``` + +The Control Plane reads the token file on each request, so rotating the Secret +does not require a Control Plane redeploy. + +## Data Plane auth modes + +Choose the Data Plane auth mode based on how callers reach the Data Plane. + +| Mode | Config | Use when | +| --- | --- | --- | +| Auth-disabled Data Plane | `auth.method: none` | The Data Plane is reachable only by trusted internal components. | +| Agent-key authentication | `auth.method: agent_key` | Agent Memory should validate keys and enforce per-store grants. | + +### Auth-disabled Data Plane + +```yaml +auth: + method: none +``` + +Use this only when hosting controls restrict Data Plane access to trusted +components. + +{{< warning >}} +Do not expose an auth-disabled Data Plane to untrusted callers. In auth-disabled +mode, Agent Memory does not authenticate or authorize Data Plane requests; any +caller that can reach the API can read or write memory for configured stores. +{{< /warning >}} + +### Agent-key authentication + +Agent-key auth requires Control Plane managed stores. The Data Plane must be +able to load both store records and agent-key records from Metadata Redis. + +Starting from static store configuration, make these changes: + +1. Replace static `metadata.stores` with `metadata.source: live`. +2. Configure `metadata.live.urls`, `metadata.live.namespace`, and + `metadata.live.store_db.urls`. +3. Set `auth.method: agent_key`. +4. Add the `embedding` selection block. +5. Keep `embedders_connection_details` for the embedder endpoint and + credentials. +6. If Agent Memory workers are enabled, configure worker identity as described in + [Worker callbacks](#worker-callbacks) so worker-to-Data Plane calls carry an + accepted credential. + +Working Data Plane config shape for Control Plane managed stores: + +```yaml +metadata: + source: live + live: + urls: + - redis://default:@: + namespace: iris:memory + store_db: + urls: + - redis://default:@: + +auth: + method: agent_key + +embedding: + provider: openai + models: + default_embedding_model: text-embedding-3-large + dimensions: 3072 + +embedders_connection_details: + openai: + base_url: https://api.openai.com + credentials: + type: static + api_key: "" + batching: + embeddings: + enabled: true + max_batch_size: 10 + max_wait_time: 20ms + num_workers: 10 + queue_size: 1000 +``` + +When the Data Plane uses `metadata.source: live`, Agent Memory defaults to +`auth.method: agent_key` when no auth method is configured. Set it explicitly in +production values so the intended security posture is visible in review. If +agent-key auth is enabled without `metadata.source: live`, the Data Plane fails +startup with a validation error. + +Clients send agent keys as Bearer credentials: + +```http +Authorization: Bearer +``` + +Treat agent keys as opaque credentials. Do not parse their contents. + +## Store authorization and grants + +For agent-key requests, Agent Memory checks both identity and resource +authorization: + +1. The key exists in metadata Redis and its secret validates. +2. The key has a grant for the requested store resource. +3. The grant includes the permission required by the operation. + +Grant actions: + +| Action | Meaning | +| --- | --- | +| `read` | Read and search memory data. | +| `write` | Mutate memory data. `write` implies `read`. | + +Operation mapping: + +{{< table-scrollable >}} +| Required permission | Data Plane operations | +| --- | --- | +| `read` | List sessions, get session memory, get session event, get session property, search long-term memory, get long-term memory. | +| `write` | Add/delete session events, delete session memory, set session summary, set session property, create/update/delete long-term memory. | +{{< /table-scrollable >}} + +## Worker callbacks + +Agent Memory workers consume background jobs and call the Data Plane to read +session events and write extracted long-term memories. + +For deployments where Agent Memory Data Plane auth is enabled, workers should +authenticate with Kubernetes projected service-account tokens. The Helm +`workerAuth.enabled` preset creates or uses a worker ServiceAccount and mounts a +projected token into the worker pod. The Data Plane must also be configured to +validate and authorize that token through `auth.worker_identity`. + +Minimal worker-auth values: + +```yaml +workerAuth: + enabled: true + +# Optional customization when the default worker ServiceAccount is not suitable. +worker: + serviceAccount: + name: "" # set to use an existing ServiceAccount + create: false # set true to create a dedicated ServiceAccount without the preset + annotations: {} + token: + audience: redis-agent-memory + expirationSeconds: 3600 + mountPath: /var/run/secrets/redis-agent-memory-worker + fileName: token +``` + +Matching Data Plane config: + +```yaml +dataplane_client: + base_url: http://redis-agent-memory:9000 + auth: + disabled: false + type: service_account_token + token_file: /var/run/secrets/redis-agent-memory-worker/token + +auth: + method: agent_key + worker_identity: + enabled: true + issuer: "https://kubernetes.default.svc" + jwks_uri: "https://kubernetes.default.svc/openid/v1/jwks" + audience: + - redis-agent-memory + subjects: + - subject: "system:serviceaccount::redis-agent-memory-worker" + user_id: "redis-agent-memory-worker" + roles: + - operator + resources: + "mem-store:*": + permissions: + - write +``` + +Worker identity access is controlled by `auth.worker_identity.subjects`. +Configure worker identity to: + +- Trust one or more exact Kubernetes service-account subjects. +- Validate worker tokens by issuer, JWKS URI, audience, and signing algorithm. +- Map each trusted subject to an Agent Memory `Principal` with roles, scopes, + and resource grants. +- Grant store access with resource keys such as `mem-store:`. +- Use `mem-store:*` for a shared worker identity, or use narrower store grants + and separate worker ServiceAccounts for stronger isolation. +- Grant `read`, `write`, or `full`; `write` implies `read`, and `full` implies + `write`. + +The Helm ServiceAccount/token settings only provide the Kubernetes credential. +Agent Memory authorization still comes from the server-side +`auth.worker_identity` subject grants. If worker auth is not configured, keep +the Data Plane auth-disabled and reachable only by trusted internal components. + +## Gateway and identity provider integration + +Use a gateway when it owns external authentication and coarse policy. For +example, a gateway can authenticate callers through an identity provider before +it forwards requests to Agent Memory. + +If the gateway also owns the standard `Authorization` header, forward the Agent +Memory key in `X-Api-Key`: + +```http +Authorization: Bearer +X-Api-Key: +``` + +Agent Memory uses `X-Api-Key` as the Agent Memory credential when present. The +gateway token is still available to the gateway, but Agent Memory authorizes the +request from the server-side grants attached to the Agent Memory key. + +Gateway rules: + +- The gateway owns external authentication and perimeter policy. +- Agent Memory owns store-level authorization. +- Agent Memory keys are stored and forwarded by trusted infrastructure or + trusted applications. +- Callers must not be able to bypass the gateway and reach the Data Plane + directly unless they also present a valid Agent Memory credential. diff --git a/content/develop/ai/context-engine/agent-memory/self-managed/control-plane-api-reference.md b/content/develop/ai/context-engine/agent-memory/self-managed/control-plane-api-reference.md new file mode 100644 index 0000000000..a79f0687a0 --- /dev/null +++ b/content/develop/ai/context-engine/agent-memory/self-managed/control-plane-api-reference.md @@ -0,0 +1,10 @@ +--- +Title: Redis Agent Memory Control Plane API reference +linkTitle: Control Plane API reference +layout: apireference +type: page +weight: 80 +params: + sourcefile: ./openapi-control-plane.json + sortOperationsAlphabetically: false +--- diff --git a/content/develop/ai/context-engine/agent-memory/self-managed/control-plane-api-reference/openapi-control-plane.json b/content/develop/ai/context-engine/agent-memory/self-managed/control-plane-api-reference/openapi-control-plane.json new file mode 100644 index 0000000000..c4f10ab035 --- /dev/null +++ b/content/develop/ai/context-engine/agent-memory/self-managed/control-plane-api-reference/openapi-control-plane.json @@ -0,0 +1,1867 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Redis Agent Memory Control Plane API reference", + "version": "0.5.3", + "description": "Admin API for the Redis Agent Memory self-managed Control Plane.\n\nAuthentication: every endpoint requires `Authorization: Bearer `. This is distinct from the agent keys used by the Data Plane. Operational endpoints (health, build-info) are served separately and are unauthenticated.\n\nSelf-managed deployments use a shared store database. Embedding configuration is resolved server-side from deployment config rather than supplied in Control Plane requests.\n" + }, + "paths": { + "/v1/api-keys": { + "get": { + "description": "List agent-key metadata. Secret values are never returned.", + "operationId": "ListAPIKeys", + "parameters": [ + { + "name": "nextToken", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "maxResults", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "ListAPIKeys 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListAPIKeysResponseContent" + } + } + } + }, + "401": { + "description": "AuthenticationError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "500": { + "description": "UnexpectedError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnexpectedErrorResponseContent" + } + } + } + } + }, + "tags": [ + "API Keys" + ] + }, + "post": { + "description": "Mint a new agent key. Every grant must reference an existing resource. The token is returned only in this response.", + "operationId": "MintAPIKey", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MintAPIKeyRequestContent" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "MintAPIKey 201 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MintAPIKeyResponseContent" + } + } + } + }, + "400": { + "description": "BadRequestError 400 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadRequestErrorResponseContent" + } + } + } + }, + "401": { + "description": "AuthenticationError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "500": { + "description": "UnexpectedError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnexpectedErrorResponseContent" + } + } + } + } + }, + "tags": [ + "API Keys" + ] + } + }, + "/v1/api-keys/{keyId}": { + "delete": { + "description": "Revoke an agent key: deleting the record revokes the key (effective at the data plane within its cache interval).", + "operationId": "RevokeAPIKey", + "parameters": [ + { + "name": "keyId", + "in": "path", + "description": "Agent-key id: a UUID as 32 lowercase hex chars (the embedded, non-secret part of the bearer).", + "schema": { + "type": "string", + "pattern": "^[0-9a-f]{32}$", + "description": "Agent-key id: a UUID as 32 lowercase hex chars (the embedded, non-secret part of the bearer)." + }, + "required": true + } + ], + "responses": { + "204": { + "description": "RevokeAPIKey 204 response" + }, + "401": { + "description": "AuthenticationError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "404": { + "description": "NotFoundError 404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundErrorResponseContent" + } + } + } + }, + "500": { + "description": "UnexpectedError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnexpectedErrorResponseContent" + } + } + } + } + }, + "tags": [ + "API Keys" + ] + }, + "get": { + "operationId": "GetAPIKey", + "parameters": [ + { + "name": "keyId", + "in": "path", + "description": "Agent-key id: a UUID as 32 lowercase hex chars (the embedded, non-secret part of the bearer).", + "schema": { + "type": "string", + "pattern": "^[0-9a-f]{32}$", + "description": "Agent-key id: a UUID as 32 lowercase hex chars (the embedded, non-secret part of the bearer)." + }, + "required": true + } + ], + "responses": { + "200": { + "description": "GetAPIKey 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetAPIKeyResponseContent" + } + } + } + }, + "401": { + "description": "AuthenticationError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "404": { + "description": "NotFoundError 404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundErrorResponseContent" + } + } + } + }, + "500": { + "description": "UnexpectedError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnexpectedErrorResponseContent" + } + } + } + } + }, + "tags": [ + "API Keys" + ] + }, + "patch": { + "description": "Update an agent key's grants and/or name. Updating grants changes resource/action access without re-issuing the secret. Unknown grant resources return 400.", + "operationId": "UpdateAPIKey", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAPIKeyRequestContent" + } + } + } + }, + "parameters": [ + { + "name": "keyId", + "in": "path", + "description": "Agent-key id: a UUID as 32 lowercase hex chars (the embedded, non-secret part of the bearer).", + "schema": { + "type": "string", + "pattern": "^[0-9a-f]{32}$", + "description": "Agent-key id: a UUID as 32 lowercase hex chars (the embedded, non-secret part of the bearer)." + }, + "required": true + } + ], + "responses": { + "200": { + "description": "UpdateAPIKey 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAPIKeyResponseContent" + } + } + } + }, + "400": { + "description": "BadRequestError 400 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadRequestErrorResponseContent" + } + } + } + }, + "401": { + "description": "AuthenticationError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "404": { + "description": "NotFoundError 404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundErrorResponseContent" + } + } + } + }, + "500": { + "description": "UnexpectedError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnexpectedErrorResponseContent" + } + } + } + } + }, + "tags": [ + "API Keys" + ] + } + }, + "/v1/api-keys/{keyId}/rotate": { + "post": { + "description": "Rotate an agent key, make-before-break: mints a brand-new key (new id + secret, same\nname/scope) and winds the old one down over a grace window (default 3600 s) during which\nboth tokens authenticate. The old key's id is recorded on the new key as rotatedFrom.\n", + "operationId": "RotateAPIKey", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RotateAPIKeyRequestContent" + } + } + } + }, + "parameters": [ + { + "name": "keyId", + "in": "path", + "description": "Agent-key id: a UUID as 32 lowercase hex chars (the embedded, non-secret part of the bearer).", + "schema": { + "type": "string", + "pattern": "^[0-9a-f]{32}$", + "description": "Agent-key id: a UUID as 32 lowercase hex chars (the embedded, non-secret part of the bearer)." + }, + "required": true + } + ], + "responses": { + "201": { + "description": "RotateAPIKey 201 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RotateAPIKeyResponseContent" + } + } + } + }, + "400": { + "description": "BadRequestError 400 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadRequestErrorResponseContent" + } + } + } + }, + "401": { + "description": "AuthenticationError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "404": { + "description": "NotFoundError 404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundErrorResponseContent" + } + } + } + }, + "500": { + "description": "UnexpectedError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnexpectedErrorResponseContent" + } + } + } + } + }, + "tags": [ + "API Keys" + ] + } + }, + "/v1/stores": { + "get": { + "operationId": "ListStores", + "responses": { + "200": { + "description": "ListStores 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListStoresResponseContent" + } + } + } + }, + "401": { + "description": "AuthenticationError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "500": { + "description": "UnexpectedError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnexpectedErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Memory Stores" + ] + }, + "post": { + "operationId": "CreateStore", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreRequestContent" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "CreateStore 201 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreResponseContent" + } + } + } + }, + "400": { + "description": "BadRequestError 400 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadRequestErrorResponseContent" + } + } + } + }, + "401": { + "description": "AuthenticationError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "424": { + "description": "FailedDependencyError 424 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FailedDependencyErrorResponseContent" + } + } + } + }, + "500": { + "description": "UnexpectedError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnexpectedErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Memory Stores" + ] + } + }, + "/v1/stores/{storeId}": { + "delete": { + "operationId": "DeleteStore", + "parameters": [ + { + "name": "storeId", + "in": "path", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated IDs are 32-char UUIDs without dashes.", + "schema": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated IDs are 32-char UUIDs without dashes." + }, + "required": true + } + ], + "responses": { + "204": { + "description": "DeleteStore 204 response" + }, + "401": { + "description": "AuthenticationError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "404": { + "description": "NotFoundError 404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundErrorResponseContent" + } + } + } + }, + "500": { + "description": "UnexpectedError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnexpectedErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Memory Stores" + ] + }, + "get": { + "operationId": "GetStore", + "parameters": [ + { + "name": "storeId", + "in": "path", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated IDs are 32-char UUIDs without dashes.", + "schema": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated IDs are 32-char UUIDs without dashes." + }, + "required": true + } + ], + "responses": { + "200": { + "description": "GetStore 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetStoreResponseContent" + } + } + } + }, + "401": { + "description": "AuthenticationError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "404": { + "description": "NotFoundError 404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundErrorResponseContent" + } + } + } + }, + "500": { + "description": "UnexpectedError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnexpectedErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Memory Stores" + ] + }, + "patch": { + "operationId": "UpdateStore", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreRequestContent" + } + } + } + }, + "parameters": [ + { + "name": "storeId", + "in": "path", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated IDs are 32-char UUIDs without dashes.", + "schema": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated IDs are 32-char UUIDs without dashes." + }, + "required": true + } + ], + "responses": { + "200": { + "description": "UpdateStore 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreResponseContent" + } + } + } + }, + "400": { + "description": "BadRequestError 400 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadRequestErrorResponseContent" + } + } + } + }, + "401": { + "description": "AuthenticationError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "404": { + "description": "NotFoundError 404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundErrorResponseContent" + } + } + } + }, + "500": { + "description": "UnexpectedError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnexpectedErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Memory Stores" + ] + } + } + }, + "components": { + "schemas": { + "Action": { + "type": "string", + "description": "An action verb a grant authorizes. The data plane maps each endpoint to one of these.", + "enum": [ + "read", + "write" + ] + }, + "ApiKeyOutput": { + "type": "object", + "properties": { + "keyId": { + "type": "string", + "pattern": "^[0-9a-f]{32}$", + "description": "Agent-key id: a UUID as 32 lowercase hex chars (the embedded, non-secret part of the bearer)." + }, + "name": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[A-Za-z0-9._-]+$", + "description": "Operator-supplied key label (non-unique)." + }, + "grants": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Grant" + }, + "minItems": 1 + }, + "createdAt": { + "type": "integer", + "format": "int64" + }, + "expiresAt": { + "type": "integer", + "description": "Unix seconds after which the key no longer authenticates; 0 = never. > 0 flags a key winding down after rotation.", + "format": "int64" + }, + "rotatedFrom": { + "type": "string", + "description": "The key id this key replaced, when minted by rotation." + } + }, + "required": [ + "createdAt", + "grants", + "keyId", + "name" + ] + }, + "AuthenticationErrorResponseContent": { + "type": "object", + "description": "Authentication credentials are missing, malformed, or invalid.", + "properties": { + "title": { + "type": "string", + "description": "A short, human-readable summary of the problem\n type. It SHOULD NOT change from occurrence to occurrence of the\n problem, except for purposes of localization (e.g., using\n proactive content negotiation; see [RFC7231], Section 3.4)." + }, + "status": { + "type": "integer", + "default": 401, + "description": "The HTTP status code ([RFC7231], Section 6) generated by the origin server for this occurrence of the problem.", + "format": "int32" + }, + "detail": { + "type": "string", + "description": "A human-readable explanation specific to this occurrence of the problem." + }, + "instance": { + "type": "string", + "description": "A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced." + }, + "type": { + "$ref": "#/components/schemas/AuthenticationErrorType" + } + }, + "required": [ + "status", + "title", + "type" + ] + }, + "AuthenticationErrorType": { + "type": "string", + "description": "Problem type URI for authentication errors.", + "enum": [ + "/errors/authentication-failed" + ] + }, + "BadRequestErrorResponseContent": { + "type": "object", + "description": "Request validation or input decoding failed.", + "properties": { + "title": { + "type": "string", + "description": "A short, human-readable summary of the problem\n type. It SHOULD NOT change from occurrence to occurrence of the\n problem, except for purposes of localization (e.g., using\n proactive content negotiation; see [RFC7231], Section 3.4)." + }, + "status": { + "type": "integer", + "default": 400, + "description": "The HTTP status code ([RFC7231], Section 6) generated by the origin server for this occurrence of the problem.", + "format": "int32" + }, + "detail": { + "type": "string", + "description": "A human-readable explanation specific to this occurrence of the problem." + }, + "instance": { + "type": "string", + "description": "A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced." + }, + "type": { + "$ref": "#/components/schemas/BadRequestErrorType" + }, + "fields": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FieldError" + }, + "description": "Optional field-level validation errors." + } + }, + "required": [ + "status", + "title", + "type" + ] + }, + "BadRequestErrorType": { + "type": "string", + "description": "Problem type URI for bad request errors.", + "enum": [ + "/errors/invalid-data" + ] + }, + "CreateStoreRequestContent": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[A-Za-z0-9._-]+$", + "description": "Operator-supplied store name." + }, + "shortMemory": { + "$ref": "#/components/schemas/ShortMemoryConfig" + }, + "longTermMemory": { + "$ref": "#/components/schemas/LongTermMemoryConfig" + }, + "extractionStrategy": { + "$ref": "#/components/schemas/ExtractionStrategy" + }, + "summarization": { + "$ref": "#/components/schemas/SummarizationConfig" + }, + "customMemoryTypes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomMemoryType" + }, + "description": "Custom memory types to register on the store. Optional; when omitted the\nstore uses only built-in memory types." + }, + "extractionCadence": { + "$ref": "#/components/schemas/ExtractionCadenceConfig" + } + }, + "required": [ + "name" + ] + }, + "CreateStoreResponseContent": { + "type": "object", + "properties": { + "storeId": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated IDs are 32-char UUIDs without dashes." + } + }, + "required": [ + "storeId" + ] + }, + "CustomExtractionStrategy": { + "type": "object", + "description": "Extraction strategy for a custom memory type.", + "properties": { + "prompt": { + "type": "string", + "description": "Prompt used to extract memories of this type from session events." + }, + "enabled": { + "type": "boolean", + "description": "Whether the strategy is active. Defaults to true when omitted." + } + }, + "required": [ + "prompt" + ] + }, + "CustomField": { + "type": "object", + "description": "A structured field within a custom memory type.", + "properties": { + "name": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$", + "description": "Field name. Letters, digits, underscores, and dashes; must start with a letter." + }, + "description": { + "type": "string", + "maxLength": 200, + "minLength": 1, + "description": "Human-readable description of the field (1-200 chars)." + }, + "type": { + "$ref": "#/components/schemas/CustomFieldType" + } + }, + "required": [ + "description", + "name", + "type" + ] + }, + "CustomFieldType": { + "type": "string", + "description": "Supported custom field data types. Mirrors the Context Engine\nFieldDescription.type vocabulary exactly.", + "enum": [ + "str", + "int", + "float", + "bool", + "list[str]", + "list[float]", + "object" + ] + }, + "CustomMemoryType": { + "type": "object", + "description": "A custom memory type registered on a store: a named category of long-term\nmemory with its structured fields and, optionally, an extraction strategy.", + "properties": { + "name": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$", + "description": "Unique type name within the store. Must not collide with built-in types\n(semantic, episodic, message, session_summary_view)." + }, + "description": { + "type": "string", + "maxLength": 200, + "minLength": 1, + "description": "Human-readable description of what this memory type captures (1-200 chars)." + }, + "fields": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomField" + }, + "description": "Structured fields that make up a record of this type. Optional; a type\nwith no fields accepts direct writes carrying no structured attributes." + }, + "extractionStrategy": { + "$ref": "#/components/schemas/CustomExtractionStrategy" + } + }, + "required": [ + "description", + "name" + ] + }, + "ExtractionCadenceConfig": { + "type": "object", + "description": "Cadence configuration for the extraction pipeline.", + "properties": { + "activeIntervalSeconds": { + "type": "integer", + "maximum": 600, + "minimum": 60, + "description": "Interval in seconds between extraction runs while a session is active (60-600).", + "format": "int32" + } + }, + "required": [ + "activeIntervalSeconds" + ] + }, + "ExtractionStrategy": { + "type": "string", + "enum": [ + "instruct" + ] + }, + "FailedDependencyErrorResponseContent": { + "type": "object", + "description": "A dependent resource required to process the request is unavailable or unhealthy.", + "properties": { + "title": { + "type": "string", + "description": "A short, human-readable summary of the problem\n type. It SHOULD NOT change from occurrence to occurrence of the\n problem, except for purposes of localization (e.g., using\n proactive content negotiation; see [RFC7231], Section 3.4)." + }, + "status": { + "type": "integer", + "default": 424, + "description": "The HTTP status code ([RFC7231], Section 6) generated by the origin server for this occurrence of the problem.", + "format": "int32" + }, + "detail": { + "type": "string", + "description": "A human-readable explanation specific to this occurrence of the problem." + }, + "instance": { + "type": "string", + "description": "A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced." + }, + "type": { + "$ref": "#/components/schemas/FailedDependencyErrorType" + } + }, + "required": [ + "status", + "title", + "type" + ] + }, + "FailedDependencyErrorType": { + "type": "string", + "description": "Problem type URI for failed-dependency errors.", + "enum": [ + "/errors/resource-unavailable", + "/errors/database-out-of-memory" + ] + }, + "FieldError": { + "type": "object", + "description": "Validation error details for a single request field.", + "properties": { + "field": { + "type": "string", + "description": "Name of the invalid request field." + }, + "rule": { + "type": "string", + "description": "Validation rule that was violated." + }, + "message": { + "type": "string", + "description": "Human-readable validation error message." + } + }, + "required": [ + "field", + "message", + "rule" + ] + }, + "ForbiddenErrorResponseContent": { + "type": "object", + "description": "The caller is authenticated but not allowed to access the requested resource.", + "properties": { + "title": { + "type": "string", + "description": "A short, human-readable summary of the problem\n type. It SHOULD NOT change from occurrence to occurrence of the\n problem, except for purposes of localization (e.g., using\n proactive content negotiation; see [RFC7231], Section 3.4)." + }, + "status": { + "type": "integer", + "default": 403, + "description": "The HTTP status code ([RFC7231], Section 6) generated by the origin server for this occurrence of the problem.", + "format": "int32" + }, + "detail": { + "type": "string", + "description": "A human-readable explanation specific to this occurrence of the problem." + }, + "instance": { + "type": "string", + "description": "A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced." + }, + "type": { + "$ref": "#/components/schemas/ForbiddenErrorType" + } + }, + "required": [ + "status", + "title", + "type" + ] + }, + "ForbiddenErrorType": { + "type": "string", + "description": "Problem type URI for authorization errors.", + "enum": [ + "/errors/insufficient-permissions" + ] + }, + "GetAPIKeyResponseContent": { + "type": "object", + "properties": { + "keyId": { + "type": "string", + "pattern": "^[0-9a-f]{32}$", + "description": "Agent-key id: a UUID as 32 lowercase hex chars (the embedded, non-secret part of the bearer)." + }, + "name": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[A-Za-z0-9._-]+$", + "description": "Operator-supplied key label (non-unique)." + }, + "grants": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Grant" + }, + "minItems": 1 + }, + "createdAt": { + "type": "integer", + "format": "int64" + }, + "expiresAt": { + "type": "integer", + "description": "Unix seconds after which the key no longer authenticates; 0 = never. > 0 flags a key winding down after rotation.", + "format": "int64" + }, + "rotatedFrom": { + "type": "string", + "description": "The key id this key replaced, when minted by rotation." + } + }, + "required": [ + "createdAt", + "grants", + "keyId", + "name" + ] + }, + "GetStoreResponseContent": { + "type": "object", + "properties": { + "storeId": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated IDs are 32-char UUIDs without dashes." + }, + "name": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[A-Za-z0-9._-]+$", + "description": "Operator-supplied store name." + }, + "status": { + "$ref": "#/components/schemas/StoreStatus" + }, + "shortMemory": { + "$ref": "#/components/schemas/ShortMemoryConfig" + }, + "longTermMemory": { + "$ref": "#/components/schemas/LongTermMemoryConfig" + }, + "extractionStrategy": { + "$ref": "#/components/schemas/ExtractionStrategy" + }, + "summarization": { + "$ref": "#/components/schemas/SummarizationConfig" + }, + "customMemoryTypes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomMemoryType" + }, + "description": "Custom memory types registered on the store." + }, + "extractionCadence": { + "$ref": "#/components/schemas/ExtractionCadenceConfig" + } + }, + "required": [ + "name", + "status", + "storeId" + ] + }, + "Grant": { + "type": "object", + "description": "A grant pairs one resource with the actions allowed on it. Use `mem-store` for `resourceType` and `read`, `write`, or both for actions.", + "properties": { + "resourceType": { + "$ref": "#/components/schemas/ResourceType" + }, + "resourceId": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[A-Za-z0-9-]+$", + "description": "Resource identifier within its type (e.g. a store id)." + }, + "actions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Action" + }, + "minItems": 1, + "uniqueItems": true + } + }, + "required": [ + "actions", + "resourceId", + "resourceType" + ] + }, + "ListAPIKeysResponseContent": { + "type": "object", + "properties": { + "apiKeys": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApiKeyOutput" + } + }, + "nextToken": { + "type": "string" + } + }, + "required": [ + "apiKeys" + ] + }, + "ListStoresResponseContent": { + "type": "object", + "properties": { + "stores": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StoreOutput" + } + } + }, + "required": [ + "stores" + ] + }, + "LongTermMemoryConfig": { + "type": "object", + "properties": { + "ttlSeconds": { + "type": "integer", + "format": "int32" + } + } + }, + "MemoryTypeStrategyUpdate": { + "type": "object", + "description": "A strategy edit targeting one existing custom memory type. Omitted optional\nfields are left unchanged.", + "properties": { + "typeName": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$", + "description": "Name of the existing custom memory type whose strategy is being edited." + }, + "prompt": { + "type": "string", + "description": "Replacement extraction prompt. Omit to leave the prompt unchanged." + }, + "enabled": { + "type": "boolean", + "description": "Replacement enabled flag. Omit to leave the flag unchanged." + } + }, + "required": [ + "typeName" + ] + }, + "MintAPIKeyRequestContent": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[A-Za-z0-9._-]+$", + "description": "Operator-supplied key label (non-unique)." + }, + "grants": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Grant" + }, + "minItems": 1 + } + }, + "required": [ + "grants", + "name" + ] + }, + "MintAPIKeyResponseContent": { + "type": "object", + "properties": { + "keyId": { + "type": "string", + "pattern": "^[0-9a-f]{32}$", + "description": "Agent-key id: a UUID as 32 lowercase hex chars (the embedded, non-secret part of the bearer)." + }, + "token": { + "type": "string", + "description": "Agent-key credential returned when the key is minted or rotated. Store it immediately; it is not returned by list or get operations.\n\nTreat the credential as opaque. Present it on each Data Plane request with `Authorization: Bearer `, or use a custom header such as `X-Api-Key` when the standard Authorization header is reserved for another credential.", + "format": "password" + }, + "createdAt": { + "type": "integer", + "format": "int64" + } + }, + "required": [ + "createdAt", + "keyId", + "token" + ] + }, + "NotFoundErrorResponseContent": { + "type": "object", + "description": "The requested resource does not exist.", + "properties": { + "title": { + "type": "string", + "description": "A short, human-readable summary of the problem\n type. It SHOULD NOT change from occurrence to occurrence of the\n problem, except for purposes of localization (e.g., using\n proactive content negotiation; see [RFC7231], Section 3.4)." + }, + "status": { + "type": "integer", + "default": 404, + "description": "The HTTP status code ([RFC7231], Section 6) generated by the origin server for this occurrence of the problem.", + "format": "int32" + }, + "detail": { + "type": "string", + "description": "A human-readable explanation specific to this occurrence of the problem." + }, + "instance": { + "type": "string", + "description": "A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced." + }, + "type": { + "$ref": "#/components/schemas/NotFoundErrorType" + } + }, + "required": [ + "status", + "title", + "type" + ] + }, + "NotFoundErrorType": { + "type": "string", + "description": "Problem type URI for not-found errors.", + "enum": [ + "/errors/resource-not-found" + ] + }, + "ResourceType": { + "type": "string", + "description": "Resource type a grant authorizes. Product-qualified so values stay unique across IRIS products.", + "enum": [ + "mem-store" + ] + }, + "RotateAPIKeyRequestContent": { + "type": "object", + "properties": { + "graceSeconds": { + "type": "integer", + "description": "Grace window in seconds the old key keeps authenticating (min 1; default 3600).", + "format": "int64" + } + } + }, + "RotateAPIKeyResponseContent": { + "type": "object", + "properties": { + "keyId": { + "type": "string", + "pattern": "^[0-9a-f]{32}$", + "description": "Agent-key id: a UUID as 32 lowercase hex chars (the embedded, non-secret part of the bearer)." + }, + "token": { + "type": "string", + "description": "Agent-key credential returned when the key is minted or rotated. Store it immediately; it is not returned by list or get operations.\n\nTreat the credential as opaque. Present it on each Data Plane request with `Authorization: Bearer `, or use a custom header such as `X-Api-Key` when the standard Authorization header is reserved for another credential.", + "format": "password" + }, + "createdAt": { + "type": "integer", + "format": "int64" + }, + "oldExpiresAt": { + "type": "integer", + "description": "Unix seconds at which the rotated (old) key stops authenticating.", + "format": "int64" + } + }, + "required": [ + "createdAt", + "keyId", + "oldExpiresAt", + "token" + ] + }, + "ShortMemoryConfig": { + "type": "object", + "properties": { + "ttlSeconds": { + "type": "integer", + "format": "int32" + } + } + }, + "StoreOutput": { + "type": "object", + "properties": { + "storeId": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated IDs are 32-char UUIDs without dashes." + }, + "name": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[A-Za-z0-9._-]+$", + "description": "Operator-supplied store name." + }, + "status": { + "$ref": "#/components/schemas/StoreStatus" + }, + "shortMemory": { + "$ref": "#/components/schemas/ShortMemoryConfig" + }, + "longTermMemory": { + "$ref": "#/components/schemas/LongTermMemoryConfig" + }, + "extractionStrategy": { + "$ref": "#/components/schemas/ExtractionStrategy" + }, + "summarization": { + "$ref": "#/components/schemas/SummarizationConfig" + }, + "customMemoryTypes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomMemoryType" + }, + "description": "Custom memory types registered on the store." + }, + "extractionCadence": { + "$ref": "#/components/schemas/ExtractionCadenceConfig" + } + }, + "required": [ + "name", + "status", + "storeId" + ] + }, + "StoreStatus": { + "type": "string", + "enum": [ + "PROVISIONING", + "READY", + "UNAVAILABLE" + ] + }, + "SummarizationConfig": { + "type": "object", + "description": "Configuration for session summarization.\nWhen enabled, long sessions are periodically condensed into a running summary\nwhile retaining the most recent events.", + "properties": { + "enabled": { + "type": "boolean", + "description": "Whether summarization is enabled for this store" + }, + "triggerStrategy": { + "$ref": "#/components/schemas/SummarizationTriggerStrategy" + }, + "eventCount": { + "$ref": "#/components/schemas/SummarizationThresholdConfig" + } + }, + "required": [ + "enabled" + ] + }, + "SummarizationThresholdConfig": { + "type": "object", + "description": "Configuration for a summarization trigger.", + "properties": { + "threshold": { + "type": "integer", + "description": "The threshold value that triggers summarization", + "format": "int32" + }, + "retainCount": { + "type": "integer", + "description": "Number of most recent events to retain after each summarization cycle.", + "format": "int32" + } + }, + "required": [ + "retainCount", + "threshold" + ] + }, + "SummarizationTriggerStrategy": { + "type": "string", + "description": "Strategy that determines when summarization is triggered.\nAdditional strategies such as token_count can be added later.", + "enum": [ + "event_count" + ] + }, + "UnexpectedErrorResponseContent": { + "type": "object", + "description": "The service failed with an unexpected internal error.", + "properties": { + "title": { + "type": "string", + "description": "A short, human-readable summary of the problem\n type. It SHOULD NOT change from occurrence to occurrence of the\n problem, except for purposes of localization (e.g., using\n proactive content negotiation; see [RFC7231], Section 3.4)." + }, + "status": { + "type": "integer", + "default": 500, + "description": "The HTTP status code ([RFC7231], Section 6) generated by the origin server for this occurrence of the problem.", + "format": "int32" + }, + "detail": { + "type": "string", + "description": "A human-readable explanation specific to this occurrence of the problem." + }, + "instance": { + "type": "string", + "description": "A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced." + }, + "type": { + "$ref": "#/components/schemas/UnexpectedErrorType" + } + }, + "required": [ + "status", + "title", + "type" + ] + }, + "UnexpectedErrorType": { + "type": "string", + "description": "Problem type URI for unexpected internal errors.", + "enum": [ + "/errors/unexpected-error" + ] + }, + "UpdateAPIKeyRequestContent": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[A-Za-z0-9._-]+$", + "description": "Operator-supplied key label (non-unique)." + }, + "grants": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Grant" + }, + "minItems": 1 + } + } + }, + "UpdateAPIKeyResponseContent": { + "type": "object", + "properties": { + "keyId": { + "type": "string", + "pattern": "^[0-9a-f]{32}$", + "description": "Agent-key id: a UUID as 32 lowercase hex chars (the embedded, non-secret part of the bearer)." + }, + "name": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[A-Za-z0-9._-]+$", + "description": "Operator-supplied key label (non-unique)." + }, + "grants": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Grant" + }, + "minItems": 1 + }, + "createdAt": { + "type": "integer", + "format": "int64" + }, + "expiresAt": { + "type": "integer", + "description": "Unix seconds after which the key no longer authenticates; 0 = never. > 0 flags a key winding down after rotation.", + "format": "int64" + }, + "rotatedFrom": { + "type": "string", + "description": "The key id this key replaced, when minted by rotation." + } + }, + "required": [ + "createdAt", + "grants", + "keyId", + "name" + ] + }, + "UpdateStoreRequestContent": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[A-Za-z0-9._-]+$", + "description": "Operator-supplied store name." + }, + "shortMemory": { + "$ref": "#/components/schemas/ShortMemoryConfig" + }, + "longTermMemory": { + "$ref": "#/components/schemas/LongTermMemoryConfig" + }, + "summarization": { + "$ref": "#/components/schemas/SummarizationConfig" + }, + "addCustomMemoryTypes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomMemoryType" + }, + "description": "New custom memory types to append. Editing an existing type's prompt or\nenabled flag is done via updateCustomMemoryTypeStrategies; renaming,\nchanging fields, or removing a type is not supported." + }, + "updateCustomMemoryTypeStrategies": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MemoryTypeStrategyUpdate" + }, + "description": "Strategy edits (prompt / enabled) to apply to existing custom memory types." + }, + "extractionCadence": { + "$ref": "#/components/schemas/ExtractionCadenceConfig" + } + } + }, + "UpdateStoreResponseContent": { + "type": "object", + "properties": { + "storeId": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated IDs are 32-char UUIDs without dashes." + }, + "name": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[A-Za-z0-9._-]+$", + "description": "Operator-supplied store name." + }, + "status": { + "$ref": "#/components/schemas/StoreStatus" + }, + "shortMemory": { + "$ref": "#/components/schemas/ShortMemoryConfig" + }, + "longTermMemory": { + "$ref": "#/components/schemas/LongTermMemoryConfig" + }, + "extractionStrategy": { + "$ref": "#/components/schemas/ExtractionStrategy" + }, + "summarization": { + "$ref": "#/components/schemas/SummarizationConfig" + }, + "customMemoryTypes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomMemoryType" + }, + "description": "Custom memory types registered on the store." + }, + "extractionCadence": { + "$ref": "#/components/schemas/ExtractionCadenceConfig" + } + }, + "required": [ + "name", + "status", + "storeId" + ] + } + }, + "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "admin-token", + "description": "Control Plane admin token." + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] +} diff --git a/content/develop/ai/context-engine/agent-memory/self-managed/data-plane-configuration.md b/content/develop/ai/context-engine/agent-memory/self-managed/data-plane-configuration.md new file mode 100644 index 0000000000..f5d79e7544 --- /dev/null +++ b/content/develop/ai/context-engine/agent-memory/self-managed/data-plane-configuration.md @@ -0,0 +1,183 @@ +--- +Title: Data Plane configuration +alwaysopen: false +categories: +- docs +- develop +- ai +description: Configure the Redis Agent Memory Data Plane for static stores or Control Plane managed stores. +linkTitle: Data Plane configuration +weight: 30 +hideListLinks: true +--- + +The Data Plane reads `memory-dataplane.config.yaml` from a Kubernetes Secret. +Use one store mode: static stores or Control Plane managed stores. + +## Shared settings + +The following settings are common to both deployment modes: + +| Setting | Purpose | +| --- | --- | +| `server` | Data Plane bind address and port. | +| `license.license_path` | Path where the license Secret is mounted. | +| `request_region` | Region used for background work routing. | +| `background_jobs.redis` | Job Redis connection used by background workers. | +| `embedders_connection_details` | Embedding provider endpoint and credentials. | +| `dataplane_client` | Worker callback client configuration. | +| `promote_session_memory` | Promotion strategy and LLM connection used by workers. | + +## Static stores example + +Use this config when stores are declared directly under `metadata.stores`. + +```yaml +server: + host: 0.0.0.0 + port: 9000 + +license: + license_path: /etc/redis-agent-memory/license +default_extraction_strategy: instruct + +request_region: + default: eu1 + +background_jobs: + redis: + enabled: true + queue_prefix: ram + urls: + - redis://redis-jobs:6379 + worker_regions: + - eu1 + +metadata: + stores: + # metadata.stores is a map keyed by store ID. + "00000000000000000000000000000001": + urls: + - redis://redis-store:6379 + extraction_strategy: instruct + long_term_memory: + embedding_provider: openai + embedding_model: text-embedding-3-large + embedding_dimensions: 3072 + +auth: + method: none + +embedders_connection_details: + openai: + base_url: https://api.openai.com + credentials: + type: static + api_key: "" + +dataplane_client: + base_url: http://redis-agent-memory:9000 + auth: + disabled: true + +promote_session_memory: + strategies: + instruct: + llm: + provider: openai + endpoint: + base_url: https://api.openai.com/v1 + auth_format: bearer + credentials: + type: static + api_key: "" + models: + default_chat_model: gpt-4o +``` + +## Control Plane managed stores example + +Use this config when the Data Plane serves stores created by the Control Plane. +The Data Plane and Control Plane must point to the same Metadata Redis namespace +and Store Redis. + +```yaml +server: + host: 0.0.0.0 + port: 9000 + +license: + license_path: /etc/redis-agent-memory/license +default_extraction_strategy: instruct + +request_region: + default: eu1 + +background_jobs: + redis: + enabled: true + queue_prefix: ram + urls: + - redis://redis-jobs:6379 + worker_regions: + - eu1 + +metadata: + source: live + live: + urls: + - redis://redis-meta:6379 + namespace: iris:memory + store_db: + urls: + - redis://redis-store:6379 + +auth: + method: none + +embedding: + provider: openai + models: + default_embedding_model: text-embedding-3-large + dimensions: 3072 + +embedders_connection_details: + openai: + base_url: https://api.openai.com + credentials: + type: static + api_key: "" + +dataplane_client: + base_url: http://redis-agent-memory:9000 + auth: + disabled: true + +promote_session_memory: + strategies: + instruct: + llm: + provider: openai + endpoint: + base_url: https://api.openai.com/v1 + auth_format: bearer + credentials: + type: static + api_key: "" + models: + default_chat_model: gpt-4o +``` + +This Control Plane managed example leaves Data Plane auth disabled at the Agent +Memory layer. To use Agent Memory agent keys, set `auth.method: agent_key` and +follow [Authentication and authorization]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/authentication" >}}). + +## Secret key + +Create the Data Plane config Secret with the key +`memory-dataplane.config.yaml`: + +```bash +kubectl -n create secret generic ram-config \ + --from-file=memory-dataplane.config.yaml=./memory-dataplane.config.yaml +``` diff --git a/content/develop/ai/context-engine/agent-memory/self-managed/deploy-control-plane.md b/content/develop/ai/context-engine/agent-memory/self-managed/deploy-control-plane.md new file mode 100644 index 0000000000..04f871bd08 --- /dev/null +++ b/content/develop/ai/context-engine/agent-memory/self-managed/deploy-control-plane.md @@ -0,0 +1,232 @@ +--- +Title: Deploy with Control Plane managed stores +alwaysopen: false +categories: +- docs +- develop +- ai +description: Deploy Redis Agent Memory with stores managed by the self-managed Control Plane. +linkTitle: Deploy with Control Plane managed stores +weight: 50 +hideListLinks: true +aliases: +- /develop/ai/context-engine/agent-memory/self-managed/control-plane/ +--- + +Use Control Plane managed stores when operators need to create stores or agent +keys at runtime. In this mode, the Data Plane reads store and agent-key records +from Metadata Redis. + +Before you begin, review [prerequisites]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/prerequisites" >}}) +and create `memory-dataplane.config.yaml` from the +[Control Plane managed stores example]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/data-plane-configuration#control-plane-managed-stores-example" >}}). + +## Create the namespace + +```bash +kubectl create namespace +``` + +## Create shared Secrets + +Create the license Secret: + +```bash +kubectl -n create secret generic ram-license \ + --from-file=license=./license +``` + +Create the Data Plane config Secret: + +```bash +kubectl -n create secret generic ram-config \ + --from-file=memory-dataplane.config.yaml=./memory-dataplane.config.yaml +``` + +## Create the Control Plane config + +Create `controlplane-onprem.config.yaml`: + +```yaml +profile: prod + +auth: + type: admin-token + admin_token: + token_file: /etc/controlplane-onprem/admin/token + +license: + license_path: /etc/redis-agent-memory/license + +metadata: + urls: + - redis://redis-meta:6379 + namespace: iris:memory + +store_db: + urls: + - redis://redis-store:6379 + +embedding: + dimensions: 3072 +``` + +The Control Plane only needs `embedding.dimensions` for Control Plane managed +store metadata. Configure the embedding provider, model, credentials, and +batching settings in the Data Plane config. + +Create the Control Plane config Secret: + +```bash +kubectl -n create secret generic ram-controlplane-config \ + --from-file=controlplane-onprem.config.yaml=./controlplane-onprem.config.yaml +``` + +Bring your own admin token: + +```bash +kubectl -n create secret generic ram-controlplane-admin-token \ + --from-literal=token='' +``` + +To read the admin token from the Secret used in this guide: + +```bash +kubectl -n get secret \ + ram-controlplane-admin-token \ + -o jsonpath="{.data.token}" | base64 -d +``` + +## Create Helm values + +Create SHA-256 checksums for externally managed Secrets. These values are used +by Helm values to roll pods after Secret changes; they are not used to validate +Secret integrity. + +{{< multitabs id="agent-memory-control-plane-secret-checksums" +tab1="Linux" +tab2="macOS" >}} + +```bash +LICENSE_CHECKSUM="$(sha256sum ./license | awk '{print $1}')" +CONFIG_CHECKSUM="$(sha256sum ./memory-dataplane.config.yaml | awk '{print $1}')" +CONTROLPLANE_CONFIG_CHECKSUM="$(sha256sum ./controlplane-onprem.config.yaml | awk '{print $1}')" +``` + +-tab-sep- + +```bash +LICENSE_CHECKSUM="$(shasum -a 256 ./license | awk '{print $1}')" +CONFIG_CHECKSUM="$(shasum -a 256 ./memory-dataplane.config.yaml | awk '{print $1}')" +CONTROLPLANE_CONFIG_CHECKSUM="$(shasum -a 256 ./controlplane-onprem.config.yaml | awk '{print $1}')" +``` + +{{< /multitabs >}} + +Create `ram-values.yaml`: + +```yaml +license: + existingSecret: ram-license + existingSecretChecksum: "" + +config: + existingSecret: ram-config + existingSecretChecksum: "" + +image: + repository: redislabs/agent-memory + tag: "" + +controlplane: + enabled: true + image: + repository: redislabs/agent-memory-control-plane + tag: "" + config: + existingSecret: ram-controlplane-config + existingSecretChecksum: "" + adminToken: + existingSecret: ram-controlplane-admin-token + secretKey: token + autoGenerate: false +``` + +## Install the chart + +Add the Helm repository when installing from the public repository: + +```bash +helm repo add redis-ai https://helm.redis.io/ai +helm repo update redis-ai +helm search repo redis-ai/redis-agent-memory --versions +``` + +Install with `redis-agent-memory` as the Helm release name: + +```bash +helm install redis-agent-memory redis-ai/redis-agent-memory \ + --version \ + --namespace \ + --create-namespace \ + -f ram-values.yaml +``` + +On small clusters, install without `--atomic --wait`, then watch pod status: + +```bash +kubectl -n get pods -w +``` + +If you want Helm to wait, set an explicit timeout that matches the environment: + +```bash +helm install redis-agent-memory redis-ai/redis-agent-memory \ + --version \ + --namespace \ + --create-namespace \ + -f ram-values.yaml \ + --wait \ + --timeout 15m +``` + +## Verify the deployment + +Check pods: + +```bash +kubectl -n get pods -l app.kubernetes.io/name=redis-agent-memory +``` + +Port-forward the Data Plane: + +```bash +kubectl -n port-forward svc/redis-agent-memory 9000:9000 +``` + +Check Data Plane health: + +```bash +curl http://localhost:9000/health +curl http://localhost:9000/health/liveness +curl http://localhost:9000/health/readiness +``` + +Port-forward the Control Plane: + +```bash +kubectl -n port-forward svc/redis-agent-memory-controlplane 9100:9100 +``` + +Verify the admin API: + +```bash +curl -H "Authorization: Bearer " \ + http://localhost:9100/v1/stores +``` + +For the full self-managed admin API schema, see the +[Control Plane API reference]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/control-plane-api-reference" >}}). + +After you deploy Control Plane managed stores, configure Data Plane auth in +[Authentication and authorization]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/authentication" >}}). diff --git a/content/develop/ai/context-engine/agent-memory/self-managed/deploy-static.md b/content/develop/ai/context-engine/agent-memory/self-managed/deploy-static.md new file mode 100644 index 0000000000..77f67c0df9 --- /dev/null +++ b/content/develop/ai/context-engine/agent-memory/self-managed/deploy-static.md @@ -0,0 +1,154 @@ +--- +Title: Deploy with static stores +alwaysopen: false +categories: +- docs +- develop +- ai +description: Deploy Redis Agent Memory with static stores and no Control Plane. +linkTitle: Deploy with static stores +weight: 40 +hideListLinks: true +aliases: +- /develop/ai/context-engine/agent-memory/self-managed/install-k8s/ +--- + +Use static stores for a first install or a private single-store deployment. In +this mode, stores are declared directly in Data Plane configuration. The +deployment does not include the Control Plane and does not use Metadata Redis. + +Before you begin, review [prerequisites]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/prerequisites" >}}) +and create `memory-dataplane.config.yaml` from the +[static stores example]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/data-plane-configuration#static-stores-example" >}}). + +## Create the namespace + +```bash +kubectl create namespace +``` + +## Create Secrets + +Create the license Secret: + +```bash +kubectl -n create secret generic ram-license \ + --from-file=license=./license +``` + +Create the Data Plane config Secret: + +```bash +kubectl -n create secret generic ram-config \ + --from-file=memory-dataplane.config.yaml=./memory-dataplane.config.yaml +``` + +## Create Helm values + +Create SHA-256 checksums for externally managed Secrets. These values are used +by Helm values to roll pods after Secret changes; they are not used to validate +Secret integrity. + +{{< multitabs id="agent-memory-static-secret-checksums" +tab1="Linux" +tab2="macOS" >}} + +```bash +LICENSE_CHECKSUM="$(sha256sum ./license | awk '{print $1}')" +CONFIG_CHECKSUM="$(sha256sum ./memory-dataplane.config.yaml | awk '{print $1}')" +``` + +-tab-sep- + +```bash +LICENSE_CHECKSUM="$(shasum -a 256 ./license | awk '{print $1}')" +CONFIG_CHECKSUM="$(shasum -a 256 ./memory-dataplane.config.yaml | awk '{print $1}')" +``` + +{{< /multitabs >}} + +Create `ram-values.yaml`: + +```yaml +license: + existingSecret: ram-license + existingSecretChecksum: "" + +config: + existingSecret: ram-config + existingSecretChecksum: "" + +image: + repository: redislabs/agent-memory + tag: "" +``` + +## Install the chart + +Add the Helm repository when installing from the public repository: + +```bash +helm repo add redis-ai https://helm.redis.io/ai +helm repo update redis-ai +helm search repo redis-ai/redis-agent-memory --versions +``` + +Install with `redis-agent-memory` as the Helm release name: + +```bash +helm install redis-agent-memory redis-ai/redis-agent-memory \ + --version \ + --namespace \ + --create-namespace \ + -f ram-values.yaml +``` + +On small clusters, install without `--atomic --wait`, then watch pod status: + +```bash +kubectl -n get pods -w +``` + +If you want Helm to wait, set an explicit timeout that matches the environment: + +```bash +helm install redis-agent-memory redis-ai/redis-agent-memory \ + --version \ + --namespace \ + --create-namespace \ + -f ram-values.yaml \ + --wait \ + --timeout 15m +``` + +## Verify the deployment + +Check pods: + +```bash +kubectl -n get pods -l app.kubernetes.io/name=redis-agent-memory +``` + +Port-forward the Data Plane: + +```bash +kubectl -n port-forward svc/redis-agent-memory 9000:9000 +``` + +Check health endpoints: + +```bash +curl http://localhost:9000/health +curl http://localhost:9000/health/liveness +curl http://localhost:9000/health/readiness +``` + +Expected `/health` response: + +```json +{"status":"healthy"} +``` + +Do not expose an auth-disabled Data Plane to untrusted callers. Use Kubernetes +NetworkPolicy, private service exposure, ingress, gateway, service mesh, or +equivalent controls to restrict access. diff --git a/content/develop/ai/context-engine/agent-memory/self-managed/operations.md b/content/develop/ai/context-engine/agent-memory/self-managed/operations.md new file mode 100644 index 0000000000..b081265e4f --- /dev/null +++ b/content/develop/ai/context-engine/agent-memory/self-managed/operations.md @@ -0,0 +1,239 @@ +--- +Title: Operations +alwaysopen: false +categories: +- docs +- develop +- ai +description: Operate Redis Agent Memory with backups, secret rotation, updates, FIPS posture, and network policy. +linkTitle: Operations +weight: 90 +hideListLinks: true +--- + +## Backups + +- Back up metadata Redis. Losing metadata removes Control Plane store records + and agent-key records. +- Back up Store Redis according to the customer's memory-retention policy. +- Back up Job Redis if background job replay or delayed-job preservation is + required by the deployment's recovery policy. +- Back up any external secret manager material used to recreate Kubernetes + Secrets. +- For Job Redis, use persistent storage where supported and a non-volatile / + `noeviction` policy. OOM can still lose jobs or leave worker state invalid; + capacity alerts and compatibility checks should make that caveat visible. +- For Metadata Redis, use persistent storage and an eviction policy that does + not evict store or agent-key records under memory pressure. + +## Secret rotation + +Rotate Agent Memory agent keys through the Control Plane API: + +```bash +curl -sS -X POST "$CP_URL/v1/api-keys//rotate" \ + -H "Authorization: Bearer $RAM_ADMIN_TOKEN" +``` + +The response contains the new credential. Store it immediately; credentials are +returned only when a key is minted or rotated. + +Rotate the Control Plane admin token by updating `ram-controlplane-admin-token`. +The Control Plane reads the token on use, so changing the token value does not +require a Control Plane redeploy. + +```bash +kubectl -n create secret generic ram-controlplane-admin-token \ + --from-literal=token='' \ + --dry-run=client \ + -o yaml | kubectl apply -f - +``` + +Rotate the Agent Memory license by updating the license Secret and changing +`license.existingSecretChecksum` so Helm rolls the Data Plane and worker pods. +Agent Memory reads and validates the license file during process startup; updating only +the Secret data is not sufficient. + +```bash +kubectl -n create secret generic ram-license \ + --from-file=license=./license \ + --dry-run=client \ + -o yaml | kubectl apply -f - +``` + +Calculate the new SHA-256 checksum. This value is used by Helm values to roll +pods after the license Secret changes; it is not used to validate Secret +integrity. + +{{< multitabs id="agent-memory-license-secret-checksum" +tab1="Linux" +tab2="macOS" >}} + +```bash +LICENSE_CHECKSUM="$(sha256sum ./license | awk '{print $1}')" +``` + +-tab-sep- + +```bash +LICENSE_CHECKSUM="$(shasum -a 256 ./license | awk '{print $1}')" +``` + +{{< /multitabs >}} + +```yaml +license: + existingSecret: ram-license + existingSecretChecksum: "" +``` + +Apply the updated values and verify both workloads rolled: + +```bash +helm upgrade redis-agent-memory redis-ai/redis-agent-memory \ + --version \ + --namespace \ + -f ram-values.yaml + +kubectl -n rollout status deploy/redis-agent-memory +kubectl -n rollout status deploy/redis-agent-memory-worker +``` + +For immutable license Secrets, create a new Secret name instead, then update +both `license.existingSecret` and `license.existingSecretChecksum`. + +Rotate the shared Data Plane config by updating the config Secret and changing +`config.existingSecretChecksum`. Rotate the Control Plane config by updating the +config Secret and changing `controlplane.config.existingSecretChecksum`. + +## Updates + +For every update: + +1. Update chart version and image tags. +2. Recalculate Secret checksums for changed files. +3. Run `helm upgrade`. +4. Verify pod rollout and health endpoints. + +Example: + +```bash +helm upgrade redis-agent-memory redis-ai/redis-agent-memory \ + --version \ + --namespace \ + -f ram-values.yaml +``` + +On small clusters, avoid `--atomic` unless the timeout and capacity are known to +be sufficient. + +## Chart tests + +The chart can render optional `helm test` resources when `tests.enabled=true`. + +Enable and run the basic chart test: + +```bash +helm upgrade --install redis-agent-memory redis-ai/redis-agent-memory \ + --version \ + --namespace \ + -f ram-values.yaml \ + --set tests.enabled=true + +helm test redis-agent-memory --namespace +``` + +To run the API smoke test, also provide a configured store ID: + +```bash +helm upgrade --install redis-agent-memory redis-ai/redis-agent-memory \ + --version \ + --namespace \ + -f ram-values.yaml \ + --set tests.enabled=true \ + --set tests.smoke.enabled=true \ + --set tests.smoke.storeId= + +helm test redis-agent-memory --namespace --logs +``` + +Use the smoke test only for auth-disabled Data Plane deployments or for +environments where the in-cluster test path is allowed. + +## FIPS-oriented posture + +The chart supports an opt-in FIPS-oriented posture for regulated environments: + +```yaml +security: + profile: fips +``` + +You can also apply the bundled FIPS values overlay with the normal values file: + +```bash +helm upgrade --install redis-agent-memory redis-ai/redis-agent-memory \ + --version \ + --namespace \ + -f ram-values.yaml \ + -f deployment/redis-agent-memory/values-fips.yaml +``` + +When enabled, the chart sets `MEM_SECURITY_PROFILE=fips` on the Data Plane, +worker, and Control Plane pods and enables FIPS-oriented runtime checks. + +This is not a formal FIPS 140 compliance or validation claim. Treat it as a +deployment posture and guardrail that must still be reviewed against the +customer's compliance boundary. + +When the posture is active, the Data Plane and worker reject config that: + +- enables `skip_verify` on outbound HTTP clients; or +- uses non-`rediss://` URLs for Redis connections covered by the posture. + +The Control Plane runs under the same posture and rejects non-`rediss://` +`metadata.urls` or `store_db.urls`. + +The Agent Memory API listener itself speaks HTTP inside the cluster. Edge TLS termination +is owned by the hosting environment, such as ingress, service mesh, or external +load balancer. Outbound TLS to Redis, embedding providers, LLM providers, and +worker callback endpoints is configured through Agent Memory config and is covered by the +posture checks. + +Verify the runtime posture with: + +```bash +kubectl -n get deploy redis-agent-memory \ + -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="MEM_SECURITY_PROFILE")].value}' + +kubectl -n logs deploy/redis-agent-memory | grep -i 'FIPS security profile' +``` + +Use the chart-generated Deployment to enable the FIPS-oriented posture. Do not +override the container command to configure it. + +## Network policy + +For auth-disabled Data Plane deployments, restrict access to trusted callers. +For agent-key deployments behind a gateway, prevent direct bypass paths unless +the direct caller also has a valid Agent Memory credential. + +The chart includes `deployment/redis-agent-memory/networkpolicy.reference.yaml` +as a reference manifest. It is not templated because allowed callers are +environment-specific. + +Customize the placeholders before applying it: + +- ``: namespace where Agent Memory is installed. +- `redis-agent-memory`: Helm release name used in this guide. If you use a + different release name, update release-derived service and deployment names. + `nameOverride` and `fullnameOverride` change rendered resource names, but the + `app.kubernetes.io/instance` selector remains the Helm release name. +- `` and caller pod labels: ingress controller, service mesh + gateway, application pod, or approved internal caller allowed to call Agent Memory. + +The reference policy default-denies ingress to Agent Memory chart pods, then allows TCP +traffic to server pods on port `9000` from approved callers and the worker +Deployment. It also includes a Control Plane stanza for port `9100` when +`controlplane.enabled=true`. Review the manifest against the customer's CNI, +ingress path, and service mesh behavior before production use. diff --git a/content/develop/ai/context-engine/agent-memory/self-managed/plan-deployment.md b/content/develop/ai/context-engine/agent-memory/self-managed/plan-deployment.md new file mode 100644 index 0000000000..f4fe07ba97 --- /dev/null +++ b/content/develop/ai/context-engine/agent-memory/self-managed/plan-deployment.md @@ -0,0 +1,44 @@ +--- +Title: Plan a self-managed Agent Memory deployment +alwaysopen: false +categories: +- docs +- develop +- ai +description: Choose a self-managed Redis Agent Memory deployment mode before installing the Helm chart. +linkTitle: Plan a deployment +weight: 10 +hideListLinks: true +--- + +Choose the deployment mode before you create Redis databases, configuration +Secrets, or Helm values. + +## Deployment modes + +{{< table-scrollable >}} +| Mode | What it deploys | Redis databases | Data Plane auth | Start here | +| --- | --- | --- | --- | --- | +| Static stores | Data Plane and, when enabled, Agent Memory workers. Stores are declared directly in `memory-dataplane.config.yaml`. | Store Redis. Job Redis when workers are enabled. | Disabled at the Agent Memory layer. Protect access with Kubernetes, ingress, gateway, or service-mesh controls. | [Deploy with static stores]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/deploy-static" >}}) | +| Control Plane managed stores | Data Plane, Control Plane, and, when enabled, Agent Memory workers. Stores and agent keys are managed at runtime. | Store Redis, Metadata Redis, and Job Redis when workers are enabled. | Auth-disabled Data Plane or Agent Memory agent keys. | [Deploy with Control Plane managed stores]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/deploy-control-plane" >}}) | +{{< /table-scrollable >}} + +## Mode rules + +Do not combine static `metadata.stores` with Control Plane managed store +metadata in the same Data Plane config. Static stores do not use Metadata Redis. +Control Plane managed stores use `metadata.source: live` and require Metadata +Redis. + +Agent-key authentication requires Control Plane managed stores because the Data +Plane reads agent-key records and store grants from Metadata Redis. + +The walkthroughs in this section use `redis-agent-memory` as the Helm release +name. If you choose a different release name, update release-derived service and +deployment names in the verification commands. + +## Before you deploy + +1. Review [prerequisites]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/prerequisites" >}}). +1. Prepare the appropriate [Data Plane configuration]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/data-plane-configuration" >}}). +1. Follow either [Deploy with static stores]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/deploy-static" >}}) or [Deploy with Control Plane managed stores]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/deploy-control-plane" >}}). diff --git a/content/develop/ai/context-engine/agent-memory/self-managed/prerequisites.md b/content/develop/ai/context-engine/agent-memory/self-managed/prerequisites.md new file mode 100644 index 0000000000..cc6fd75198 --- /dev/null +++ b/content/develop/ai/context-engine/agent-memory/self-managed/prerequisites.md @@ -0,0 +1,258 @@ +--- +Title: Self-managed Agent Memory prerequisites +alwaysopen: false +categories: +- docs +- develop +- ai +description: Review software, Redis, network, Secret, image, and sizing prerequisites for self-managed Redis Agent Memory. +linkTitle: Prerequisites +weight: 20 +hideListLinks: true +--- + +Redis Agent Memory is distributed as container images on Docker Hub plus a Helm +chart in the Redis AI Helm repository. The chart deploys the Agent Memory Data +Plane, Agent Memory workers, and optionally the Agent Memory Control Plane. + +You provide the Redis databases, provider credentials, Kubernetes exposure, and +license material used by the deployment. + +{{< note >}} +This guide is for system administrators deploying Agent Memory on a self-managed +Kubernetes cluster. +{{< /note >}} + +## What you need + +| Item | Where it comes from | +| ---- | ------------------- | +| Container images | Docker Hub: `redislabs/agent-memory` and, when the Control Plane is enabled, `redislabs/agent-memory-control-plane` | +| Helm chart | `redis-agent-memory` chart in the Redis AI Helm repository, or a chart package provided by Redis | +| Redis databases | You provide Store Redis, Job Redis, and Metadata Redis as needed by the deployment mode | +| License key | Contact your Redis representative or [contact sales](https://redis.io/contact/). | +| Provider credentials | You provide embedding provider credentials and, when worker features are enabled, promotion or summarization LLM credentials | + +## Required software + +| Software | Minimum version | Purpose | +| -------- | --------------- | ------- | +| Kubernetes | 1.19+ | Orchestration | +| kubectl | 1.19+ | Kubernetes CLI | +| Helm | 3.x | Package manager | + +## Redis databases + +The Helm chart does not deploy Redis databases. Provision the Redis databases +outside the Agent Memory chart and pass their URLs in +`memory-dataplane.config.yaml` and, when the Control Plane is enabled, +`controlplane-onprem.config.yaml`. + +Store Redis must support Search and JSON capabilities because Agent Memory +creates JSON and vector search indexes for memory data. Job Redis and Metadata +Redis do not need those capabilities when they are deployed as separate Redis +databases. + +### Static stores + +Use static stores for a first install or a private single-store deployment. +Stores are declared directly in `memory-dataplane.config.yaml`. The Control +Plane and Metadata Redis are not used. + +{{< table-scrollable >}} +| Redis database | Required when | Configure in `memory-dataplane.config.yaml` | Purpose | +| --- | --- | --- | --- | +| Store Redis | Always | `metadata.stores..urls` | Session memory JSON, long-term memory hashes, RediSearch indexes, vectors, and TTL-managed data. | +| Job Redis | Worker/background jobs enabled | `background_jobs.redis.urls` | Background work, retry state, delayed jobs, and idempotency markers. | +{{< /table-scrollable >}} + +### Control Plane managed stores + +Use Control Plane managed stores when operators need to create stores or agent +keys at runtime. The Data Plane and Control Plane must point to the same +Metadata Redis namespace and Store Redis. + +{{< table-scrollable >}} +| Redis database | Required when | Configure in `memory-dataplane.config.yaml` | Configure in `controlplane-onprem.config.yaml` | Purpose | +| --- | --- | --- | --- | --- | +| Store Redis | Always | `metadata.live.store_db.urls` | `store_db.urls` | Memory data for Control Plane managed stores. | +| Metadata Redis | Always | `metadata.live.urls` and `metadata.live.namespace` | `metadata.urls` and `metadata.namespace` | Store records, agent-key records, and grant metadata. | +| Job Redis | Worker/background jobs enabled | `background_jobs.redis.urls` | Not used by Control Plane | Background work, retry state, delayed jobs, and idempotency markers. | +{{< /table-scrollable >}} + +Do not combine static `metadata.stores` with Control Plane managed store +metadata for the same Data Plane. Static stores do not use Metadata Redis. +Control Plane managed stores require Metadata Redis and are required for +agent-key authentication. + +For a lab deployment, the Redis roles required by your chosen mode can point to +the same Redis endpoint if it has the required modules and capacity. For +production, separate Store Redis, Job Redis, and Metadata Redis when possible so +memory data, background work, and control metadata can be scaled, backed up, and +operated independently. + +For Job Redis, use a non-evicting policy such as `noeviction` or +`volatile-ttl`. + +### Metadata Redis durability + +Metadata Redis is small compared with Store Redis, but it is operationally +critical. Use persistent storage, Redis authentication, network isolation, and +TLS where required. + +In FIPS-oriented deployments, Redis URLs covered by the posture must use +`rediss://`. Avoid eviction of metadata keys; losing metadata removes Control +Plane store records and agent-key records. + +## Network access + +- **Connected install:** the cluster must be able to pull images from + `docker.io` and reach the Redis AI Helm repository. +- **Air-gapped install:** mirror the images into an internal registry and use a + chart package or locally downloaded chart. +- **Runtime access:** Agent Memory pods must reach the Redis databases and any + embedding or LLM provider endpoints used by the deployment. +- **Data Plane exposure:** use NetworkPolicy, ingress, gateway, service mesh, + private load balancer, or equivalent controls to restrict API access. + +## Credentials and Secrets + +The chart consumes configuration and license material from Kubernetes Secrets: + +| Secret | Required when | Default key | +| --- | --- | --- | +| Agent Memory license Secret | Always | `license` | +| Agent Memory Data Plane config Secret | Always | `memory-dataplane.config.yaml` | +| Control Plane config Secret | Control Plane enabled | `controlplane-onprem.config.yaml` | +| Control Plane admin-token Secret | Control Plane enabled | `token` | + +The config file is mounted as a Secret because it commonly contains provider API +keys and Redis URLs may include credentials. + +## Release artifacts and image tags + +Agent Memory self-managed image tags use the release SemVer value, for example: + +```yaml +image: + repository: redislabs/agent-memory + tag: "" +``` + +Use the image tag listed for the release on Docker Hub or provided by Redis. + +Use the chart version supplied by Redis for the release. The published chart is +`redis-ai/redis-agent-memory` from `https://helm.redis.io/ai`. + +Standard customer installs use the public Docker Hub images published by the +Agent Memory self-managed release: `docker.io/redislabs/agent-memory:` +and, when the Control Plane is enabled, +`docker.io/redislabs/agent-memory-control-plane:`. + +## Air-gapped and private registry installs + +Mirror the published images into your internal registry: + +```bash +docker pull redislabs/agent-memory: +docker tag redislabs/agent-memory: \ + registry.example.com/redislabs/agent-memory: +docker push registry.example.com/redislabs/agent-memory: + +docker pull redislabs/agent-memory-control-plane: +docker tag redislabs/agent-memory-control-plane: \ + registry.example.com/redislabs/agent-memory-control-plane: +docker push registry.example.com/redislabs/agent-memory-control-plane: +``` + +If the registry requires authentication, create an image pull Secret: + +```bash +kubectl -n create secret docker-registry ram-registry \ + --docker-server=registry.example.com \ + --docker-username= \ + --docker-password= +``` + +Add the registry settings to `ram-values.yaml`: + +```yaml +image: + repository: registry.example.com/redislabs/agent-memory + tag: "" + +controlplane: + image: + repository: registry.example.com/redislabs/agent-memory-control-plane + tag: "" + +imagePullSecrets: + - name: ram-registry +``` + +Omit `imagePullSecrets` if the internal registry does not require +authentication. + +For air-gapped deployments, also set: + +```yaml +airgap: + enabled: true +``` + +## System requirements + +Default chart values: + +| Component | Default | Purpose | +| --------- | ------- | ------- | +| Agent Memory server | 2 replicas with autoscaling enabled and a minimum of 2 | Data Plane API traffic | +| Agent Memory worker | 2 replicas with autoscaling enabled and a minimum of 2 | Background promotion, summarization, and forgetting jobs | +| Agent Memory Control Plane | 1 replica when `controlplane.enabled=true` | Admin API for stores and agent keys | + +During a rolling update, Kubernetes may temporarily run old and new pods at the +same time. A small two-node test cluster can run out of CPU during install or +upgrade. + +For realistic validation, use at least three nodes or enough CPU headroom for +the maximum rolling-update overlap. + +For a constrained lab cluster, reduce replicas and autoscaling explicitly: + +```yaml +server: + replicaCount: 1 + autoscaling: + enabled: false + +worker: + replicaCount: 1 + autoscaling: + enabled: false +``` + +Do not use reduced replica counts as the production HA recommendation. + +## Helm values to review + +The walkthroughs use `redis-agent-memory` as the Helm release name. The +generated service and deployment names in the verification steps assume that +release name. + +{{< table-scrollable >}} +| Area | Values | Use when | +| --- | --- | --- | +| Image | `image.repository`, `image.tag`, `imagePullSecrets` | Selecting a release, private registry, or mirrored image. | +| Air-gapped installs | `airgap.enabled` | Validating a disconnected or private-registry install. | +| API server capacity | `server.resources`, `server.autoscaling.*` | Tuning request capacity or memory footprint. | +| Worker capacity | `worker.resources`, `worker.autoscaling.*` | Tuning background job throughput. | +| Scheduling | `server.nodeSelector`, `worker.nodeSelector`, `server.affinity`, `worker.affinity`, `server.tolerations`, `worker.tolerations` | Controlling pod placement. | +| Networking | `service.type`, `ingress.*` | Exposing Agent Memory outside the cluster. | +| Naming | `fullnameOverride` | Running more than one Agent Memory release in a namespace. | +| Service account | `serviceAccount.*` | Matching customer namespace security policy. | +| Worker authentication | `workerAuth.enabled`, `worker.serviceAccount.*`, `worker.serviceAccount.token.*` | Giving Agent Memory workers a Kubernetes projected service-account token for authenticated Data Plane callbacks. | +| Secret rollouts | `license.existingSecretChecksum`, `config.existingSecretChecksum`, `controlplane.config.existingSecretChecksum` | Rolling pods after externally managed Secret changes. | +| Control Plane | `controlplane.enabled`, `controlplane.image.*`, `controlplane.config.existingSecret`, `controlplane.adminToken.*` | Enabling the optional admin API for stores and agent keys. | +{{< /table-scrollable >}} + +Do not use floating image tags in production. diff --git a/content/develop/ai/context-engine/agent-memory/self-managed/reference.md b/content/develop/ai/context-engine/agent-memory/self-managed/reference.md new file mode 100644 index 0000000000..92a466f68a --- /dev/null +++ b/content/develop/ai/context-engine/agent-memory/self-managed/reference.md @@ -0,0 +1,73 @@ +--- +Title: Configuration and troubleshooting +alwaysopen: false +categories: +- docs +- develop +- ai +description: Review self-managed Redis Agent Memory configuration, troubleshooting guidance, and reference links. +linkTitle: Configuration and troubleshooting +weight: 100 +hideListLinks: true +--- + +## Configuration reference + +Use these files to configure a self-managed deployment: + +| File | Purpose | +| --- | --- | +| `ram-values.yaml` | Helm values for images, replicas, services, Secret names, and optional Control Plane settings. | +| `memory-dataplane.config.yaml` | Data Plane stores, Redis URLs, auth mode, embedding provider, and worker callback settings. | +| `controlplane-onprem.config.yaml` | Control Plane metadata Redis, store Redis, admin-token auth, and embedding settings. | +| `license` | Agent Memory license file provided by Redis. | + +### External secret managers + +If you use an external secret manager, expose the license, config, and +admin-token material to the chart as Kubernetes Secrets and set the chart's +`existingSecret` values to those Secret names. + +Direct CSI file mounts that bypass Kubernetes Secrets are not supported for the +Agent Memory license, Data Plane config, Control Plane config, or Control Plane +admin-token paths. + +For Secrets Store CSI Driver, use sync-to-Kubernetes-Secret +(`SecretProviderClass.secretObjects`). If the Control Plane consumes +CSI-synced Secrets, make sure a Control Plane pod also mounts the corresponding +`SecretProviderClass` volume so the synced Secret exists while the pod runs. + +## Troubleshooting + +{{< table-scrollable >}} +| Symptom | Likely cause | Fix | +| --- | --- | --- | +| `helm search repo redis-ai/redis-agent-memory --versions` returns no results | Helm repo not added/updated, or the chart version has not been published to the repo yet | Run `helm repo add`, `helm repo update`, or install from the chart package provided by Redis. | +| Docker pull fails for the configured image tag | Image tag is wrong or has not been published to the configured registry | Use the image tag listed for the release on Docker Hub or provided by Redis. | +| Pod is stuck in `ImagePullBackOff` or `ErrImagePull` | Cluster cannot pull the configured image, image tag is wrong, registry requires credentials, or `imagePullSecrets` is missing/wrong | Verify `image.repository`, `image.tag`, registry reachability, and `imagePullSecrets`; use the Agent Memory release image tag. | +| `helm install --atomic --wait` times out and rolls back | Cluster is small or image pull/startup takes longer than Helm's default timeout | Install without `--atomic --wait`, or set a longer `--timeout` and ensure enough cluster capacity. | +| Pods are pending during install or upgrade | CPU/memory capacity is insufficient for default replicas and rollout overlap | Add nodes/headroom or lower replicas for test deployments. | +| Data Plane health fails | Pod not ready, config invalid, Redis unavailable, or license invalid | Check pod logs and call `/health`, `/health/liveness`, and `/health/readiness`. | +| Data Plane fails with `auth.agent_keys.enabled requires metadata.source=live` | Agent-key auth enabled with static stores, for example through `auth.method: agent_key` | Use Control Plane managed stores with `metadata.source: live`, or set `auth.method: none`. | +| Pod fails to start in FIPS posture | A Redis URL is not `rediss://` or an outbound HTTP client uses `skip_verify: true` | Update Redis URLs and HTTP client config to satisfy the posture checks. | +| Agent receives `401` | Missing, malformed, revoked, expired, or invalid key | Check `Authorization` / `X-Api-Key`, key status, and metadata Redis connectivity. | +| Agent receives `403` | Key exists but lacks the required store grant or action | Update grants through `/v1/api-keys/{keyId}`. | +| Store created by the Control Plane is not visible to the Data Plane | CP and DP point at different Metadata Redis URLs or namespaces | Make CP `metadata.urls` / `metadata.namespace` match DP `metadata.live.urls` / `metadata.live.namespace`. | +| Agent key minted by the Control Plane is rejected by the Data Plane | Data Plane cannot read the same Metadata Redis records, the key was rotated/revoked, or the key secret is wrong | Check `metadata.source: live`, Metadata Redis connectivity, and send the latest credential returned by mint or rotate. | +| Minting an agent key returns `400` for a grant | Unknown store ID or invalid grant shape | Create the store first; use `resourceType: "mem-store"` and `actions: ["read"]`, `["write"]`, or both. | +| Worker jobs fail after agent-key auth is enabled | Worker callback request has no accepted credential, the projected token is not mounted, or `auth.worker_identity` does not trust the worker subject/audience/issuer | Enable `workerAuth`, set `dataplane_client.auth.type=service_account_token`, and configure `auth.worker_identity.subjects` with the worker ServiceAccount subject and required store grants. | +| Gateway path succeeds but direct external path also works | Data Plane is reachable outside the intended gateway path | Add NetworkPolicy, ingress, service mesh, or load balancer controls. | +| NetworkPolicy blocks expected traffic | Placeholder namespace, release name, or caller selectors were not customized correctly | Check the Helm release label `app.kubernetes.io/instance`, caller namespace, and caller pod labels. | +{{< /table-scrollable >}} + +## References + +| Need | Reference | +| --- | --- | +| Helm chart values | `deployment/redis-agent-memory/values.yaml` | +| FIPS values overlay | `deployment/redis-agent-memory/values-fips.yaml` | +| NetworkPolicy reference | `deployment/redis-agent-memory/networkpolicy.reference.yaml` | +| Redis Agent Memory API reference | [Redis Agent Memory API]({{< relref "/develop/ai/context-engine/agent-memory/api-reference" >}}) | +| Control Plane API reference | [Control Plane API reference]({{< relref "/develop/ai/context-engine/agent-memory/self-managed/control-plane-api-reference" >}}) | +| Agent Memory Data Plane image tags | [Docker Hub: redislabs/agent-memory](https://hub.docker.com/r/redislabs/agent-memory/tags) | +| Agent Memory Control Plane image tags | [Docker Hub: redislabs/agent-memory-control-plane](https://hub.docker.com/r/redislabs/agent-memory-control-plane/tags) |