9.4 KiB
Memory Gateway Package and User Provisioning Design
Goal
Reorganize Beaver's Memory Gateway code under the beaver.memory domain and
replace the single fixed Gateway identity with per-Beaver-user credentials.
The final model has two independent configuration layers:
- One shared, non-secret Memory Gateway configuration used by every Beaver instance.
- One per-instance credential file containing the Gateway identities created for Beaver frontend users.
Curated memory remains enabled and isolated. Gateway failures or missing user
credentials must not modify MEMORY.md, USER.md, or the memory tool.
Source Package
All Beaver-side Gateway source moves to:
app-instance/backend/beaver/memory/gateway/
├── __init__.py
├── config.py
├── client.py
├── credentials.py
└── service.py
config.pyowns the shared typed Gateway configuration.client.pyownsMemoryGatewayClientand sanitized client exceptions.credentials.pyowns typed user credentials and atomic credential-file persistence.service.pyowns search/add/flush orchestration and result types.__init__.pyexposes the supported public Gateway API.
Remove the old source locations:
beaver/integrations/memory_gateway/beaver/services/memory_gateway_service.py- Gateway configuration dataclasses in
beaver.foundation.config.schema - The lazy
MemoryGatewayServiceexport frombeaver.services
No compatibility forwarding modules are retained. After migration,
beaver.memory.gateway is the only supported source entry point.
Shared Configuration
All Beaver instances read the same public Gateway configuration from:
/home/tom/beaver_project/app-instance/backend/memory/config.json
Inside the app-instance image this is available as:
/opt/app/backend/memory/config.json
The file contains no user credentials:
{
"memory": {
"mode": "hybrid",
"gateway": {
"baseUrl": "http://172.19.207.37:8010",
"appId": "default",
"projectId": "default",
"scope": ["current_chat", "resources", "all_user_memory"],
"topK": 8,
"timeoutSeconds": 10
}
}
}
Rules:
- Valid modes remain
curatedandhybrid. - Curated memory is always initialized.
hybridenables Gateway only for runs with a resolved user credential.baseUrlis fixed tohttp://172.19.207.37:8010in the initial shared configuration.- Scope includes
current_chat,resources, andall_user_memory. - The shared file is the authoritative Memory Gateway configuration. Instance
config.jsonfiles continue to own providers, tools, channels, AuthZ, and backend identity, but no longer carry Gateway user credentials. - An optional
BEAVER_MEMORY_CONFIG_PATHmay override the shared file path for tests or non-image development runs.
Per-Instance User Credentials
Each Beaver instance stores Gateway user credentials alongside its existing
config.json, runtime.env, and web_auth_users.json:
app-instance/runtime/instances/<instance-slug>/beaver-home/
├── config.json
├── runtime.env
├── web_auth_users.json
└── memory_gateway_users.json
The existing beaver-home mount exposes the file inside the container as:
/root/.beaver/memory_gateway_users.json
The JSON format is:
{
"users": {
"tom": {
"userId": "tom",
"userKey": "uk_xxx"
}
}
}
Rules:
- The map key is the authenticated Beaver login username.
- Gateway
userIdis exactly the Beaver login username, with no prefix. userKeyis secret and must never appear in API responses, logs, audit events, exceptions, or tracked configuration.- Writes use a sibling temporary file followed by atomic replace.
- The credential file is created with mode
0600. BEAVER_MEMORY_GATEWAY_USERS_PATHmay override the default path for tests.
Frontend User Provisioning
The frontend continues to call Beaver's existing POST /api/auth/register
endpoint. The browser never calls Memory Gateway directly and never receives
the Gateway userKey.
For a registration request with username tom, Beaver performs:
POST http://172.19.207.37:8010/users
Content-Type: application/json
{"user_id":"tom"}
Beaver validates that the response contains non-empty user_id and
user_key, requires the returned user_id to equal tom, and stores the
credential under the tom entry in memory_gateway_users.json.
The Gateway /users API is treated as idempotent. Registering an existing
Beaver username may refresh the same credential entry without creating a
second local identity.
For this first version:
- Gateway provisioning has no Beaver-side retries.
- A Gateway provisioning failure does not roll back an otherwise valid Beaver registration.
- A user without stored Gateway credentials continues with curated memory only.
- No separate repair UI or background credential provisioning job is added.
Authenticated Chat Identity
Gateway credential selection must use a trusted server-side principal.
- REST and WebSocket frontend chat paths resolve the Beaver username from the issued access token.
- The resolved username is passed separately into the agent runtime as the Gateway identity key.
- Client-provided
user_idfields do not select Gateway credentials and cannot impersonate another Gateway user. - Runs without an authenticated frontend username, including channel or scheduled runs without a trusted mapped identity, continue with curated memory only.
This identity key is runtime-only. It is not included in provider prompts or Gateway persisted message content.
Runtime Architecture
EngineLoader loads:
- Curated
MemoryService, unconditionally. - Shared
MemoryGatewayConfigfrommemory/config.json. - A
MemoryGatewayCredentialStorefor the instance credential file.
It does not construct one fixed-user MemoryGatewayService at startup.
For each authenticated run in hybrid mode:
AgentLoopreceives the trusted Beaver username.- It reads that username's credential from the credential store.
- If a credential exists, it constructs a run-local Gateway service/client from the shared config and that credential.
- It performs Gateway recall before context construction.
- It performs Gateway add and flush after normal completion.
The run-local service has no shared mutable credential state, so concurrent runs for different users cannot exchange identities. No service cache is added in this version.
Recall and Persistence
The existing hybrid behavior remains unchanged once a user credential has been resolved:
- Search uses the current Beaver session id, current prompt, configured top K, and all three configured scopes.
- Sanitized Gateway results are injected as one ephemeral untrusted-reference message outside the system prompt.
- Normal completion persists exactly the original current user prompt and final assistant text.
- Add is called once, followed by flush once only after add succeeds.
- Tool calls, tool results, system prompts, curated memory, recalled Gateway text, reasoning, and skills are not persisted to Gateway.
- Gateway and curated memory remain isolated and do not synchronize, merge, overwrite, or deduplicate each other.
Security
- The shared configuration is safe to track because it contains no
userKey. - Per-user credentials live only under ignored instance runtime data.
- Credential-file permissions are
0600. - Credential objects suppress secrets from
repr. - Gateway client exceptions contain only operation, category, path, and status metadata.
- Registration responses expose Beaver authentication data only; Gateway credentials remain server-side.
- Hidden Gateway audit events may include the Beaver/Gateway user id but never the user key or complete request/response body.
Testing
Package migration
- All imports use
beaver.memory.gateway. - No references remain to the removed integration/service modules.
- Gateway config, client, service, and credential-store tests remain isolated from curated memory.
Shared configuration
- The shared file parses the fixed URL and three scopes.
- Invalid mode, URL, scope, top K, or timeout fails with sanitized errors.
- Instance config loading remains unchanged for non-memory settings.
- Test overrides can select a temporary shared config file.
Credential persistence
- Missing files produce an empty credential map.
- Credentials round-trip by Beaver username.
- Updating one user preserves all other users.
- Files are atomically replaced and have mode
0600. - No exception or representation contains
userKey.
Registration
- New frontend registration calls
/userswith the Beaver username. - Valid Gateway responses are stored without returning the key to the browser.
- Existing usernames refresh the same credential entry.
- Provisioning failure does not roll back Beaver registration and stores no partial credential.
Agent runtime
- Authenticated username selects only its own Gateway credential.
- Client-provided
user_idcannot select another user's credential. - Concurrent users construct independent run-local Gateway services.
- Missing credentials perform no Gateway calls and preserve curated behavior.
- Existing recall/add/flush ordering, payload, audit, and failure tests remain valid.
Verification
- Run targeted Gateway/config/auth/chat tests.
- Run Python compile checks and the complete backend test suite.
- Scan tracked files and diffs for real
userKeyvalues.