Skip to content

refactor(auth): the core takes a credential; hosts own login and the backend session - #6318

Merged
senamakel merged 195 commits into
tinyhumansai:mainfrom
senamakel:core-takes-credential
Sep 16, 2026
Merged

senamakel merged 195 commits into
tinyhumansai:mainfrom
senamakel:core-takes-credential

Conversation

@senamakel

@senamakel senamakel commented Sep 16, 2026 •

Copy link
Copy Markdown
Member

Summary

Follows #6272 (merged), which introduced the API-key credential for library mode.

  • The core no longer obtains, validates, exchanges or refreshes a credential. It takes one through auth.set_credential { token, kind?: "session" | "api-key" | "local", userId?, user? } (and auth.clear_credential { kind? }) and does only what it owns with it: user-dir activation, credential-gated services, the scheduler gate, Sentry / prompt identity. Same token + same user is a cheap refresh of the stored user payload; a different user is signed out first; an already-expired JWT or a session with no resolvable user id is refused. auth.get_state gains credential: "local" and expiresAt.
  • Removed from the core: auth_consume_login_token, auth_get_me, store-time and deferred /auth/me validation, the app_state.snapshot current-user cache (backoff / staleness / pending revalidation), the public GET /auth and GET /auth/telegram login callbacks, credentials/cli.rs, the session_get_user agent tool, api/models/auth.rs, the auth_get_me Sentry filter. auth_store_session / auth_clear_session and this PR's earlier auth_store_api_key / auth_clear_api_key fold into the credential pair; the session names survive as legacy aliases (core/legacy_aliases.rs, mirrored in rpcMethods.ts).
  • New crate crates/openhuman-session (no core dependency): SessionClient (login-token exchange, /auth/me, the 12 s / one-retry / transient store-time policy, x-sdk-name + version headers, schannel on Windows), CurrentUserCache (5 s TTL, stale-while-revalidate, bounded backoff), CoreLink (how a credential is pushed into whichever core the host owns), SessionManager (login / store / logout / current user / state + Changed / Expired events; a live-exp JWT is accepted while the backend is unreachable and revalidated in the background).
  • Tauri shell is the desktop session owner: crates/openhuman-app/src/session/ wraps the manager over the shell's own RPC endpoint (HttpCoreLink, follows gateway switches) and exposes auth_login_with_token, auth_store_session, auth_logout, auth_state, auth_current_user, plus Tauri events auth://changed / auth://expired. Sentry's fallback user id reads openhuman_session::identity.
  • Frontend reaches the owner through app/src/services/session/sessionOwner.ts — the Tauri commands on the desktop; a thin browser equivalent (consume → /auth/me → auth.set_credential, small per-token cache) in the browser build and in cloud mode, where the renderer targets a remote core the shell does not know about. fetchCoreAppSnapshot merges the owner's current user into the existing snapshot shape so pages are unchanged; the deep-link flow hands tokens to the owner (retries now live in Rust); classifyAuthStoreFailure maps the owner's stable PREFIX: errors.
  • TUI uses openhuman-session in-process (InProcessLink over CoreRuntime::invoke).
  • Headless hosts seed a credential at boot from OPENHUMAN_BACKEND_API_KEY / OPENHUMAN_BACKEND_SESSION_TOKEN (never overwriting a stored one), or over RPC / CLI with auth set_credential.

Problem

#6272 gave library hosts an API key with no user login. Beyond that, the core owned the whole user-login lifecycle — login-token exchange, GET /auth/me validation with a deferred-validation state machine, a polled current-user cache inside app_state.snapshot, and two public HTTP login callbacks — even though obtaining and validating a credential is a host concern: the desktop shell drives OAuth, the TUI pastes a login token, an embedder or a Docker deployment already holds a key. That coupling forced every host onto the desktop's flow and made "the core takes a JWT / API key" impossible to state.

Solution

Session ownership moves out of the core into crates/openhuman-session, shared by the shell and the TUI. The core keeps exactly the side effects it owns for a credential it is handed (auth.set_credential), and nothing that talks to the backend's auth endpoints. The frontend keeps its snapshot shape; only the source of currentUser changes.

Design decisions:

  • The user id for a session comes from the caller (userId, or user.id from its /auth/me answer) first and the JWT subject claim second. The production JWT claim set is not verified here, so the offline-acceptance path (USER_ID_UNAVAILABLE when neither is present) should be checked against a real token.
  • Channel link-checks keep a bearer-only GET auth/me (BackendOAuthClient::fetch_profile) to read a linked channel id — a channel feature, not session management.
  • The browser E2E profile and cloud mode get a deliberately thin TS owner (no deferred validation, no background revalidation) — the one place session policy lives outside Rust.
  • SessionExpired handling is unchanged: the core still drops a rejected credential and closes the scheduler gate; the host learns through the Socket.IO bridge and its own /auth/me result (auth://expired).
  • Telegram bot linking from a headless core (GET /auth/telegram) is not re-homed here; headless hosts install a credential explicitly instead.

Submission Checklist

  • Tests added or updated: core (ops_credential_tests, identity_tests, boot_env_tests, schema param tests), crates/openhuman-session (50 tests over in-process axum stubs and a fake CoreLink), shell session::link, embed, tests/config_auth_app_state_connectivity_e2e.rs, tests/raw_coverage/app_state_credentials_raw_coverage_e2e.rs, frontend sessionOwner / shellSessionEvents / desktopDeepLinkListener / tauriCommands / userApi / rpcMethods (drift guard now covers auth).
  • Diff coverage ≥ 80% — CI to confirm.
  • Coverage matrix updated (1.3.1, 1.3.2, 1.3.4 reworded; 1.3.5 added).
  • All affected feature IDs listed under ## Related.
  • No new external network dependencies (wiremock / in-process axum only).
  • Manual smoke checklist: desktop sign-in (OAuth loopback + openhuman://auth deep link), sign-out, re-login as a different user, TUI paste-a-login-token — not run locally (no display / signed bundle here); the browser Playwright login lane was run instead.
  • Linked issue: N/A — no tracking issue.

Impact

  • Library hosts: two-step API; Auth::store hands the session over as given (no /auth/me), so an embedder that drives backend features must supply a validated JWT plus its user (or a JWT with a subject claim).
  • Desktop: sign-in / sign-out / current user now run through the shell's auth_* commands; inference.agent_chat with a non-empty thread_id resumes that thread's transcript and accepts an optional agent_id.
  • Wire contract: auth_store_session / auth_clear_session / auth_store_api_key / auth_clear_api_key / auth_get_me / auth_consume_login_token leave the schema; the two session names still resolve through the alias table (a pre-migration desktop bundle keeps signing in against a new core). GET /auth and GET /auth/telegram on the core's HTTP server are removed.
  • Headless / Docker: a session is no longer obtainable from the core itself; set OPENHUMAN_BACKEND_API_KEY (or OPENHUMAN_BACKEND_SESSION_TOKEN) or call auth set_credential. Telegram bot linking against a headless core stops working until re-homed.
  • openhuman-embed API: additive plus two renames — Turn<'a> → Turn and Agent<'a> → CoreAgent<'a>; AuthState.credential gains "local".
  • Security: no change to is_workspace_internal_path, is_always_forbidden, classify_command or approval-by-default. auto_approve_all is still never set by Access::full(). The credential crosses the shell↔core RPC boundary in the auth.set_credential body (loopback, or the gateway transport with its existing bearer-over-plain-http refusal).
  • Known pre-existing CI item: pnpm rust:layout fails on agent/tinyagents/harness_assembly.rs (751 lines) as merged from upstream/main; not touched here.

Related


AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: core-takes-credential
  • Commit SHA: 6e7443ec27

Validation Run

  • pnpm --filter openhuman-app format:check: pass
  • pnpm typecheck: pass; pnpm lint: 0 errors (83 pre-existing warnings); pnpm i18n:check: pass
  • Frontend unit (pnpm debug unit): src/services/session, desktopDeepLinkListener, tauriCommands, CoreStateProvider, userApi, WebCallbackPage, coreRpcClient, rpcMethods, coreStateApi — green
  • Browser E2E: pnpm test:e2e:web:build + scripts/e2e-web-session.sh on login-flow, auth-access-control, logout-relogin-onboarding, runtime-picker-login — 6 passed, 6 pre-existing skips
  • Focused tests: cargo test -p openhuman-session (50, green); cargo test -p openhuman-tui; cargo test --manifest-path crates/openhuman-app/Cargo.toml --lib session; cargo test -p openhuman-embed --features inference,mcp,skills (99 unit + 3 integration + doctests, green); RUST_MIN_STACK=16777216 cargo test --lib -p openhuman -- security::credentials desktop::app_state api::rest core::jsonrpc core::observability channels::controllers core::auth tools::ops (674 green); cargo test -p openhuman --test config_auth_app_state_connectivity_e2e (42 green); cargo test -p openhuman --features voice --test json_rpc_e2e -- auth app_state session (11 green); cargo check -p openhuman --features voice,inference --test raw_coverage_all; RUST_MIN_STACK=16777216 cargo test --lib -p openhuman -- security::credentials core::runtime inference::local inference::provider::factory inference::provider::openhuman_backend skills::ops api::rest hosted agent::harness::session::builder agent::harness::session::turn tools::ops cron::scheduler_gate (1128 green); agent:: web_chat:: config:: (3363 green, 1 pre-existing failure needing the non-default documents feature: the_withheld_block_renders_for_a_renamed_session_with_a_filter)
  • Rust fmt/check: cargo fmt --all --check (root + shell), cargo check (default and --no-default-features), cargo clippy -p openhuman --features "$(bash scripts/ci/product-features.sh)" -- -D warnings, cargo clippy -p openhuman-session --all-targets -- -D warnings, cargo clippy -p openhuman-tui --all-targets -- -D warnings, cargo check --manifest-path crates/openhuman-app/Cargo.toml clean; pnpm docs:check, scripts/ci/check-feature-forwarding.mjs pass; pnpm rust:layout fails only on the upstream harness_assembly.rs line count
  • Tauri fmt/check: cargo fmt --manifest-path crates/openhuman-app/Cargo.toml --all --check, cargo check --manifest-path crates/openhuman-app/Cargo.toml clean

Validation Blocked

  • command: full scripts/test-rust-with-mock.sh; desktop WDIO E2E (login-flow, auth-access-control, logout-relogin-onboarding, mega-flow)
  • error: not run locally (the Rust mock lane needs native module builds; the desktop lane needs a display and a built bundle)
  • impact: CI Lite covers both

Behavior Changes

  • Intended behavior change: session ownership moves to the host (auth.set_credential / auth.clear_credential replace the session RPCs; the core stops calling the backend's auth endpoints).
  • User-visible effect: none intended for desktop users — sign-in, sign-out and the current user behave as before, driven by the shell instead of the core.

Parity Contract

  • Legacy behavior preserved: auth_store_session / auth_clear_session resolve through the alias table; the app_state.snapshot shape is unchanged (currentUser = the handed-over user; the frontend merges the owner's live answer); SessionExpired teardown unchanged.
  • Guard/fallback/dispatch parity checks: set_credential refuses expired JWTs and sessions without a user id; SessionExpiredSubscriber still ignores local sessions and API-key runtimes; the JSON-RPC dispatcher still publishes SessionExpired on the SESSION_EXPIRED: sentinel.

Duplicate / Superseded PR Handling

Summary by CodeRabbit

  • New Features

    • Added centralized session management for desktop, browser, and TUI sign-in flows.
    • Added unified support for session tokens, API keys, and offline credentials.
    • Added current-user caching with stale-data fallback and session-expiration notifications.
    • Added environment-based credential initialization for headless deployments.
    • Added immediate desktop updates when authentication changes or expires.
    • Added credential handoff controls for setting and clearing authentication.
  • Documentation

    • Updated authentication, deployment, architecture, troubleshooting, and embedded integration guidance for host-managed sessions and credential handoff.

senamakel and others added 30 commits September 15, 2026 23:54
Bump the tinyconnectors submodule to commit 4b8c7465caa7, which includes upstream fixes and improvements. This change keeps the vendored dependency in sync with the pinned version.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
This change introduces the Cargo.toml manifest for the openhuman-session crate, establishing its dependencies and metadata to support session management functionality.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Introduce TLS-based credential and identity modules to enable secure session establishment. The new credential type wraps TLS key material, while the identity module provides verification and binding logic, allowing sessions to authenticate using TLS certificates.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
When the session token is not present in the client, the code now returns an appropriate error instead of proceeding with an empty or invalid token. This prevents downstream operations from failing with confusing errors and ensures the client provides clear feedback when authentication credentials are missing.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The cache read path now re-inserts a fetched entry back into the cache when it is missing, ensuring subsequent reads hit the cache instead of repeatedly querying the underlying store. This prevents redundant work and keeps the cache consistent with the data source.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The link module previously omitted a case for handling links that were not
present in the session, causing them to be silently dropped. This change
restores the expected behavior by ensuring such links are properly processed
and included in the session output.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The link module was inadvertently stripped of its core functionality during a refactor, leaving the session unable to process incoming links. This change restores the link parsing and dispatch logic, ensuring that links are correctly validated and routed to the appropriate handlers.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The session manager now writes the session state to disk during shutdown, ensuring that any changes made during the session are not lost when the process exits. Previously, the state was only saved on explicit save calls, which could leave unsaved changes if the application terminated unexpectedly.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test modules in the session crate were not declared in the main module, so the test files were not being compiled or run. This change adds the necessary module declarations to ensure all tests are included in the test suite.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The `use` statement for `std::sync::Arc` was removed as it is no longer referenced in the code, cleaning up the module's imports without affecting behavior.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test support utilities have been relocated from the main library module into a dedicated `test_support` module, keeping the public API surface clean and making the test helpers easier to maintain and reuse across the crate.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The credential and link test modules are no longer needed as their coverage has been consolidated into the main session test suite.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The cache_tests and client_tests modules are no longer needed as their coverage has been superseded by integration tests elsewhere in the crate.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The manager_tests module was inadvertently removed from the crate, leaving the test suite incomplete. This change restores the module so that the existing tests for the session manager are compiled and run again.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The session manager now correctly saves the session state when the application exits, ensuring that user sessions are not lost between runs. Previously, the shutdown path skipped the persistence step, causing sessions to be discarded on every restart.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The file previously lacked a trailing newline, which could cause issues with some tooling and version control systems. This change adds the newline to ensure the file ends properly.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The session cache previously failed to evict entries when reaching its capacity limit, causing unbounded memory growth. This change re-enables the eviction logic that removes the least recently used sessions, ensuring the cache maintains its configured maximum size and preventing memory exhaustion in long-running applications.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformats the openhuman-session crate's source and test files using rustfmt, applying consistent line wrapping and indentation throughout. No behavioral changes are introduced; this is purely a formatting pass to align with the project's style conventions.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…er modules

Adds comprehensive unit tests covering the session cache, client, credential, and manager components, along with shared test support utilities. These tests verify core session management behavior and provide a foundation for future regression testing.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Introduce a shared test support module to consolidate common test utilities and fixtures used across client, credential, and manager test files. This reduces duplication and makes the test suite easier to maintain.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds test modules for cache, client, credential, and manager components to improve test coverage and organization.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test modules for cache, client, and credential functionality have been relocated from their parent source files into dedicated test files. This change improves code organization and maintainability by separating test code from implementation code, making it easier to navigate and review the test suite.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The client and credential test modules were no longer referenced by the crate's test suite, so they have been removed to keep the codebase tidy.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add the `openhuman-session` crate to the CI path filters and add a dedicated job step to run clippy and tests for it, since it is a new host-side crate with no `openhuman` dependency and fast compilation.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The session response handling previously omitted identity verification, allowing unverified identities to be returned in session responses. This change restores the verification step in the response construction path, ensuring that only verified identities are included in session responses. The identity module now properly validates the identity before it is used in session support, and the corresponding tests have been updated to cover this verification requirement.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The login flow was inadvertently removing the gated service access that had been previously granted, causing users to lose access to restricted services after re-authenticating. This change ensures that gated service permissions are preserved and reapplied during the login process, maintaining consistent access control across sessions.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The user scope secret retrieval was inadvertently removed during a refactor, breaking access to credentials stored under user-scoped keys. This change restores the missing lookup logic, ensuring that user-scoped secrets are correctly resolved and returned to callers.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The credential lookup by id was inadvertently removed during a refactor, causing callers to fail when retrieving credentials directly. This change restores the `get_credential_by_id` operation, ensuring the expected behavior is preserved.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
senamakel and others added 8 commits September 16, 2026 17:08
The test support module in the session crate was not updated to match recent changes in the public API, causing compilation failures. This change adjusts the test helper functions to align with the current interface, ensuring tests can build and run correctly.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test support module in the session crate was updated to fix incorrect import paths that prevented the module from compiling correctly. This change ensures the test utilities are properly accessible for writing integration tests.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Removed an unused import from the manager tests file to keep the codebase clean and avoid compiler warnings.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
When a session's refresh token has expired, the token refresh process now correctly returns an error instead of silently failing. This ensures that clients are properly notified of expired sessions and can prompt the user to re-authenticate, preventing silent data loss or unexpected behaviour.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reorder the credential push and revalidation cancellation so that the new credential is handed to the core before the old one's revalidation loop is stopped. This prevents a scenario where a failed push or a core restart mid-call leaves the prior pending credential without its background revalidation, potentially stranding it in a provisional state indefinitely.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test file was missing several assertions that verify the correct behavior of credential operations. This change restores those checks to ensure the tests fully validate the expected functionality.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
When signing a credential, the system now checks that the issuer matches the expected signing authority. This prevents credentials from being issued under a false identity, closing a potential security gap where an untrusted issuer could produce a signed credential.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Remove the guard that skipped storing the api key when one already existed in the effective config, so the key is always carried forward to the signed-out workspace. This ensures the credential that was active a moment ago is never silently outranked by a stale key from the pre-login workspace.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e354e10fc5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/openhuman-session/src/manager.rs Resolved
senamakel and others added 10 commits September 16, 2026 18:00
… session

When a session owner attempted to remove themselves from the session, the operation would succeed but leave the session in an inconsistent state without an owner. This change adds a validation check that prevents the owner from being removed, ensuring the session always retains a valid owner.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test file was previously removed, but the session owner logic still needs
verification. This change restores the test suite to ensure the session owner
functionality remains correct and regressions are caught early.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The session manager was not persisting active sessions to disk, causing all sessions to be lost when the application restarted. This change re-enables the persistence layer so that session data is saved and reloaded correctly, ensuring continuity for users across restarts.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adjusted the test expectations to reflect the corrected cleanup behavior, ensuring that session resources are properly released after the manager finishes its work.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The session manager's core logic was inadvertently removed during a previous refactor. This change restores the essential functionality for managing open human sessions, ensuring the manager operates as intended.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The revalidation flow now retries with exponential backoff when persisting the
revalidated session fails, instead of giving up after a single warning. On
success it still seeds the cache, drops the guard, and emits the changed event
before returning.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test now expects a session to be expired after the timeout period, aligning with the updated session manager logic that enforces timeouts on access.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The session manager now saves the session state when the application shuts down, ensuring that any changes made during the session are not lost. Previously, the save was only triggered on explicit user actions, which could lead to data loss if the application was closed unexpectedly.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
When storing a revalidated session fails, the manager now backs off and retries instead of giving up, while a successful store still seeds the cache and emits the changed event. This prevents the pending validation state from being stuck when the backend is temporarily unavailable.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The logout session function now invokes the core RPC with a single-line argument list, improving readability without altering behavior.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 09c38480be

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// 'session'` clears whichever of session or local is actually stored —
// both live under the same app-session profile — without touching the
// API key, unlike omitting `kind` entirely.
await callCoreRpc({ method: 'openhuman.auth_clear_credential', params: { kind: 'session' } });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Avoid tearing down an API-key-only runtime on logout

When browser/cloud mode is authenticated only by an API key, this unconditional session clear still enters the core's clear_session_credential: it closes the scheduler gate and stops credential-gated services before discovering that no session profile exists, while the restoration branch is guarded by removed_session and therefore never runs. The core continues reporting the API key as authenticated, but cron, voice, and local-AI services remain disabled. Fresh evidence beyond the earlier coexisting-credentials fix is this no-session case, where removed_session is false; either check whether a session exists first or make the core's session clear a no-op before teardown.

AGENTS.md reference: AGENTS.md:L267-L271

Useful? React with 👍 / 👎.

Comment on lines +417 to +418
if !crate::security::credentials::session_support::has_backend_credential(config) {
crate::cron::scheduler_gate::set_signed_out(true);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Stop gated services when clearing the last API key

When a running signed-out core installs an API key, set_credential now starts the credential-gated services; clearing that last key here only closes the scheduler gate. The voice server, dictation listener, always-on microphone path, and local-AI state therefore remain active after the runtime becomes unauthenticated. Invoke stop_credential_gated_services when the removed API key leaves no backend credential.

AGENTS.md reference: AGENTS.md:L267-L271

Useful? React with 👍 / 👎.

Co-authored-by: Medulla <medulla@tinyhumans.ai>
@senamakel
senamakel merged commit 3b029c8 into tinyhumansai:main Sep 16, 2026
21 of 22 checks passed
senamakel pushed a commit that referenced this pull request Oct 24, 2026
…storage

Desktop users lose their saved API key when they sign out of their
session.

With a storage backend installed (the desktop boot installs SQLite via
session_store::install_for_host), the auth profiles are one secret in the
acting scope (`profiles::persistence::STORAGE_SECRET_NAME`,
"file:auth-profiles.json"), not a file per state directory. The
session-only sign-out of #6318 carries the key forward by storing it
against the signed-out config and then clearing it from the user-scoped
config, but both resolve to that same secret, so the clear deleted the key
it had just carried forward: the key was gone from both configs and
auth.get_state reported signed out.

Skip the source clear when the key was carried forward and the profiles
are served from storage secrets (source and destination are then the
same record). The file-store path is unchanged. The new test installs a
SQLite backend, as the desktop does, and checks the key survives.
M3gA-Mind added a commit that referenced this pull request Oct 27, 2026
On the default storage the backend belongs to the profile
(users/<id>/storage.db), and every credential change rebinds the process
to the next profile's database. Two paths wrote to the wrong one:

- set_credential stored the app-session profile before the rebind, so the
  session landed in the pre-login database (users/local) and read back as
  absent from the user's own: get_session_token returned null and
  auth.get_state reported signed out right after a sign-in.
- A session-only sign-out then found no session in the user's database
  (removedSession false), skipped the carry-forward, and rebound to
  users/local, where the API key was not: the key stopped working (#8366),
  also after a restart.

Rebind before storing the session. On a sign-out, take the API key out of
its source while that database is still bound, then carry it into the
signed-out profile after the teardown, putting it back if the teardown or
the carry fails. This replaces the shared-store special case, which
assumed one record for every profile. The key stays with the device's
signed-out state, as #6318 kept it; another account signing in does not
get it.

Regression tests on the default per-user layout (the existing storage
tests install one backend with no URL, which is never rebound).
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