fix(server): harden self-hosted server — pgvector upgrade, admin auth, endpoint security#5360
Merged
Merged
Conversation
…es config - Replace ankane/pgvector:v0.5.1 (archived, unpatched for 2+ years) with pgvector/pgvector:pg17 (actively maintained, pgvector 0.8.2) - Remove exposed postgres port (8432:5432) — postgres only needs to be reachable by the mem0 service on the docker network, not the host - Require POSTGRES_PASSWORD via .env instead of hardcoding postgres/postgres The archived ankane image with default credentials and an exposed port made self-hosted deployments vulnerable to the JINX-0126 cryptomining campaign that has compromised 1,500+ PostgreSQL servers via COPY FROM PROGRAM.
Add sensible defaults to .env.example matching docker-compose config and document that POSTGRES_PASSWORD is required (docker-compose will refuse to start without it).
Restore ports 8432:5432 — developers need host access to postgres for debugging with psql/pgAdmin/DBeaver. The security risk was the hardcoded default credentials, not the port exposure itself.
Any authenticated API key holder could modify the global LLM/embedder configuration or wipe all memories. Add a require_admin dependency that composes on top of require_auth and enforces user.role == "admin", returning 403 for non-admin callers. Closes #5127
- require_admin: handle ADMIN_API_KEY and AUTH_DISABLED on fresh empty DB by falling through to allow bootstrap when no users exist yet, instead of raising 401 (Finding #1) - docker-compose healthcheck: use ${POSTGRES_USER:-postgres} instead of hardcoded -U postgres so custom POSTGRES_USER values work (Finding #2) - require_admin: restore request: Request param (needed for auth_type check) but now used in function body (Finding #7 resolved) - docs: sync migration guide password placeholder with README (Finding #9)
The migration guide previously said to start the full stack, then restore, then restart. This fails because the mem0 API runs alembic on startup, creating empty tables that conflict with the pg_dumpall restore (duplicate-key errors silently drop API keys and settings). Correct procedure: start only postgres → restore → then start mem0. Tested end-to-end: PG15/pgvector 0.5.1 → PG17/pgvector 0.8.2 with 9 memories, 1 user, 1 API key, 1 config — all preserved.
…n path When ADMIN_API_KEY authenticates on a fresh empty database, require_admin previously returned None which broke the User return-type contract. Replace with a module-level _BOOTSTRAP_ADMIN User instance (not persisted) so callers always get a real User with role="admin".
SQLAlchemy column defaults (default=_new_uuid, default=_utcnow) only fire on INSERT, not on bare __init__. The sentinel had id=None and created_at=None, which would crash any future admin endpoint that reads user.id. Use a zero-UUID and datetime.min as stable sentinels.
- DELETE /memories (bulk): require_admin — prevents non-admin from
wiping memories for arbitrary user_id/agent_id/run_id
- DELETE /entities/{type}/{id}: require_admin — cascade-deletes all
memories for an entity
- GET /requests (audit log): require_admin — exposes request metadata
for all API calls
- GET /memories (no filters): inline admin check — unfiltered listing
returns all memories across all users; filtered reads remain open
whysosaket
approved these changes
Jun 5, 2026
This was referenced Jun 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Linked Issue
Closes #5127
Summary
Security hardening for the self-hosted Mem0 server across three areas:
1. Replace archived pgvector Docker image
ankane/pgvector:v0.5.1→pgvector/pgvector:pg17(PostgreSQL 17.10, pgvector 0.8.2)POSTGRES_PASSWORDvia${POSTGRES_PASSWORD:?...}— compose fails fast if unset${POSTGRES_USER:-postgres}instead of hardcoded-U postgresserver/README.mdanddocs/migration/server-pgvector-upgrade.mdx2. Require admin role on config and reset endpoints (#5127)
POST /configureandPOST /resetnow userequire_admin— non-admin callers get 403require_adminFastAPI dependency inserver/auth.pythat composes onverify_authand enforcesuser.role == "admin"ADMIN_API_KEYbootstrap works on fresh empty DB via a_BOOTSTRAP_ADMINsentinel (stable UUID, never persisted)GET /configure,GET /configure/providers) remain open to all authenticated users3. Harden destructive and sensitive endpoints
DELETE /memories(bulk delete):require_admin— prevents non-admin from wiping memories for any user_id/agent_id/run_idDELETE /entities/{type}/{id}:require_admin— cascade-deletes all memories for an entityGET /requests(audit log):require_admin— full request log is admin-only informationGET /memories(no filters): inline admin check — unfiltered listing returns all memories across all users; filtered reads remain open to any authenticated callerTesting
Admin auth (10/10 edge cases pass):
POST /configure= 403POST /reset= 403POST /configure= 200POST /reset= 200POST /configure= 200GET /configure(read-only) = 200POST /configure= 401POST /configure= 401ADMIN_API_KEYon empty DB →POST /configure= 200 (bootstrap)pgvector migration (tested end-to-end):
main(ankane/pgvector:v0.5.1, PG 15.4, pgvector 0.5.1)pg_dumpallbackup (185KB)Breaking Changes
POST /configureandPOST /resetnow require admin role: API keys from non-admin users get 403. Admin JWT andADMIN_API_KEYstill work.DELETE /memories(bulk) andDELETE /entities/{type}/{id}now require admin role: Same as above.GET /requests(audit log) now requires admin role: Non-admin users can no longer read the request log.GET /memories(unfiltered) now requires admin role: Listing all memories across all users requires admin. Filtered reads by user_id/agent_id/run_id remain open.POSTGRES_PASSWORDnow required:docker compose upfails if unset in.env.ankane/pgvectorvolumes need to follow the migration guide (pg_dumpall→ restore).Type of Change
Test Coverage
Checklist