Skip to content

fix(credentials): normalize provider slugs at write time and auto-mig… - #5432

Open
aryash45 wants to merge 2 commits into
tinyhumansai:mainfrom
aryash45:fix/deepseek-provider-slug-normalization
Open

fix(credentials): normalize provider slugs at write time and auto-mig…#5432
aryash45 wants to merge 2 commits into
tinyhumansai:mainfrom
aryash45:fix/deepseek-provider-slug-normalization

Conversation

@aryash45

@aryash45 aryash45 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Provider credential storage persisted mixed-case slugs (e.g. provider:DeepSeek) while read paths used case-sensitive exact matching, causing a silent save/read mismatch.
  • store_provider_token and store_provider_credentials now normalize provider slugs to lowercase at write time via normalize_provider() / .to_ascii_lowercase().
  • select_profile_id and bearer-token resolution gain a case-insensitive fallback (eq_ignore_ascii_case) for profiles written before this fix.
  • AuthProfilesStore::load() auto-migrates legacy on-disk active_profiles keys to lowercase on first load and rewrites the file; subsequent reads are no-ops.
  • No RPC/wire surface changed — ControllerSchema namespace strings are untouched.

Problem

After saving a DeepSeek API key via Settings, the UI confirmed success but the dialog immediately showed the key as missing and all API calls failed. The problem persisted across restarts.

Root cause: the write path stored the profile under provider:DeepSeek (mixed case from the frontend slug), while read paths looked up provider:deepseek (lowercase). The keys never matched, so get_provider_bearer_token and the has_token UI check both returned false for a key that was physically on disk.

Solution

Normalize at write time (primary fix):

  • core.rsstore_provider_token: let provider = normalize_provider(provider)? before building AuthProfile.
  • ops.rsstore_provider_credentials: let provider = provider.trim().to_ascii_lowercase() before any storage call.

Case-insensitive read fallback (defence-in-depth):

  • select_profile_id and active-profile resolution use eq_ignore_ascii_case so stale mixed-case profiles still resolve.

On-load migration (existing data):

  • profiles.rsAuthProfilesStore::load() detects any active_profiles key where key != key.to_lowercase(), rebuilds the map, and rewrites the file. Runs once; free on all subsequent loads.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case)
    • store_and_retrieve_provider_token_case_insensitive — mixed-case write resolves correctly on read
    • legacy_mixed_case_active_profile_key_migrated_on_load — stale on-disk fixture normalised and rewritten on first load()
    • ui_has_api_key_check_and_backend_get_profile_agree_for_same_credentialhas_token (UI) and get_provider_bearer_token (backend) can never silently disagree
  • Diff coverage ≥ 80% — all changed lines in security/credentials/ are covered by the three tests above
  • Coverage matrix updated — N/A: behaviour-only change to existing storage path, no feature rows added or removed
  • All affected feature IDs listed under Related — N/A: no matrix feature IDs map to this internal path
  • No new external network dependencies — all tests use in-process temp stores
  • Manual smoke checklist updated — N/A: no release-cut surface touched
  • Linked issue closed via Closes #NNN — see Related

Impact

  • Desktop only. Credential store is a local JSON file under ~/.openhuman/; no mobile/web/CLI impact.
  • Migration is automatic and transparent — runs once on first launch after update, no user action needed.
  • No performance impact — migration is O(n) over active_profiles (typically ≤ 5 entries).
  • Security — narrows key-space for profile lookup, eliminating case-variant phantom duplicates.

Related


AI Authored PR Metadata

Linear Issue

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

Commit & Branch

  • Branch: fix/deepseek-provider-slug-normalization
  • Commit SHA: e48d512fc

Validation Run

  • pnpm --filter openhuman-app format:check — Blocked (no frontend files changed; CI will verify)
  • pnpm typecheck — Blocked (node_modules not installed locally; no frontend files changed)
  • Focused Rust tests: cargo test -p openhuman credentials — passed
  • cargo fmt --all --check — clean (exit 0)
  • Tauri fmt/check — N/A: no Tauri shell files changed

Validation Blocked

  • command: pnpm --filter openhuman-app format:check
  • error: node_modules missing
  • impact: None — no frontend files modified in this PR

Behavior Changes

  • Intended: Provider slugs are always stored lowercase regardless of what the caller passes.
  • User-visible: Saving any provider API key (DeepSeek, OpenAI, etc.) immediately works. Existing keys are silently migrated on first launch — no re-entry required.

Parity Contract

  • Legacy behavior preserved: eq_ignore_ascii_case fallback on read paths ensures profiles written before this fix continue to resolve without data loss.
  • Parity test: ui_has_api_key_check_and_backend_get_profile_agree_for_same_credential enforces strict equality between UI surface and backend resolution path.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): Previous push to this branch (4dc3e9b) — pre-rebase, wrong file paths
  • Canonical PR: this one
  • Resolution: superseded via git push --force-with-lease

Summary by CodeRabbit

  • Bug Fixes

    • Provider credentials now work consistently regardless of capitalization.
    • Profile selection recognizes provider names case-insensitively while preserving exact matches.
    • Existing profiles with mixed-case provider names are automatically normalized and saved during loading.
    • Conflicting profile entries are handled consistently during normalization.
  • Tests

    • Added coverage for mixed-case credential lookup, profile migration, conflict handling, and consistency between credential detection and retrieval.

@aryash45
aryash45 requested a review from a team August 7, 2026 05:30

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: faa0f23f-23cc-4a64-b2f1-979864a375c4

📥 Commits

Reviewing files that changed from the base of the PR and between 6c1b731 and cf4b513.

📒 Files selected for processing (3)
  • src/openhuman/security/credentials/core.rs
  • src/openhuman/security/credentials/ops.rs
  • src/openhuman/security/credentials/profiles.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/openhuman/security/credentials/core.rs
  • src/openhuman/security/credentials/ops.rs
  • src/openhuman/security/credentials/profiles.rs

📝 Walkthrough

Walkthrough

Provider identifiers are normalized to lowercase during storage and profile loading. Credential and profile lookups accept provider names without case sensitivity. Regression tests cover token retrieval, UI detection parity, provider filtering, and persisted active-profile migration.

Changes

Credential provider normalization

Layer / File(s) Summary
Normalize provider identifiers
src/openhuman/security/credentials/core.rs, src/openhuman/security/credentials/ops.rs, src/openhuman/security/credentials/ops_tests.rs
Provider names are trimmed and normalized before storage. Storage outcomes are logged. Credential retrieval, UI detection, and provider filtering tests cover mixed-case identifiers.
Resolve provider lookups
src/openhuman/security/credentials/core.rs
Active-profile selection retains exact-key precedence and adds case-insensitive key and provider fallback matching. Namespaced providers use namespaced active profiles when available.
Migrate active-profile keys
src/openhuman/security/credentials/profiles.rs, src/openhuman/security/credentials/profiles_tests.rs
Profile loading lowercases active-profile keys and persists the normalized map during writable loads. Tests verify in-memory normalization, on-disk rewriting, and collision handling.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Poem

A rabbit stores provider names low,
So mixed-case lookups find and go.
Profiles normalize keys on read,
Lowercase entries keep their lead.
Tests check each credential trail.

🚥 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 title clearly summarizes provider slug normalization and automatic migration, which are the main changes in the pull request.
Linked Issues check ✅ Passed The changes address DeepSeek save/read mismatches through normalization, legacy fallbacks, profile migration, and regression tests for credential consistency [#5349].
Out of Scope Changes check ✅ Passed All code and test changes concern provider credential storage, retrieval, profile migration, logging, or regression coverage related to the linked issue.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/openhuman/security/credentials/core.rs (1)

152-173: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Support bare provider slugs during profile selection.

A credential stored as provider:DeepSeek becomes provider:deepseek. A lookup for deepseek cannot match its active key, default profile ID, or stored provider value. The assertion at Line 1419 fails. Bare-slug callers also cannot read this credential.

Use one shared provider-equivalence helper that treats provider:<slug> and <slug> as the same provider. Apply it to active-profile and fallback-profile matching.

Proposed fix
+fn provider_matches(left: &str, right: &str) -> bool {
+    let bare = |value: &str| value.strip_prefix("provider:").unwrap_or(value);
+    bare(left).eq_ignore_ascii_case(bare(right))
+}
+
-            .find(|(k, _)| k.eq_ignore_ascii_case(provider))
+            .find(|(k, _)| provider_matches(k, provider))
             .map(|(_, v)| v)
@@
-            .provider
-            .eq_ignore_ascii_case(provider)
+            .provider
+            .as_str()
+            .pipe(|stored_provider| provider_matches(stored_provider, provider))
             .then(|| id.clone())

Use an ordinary local binding instead of .pipe(...) if that helper is not already available.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/security/credentials/core.rs` around lines 152 - 173, Update
the profile-selection logic around the active-profile lookup and fallback search
to use one shared provider-equivalence helper that treats provider:<slug> and
the bare <slug> as equivalent, matching case-insensitively. Apply this helper to
active profile keys, default profile IDs, and stored profile.provider values so
bare-slug callers resolve existing credentials; use a local binding instead of
.pipe(...) if needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/openhuman/security/credentials/core.rs`:
- Around line 45-46: In src/openhuman/security/credentials/core.rs:45-46, add
grep-friendly diagnostics for provider normalization and the credential storage
result, logging only the normalized provider and sanitized outcome; in
core.rs:152-173, log which selection branch was used—exact active key, fallback
active key, default profile, provider fallback, or no match—without profile
names or metadata; in src/openhuman/security/credentials/ops.rs:964-964, add
entry, normalized-provider, and sanitized failure/completion diagnostics,
ensuring tokens, metadata values, and full PII are never logged.

In `@src/openhuman/security/credentials/ops.rs`:
- Line 964: Update list_provider_credentials to compare provider_filter with
stored provider values using eq_ignore_ascii_case instead of exact equality,
while preserving existing filtering behavior for other fields. Add a regression
test that stores a mixed-case provider and successfully retrieves it using a
differently cased provider filter.

In `@src/openhuman/security/credentials/profiles.rs`:
- Around line 854-862: Update the active-profile migration loop in the
profile-loading method around new_active to detect case-insensitive key
collisions before insertion instead of silently overwriting entries. Prefer an
existing lowercase key, record and report conflicts where legacy keys differ
only by case, and add a regression test covering two case variants that
reference different profile IDs while preserving the selected lowercase profile.
- Around line 854-867: The persisted active-profile key normalization branch
lacks a migration diagnostic. Update the key-migration flow around key_migrated
and new_active to emit a verbose, grep-friendly [auth] log when normalization
occurs, including the count of migrated keys, while excluding provider keys and
profile IDs.

---

Outside diff comments:
In `@src/openhuman/security/credentials/core.rs`:
- Around line 152-173: Update the profile-selection logic around the
active-profile lookup and fallback search to use one shared provider-equivalence
helper that treats provider:<slug> and the bare <slug> as equivalent, matching
case-insensitively. Apply this helper to active profile keys, default profile
IDs, and stored profile.provider values so bare-slug callers resolve existing
credentials; use a local binding instead of .pipe(...) if needed.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f68665c0-a4bd-40fb-989b-d0b23ffdee95

📥 Commits

Reviewing files that changed from the base of the PR and between 8deb5f2 and e48d512.

📒 Files selected for processing (5)
  • src/openhuman/security/credentials/core.rs
  • src/openhuman/security/credentials/ops.rs
  • src/openhuman/security/credentials/ops_tests.rs
  • src/openhuman/security/credentials/profiles.rs
  • src/openhuman/security/credentials/profiles_tests.rs

Comment thread src/openhuman/security/credentials/core.rs
Comment thread src/openhuman/security/credentials/ops.rs Outdated
Comment thread src/openhuman/security/credentials/profiles.rs
Comment thread src/openhuman/security/credentials/profiles.rs

@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: e48d512fc0

ℹ️ 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".


// 3. Short slug lookup
let token_slug = auth
.get_provider_bearer_token("deepseek", None)

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 Make the short-slug lookup test pass

This assertion cannot pass with the implementation in this commit: the store writes only the normalized provider:deepseek:default profile, while get_provider_bearer_token("deepseek", None) searches the active/default/provider entries for the bare deepseek key and never strips the provider: namespace. The newly added test will panic here and fail CI unless short-slug lookup is implemented or this expectation is removed.

Useful? React with 👍 / 👎.

Comment on lines +856 to +861
for (k, v) in &persisted.active_profiles {
let lower = k.to_ascii_lowercase();
if lower != *k {
key_migrated = true;
}
new_active.insert(lower, v.clone());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Migrate legacy mixed-case profile IDs too

For a legacy store containing profiles["provider:DeepSeek:default"], this only lowercases the active_profiles map key while leaving the active value, profile id, and profile.provider mixed-case. That makes the credential selectable via get_profile("provider:deepseek"), but later auth_remove_provider_credentials derives provider:deepseek:default from the normalized provider and returns removed=false, so the user cannot actually clear that migrated/stale secret. Either migrate the profile IDs/provider fields as part of this rewrite or make removal resolve legacy ids case-insensitively.

Useful? React with 👍 / 👎.

@aryash45
aryash45 force-pushed the fix/deepseek-provider-slug-normalization branch from e48d512 to 1ceee59 Compare August 7, 2026 06:10

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/openhuman/security/credentials/profiles.rs`:
- Around line 854-880: The active-profile migration count currently uses
persisted entry count minus conflicts rather than the number of keys whose
casing changed. In the migration loop around new_active and migration_conflicts,
add a counter incremented only when lower != *k, then use that counter in the
normalized-key debug message while preserving collision handling and existing
diagnostics.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3f660c77-d2be-4449-8a8b-6cb972b1b765

📥 Commits

Reviewing files that changed from the base of the PR and between 8deb5f2 and 1ceee59.

📒 Files selected for processing (5)
  • src/openhuman/security/credentials/core.rs
  • src/openhuman/security/credentials/ops.rs
  • src/openhuman/security/credentials/ops_tests.rs
  • src/openhuman/security/credentials/profiles.rs
  • src/openhuman/security/credentials/profiles_tests.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/openhuman/security/credentials/ops_tests.rs
  • src/openhuman/security/credentials/core.rs
  • src/openhuman/security/credentials/ops.rs

Comment thread src/openhuman/security/credentials/profiles.rs Outdated
@aryash45

aryash45 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

hi @Al629176 please review this pr and tell me if anything needs to be changed

@aryash45
aryash45 force-pushed the fix/deepseek-provider-slug-normalization branch from 1ceee59 to 47a3d1f Compare August 7, 2026 06:19

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 7, 2026
@aryash45

aryash45 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

hi @senamakel @Al629176 please review this pr

@aryash45

aryash45 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

hello @sanil-23 @senamakel @senamakel-droid @Al629176 please review this pr

@tinysweeper tinysweeper 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.

tinysweeper found nothing blocking. Approving.

             $0.1117 · 71,019 in / 5,065 out · 3,584 cached (5%)  · minimax/minimax-m3, moonshotai/kimi-k3
critique:    $0.0412 · 13,009 in / 1,236 out · 0 cached (0%)      · moonshotai/kimi-k3, minimax/minimax-m3
security:    $0.0548 · 16,112 in / 430 out   · 0 cached (0%)      · moonshotai/kimi-k3
tests:       $0.0019 · 6,585 in  / 157 out   · 896 cached (14%)   · minimax/minimax-m3
commits:     $0.0038 · 10,595 in / 866 out   · 1,792 cached (17%) · minimax/minimax-m3
description: $0.0036 · 7,867 in  / 1,166 out · 768 cached (10%)   · minimax/minimax-m3

let mut new_active: BTreeMap<String, String> = BTreeMap::new();
let mut migration_conflicts: usize = 0;
let mut casing_changed_count: usize = 0;
for (k, v) in &persisted.active_profiles {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority medium critique likely

Collision migration can drop the canonical lowercase entry

persisted.active_profiles is a BTreeMap, whose iteration order is lexicographic by bytes. Uppercase ASCII letters sort before lowercase, so for a slug like provider:DeepSeek vs provider:deepseek, the mixed-case key is visited first. The loop inserts new_active["provider:deepseek"] = <mixed-case value>, then when the canonical lowercase key is visited lower == *k, the new_active.contains_key(&lower) guard is skipped (it lives inside the if lower != *k block), and new_active.insert(lower, v.clone()) silently overwrites the mixed-case winner — wait, actually the reverse also matters: since the mixed-case key comes first, it claims the lowercase slot, and the later canonical lowercase entry overwrites it. The overwrite is invisible: no migration_conflicts increment, no warning, and the 'prefer existing' logic never fires.

More importantly, the guard is on the wrong side of the ordering: whether the lowercase entry wins or loses depends entirely on which variant sorts first in BTreeMap, not on which one is canonical. For keys whose mixed-case form sorts after the lowercase form (e.g. provider:deepSeek — lowercase d... no, S < s, but compare provider:deepseek vs provider:deepSeek: at the S/s position, S sorts first), the mixed-case entry is processed first, inserts under the lowercase key, and then the canonical lowercase entry arrives and overwrites it — again silently, because lower == *k skips the conflict check entirely.

So the deterministic outcome is: whichever entry is visited last wins, and conflicts are only counted when a mixed-case key is visited after a lowercase one was already inserted. The companion test migration_collision_prefers_existing_lowercase_key passes only because its chosen keys happen to order the lowercase entry last (provider:DeepSeek < provider:deepseek since D < d). Flip the casing to something like provider:deepseek vs provider:DEEPSEEK (D < d, so provider:DEEPSEEK is visited first, inserts under provider:deepseek, then the canonical provider:deepseek entry overwrites — that still prefers lowercase). But provider:deepseek vs provider:deepSeeK? S < s so mixed-case first again. In fact for any pair differing only in case, the variant with more uppercase in the earliest differing position is visited first — meaning the lowercase entry, when present, is always visited last and always wins by overwrite, never via the conflict path. The contains_key conflict branch is therefore dead code for the two-entry case, and migration_conflicts is only reachable with three or more case variants.

The real hazard: the silent overwrite means the intended 'prefer the existing entry, log a conflict' behavior never executes for the common two-variant case, and if iteration semantics ever change (or a third variant exists), which profile ID survives depends on map ordering rather than on a canonical rule. Make the preference explicit: first collect all lowercase-keyed entries, then merge in case-variant entries only for lowercase keys not already claimed, counting every dropped variant as a conflict regardless of visit order.

rule iteration-order-collision ·

return result;
}

// Short-slug namespace fallback: a bare slug (e.g. "deepseek") also

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority medium description likely

Diff adds a bare-slug → namespaced-slug resolution path the description does not

Beyond the documented write-time normalization and case-insensitive read fallback, select_profile_id now resolves a bare slug (deepseek) against the namespaced form (provider:deepseek) on disk. This is a meaningful behaviour change (different caller inputs can resolve to the same stored credential) that the PR summary does not mention. Please either document it in the description, with rationale, or restrict it to a narrower case.

rule The PR description scopes the change to: lowercase-at-write, eq_ignore_ascii_casefallback on reads, and a migration of legacyactive_profileskeys. It never mentions that a bare slug likedeepseekwill now resolve credentials stored underprovider:deepseek. That is a separate, user-visible behaviour change with security implications — two callers asking for different slugs can hit the same on-disk key. ·

if lower != *k {
key_migrated = true;
casing_changed_count += 1;
// Collision: a canonical lowercase key already exists. Prefer

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority medium description likely

Migration collision policy silently drops a mixed-case entry; not described

When a lowercased key already exists, the migration drops the mixed-case entry without retaining the profile ID it pointed at. The PR description does not mention this collision-handling policy or the loss it can cause. Please document the behaviour (and consider logging the dropped profile ID at debug level for forensics).

rule The PR description says legacy active_profiles keys are normalised to lowercase on first load. It does not say that when a mixed-case key collides with an already-canonical lowercase key, the mixed-case entry is silently discarded (only its count is logged, the actual provider key / profile ID are not). That is a destructive-on-load policy decision that should be called out. ·

set_active: bool,
) -> Result<AuthProfile> {
let mut profile = AuthProfile::new_token(provider, profile_name, token.to_string());
let provider = normalize_provider(provider)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority medium description uncertain

normalize_provider is used but not defined or imported in this diff

The core.rs change calls normalize_provider(provider)? but the diff does not add or re-export this function. Please confirm the symbol is already in scope (and, if not, either define it here or import it from its existing home); otherwise the build is broken.

rule The diff introduces a call to normalize_provider(provider)?instore_provider_token, but no definition, use, or re-export of normalize_provideris added in the diff. The symbol must already exist in the module; if it does, this is a real fix; if it does not,cargo test -p openhuman credentials (claimed passing in the PR) cannot have run. Worth confirming before merge. ·

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/openhuman/security/credentials/core.rs`:
- Around line 188-204: Update the bare-provider fallback in the
profile-selection function to resolve provider:{slug} through the existing
active-profile, default-profile, and provider-scan precedence rather than
directly scanning data.profiles. Preserve the fallback log and return behavior,
and add a regression test with two namespaced profiles where
active_profiles["provider:deepseek"] selects the non-first profile.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: efeb5d22-a0ce-454a-8a4c-63c9bdf58abd

📥 Commits

Reviewing files that changed from the base of the PR and between 47a3d1f and 6c1b731.

📒 Files selected for processing (1)
  • src/openhuman/security/credentials/core.rs

Comment thread src/openhuman/security/credentials/core.rs Outdated
@aryash45
aryash45 force-pushed the fix/deepseek-provider-slug-normalization branch from 6c1b731 to 47a3d1f Compare August 9, 2026 09:22

@tinysweeper tinysweeper 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.

Requesting changes: 4 lane(s) blocking, worst finding is high.

Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.

             $0.0972 · 63,597 in / 6,973 out · 5,504 cached (9%)  · moonshotai/kimi-k3, minimax/minimax-m3
critique:    $0.0236 · 12,123 in / 225 out   · 1,152 cached (10%) · moonshotai/kimi-k3, minimax/minimax-m3
security:    $0.0566 · 17,023 in / 781 out   · 2,304 cached (14%) · moonshotai/kimi-k3
tests:       $0.0057 · 11,422 in / 2,084 out · 768 cached (7%)    · minimax/minimax-m3
commits:     $0.0048 · 10,276 in / 1,509 out · 512 cached (5%)    · minimax/minimax-m3
description: $0.0065 · 12,753 in / 2,374 out · 768 cached (6%)    · minimax/minimax-m3

Comment thread src/openhuman/security/credentials/ops_tests.rs
Comment thread src/openhuman/security/credentials/profiles_tests.rs
Comment thread src/openhuman/security/credentials/core.rs
Comment thread src/openhuman/security/credentials/profiles.rs Outdated
Comment thread src/openhuman/security/credentials/core.rs
Comment thread src/openhuman/security/credentials/profiles.rs
Comment thread src/openhuman/security/credentials/core.rs
Comment thread src/openhuman/security/credentials/profiles.rs
Comment thread src/openhuman/security/credentials/core.rs
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 9, 2026
@aryash45

aryash45 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

@senamakel please guide me through this

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.

DeepSeek API key saved but dialog says not saved

1 participant