feat(memory): persistent default user + session user-tagging foundation#1224
Open
pbranchu wants to merge 2 commits into
Open
feat(memory): persistent default user + session user-tagging foundation#1224pbranchu wants to merge 2 commits into
pbranchu wants to merge 2 commits into
Conversation
Lays the groundwork for per-user memory by giving every install a stable default-user UUID and tagging every session with an owning user. Sessions are now consistently user-scoped: - `Session::user_id: UserId` (required, not Option) — defaults to the kernel's persistent default user - `Session::parent_session_id: Option<SessionId>` — foundation for future tree-scoped cascade deletion of forked sessions (no producer yet) - `MessageSource` enum + optional `Message::source` — additive type that later PRs (structured extraction filtering) will read; no consumer here - `UserConfig::is_default: bool` — `[[users]]` blocks can attach display name and channel bindings to the persistent default identity Kernel boots the default user once and caches it process-wide: - `bootstrap_default_user` — load-or-generate the UUID from `kv_store[shared, "default_user_uuid"]`, install via `set_default_user_id`, then run a one-shot rewrite of legacy nil-UUID sessions, gated by the `default_user_bootstrap_done` sentinel - `resolve_user_id` (strict, HTTP boundary) — folds the deprecated "test" alias and the nil UUID to the default user with `warn!` logs so reserved-bucket abuse is auditable - `resolve_user_id_internal` (raw mapper) — preserves the pre-fix behaviour for in-process test callers - `AuthManager::new_with_default` — binds the `is_default = true` user (or the first user) to the persistent UUID Storage and migration: - Schema v9 adds `user_id` (NOT NULL, default nil UUID) and `parent_session_id` (nullable) to `sessions`, plus `(agent_id, user_id)` and `parent_session_id` indexes - `MemorySubstrate::rewrite_nil_user_sessions` — atomic transaction wrapping the legacy-bucket UPDATE; the kernel only sets the bootstrap-done sentinel after a clean rewrite, so a failure leaves the retry path intact Single-user installs see no behaviour change: everyone is the default user, one session per agent, same as today. Tests: - `MessageSource` deserialises cleanly from pre-field payloads (JSON + msgpack) and survives full round-trips - `UserConfig::is_default` defaults to `false` for existing configs - `AuthManager::new_with_default` honours `is_default`, falls back to first user, and is a no-op for empty configs - Migration v9 adds the columns and indexes, and a v8-built DB upgrades cleanly to v9 with pre-existing rows preserved - `default_user_id`/`test_user_id` are distinct - `create_session(agent, user)` round-trips through SQLite — both the default and an explicit user - `rewrite_nil_user_sessions` is idempotent, targeted, and atomic - Kernel: default-user UUID persists across kernel restarts; the strict filter folds `"test"` and nil to default while passing other UUIDs through; the internal mapper preserves the raw `"test"` alias Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cargo update bumps lettre from 0.11.21 to 0.11.22 to clear RUSTSEC-2026-0141. Pulls in transitive dependency updates as a side effect (mostly windows-sys/socket2 version consolidation) — no API surface change in our own code. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This was referenced Jun 3, 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.
Summary
Sessions become consistently user-scoped. First boot generates a persistent default user UUID; every session is now tagged with the user it belongs to. Foundation for future per-user features (memory, isolation, audit, etc.).
Standalone improvement — no behavior change for single-user installs (everyone is the default user, one session per agent, same as today). The data model just becomes honest about which user owns what.
This is PR 1 of a 4-PR memory series. Each PR is reviewable on its own.
What this PR adds
MessageSourceenum + additiveMessage::sourcefield (foundation; no consumer in PR 1 — PR 2 consumes it for extraction filtering)Session::user_id: UserId(required, defaults to persistent default user) andSession::parent_session_id: Option<SessionId>user_id(NOT NULL, default nil-UUID) +parent_session_idcolumns + indexesUserConfig::is_default: bool— let[[users]]blocks attach metadata to the default userAuthManager::new_with_default(configs, Option<UserId>)— respectsis_defaultor first-user-winsbootstrap_default_user()— load/generate UUID from kv_store, install viaset_default_user_id, run nil-UUID rewrite if not yet done (sentinel-guarded for exactly-once semantics)resolve_user_id(Option<&str>) -> UserId— public HTTP-boundary version that folds"test"and nil-UUID to default withwarn!resolve_user_id_internal(...)— raw mapper for test code onlyMemorySubstrate::rewrite_nil_user_sessions(target)— atomic transaction wrapping the sessions UPDATEWhy nil UUID is rejected
The HTTP-boundary
resolve_user_idfolds nil UUID (00000000-0000-0000-0000-000000000000) to the persistent default user. Without this, an API-key holder could write sessions attributed to the nil bucket — which would then be unreachable via future per-user controls (PR 2'sparse_user_idrejects nil at the HTTP layer). Closes the back door at the kernel layer too so all call paths agree.resolve_user_id_internalkeeps the raw mapping for test-harness code that needs the test-user bucket directly.Compatibility
user_id = nil-UUID, then the kernel bootstrap rewrites them to the persistent default user UUID on first boot after upgrade.user_id(the default user), so lookups still return one session per agent.SessionStore::create_session(agent_id)→create_session(agent_id, user_id)is the only signature change. All in-tree callers updated.Test plan
cargo fmt --checkcleancargo clippy --workspace --tests --all-targets -- -D warningscleancargo build --workspacecleancargo test -p openfang-types --lib393/393 passcargo test -p openfang-memory --lib50/50 pass (incl. new v9 migration, v8→v9 upgrade, atomic rewrite_nil_user_sessions, default/test user distinct, no collision withshared_memory_agent_id)cargo test -p openfang-kernel --lib296/298 (only the pre-existingtest_referenced_providers_*failures, unrelated)cargo test -p openfang-api --lib92/92 passcargo test -p openfang-runtime --lib939/939 pass (skipping process_manager/subprocess_sandbox)Reviewed independently
This PR was carved from a working
branchudeployment and reviewed by an independent agent across two rounds. The agent flagged 1 blocker and 5 issues — all addressed with traceable commits before submission.