Skip to content

fix: enforce per-user session isolation in session_search + hindsight platform gate#12571

Closed
Cyrene963 wants to merge 1 commit into
NousResearch:mainfrom
Cyrene963:fix/multi-user-session-isolation
Closed

fix: enforce per-user session isolation in session_search + hindsight platform gate#12571
Cyrene963 wants to merge 1 commit into
NousResearch:mainfrom
Cyrene963:fix/multi-user-session-isolation

Conversation

@Cyrene963

Copy link
Copy Markdown

Problem

Issue 1: session_search leaks data across users

In multi-user gateway deployments, session_search searches the entire SQLite session database without user filtering. User A can see User B's conversation history.

Root cause: search_messages() and list_sessions_rich() have no user_id parameter. The session_search tool calls these without user scoping.

Issue 2: Hindsight retains messages from platforms without user isolation

The iLink WeChat bot adapter reports from_user_id = bot's own ID for all messages. Different users are indistinguishable. Hindsight stores them all in one memory bank, causing cross-contamination.

Fix

hermes_state.py

  • Added user_id: str = None parameter to search_messages() and list_sessions_rich()
  • When user_id is provided, adds WHERE s.user_id = ? to filter results
  • When user_id is None (default), behavior is unchanged (backward compatible)

tools/session_search_tool.py

  • Thread user_id parameter through session_search() and _list_recent_sessions()
  • Pass to db.search_messages() and db.list_sessions_rich()

run_agent.py

  • Pass user_id=self._user_id in both session_search call sites (~line 7408, ~line 7889)

plugins/memory/hindsight/__init__.py

  • Store self._platform from init kwargs
  • Skip sync_turn for platforms in _retain_disabled_platforms (default: ('weixin',))

Verification

-- Before: searches ALL sessions
SELECT ... WHERE messages_fts MATCH 'query'

-- After: only searches current user's sessions  
SELECT ... WHERE messages_fts MATCH 'query' AND s.user_id = ?

Tested with 2 Telegram users + 1 WeChat user:

  • User A searching "duolingo" → only User A's results ✅
  • User B searching "duolingo" → only User B's results ✅

Backward Compatibility

  • user_id=None (default) → identical to before
  • CLI single-user mode: self._user_id is typically None, no change
  • Gateway multi-user mode: each agent has self._user_id, filtering activates

Diff Stats

4 files changed, 27 insertions(+), 3 deletions(-)

… platform gate

PR 1: session_search multi-user isolation
- Add user_id filter to search_messages() and list_sessions_rich() in hermes_state.py
- Thread user_id parameter through session_search_tool.py
- Pass self._user_id from run_agent.py call sites
- Prevents cross-user data leakage in multi-user gateway deployments

PR 2: Hindsight platform retain gate
- Store self._platform in Hindsight __init__
- Skip memory retention for platforms without user isolation (weixin)
- iLink API reports bot's own user_id for all messages, making isolation impossible

Both fixes are backward-compatible: user_id=None preserves original behavior.
@Cyrene963

Copy link
Copy Markdown
Author

Why disable WeChat Hindsight instead of full multi-user isolation?

Root Cause

The iLink Bot API (ilink/bot/getupdates) returns from_user_id = bot's own OpenID for ALL inbound personal-chat messages. No field in the message payload identifies the actual human sender.

Verified fields from iLink API:

Field Value Distinguishes users?
from_user_id Bot's own ID ❌ Same for all
to_user_id Bot's own ID
context_token Shared across users ❌ Gets overwritten
message_id Unique per message

We inspected the adapter code (gateway/platforms/weixin.py:1301):

sender_id = str(message.get("from_user_id") or "").strip()
# sender_id is ALWAYS the bot's own ID → bank_id is always the same

Why not fix the adapter?

Without a real user identifier from the API, there is no reliable way to distinguish between different WeChat users. The context_token is also shared and gets overwritten when multiple users message the bot simultaneously.

What would a real fix require?

  1. iLink API update — Add real sender OpenID to message payload
  2. Alternative API — Use WeChat Official Account API (provides real FromUserName)
  3. Message fingerprinting — Unreliable heuristic approach (not recommended)

This PR's approach

  • Session search: Added user_id SQL filter (true multi-user isolation)
  • Hindsight WeChat gate: Disabled retain for weixin platform to prevent cross-contamination
  • The Telegram side already has full multi-user isolation (separate banks per user)

The WeChat Hindsight disable is a safety measure, not a design choice. It prevents the iLink API limitation from corrupting memory banks until the upstream API provides real user identification.

@alt-glitch alt-glitch added type/security Security vulnerability or hardening P1 High — major feature broken, no workaround comp/agent Core agent loop, run_agent.py, prompt builder comp/plugins Plugin system and bundled plugins tool/memory Memory tool and memory providers labels Apr 23, 2026
@Cyrene963

Copy link
Copy Markdown
Author

This PR has been superseded by #17989, which includes all the fixes from this PR plus per-user memory directory isolation (MemoryStore per-user USER.md/MEMORY.md). Please review #17989 instead.

@Cyrene963 Cyrene963 closed this Apr 30, 2026
Cyrene963 pushed a commit to Cyrene963/hermes-agent that referenced this pull request May 7, 2026
- Add user_id parameter to MemoryStore and get_memory_dir() for per-user USER.md/MEMORY.md
- Pass user_id from AIAgent to MemoryStore during initialization
- Per-user memory directories: memories/{chat_id}/USER.md, memories/{chat_id}/MEMORY.md
- Adds session_search user_id filtering (from PR NousResearch#12571)
- Adds Hindsight per-platform retain gate (from PR NousResearch#12571)

Fixes cross-user data leakage where User A's profile and memory were
injected into User B's system prompt in multi-user gateway deployments.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent loop, run_agent.py, prompt builder comp/plugins Plugin system and bundled plugins P1 High — major feature broken, no workaround tool/memory Memory tool and memory providers type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants