Skip to content

fix(memos-local-plugin): per-agent legacy migration path + per-agent auth cookie#1548

Merged
hijzy merged 1 commit into
MemTensor:mem-agent-0424from
hijzy:feat/multi-agent-cookie-isolation-and-legacy-migration
Apr 27, 2026
Merged

fix(memos-local-plugin): per-agent legacy migration path + per-agent auth cookie#1548
hijzy merged 1 commit into
MemTensor:mem-agent-0424from
hijzy:feat/multi-agent-cookie-isolation-and-legacy-migration

Conversation

@hijzy
Copy link
Copy Markdown
Collaborator

@hijzy hijzy commented Apr 27, 2026

Summary

Two viewer-side bugs surface as soon as openclaw and hermes are installed side by side on the same host. Both are fixed in this PR.

1. Legacy DB migration always read openclaw's path

server/routes/migrate.ts hard-coded the source DB to ~/.openclaw/memos-local/memos.db, so triggering "import from legacy plugin" inside the hermes viewer happily imported openclaw's old memories. The hermes legacy plugin actually lived at ~/.hermes/memos-state/memos-local/memos.db (note the extra memos-state segment).

  • The migration now resolves the legacy path from options.agent:
    • openclaw~/.openclaw/memos-local/memos.db
    • hermes~/.hermes/memos-state/memos-local/memos.db
  • New generic endpoints GET/POST /api/v1/migrate/legacy/{scan,run} for the viewer to call (the running agent picks its own path).
  • Explicit /openclaw/* and /hermes/* aliases kept for clarity + back-compat.
  • Response now carries agent and path so the UI shows exactly which file was read.
  • New server.migrate log channel (registered in core/logger/channels.ts and docs/LOGGING.md).

2. Refreshing one viewer logged out the other

Both servers issued the cookie name memos_sess with Path=/. Browsers do not isolate cookies by port, so:

  1. Login to openclaw → memos_sess=tokenA (signed with openclaw's sessionSecret).
  2. Login to hermes → memos_sess=tokenB (signed with hermes's sessionSecret). The second one overwrites the first.
  3. Refresh openclaw → browser sends tokenB → openclaw verifies with its own secret → MAC fails → AuthGate boots the user back to the LoginScreen.

The fix scopes the cookie name per agent (memos_sess_<agent>):

  • registerAuthRoutes(routes, deps, options) and requireSession(..., agent) now know which agent they serve.
  • cookieNameFor(agent) returns memos_sess_<agent> when an agent is configured, otherwise the legacy memos_sess (so single-agent installs and test fixtures keep working).
  • readSessionCookie falls back to the legacy name on read, so users who were already logged in before the upgrade are not kicked out by the deploy itself; the next response writes the new per-agent cookie and the transition is silent.
  • clearSessionCookie clears both the per-agent name and the legacy name on logout.

Test plan

  • npx vitest run tests/unit/server — 56 of 56 pre-existing tests still pass; 12 new tests pass:
    • 5 new migrate-route assertions in tests/unit/server/http.test.ts covering openclaw/hermes/legacy endpoints + the agent field + the path string.
    • 5 new cases in tests/unit/server/auth-cookie-isolation.test.ts:
      • per-agent cookie naming (no memos_sess, only memos_sess_<agent>)
      • end-to-end regression: a single browser jar holding both cookies → both viewers report authenticated: true (the original bug)
      • cross-agent cookie rejection (hermes refuses an openclaw cookie)
      • smooth upgrade: legacy memos_sess cookie is still honoured
      • 401 gating without a valid cookie
  • npm run lint is clean on every file touched in this PR (the repo has unrelated pre-existing TS errors that this PR does not introduce).
  • Manual smoke: install both agents, log into each, F5 refresh either viewer — the other no longer drops to LoginScreen. Cookies tab shows memos_sess_openclaw and memos_sess_hermes coexisting.
  • Manual smoke: in the hermes viewer, Import → "Scan legacy DB" reports the ~/.hermes/memos-state/memos-local/memos.db path (not .openclaw/...).

Notes

  • The 5 pre-existing test failures on mem-agent-0424 (countEpisodes/countTraces/countSkills missing from the test stub) are not introduced by this PR — they reproduce on the unmodified target branch.

…auth cookie

Two viewer-side bugs surfaced when openclaw and hermes are installed
side by side on the same machine:

1. **Legacy DB migration always read openclaw's path.**
   The "import from legacy plugin" feature hard-coded the source DB
   to `~/.openclaw/memos-local/memos.db`, so running the migration
   inside the hermes viewer imported openclaw's old memories. Make
   the path agent-aware (`hermes` → `~/.hermes/memos-state/memos-local/memos.db`),
   add a generic `/api/v1/migrate/legacy/{scan,run}` endpoint that
   uses `options.agent`, and keep explicit `/openclaw/*` and
   `/hermes/*` aliases for clarity. Response now carries `agent` +
   `path` so the viewer / tests can verify which DB was read.

2. **Viewer logged out the other agent on refresh.**
   Both servers issued the cookie name `memos_sess` with `Path=/`.
   Browsers do not isolate cookies by port, so logging into one
   agent overwrote the other's cookie; the next refresh failed MAC
   verification and the AuthGate kicked the user back to the login
   screen. Scope the cookie name per agent (`memos_sess_<agent>`),
   plumb `options.agent` through `registerAuthRoutes` and
   `requireSession`, and keep a legacy-name fallback on read so
   already-logged-in users aren't kicked out by the upgrade itself.

Tests:
- `tests/unit/server/http.test.ts` covers the openclaw/hermes/legacy
  migrate endpoints + the agent-aware path resolution.
- New `tests/unit/server/auth-cookie-isolation.test.ts` reproduces
  the original "refresh-one-logs-out-the-other" scenario end-to-end
  and pins per-agent isolation, cross-agent rejection, and legacy
  cookie fallback.
@hijzy hijzy merged commit eb2cfa9 into MemTensor:mem-agent-0424 Apr 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant