v0.2.27

moorcheh-ai/memantov0.2.27Oct 7, 2026by het0814

Upgrading?

AI Summary

Adds Go SDK and Pydantic AI integration, implements comprehensive security hardening (CORS, SSRF, DNS rebinding), and introduces new deployment settings.

Key Highlights

  • New Go SDK covering the full REST surface and auto-starting local server.
  • Pydantic AI integration with automatic recall.
  • Security hardening: CORS defaults to localhost, DNS rebinding protection, backend credentials secured.
  • New deployment settings for API docs and secure mode.

Breaking Changes

  • ALLOWED_ORIGINS no longer defaults to ["*"], only local origins are allowed.
  • API docs (/docs, /redoc) disabled by default.
  • Backend credentials (X-Api-Key) removed from caller control; server config is always used.

New Features

  • Go SDK (sdks/go).
  • Pydantic AI integration.
  • Deployment settings (MEMANTO_ENABLE_DOCS, MEMANTO_REQUIRE_SECURE, MEMANTO_PROXY_ALLOWED_IPS).
  • Agent creation rollback on namespace failure.
  • Migration tag handling improvements.
  • Plain-HTTP exposure warning.

Full Release Notes

# Release Notes for v0.2.27

This release adds two new ways to use Memanto: a **Go SDK** and a **Pydantic AI integration**. Most of the remaining changes are security fixes from bounty #1852. The local server is now harder to reach from browsers and the network: it enforces CORS/Origin/Host checks, rejects DNS-rebinding requests, keeps API docs off by default and can refuse plain-HTTP traffic, including behind a TLS-terminating proxy. Backend credentials can no longer be chosen by the caller. Migration and OKF import paths are protected against SSRF and arbitrary file reads. Memory content is protected against prompt injection, secret leakage and Unicode title forgery.

## New Features
- **Go SDK** (`sdks/go/`)
  - New Go client: `go get github.com/moorcheh-ai/memanto/sdks/go` (Go 1.24+).
  - Covers the REST surface: `Remember`, `BatchRemember`, `ExtractMemories`, `DeleteMemory`, `UploadFile`, `Recall`, `RecallAsOf`, `RecallChangedSince`, `RecallRecent`, `Answer`, `DailySummary`, conflict generate/list/resolve, agent CRUD, `Deactivate` and `Status`.
  - Point it at a server with `BaseURL`. If `BaseURL` is left empty, it starts `uvx memanto serve` on a free port when first used, the same way the TypeScript SDK does. If `uvx` is missing, it prints an install hint for the current platform.
  - Sessions are handled for you: the client activates on first use, renews when a session goes stale and can create the agent if it doesn't exist. Failed calls return `nil` results along with a typed `*APIError`.
  - Request/response types are generated from the OpenAPI schema (`sdks/go/api/types.gen.go`, via `oapi-codegen`).
  - Each Memanto release now automatically tags the Go module as `sdks/go/vX.Y.Z`.
- **Pydantic AI integration** (`integrations/pydantic-ai/`)
  - New package `memanto-pydantic-ai` (requires `memanto>=0.2.21` and `pydantic-ai-slim>=2.0.0`).
  - `create_memanto_tools()` adds three tools: `memanto_remember`, `memanto_recall` and `memanto_answer`.
  - `memory_instructions()` adds automatic recall: before each run it fetches the memories most relevant to the user's prompt and injects them into the agent's instructions. It recalls once per run and caches the result. Only active, non-expired memories are injected.
  - A failed automatic recall logs a warning and the run continues without memories. The explicit tools still raise errors, so failures stay visible.
  - Tool limits are part of the tool schemas (13 memory types, confidence 0.0–1.0, title ≤100 chars, recall limit 1–100). An invalid call is sent back to the model as a retry prompt instead of failing the run.
  - `memanto_recall` labels expired memories `(expired)` and shows when each memory was saved.
  - `MemantoSetup` handles the agent lifecycle (`setup` / `teardown`). If the client has no active session for the `agent_id`, the integration raises `ValueError` right away.
- **New deployment settings** (`memanto/app/config.py`, `memanto/app/middleware.py`)
  - `MEMANTO_ENABLE_DOCS` (default `False`): turns on `/docs`, `/redoc` and `/openapi.json`.
  - `MEMANTO_REQUIRE_SECURE`: rejects plain-HTTP requests with a 403. Only direct loopback callers are exempt; a request forwarded by a proxy is checked even when the proxy itself runs on loopback.
  - `MEMANTO_PROXY_ALLOWED_IPS`: an allowlist of reverse proxies whose `X-Forwarded-Proto` header is trusted, so the session cookie is marked `Secure` when TLS ends at the proxy. From any other peer the header is ignored. IPv4-mapped peer addresses are normalized before matching.
  - When `MEMANTO_PROXY_ALLOWED_IPS` is set, `memanto serve` and `memanto ui` turn off uvicorn's own forwarded-header handling so Memanto sees the proxy's real IP. If you launch uvicorn directly, pass `--no-proxy-headers`.
  - `CORS_ORIGIN_REGEX`: defaults to any `http://localhost` or `http://127.0.0.1` port.

## Improvements
- **Plain-HTTP exposure warning** (`memanto/cli/commands/core.py`)
  - `memanto serve` and `memanto ui` warn when bound to a non-loopback address over plain HTTP. They only print the API docs URL when docs are enabled.
- **On-prem `DirectClient` without an API key** (`memanto/cli/client/direct_client.py`)
  - `DirectClient` no longer needs an API key in on-prem mode. In cloud mode, it falls back to `MOORCHEH_API_KEY` from settings when no key is passed.
  - The daily summary and conflict routes now get the API key through the `get_moorcheh_api_key` dependency.
- **Agent creation rollback** (`memanto/app/services/agent_service.py`)
  - If saving agent metadata fails after a new Moorcheh namespace was created, that namespace is now deleted instead of being left orphaned.
  - A namespace that already existed (memories kept from an earlier `agent delete --keep-memories`) is never deleted, so recreating the agent still brings those memories back.
- **Migration tag handling** (`memanto/cli/migrate/mappers.py`)
  - Imported tags that can't be stored (over-length, containing a comma, line break or control character, or past the 20-tag cap) go to the supporting-data footer unchanged instead of failing the whole batch.

## Bug Fixes
- **Comma-separated list settings crashed every command** (`memanto/app/config.py`)
  - `ALLOWED_ORIGINS=https://a.com,https://b.com`, the format shown in the deployment docs, failed at settings load with a `SettingsError`. Because settings load on import, every `memanto` command failed, not just the server.
  - `ALLOWED_ORIGINS` and `MEMANTO_PROXY_ALLOWED_IPS` now accept comma-separated values or a JSON list, from the environment or `.env`.
  - `pydantic-settings` minimum raised to `>=2.7.0`.

## Security
- **CORS and Origin hardening** (`memanto/app/config.py`, `memanto/app/routes/auth_deps.py`, `memanto/app/ui/routes/ui_router.py`)
  - `ALLOWED_ORIGINS` no longer defaults to `["*"]`. Only local origins are allowed by default.
  - Management and UI endpoints reject browser `Origin` headers that aren't on the allowlist. Before this, any web page could read admin responses (session tokens, memories) from `127.0.0.1` via DNS rebinding or a localhost XSS.
- **DNS-rebinding protection on loopback trust** (`memanto/app/routes/auth_deps.py`, `memanto/app/ui/routes/ui_router.py`)
  - Cookie sessions and UI management routes (`/api/ui/browse`, `/config`, `/shutdown`, `/daily-summary`, `/sessions/{id}`, `/conflicts`, …) now require a loopback `Host` header, not just a loopback TCP peer.
  - Loopback trust is also dropped when a request carries a non-loopback `X-Forwarded-For`, `X-Real-IP` or `Forwarded` header.
  - Header-based API clients (`X-Session-Token`) can still be called from remote peers.
- **Backend credentials no longer chosen by the caller** (`memanto/app/clients/moorcheh.py`, `memanto/app/services/agent_service.py`)
  - `get_moorcheh_client()` / `get_async_moorcheh_client()` no longer read the `X-Api-Key` header. Before this, any session holder could run server-side memory and LLM operations under Moorcheh credentials of their choice.
  - The `api_key` parameter was removed completely so it can't come back as a `?api_key=` query override. The server-configured `MOORCHEH_API_KEY` is always used.
  - `X-Api-Key` still works as a management credential.
- **API docs disabled by default** (`memanto/app/main.py`)
  - `/docs`, `/redoc` and `/openapi.json` are now off unless `MEMANTO_ENABLE_DOCS=true`. Previously, a server on the default `0.0.0.0` bind listed every route to any network peer.
- **SSRF and file-read fixes in migration endpoints** (`memanto/cli/analyze/langfuse_export.py`, `memanto/app/ui/routes/ui_router.py`)
  - Langfuse hosts must use `https://`. Cleartext `http://` is rejected.
  - Official cloud regions are allowed as-is. A self-hosted host must resolve to a public IP; private, loopback, link-local, metadata and CGNAT (`100.64.0.0/10`) addresses are blocked.
  - The validated IP is pinned at connect time through `httpcore`, so a DNS rebind can't redirect the request (or the Basic auth header) to an internal address.
  - The migrate `file` parameter is confined to the provider's migrate directory. Before this, any `.md`/JSON file on the server could be parsed and echoed back in dry-run or discover responses.
- **OKF import/export hardening** (`memanto/cli/migrate/okf_loader.py`, `memanto/app/services/okf_export_service.py`, `memanto/app/utils/validation.py`)
  - OKF imports stay inside the selected bundle: they reject symlinks and non-regular files and close path-swap races with `O_NOFOLLOW` / directory-fd reads where the platform supports them. Other platforms get a best-effort fallback.
  - `<!-- okf-entry -->` delimiters inside memory content are now escaped on export and unescaped on import (reversibly), so content can't forge extra entries.
  - Export `output_path` now defaults to the active backend's data dir. It can't target the data dir itself or reserved internal paths (`agents/`, `sessions/`, `tokens/`, `config.json`, `secret_key`, `.env`).
- **Unicode title injection** (`memanto/app/core.py`, `memanto/app/services/session_service.py`)
  - Memory titles now fold every line-break code point (U+2028, U+2029, VT, FF, NEL), not just `\n` and `\r`.
  - Before this, a title could close its Markdown heading and forge standalone entries, including `[INSTRUCTION]` entries, in session summaries that are later passed to an LLM.
  - Session summaries also pass interpolated fields through a single-line helper.
- **Tag validation** (`memanto/app/core.py`)
  - Tags can no longer contain a comma (tags are stored comma-joined), a line break or another control character. Those are rejected with `422`.
  - Structured tags such as `project:alpha` and the LangGraph store's `lg:key:<key>` still work.
- **Prompt-injection and secret-leak protection in memory extraction** (`memanto/app/services/conversation_memory_extraction_service.py`)
  - Conversation content is wrapped in a `<conversation_content>` block, and closing tags inside it are escaped. The prompt tells the model to treat that content as untrusted data.
  - Extracted candidates whose title or content matches directive or override patterns (e.g. "ignore previous instructions", "exfiltrate") are dropped.
  - API keys (OpenAI/Anthropic, GitHub, AWS, Slack, Google, JWTs), private keys, Bearer tokens, URL passwords and `key=value` credentials are redacted before memories are saved.
- **Secret redaction in error responses** (`memanto/app/utils/errors.py`, `memanto/app/routes/memory.py`, `memanto/app/ui/routes/ui_router.py`)
  - Error details on memory, policy-preset and UI routes go through the new `redact_sensitive_text()`, including quoted and JSON secret assignments.
  - Request URLs are sanitized before logging, so CR/LF can't forge log lines (CWE-117).
- **Recall query filter injection** (`memanto/app/services/memory_read_service.py`)
  - A leading `#` on a word in the recall query is now stripped. Before this, a query like `#status:expired` was sent to Moorcheh as a server-side filter. Terms like `C#` are kept.
- **`memanto connect` stays inside the project root** (`memanto/cli/connect/engine.py`)
  - When installing or removing locally, instructions, skills, extensions, hooks and permission files are only written or deleted if they resolve inside the project root. Paths that escape it raise an error.
- **UI hardening** (`memanto/app/ui/static/index.html`)
  - CDN scripts (`chart.js`, `marked`, `dompurify`) now load with Subresource Integrity hashes.
  - Connected project paths are passed through `data-*` attributes instead of inline `onclick` handlers.

## Tests
- `tests/test_trusted_proxy_middleware.py`: new tests for forwarded-proto trust, `MEMANTO_REQUIRE_SECURE` with direct, proxied and untrusted peers, and the serve proxy-headers switch.
- `tests/test_error_redaction.py`: new tests for secret redaction in error responses, including quoted assignments.
- `tests/test_title_newline_roundtrip.py`: new tests that all seven line-break code points are folded in titles.
- `tests/test_delete_agent_memories.py`: tests that recreating an agent reuses its kept namespace and that a failed save never deletes a reused namespace.
- `tests/test_ui_auth.py`, `tests/test_remaining_ui_auth.py`: tests that DNS-rebinding Host headers and forwarded non-loopback headers are rejected.
- `tests/test_conversation_memory_extraction.py`: tests for secret redaction, the prompt-injection candidate filter and conversation tag escaping.
- `tests/test_langfuse_export.py`: tests for the Langfuse host checks and SSRF guard.
- `tests/test_okf.py`: tests for symlink and out-of-bundle import rejection, delimiter escaping and export path guards.
- `tests/test_unit.py`, `tests/test_backend.py`: tests for accepted and rejected tag characters, comma/JSON list settings, recall query `#` sanitization, on-prem `DirectClient` and that the client wrapper never forwards an `api_key`.
- `tests/test_migrate_zep_hindsight.py`: tests that unstorable tags, including ones with line breaks, move to the footer.
- `integrations/pydantic-ai/tests/test_tools.py`: tool, automatic recall, retry and setup tests against a mocked `SdkClient`.
- `sdks/go/memanto_test.go`, `sdks/go/server_test.go`: Go client and auto-spawned server tests.

## Full Changelog
Full Changelog: https://github.com/moorcheh-ai/memanto/compare/v0.2.26...v0.2.27

Related Repositories