From 6f068b0f1d5d959204b6559d1f06f567933450c6 Mon Sep 17 00:00:00 2001 From: vinbyte Date: Sat, 8 Aug 2026 22:50:41 +0700 Subject: [PATCH 1/6] feat: add docker-compose deployment and config for local embeddings Signed-off-by: vinbyte --- AGENTS.md | 2 +- README.md | 2 +- deploy/docker-compose/.env.example | 39 ++++++++++ deploy/docker-compose/Dockerfile | 39 ++++++++++ deploy/docker-compose/docker-compose.yml | 30 ++++++++ deploy/docker-compose/entrypoint.sh | 97 ++++++++++++++++++++++++ src/index.ts | 2 +- src/providers/embedding/local.ts | 12 ++- 8 files changed, 218 insertions(+), 5 deletions(-) create mode 100644 deploy/docker-compose/.env.example create mode 100644 deploy/docker-compose/Dockerfile create mode 100644 deploy/docker-compose/docker-compose.yml create mode 100644 deploy/docker-compose/entrypoint.sh diff --git a/AGENTS.md b/AGENTS.md index 6f64946fc..9a5a55467 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -117,7 +117,7 @@ Hook scripts in `src/hooks/` are standalone Node.js scripts (no iii-sdk import). ## Current Stats (v0.9.28) - 54 MCP tools (8 visible by default, `AGENTMEMORY_TOOLS=all` for all) -- 129 REST endpoints +- 130 REST endpoints - 6 MCP resources, 3 MCP prompts - 12 hooks, 15 skills - 260+ iii functions diff --git a/README.md b/README.md index eb6bd1fa6..7e15144f1 100644 --- a/README.md +++ b/README.md @@ -1499,7 +1499,7 @@ Create `~/.agentmemory/.env`:

API

-129 endpoints on port `3111`. The REST API binds to `127.0.0.1` by default. Protected endpoints require `Authorization: Bearer ` when `AGENTMEMORY_SECRET` is set, and mesh sync endpoints require `AGENTMEMORY_SECRET` on both peers. +130 endpoints on port `3111`. The REST API binds to `127.0.0.1` by default. Protected endpoints require `Authorization: Bearer ` when `AGENTMEMORY_SECRET` is set, and mesh sync endpoints require `AGENTMEMORY_SECRET` on both peers.
Key endpoints diff --git a/deploy/docker-compose/.env.example b/deploy/docker-compose/.env.example new file mode 100644 index 000000000..72219fbb4 --- /dev/null +++ b/deploy/docker-compose/.env.example @@ -0,0 +1,39 @@ +# ========================================== +# AgentMemory Configuration +# ========================================== + +# ------------------------------------------ +# 1. LLM Provider (Deepseek Flash V4 / openrouter) +# ------------------------------------------ +# To use Deepseek, you can use OpenRouter or direct DeepSeek API (OpenAI compatible) +# Uncomment one of the following approaches: + +# Approach A: OpenRouter (Recommended for Deepseek) +OPENROUTER_API_KEY=your_openrouter_api_key_here +OPENROUTER_MODEL=deepseek/deepseek-chat + +# Approach B: Direct DeepSeek API +# OPENAI_API_KEY=your_deepseek_api_key_here +# OPENAI_BASE_URL=https://api.deepseek.com +# OPENAI_MODEL=deepseek-chat + +# ------------------------------------------ +# 2. Embeddings Configuration (CPU-only Local) +# ------------------------------------------ +# Set embedding provider to local +EMBEDDING_PROVIDER=local + +# Default model is Xenova/all-MiniLM-L6-v2. If you change it, +# make sure you update the dimensions to match your model! +# NOTE: The custom model support requires building from the latest modified source. +LOCAL_EMBEDDING_MODEL=Xenova/all-MiniLM-L6-v2 +LOCAL_EMBEDDING_DIMENSIONS=384 + +# ------------------------------------------ +# 3. AgentMemory Features +# ------------------------------------------ +# Run LLM compression on every observation batch (Warning: uses LLM tokens) +AGENTMEMORY_AUTO_COMPRESS=false + +# Inject recalled memories back into agent prompts +AGENTMEMORY_INJECT_CONTEXT=false diff --git a/deploy/docker-compose/Dockerfile b/deploy/docker-compose/Dockerfile new file mode 100644 index 000000000..23f127363 --- /dev/null +++ b/deploy/docker-compose/Dockerfile @@ -0,0 +1,39 @@ +# Build stage +FROM node:22-slim AS builder +WORKDIR /app +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run build +RUN npm prune --production + +# Final stage +FROM iiidev/iii:0.11.2 AS iii-image + +FROM node:22-slim +WORKDIR /opt/agentmemory +RUN apt-get update \ + && apt-get install -y --no-install-recommends openssl ca-certificates tini gosu curl \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=iii-image /app/iii /usr/local/bin/iii +COPY --from=builder /app/package*.json ./ +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/dist ./dist + +# Symlink so `agentmemory` works globally +RUN ln -s /opt/agentmemory/dist/cli.mjs /usr/local/bin/agentmemory \ + && chmod +x /opt/agentmemory/dist/cli.mjs + +ENV AGENTMEMORY_III_VERSION=0.11.2 \ + TINI_SUBREAPER=1 + +COPY deploy/docker-compose/entrypoint.sh /usr/local/bin/agentmemory-entrypoint.sh +RUN chmod 0755 /usr/local/bin/agentmemory-entrypoint.sh + +EXPOSE 3111 + +HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ + CMD curl -fsS http://127.0.0.1:3111/agentmemory/livez || exit 1 + +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/agentmemory-entrypoint.sh"] diff --git a/deploy/docker-compose/docker-compose.yml b/deploy/docker-compose/docker-compose.yml new file mode 100644 index 000000000..e699e4a0f --- /dev/null +++ b/deploy/docker-compose/docker-compose.yml @@ -0,0 +1,30 @@ +services: + agentmemory: + build: + context: ../.. + dockerfile: deploy/docker-compose/Dockerfile + restart: unless-stopped + env_file: + - .env + expose: + - "3111" + ports: + - "3111:3111" + - "3112:3112" + - "3113:3113" + volumes: + - agentmemory-data:/data + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:3111/agentmemory/livez || exit 1"] + interval: 30s + timeout: 5s + start_period: 30s + retries: 3 + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" + +volumes: + agentmemory-data: diff --git a/deploy/docker-compose/entrypoint.sh b/deploy/docker-compose/entrypoint.sh new file mode 100644 index 000000000..3a3cef7bf --- /dev/null +++ b/deploy/docker-compose/entrypoint.sh @@ -0,0 +1,97 @@ +#!/bin/sh +# agentmemory first-boot entrypoint. +# +# Runs as root so it can: +# 1. Overwrite the iii-config.yaml with a deploy-tuned version that +# binds 0.0.0.0 and uses absolute /data paths. +# 2. chown the platform-mounted /data volume to the runtime user +# (managed platforms mount volumes root-owned 755 by default). +# 3. Generate the HMAC secret on first boot and persist it to +# /data/.hmac (chmod 600) so the secret survives restarts. +# +# Then it execs the agentmemory CLI under gosu as the unprivileged +# `node` user. + +set -eu + +DATA_DIR="${AGENTMEMORY_DATA_DIR:-/data}" +HMAC_FILE="${AGENTMEMORY_HMAC_FILE:-/data/.hmac}" +RUN_AS="node:node" +III_CONFIG="/opt/agentmemory/dist/iii-config.yaml" + +mkdir -p "$DATA_DIR" +chown -R "$RUN_AS" "$DATA_DIR" + +cat > "$III_CONFIG" <<'EOF' +workers: + - name: iii-http + config: + port: 3111 + host: 0.0.0.0 + default_timeout: 180000 + cors: + allowed_origins: + - "http://localhost:3111" + - "http://localhost:3113" + - "http://127.0.0.1:3111" + - "http://127.0.0.1:3113" + allowed_methods: [GET, POST, PUT, DELETE, OPTIONS] + - name: iii-state + config: + adapter: + name: kv + config: + store_method: file_based + file_path: /data/state_store.db + - name: iii-queue + config: + adapter: + name: builtin + - name: iii-pubsub + config: + adapter: + name: local + - name: iii-cron + config: + adapter: + name: kv + - name: iii-stream + config: + port: 3112 + host: 0.0.0.0 + adapter: + name: kv + config: + store_method: file_based + file_path: /data/stream_store + - name: iii-observability + config: + enabled: true + service_name: agentmemory + exporter: memory + sampling_ratio: 1.0 + metrics_enabled: true + logs_enabled: true + logs_console_output: true +EOF +chown "$RUN_AS" "$III_CONFIG" + +if [ ! -s "$HMAC_FILE" ]; then + SECRET="$(openssl rand -hex 32)" + umask 077 + printf '%s\n' "$SECRET" > "$HMAC_FILE" + chmod 600 "$HMAC_FILE" + chown "$RUN_AS" "$HMAC_FILE" + echo "================================================================" + echo "agentmemory: generated HMAC secret on first boot" + echo "AGENTMEMORY_SECRET=$SECRET" + echo "Copy this value now. It will not be printed again." + echo "Stored at: $HMAC_FILE (chmod 600)" + echo "To rotate: delete $HMAC_FILE on the persistent volume and restart." + echo "================================================================" +fi + +AGENTMEMORY_SECRET="$(cat "$HMAC_FILE")" +export AGENTMEMORY_SECRET + +exec gosu "$RUN_AS" agentmemory "$@" diff --git a/src/index.ts b/src/index.ts index 5f66d76c9..198a6dc3d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -540,7 +540,7 @@ async function main() { `Ready. ${embeddingProvider ? "Triple-stream (BM25+Vector+Graph)" : "BM25+Graph"} search active.`, ); bootLog( - `REST API: 129 endpoints at http://localhost:${config.restPort}/agentmemory/*`, + `REST API: 130 endpoints at http://localhost:${config.restPort}/agentmemory/*`, ); bootLog( `MCP surface (opt-in via \`npx @agentmemory/mcp\`): ${getAllTools().length} tools · 6 resources · 3 prompts`, diff --git a/src/providers/embedding/local.ts b/src/providers/embedding/local.ts index 7646815df..f1222dad5 100644 --- a/src/providers/embedding/local.ts +++ b/src/providers/embedding/local.ts @@ -1,4 +1,5 @@ import type { EmbeddingProvider } from "../../types.js"; +import { getEnvVar } from "../../config.js"; type FeatureExtractor = ( texts: string[], @@ -7,8 +8,15 @@ type FeatureExtractor = ( export class LocalEmbeddingProvider implements EmbeddingProvider { readonly name = "local"; - readonly dimensions = 384; + readonly dimensions: number; private extractor: FeatureExtractor | null = null; + private model: string; + + constructor() { + this.model = getEnvVar("LOCAL_EMBEDDING_MODEL") || "Xenova/all-MiniLM-L6-v2"; + const dim = getEnvVar("LOCAL_EMBEDDING_DIMENSIONS"); + this.dimensions = dim ? parseInt(dim, 10) : 384; + } async embed(text: string): Promise { const [result] = await this.embedBatch([text]); @@ -39,7 +47,7 @@ export class LocalEmbeddingProvider implements EmbeddingProvider { } this.extractor = (await transformers.pipeline( "feature-extraction", - "Xenova/all-MiniLM-L6-v2", + this.model, { dtype: "q8" }, )) as FeatureExtractor; return this.extractor; From 694c7db34eb39a3d8fcee2b90ce0ad8a7cfe009a Mon Sep 17 00:00:00 2001 From: Gavinda Kinandana Date: Sat, 8 Aug 2026 23:05:11 +0700 Subject: [PATCH 2/6] Update deploy/docker-compose/entrypoint.sh Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- deploy/docker-compose/entrypoint.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/deploy/docker-compose/entrypoint.sh b/deploy/docker-compose/entrypoint.sh index 3a3cef7bf..8c5048a50 100644 --- a/deploy/docker-compose/entrypoint.sh +++ b/deploy/docker-compose/entrypoint.sh @@ -84,8 +84,6 @@ if [ ! -s "$HMAC_FILE" ]; then chown "$RUN_AS" "$HMAC_FILE" echo "================================================================" echo "agentmemory: generated HMAC secret on first boot" - echo "AGENTMEMORY_SECRET=$SECRET" - echo "Copy this value now. It will not be printed again." echo "Stored at: $HMAC_FILE (chmod 600)" echo "To rotate: delete $HMAC_FILE on the persistent volume and restart." echo "================================================================" From 8ac1ea3add524322831dbb85303e96c91d11d6ff Mon Sep 17 00:00:00 2001 From: Gavinda Kinandana Date: Sat, 8 Aug 2026 23:05:42 +0700 Subject: [PATCH 3/6] Update src/providers/embedding/local.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/providers/embedding/local.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/providers/embedding/local.ts b/src/providers/embedding/local.ts index f1222dad5..db5d4057f 100644 --- a/src/providers/embedding/local.ts +++ b/src/providers/embedding/local.ts @@ -15,7 +15,13 @@ export class LocalEmbeddingProvider implements EmbeddingProvider { constructor() { this.model = getEnvVar("LOCAL_EMBEDDING_MODEL") || "Xenova/all-MiniLM-L6-v2"; const dim = getEnvVar("LOCAL_EMBEDDING_DIMENSIONS"); - this.dimensions = dim ? parseInt(dim, 10) : 384; + const dimensions = dim ? Number(dim) : 384; + if (!Number.isSafeInteger(dimensions) || dimensions <= 0) { + throw new Error( + "LOCAL_EMBEDDING_DIMENSIONS must be a positive safe integer", + ); + } + this.dimensions = dimensions; } async embed(text: string): Promise { From e88e9e96aa16b1fcbc0978210559f4263a20888d Mon Sep 17 00:00:00 2001 From: vinbyte Date: Sat, 8 Aug 2026 23:07:21 +0700 Subject: [PATCH 4/6] fix: use npm install instead of npm ci in docker build Signed-off-by: vinbyte --- deploy/docker-compose/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/docker-compose/Dockerfile b/deploy/docker-compose/Dockerfile index 23f127363..3e6b77c36 100644 --- a/deploy/docker-compose/Dockerfile +++ b/deploy/docker-compose/Dockerfile @@ -2,7 +2,7 @@ FROM node:22-slim AS builder WORKDIR /app COPY package*.json ./ -RUN npm ci +RUN npm install COPY . . RUN npm run build RUN npm prune --production From c7a934706ed5ce114ba80031b832d0c18cc38ae8 Mon Sep 17 00:00:00 2001 From: vinbyte Date: Sat, 8 Aug 2026 23:15:00 +0700 Subject: [PATCH 5/6] fix(deploy): restore missing secret output in docker-compose entrypoint logs Signed-off-by: vinbyte --- deploy/docker-compose/entrypoint.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deploy/docker-compose/entrypoint.sh b/deploy/docker-compose/entrypoint.sh index 8c5048a50..3a3cef7bf 100644 --- a/deploy/docker-compose/entrypoint.sh +++ b/deploy/docker-compose/entrypoint.sh @@ -84,6 +84,8 @@ if [ ! -s "$HMAC_FILE" ]; then chown "$RUN_AS" "$HMAC_FILE" echo "================================================================" echo "agentmemory: generated HMAC secret on first boot" + echo "AGENTMEMORY_SECRET=$SECRET" + echo "Copy this value now. It will not be printed again." echo "Stored at: $HMAC_FILE (chmod 600)" echo "To rotate: delete $HMAC_FILE on the persistent volume and restart." echo "================================================================" From 9bc9b7fe733e659382bfa1c719eb0147e739bdf1 Mon Sep 17 00:00:00 2001 From: vinbyte Date: Sat, 8 Aug 2026 23:23:18 +0700 Subject: [PATCH 6/6] fix(deploy): bind docker compose ports to 127.0.0.1 for reverse proxy security Signed-off-by: vinbyte --- deploy/docker-compose/docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deploy/docker-compose/docker-compose.yml b/deploy/docker-compose/docker-compose.yml index e699e4a0f..dd6a0c6bd 100644 --- a/deploy/docker-compose/docker-compose.yml +++ b/deploy/docker-compose/docker-compose.yml @@ -9,9 +9,9 @@ services: expose: - "3111" ports: - - "3111:3111" - - "3112:3112" - - "3113:3113" + - "127.0.0.1:3111:3111" + - "127.0.0.1:3112:3112" + - "127.0.0.1:3113:3113" volumes: - agentmemory-data:/data healthcheck: