Skip to content

Scope CEF data to OpenHuman user profiles#917

Merged
senamakel merged 7 commits into
tinyhumansai:mainfrom
senamakel:codex/cef-user-profiles
Apr 26, 2026
Merged

Scope CEF data to OpenHuman user profiles#917
senamakel merged 7 commits into
tinyhumansai:mainfrom
senamakel:codex/cef-user-profiles

Conversation

@senamakel
Copy link
Copy Markdown
Member

@senamakel senamakel commented Apr 26, 2026

Summary

  • scope the shell CEF cache to .openhuman/users/<id>/cef with a local pre-login fallback
  • restart the desktop shell when the authenticated user changes so Chromium actually switches profiles
  • queue per-user CEF profile deletion during Clear App Data and purge it on next launch
  • patch the vendored tauri-cef runtime to honor an OPENHUMAN_CEF_CACHE_PATH override before CEF boots
  • expose the configured cookies DB path to the core using the same selected CEF profile

Problem

  • Clear App Data was resetting OpenHuman state but leaving Chromium profile data behind in the shared CEF cache.
  • Logging in as a different user reused the same CEF profile, so cookies/storage did not switch with the OpenHuman user.
  • The vendored CEF runtime hardcoded a single global cache path, so app-level fixes in React/Tauri were not enough.

Solution

  • Add a Tauri-side CEF profile resolver that maps the active OpenHuman user to .openhuman/users/<id>/cef and configures that path before the runtime starts.
  • Add app restart commands so login user switches and destructive clear-data flows relaunch the shell into the correct Chromium profile.
  • Queue CEF profile purges for the next launch because the active browser process may still hold file locks during in-process deletion.
  • Update the vendored tauri-runtime-cef submodule to respect OPENHUMAN_CEF_CACHE_PATH; see Allow overriding the CEF cache path tauri-cef#9.

Submission Checklist

  • Unit tests — Vitest (app/) and/or cargo test (core) for logic you add or change
  • E2E / integration — Where behavior is user-visible or crosses UI → Tauri → sidecar → JSON-RPC; use existing harnesses (app/test/e2e, mock backend, tests/json_rpc_e2e.rs as appropriate)
  • N/A — If truly not applicable, say why (e.g. change is documentation-only)
  • Doc comments/// / //! (Rust), JSDoc or brief file/module headers (TS) on public APIs and non-obvious modules
  • Inline comments — Where logic, invariants, or edge cases aren’t clear from names alone (keep them grep-friendly; avoid restating the code)

Impact

  • Desktop/Tauri/CEF only.
  • User switching now triggers a full app restart so CEF reboots into the correct per-user profile.
  • Clear App Data now clears the selected user's Chromium profile on next launch in addition to OpenHuman state.
  • This PR depends on the vendored runtime change in Allow overriding the CEF cache path tauri-cef#9.

Related

Summary by CodeRabbit

  • New Features

    • App restart command exposed.
    • Per-user browser profile/cache management with env-configurable cache path and a scheduled-profile-purge flow.
  • Bug Fixes

    • Clear-app-data now schedules profile purge and performs a full app restart instead of a page reload.
    • App restarts automatically when the active user identity changes to ensure a clean profile on startup.
  • Chores

    • Added runtime/dev dependencies and advanced vendored browser integration.
  • Tests

    • Added unit tests for Tauri command wrappers and profile purge logic.

@senamakel senamakel requested a review from a team April 26, 2026 00:04
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 26, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Adds per-user CEF profile management: new backend module for resolving/overriding cache paths, queuing and draining pending per-user cache purges across launches, Tauri commands to schedule purges and restart the app, frontend wiring to queue purges on logout/identity changes, and a tauri-cef submodule update.

Changes

Cohort / File(s) Summary
Dependency Configuration
app/src-tauri/Cargo.toml
Added toml and directories as production deps and tempfile as a dev-dependency.
CEF Profile Backend
app/src-tauri/src/cef_profile.rs
New module: exports CEF_CACHE_PATH_ENV, provides configured_cache_path_from_env(), queue_profile_purge_for_user(), prepare_process_cache_path(); reads OPENHUMAN_CEF_CACHE_PATH, resolves default OpenHuman dirs, validates user IDs, manages pending purge TOML marker (with migration), drains queue with canonical/safety checks, sets env var, and includes unit tests.
CEF Startup & Integration
app/src-tauri/src/cef_preflight.rs, app/src-tauri/src/lib.rs
check_default_cache now honors OPENHUMAN_CEF_CACHE_PATH; lib.rs conditionally adds cef_profile (feature cef), calls prepare_process_cache_path() at startup (exit on failure), derives cookie DB path from configured cache path, and registers restart_app and schedule_cef_profile_purge Tauri commands.
Vendored Submodule
app/src-tauri/vendor/tauri-cef
Advanced vendored tauri-cef submodule commit pointer.
Frontend Session & Logout Flows
app/src/components/settings/SettingsHome.tsx, app/src/providers/CoreStateProvider.tsx
Settings "clear app data" schedules a CEF profile purge (using core user id) and awaits restartApp() instead of reload; session storage now snapshots identity before/after storing and triggers restart when identity changes.
Tauri Command Wrappers & Tests
app/src/utils/tauriCommands/core.ts, app/src/utils/tauriCommands/core.test.ts
Added restartApp() and scheduleCefProfilePurge(userId?) wrappers (with debug logs) and unit tests that mock invoke/isTauri to verify behavior and arguments.

Sequence Diagrams

sequenceDiagram
    actor User
    participant Frontend as SettingsHome
    participant TauriCmds as Tauri Commands
    participant Backend as cef_profile
    participant FS as TOML/FS
    participant AppRuntime as CEF/App Runtime

    User->>Frontend: Click "Clear App Data"
    Frontend->>Frontend: Read user_id from core state
    Frontend->>TauriCmds: scheduleCefProfilePurge(user_id)
    TauriCmds->>Backend: schedule_cef_profile_purge(user_id)
    Backend->>FS: Load/update pending_cef_purge.toml (dedupe)
    TauriCmds-->>Frontend: Return queue id
    Frontend->>Frontend: Clear session, redux, browser storage
    Frontend->>TauriCmds: restartApp()
    TauriCmds->>AppRuntime: restart_app
    AppRuntime->>Backend: prepare_process_cache_path() on startup
    Backend->>FS: Drain pending_cef_purge.toml -> delete queued dirs (safe canonical checks)
    Backend->>FS: Read active_user.toml (or default) and ensure cache dir exists
    Backend->>AppRuntime: Set OPENHUMAN_CEF_CACHE_PATH
    AppRuntime-->>User: App restarts with configured per-user CEF cache
Loading
sequenceDiagram
    actor User
    participant CoreState as CoreStateProvider
    participant API as storeSession
    participant TauriCmds as Tauri Commands
    participant CEFProfile as cef_profile
    participant App as App Runtime

    User->>CoreState: Log in / switch user
    CoreState->>CoreState: Snapshot prev identity
    CoreState->>API: storeSession(token)
    API-->>CoreState: Session stored
    CoreState->>CoreState: refresh + refreshTeams
    CoreState->>CoreState: Snapshot next identity
    alt Identity changed
        CoreState->>TauriCmds: restartApp()
        TauriCmds->>CEFProfile: prepare_process_cache_path()
        CEFProfile->>App: Set OPENHUMAN_CEF_CACHE_PATH
        App-->>User: Restart into new user's profile
    else No change
        CoreState-->>User: Continue
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 In burrows neat each user's cache I sweep,
A queued little list I faithfully keep,
When identities shift or logouts ensue,
I whisper restart, then clear paths anew —
Hops, bytes, and clean starts for me and you!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly summarizes the main change: scoping CEF (Chromium Embedded Framework) cache data to per-user OpenHuman profiles instead of a shared global cache.
Docstring Coverage ✅ Passed Docstring coverage is 84.38% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src-tauri/src/cef_profile.rs`:
- Around line 60-66: user_id is used directly in path joins which allows
absolute paths or traversal like ".." to escape the intended profile root; add
validation/sanitization before using it in user_openhuman_dir and
cache_dir_for_user: implement a helper (e.g., sanitize_user_id or
validate_user_id) that rejects any input containing path separators, components
like "." or "..", or leading separators (and optionally enforces an allowed
charset), return a Result or normalized String, and call that helper from
user_openhuman_dir and cache_dir_for_user so only verified safe IDs are joined
into the filesystem path.
- Around line 68-70: The pending CEF purge marker is currently placed inside
default_openhuman_dir (via pending_purge_marker_path and
PENDING_PURGE_STATE_FILE) which gets deleted by reset_local_data_for_paths;
change pending_purge_marker_path to return a location outside the directory
being wiped (e.g., a sibling or parent location such as
default_openhuman_dir.parent().join(PENDING_PURGE_STATE_FILE) or another
app-specific config dir) so reads/writes continue to use the same symbol and the
file survives reset_local_data_for_paths; update all callers that read/write the
marker to use pending_purge_marker_path and run/update any tests that rely on
the marker location.
- Around line 164-197: The code currently removes the purge marker
unconditionally after iterating state.paths so transient std::fs::remove_dir_all
errors become permanent; change the logic in the loop that iterates state.paths
to record whether any removal failed (e.g., a boolean like had_failures) and
only call std::fs::remove_file(&marker_path) when no failures occurred; on any
Err from remove_dir_all keep the path in state.paths (or leave state unchanged)
so the marker remains and the failed entries will be retried on next run, and
log clearly when skipping marker removal due to had_failures; update the code
around remove_dir_all, state.paths and marker_path to implement this conditional
removal.

In `@app/src-tauri/src/lib.rs`:
- Around line 780-786: When cef_profile::prepare_process_cache_path() returns
Err, do not clear cef_profile::CEF_CACHE_PATH_ENV or let the runtime fall back
to the shared cache; instead fail closed by propagating or terminating startup.
Replace the current Err arm (which logs a warn and calls std::env::remove_var)
with code that logs an error (including the error details) and then aborts
startup (e.g., propagate the Err from the surrounding function or call
std::process::exit(1)) so the application does not continue with the
global/shared CEF cache.

In `@app/src/providers/CoreStateProvider.tsx`:
- Around line 393-398: The restart log is leaking raw user identifiers
(previousIdentity and nextIdentity); modify the console.debug call in
CoreStateProvider (the block that checks nextIdentity !== previousIdentity and
calls restartApp) to avoid emitting raw PII by either redacting or hashing those
values before logging (e.g., replace with a stable hash or masked string like
****xxxx), and include only non-sensitive context in the log message so the
restart event remains visible without exposing full IDs.

In `@app/src/utils/tauriCommands/core.ts`:
- Around line 82-85: The debug is logging raw PII (userId) in core.ts; change
the console.debug in the scheduleCefProfilePurge call to avoid printing the full
userId—either remove the userId field entirely or replace it with a
non-sensitive indicator (e.g., "userId: redacted" or a truncated/masked form)
and keep the invoke('schedule_cef_profile_purge', { userId: userId ?? null })
behavior unchanged so the backend still receives the id while the renderer log
does not contain full PII.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 45e9109f-0844-4f8d-96d2-1f821fe62df3

📥 Commits

Reviewing files that changed from the base of the PR and between 7b123bc and eae5490.

⛔ Files ignored due to path filters (1)
  • app/src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • app/src-tauri/Cargo.toml
  • app/src-tauri/src/cef_preflight.rs
  • app/src-tauri/src/cef_profile.rs
  • app/src-tauri/src/lib.rs
  • app/src-tauri/vendor/tauri-cef
  • app/src/components/settings/SettingsHome.tsx
  • app/src/providers/CoreStateProvider.tsx
  • app/src/utils/tauriCommands/core.ts

Comment thread app/src-tauri/src/cef_profile.rs Outdated
Comment thread app/src-tauri/src/cef_profile.rs Outdated
Comment thread app/src-tauri/src/cef_profile.rs
Comment thread app/src-tauri/src/lib.rs
Comment thread app/src/providers/CoreStateProvider.tsx
Comment thread app/src/utils/tauriCommands/core.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
app/src-tauri/src/cef_profile.rs (1)

211-215: ⚠️ Potential issue | 🟠 Major

Redact user_id from the CEF profile logs.

These logs currently write the full logical user identifier, and this module explicitly accepts email-shaped IDs. Please log a boolean, a short hash, or omit the identifier entirely.

As per coding guidelines, "Never log secrets, raw JWTs, API keys, credentials, or full PII; redact or omit sensitive fields."

Also applies to: 239-243

🧹 Nitpick comments (1)
app/src/utils/tauriCommands/core.ts (1)

65-85: Add co-located tests for the new Tauri command wrappers.

These branches now control app restart and destructive purge scheduling, but there’s no coverage for the non-Tauri short-circuit or the userId ?? null payload shape. A small core.test.ts would make this safer to evolve.

As per coding guidelines, "Ship unit tests and coverage for behavior you are adding or changing before building additional features on top" and "Co-locate unit tests as *.test.ts or *.test.tsx under app/src/**; use Vitest with app/test/vitest.config.ts."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/utils/tauriCommands/core.ts` around lines 65 - 85, Add a co-located
Vitest file (core.test.ts) to cover the Tauri command wrappers restartApp and
scheduleCefProfilePurge: mock isTauri() and invoke() to test both short-circuit
behavior when isTauri() is false (restartApp should not call invoke and
scheduleCefProfilePurge should return null) and the Tauri path when isTauri() is
true (restartApp should call invoke('restart_app') and scheduleCefProfilePurge
should call invoke('schedule_cef_profile_purge', { userId: null }) for
undefined, { userId: null } for null, and { userId: '<id>' } for a string); also
assert scheduleCefProfilePurge returns the invoke result string and verify
console debug calls if desired. Ensure tests live under app/src/** and use the
project's Vitest config.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src-tauri/src/cef_profile.rs`:
- Around line 257-280: The purge loop is using raw_path ->
PathBuf::from(raw_path) and directly calling std::fs::remove_dir_all, which
risks deleting arbitrary directories from a corrupted TOML marker; before
calling remove_dir_all in the loop that iterates state.paths (and in functions
handling queued purge), canonicalize the target (std::fs::canonicalize) and
validate it is a descendant of the known app root (e.g., the computed
default_openhuman_dir/users/*/cef pattern) — verify the canonical path
starts_with the canonicalized default_openhuman_dir/users and optionally
ends_with "cef" or matches a persisted user ID-derived path; if validation
fails, log a warning and push the raw_path into remaining instead of deleting.
Ensure the check is applied where remove_dir_all is invoked (the match branch
around std::fs::remove_dir_all(&target)) and consider switching to deriving
purge paths from persisted user IDs rather than raw TOML strings.
- Around line 307-342: Add unit tests in the existing tests module that exercise
the purge-queue sibling-marker migration and the retry-on-failure drain path:
create temp dirs and seed the PURGE_QUEUE_FILE and SIBLING_MARKER_FILE with
representative contents, call migrate_sibling_marker(...) to verify the sibling
marker is migrated/removed and queue entries are adjusted, and call
drain_purge_queue(...) (or drain_purge_queue_with_retry if present) to assert
successful removal of queued items and that failed removals are retried
(simulate failure by making a file/entry non-removable or using the existing
retry hook), using helper functions like enqueue_purge(...) and
read_purge_queue(...) to observe state changes; place these tests alongside the
existing tests and use the same tempfile/tempdir patterns as read_active_user_id
and cache_dir_for_user.

---

Nitpick comments:
In `@app/src/utils/tauriCommands/core.ts`:
- Around line 65-85: Add a co-located Vitest file (core.test.ts) to cover the
Tauri command wrappers restartApp and scheduleCefProfilePurge: mock isTauri()
and invoke() to test both short-circuit behavior when isTauri() is false
(restartApp should not call invoke and scheduleCefProfilePurge should return
null) and the Tauri path when isTauri() is true (restartApp should call
invoke('restart_app') and scheduleCefProfilePurge should call
invoke('schedule_cef_profile_purge', { userId: null }) for undefined, { userId:
null } for null, and { userId: '<id>' } for a string); also assert
scheduleCefProfilePurge returns the invoke result string and verify console
debug calls if desired. Ensure tests live under app/src/** and use the project's
Vitest config.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4c60d8fa-66e7-4671-a357-e6752f9f0650

📥 Commits

Reviewing files that changed from the base of the PR and between eae5490 and 3905978.

📒 Files selected for processing (4)
  • app/src-tauri/src/cef_profile.rs
  • app/src-tauri/src/lib.rs
  • app/src/providers/CoreStateProvider.tsx
  • app/src/utils/tauriCommands/core.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/providers/CoreStateProvider.tsx

Comment thread app/src-tauri/src/cef_profile.rs
Comment thread app/src-tauri/src/cef_profile.rs
- Introduced unit tests for `restartApp` and `scheduleCefProfilePurge` functions in `core.test.ts`, ensuring correct behavior based on Tauri environment.
- Implemented a safety check in `cef_profile.rs` to validate purge paths, preventing unauthorized directory deletions and enhancing security.
- Added tests to verify the correct handling of trusted and untrusted paths during the purge process.
- Simplified the construction of file paths in tests by removing unnecessary line breaks.
- Consolidated the initialization of `PendingCefPurgeState` for improved readability.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/src/utils/tauriCommands/core.test.ts (1)

24-33: Prefer automatic spy cleanup to avoid leakage on failed assertions.

If an assertion fails before debug.mockRestore(), the spy can leak into subsequent tests. Use centralized cleanup (afterEach(() => vi.restoreAllMocks())) to make this robust and remove duplication.

♻️ Suggested cleanup
-import { beforeEach, describe, expect, type Mock, test, vi } from 'vitest';
+import { afterEach, beforeEach, describe, expect, type Mock, test, vi } from 'vitest';

 describe('tauriCommands/core', () => {
+  afterEach(() => {
+    vi.restoreAllMocks();
+  });
+
   const mockIsTauri = isTauri as Mock;
   const mockInvoke = invoke as Mock;
@@
-      debug.mockRestore();
@@
-      debug.mockRestore();

Also applies to: 44-54

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/utils/tauriCommands/core.test.ts` around lines 24 - 33, The test
manually restores the console.debug spy (debug.mockRestore()) which can leak if
an assertion fails; instead add centralized cleanup by calling
vi.restoreAllMocks() in an afterEach block (e.g., add afterEach(() =>
vi.restoreAllMocks()) at the test-suite top level), and remove the per-test
debug.mockRestore() calls in the tests that spy on console.debug (the tests
invoking restartApp and assertions against mockInvoke and debug). Ensure the
tests still create the spy with vi.spyOn(console, 'debug') and assert
expect(debug).toHaveBeenCalledWith(...), but rely on afterEach to clean up.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/src/utils/tauriCommands/core.test.ts`:
- Around line 24-33: The test manually restores the console.debug spy
(debug.mockRestore()) which can leak if an assertion fails; instead add
centralized cleanup by calling vi.restoreAllMocks() in an afterEach block (e.g.,
add afterEach(() => vi.restoreAllMocks()) at the test-suite top level), and
remove the per-test debug.mockRestore() calls in the tests that spy on
console.debug (the tests invoking restartApp and assertions against mockInvoke
and debug). Ensure the tests still create the spy with vi.spyOn(console,
'debug') and assert expect(debug).toHaveBeenCalledWith(...), but rely on
afterEach to clean up.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c5852174-9030-4795-ba08-224a98b55df7

📥 Commits

Reviewing files that changed from the base of the PR and between 3905978 and 5bc234f.

📒 Files selected for processing (2)
  • app/src-tauri/src/cef_profile.rs
  • app/src/utils/tauriCommands/core.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src-tauri/src/cef_profile.rs

… in `cef_profile.rs`

- Eliminated a test case that was specific to Unix systems, which was no longer necessary for the current test suite.
- This cleanup improves the maintainability of the test code.
@senamakel senamakel merged commit 87ca378 into tinyhumansai:main Apr 26, 2026
8 of 9 checks passed
AusAgentSmith pushed a commit to AusAgentSmith/openhuman that referenced this pull request May 23, 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