Skip to content

feat(oauth): support recoverable profile rename - #260

Merged
mohanagy merged 2 commits into
developmentfrom
feat/204-native-oauth-profile-rename
Jul 26, 2026
Merged

feat(oauth): support recoverable profile rename#260
mohanagy merged 2 commits into
developmentfrom
feat/204-native-oauth-profile-rename

Conversation

@mohanagy

@mohanagy mohanagy commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Part of #204.

What changed

  • make native OAuth-bound profile renames transactional across configuration, the OS vault, and connection metadata
  • add owner-only recovery journal and verified source backup without serializing OAuth credentials
  • recover deterministically after interruption, including canonical config identities through symlink paths
  • fail closed when required audit finalization cannot be recorded, rolling back the rename safely
  • surface the recovery contract in the CLI, Console, and README

Validation

  • focused OAuth/Console/README regression suite: 143 passed
  • lint, typecheck, build, CLI smoke, package contract, and packed-artifact tests passed

Known local aggregate-test infrastructure

Summary by CodeRabbit

  • New Features
    • Native OAuth-bound profiles can now be renamed, securely migrating the OS-vault credential and related non-secret connection metadata together.
    • Interrupted renames are automatically recovered before use resumes, with consistent retry guidance.
  • Bug Fixes
    • Improved native keyring credential handling (including null/missing returns) and tighter validation for OAuth rename workflows.
    • Updated Console API responses for unfinished rename recovery and stale selections (HTTP 409 with the new/expected error codes).
  • Documentation
    • Updated README, CLI, and Console API docs to reflect the OAuth rename behavior and recovery steps.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 25 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d536bfe6-4cfd-40c8-9ba6-1c12a3d9912b

📥 Commits

Reviewing files that changed from the base of the PR and between e851344 and 6543454.

📒 Files selected for processing (2)
  • src/oauth/profile-rename-transaction.ts
  • tests/oauth-profile-rename-transaction.test.ts
📝 Walkthrough

Walkthrough

Native OAuth profile renames now update configuration, OS-vault credentials, and connection metadata through a recoverable journaled transaction. Startup recovery, locking, audit events, CLI/HTTP errors, documentation, and failure-path tests were added.

Changes

Native OAuth profile rename

Layer / File(s) Summary
OAuth state coordination
src/oauth/connection-lifecycle.ts, src/oauth/connection-registry.ts, src/oauth/connection-types.ts, src/oauth/local-state-paths.ts, src/oauth/secure-credential-store.ts
Adds deterministic binding locks, profile-binding migration and restoration, stable binding comparisons, platform metadata paths, and null-safe vault reads.
Journaled rename transaction
src/oauth/profile-rename-transaction.ts, tests/oauth-profile-rename-transaction.test.ts, tests/helpers/profile-rename-oauth-dependencies.ts
Adds strict journal validation, source backups, credential and metadata migration, rollback, forward recovery, audit finalization, and cleanup.
Rename entrypoints and runtime recovery
src/setup/profile-rename-onboarding.ts, src/oauth/remote-oauth-runtime.ts, src/cli/migrate-config.ts, tests/profile-rename-onboarding.test.ts, tests/remote-oauth-runtime.test.ts
Allows OAuth-bound planning, delegates execution to the transaction, canonicalizes paths, and recovers interrupted renames before runtime startup.
Console wiring and recovery errors
src/console/..., src/utils/errors.ts, src/cli/exit-codes.ts, tests/console-*.test.ts
Wires OAuth rename dependencies and recovered audit events, and maps recovery-required failures to CLI and HTTP responses.
Behavior tests and product contracts
tests/oauth-connection-*.test.ts, tests/oauth-secure-credential-store.test.ts, README.md, docs/*.md, tests/*contract.test.ts
Covers locking, migration, recovery, vault behavior, and updated product and API documentation contracts.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ProfileRename
  participant Journal
  participant Vault
  participant Registry
  Client->>ProfileRename: request profile rename
  ProfileRename->>Journal: persist intent and source backup
  ProfileRename->>Vault: migrate exact credential keys
  ProfileRename->>Registry: migrate non-secret binding metadata
  ProfileRename->>Journal: finalize and remove recovery artifacts
Loading

Possibly related PRs

Poem

A rabbit renamed a profile with care,
Moving two vault keys through moonlit air.
The journal watched each careful flight,
Then cleaned the trail before daylight.
“No secret copied!” thumped the hare.


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Docstring Coverage ❌ Error Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning It covers the change and validation, but misses the required Summary and Security impact sections and does not list exact validation commands. Add the required Summary and Security impact sections, and replace the generic validation notes with the exact commands and results for each checklist item.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: recoverable OAuth profile renames.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/204-native-oauth-profile-rename

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

coderabbitai[bot]
coderabbitai Bot previously requested changes Jul 26, 2026

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

Actionable comments posted: 11

🤖 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 `@docs/cli.md`:
- Line 33: Update the documentation row for miftah profile rename to include the
interrupted-rename recovery contract: document that
OAUTH_PROFILE_RENAME_RECOVERY_REQUIRED is surfaced as a CLI failure with exit
code 1 and operators must retry the same command surface rather than
hand-editing state, matching the existing README guidance.

In `@docs/console-api.md`:
- Line 36: Update the endpoint summary table row for POST
/api/v1/profiles/rename to document atomic native-OAuth credential and metadata
migration, rather than rejecting configured native OAuth bindings or claiming
credentials never move. Also document the OAUTH_PROFILE_RENAME_RECOVERY_REQUIRED
outcome as HTTP 409 oauth_profile_rename_recovery_required, matching
console-server.ts.

In `@src/console/console-server.ts`:
- Around line 422-428: Update the error mapping near the existing
OAUTH_PROFILE_RENAME_RECOVERY_REQUIRED branch to handle error.code
PROFILE_SELECTION_STALE, returning a stable 409 ConsoleHttpError with the
appropriate client-facing error identifier and message instructing clients to
reload configuration before retrying.

In `@src/oauth/connection-lifecycle.ts`:
- Around line 246-265: Update status() to avoid waiting the full refresh lock
budget for read-only status checks: derive the expiry state from the current
record without acquiring the cross-process binding lock, and only acquire the
lock when a credential-state write is required, reloading the record before
persisting to avoid overwriting refresh changes.

In `@src/oauth/connection-registry.ts`:
- Around line 164-178: The profile-only binding equality predicate is duplicated
across the registry and rename transaction. Add one shared predicate beside
sameOAuthConnectionBinding in src/oauth/connection-types.ts, then delete
validProfileBindingMigration in src/oauth/connection-registry.ts (lines 164-178)
and sameBindingExceptProfile in src/oauth/profile-rename-transaction.ts (lines
227-240), importing and using the shared predicate at both call sites.

In `@src/oauth/profile-rename-transaction.ts`:
- Around line 557-558: Handle the return value of
dependencies.registry.migrateProfileBinding in the profile-rename transaction
and call recoveryRequired() when it returns undefined while the binding has
recorded originalMetadata. Preserve normal forward completion when a record is
migrated or no metadata snapshot exists, matching the module’s existing
divergence handling.
- Around line 399-430: Update readSourceBackup to lstat journal.sourceBackupPath
before calling readConfigMigrationSource, and reject recovery when the result is
not a regular file or is a symbolic link. Keep validBackupPath and the existing
sourceHash validation unchanged, and ensure cleanupJournal cannot operate on an
accepted symlink path.
- Around line 707-722: Extend the binding validation in the exported
profile-rename entrypoint around the existing request.bindings checks to match
parseJournal invariants: reject more than 128 bindings, reject duplicate from
credential keys, and require every binding to share one from.profile/to.profile
pair. Keep the existing identity and sameBindingExceptProfile validation, and
throw the established OAUTH_CONNECTION_INVALID error for each invalid set before
writing the journal.
- Around line 496-520: Update cleanupJournal to remove the journal via
store.remove(configPath) before deleting journal.sourceBackupPath, keeping the
verified source bytes available for rollback if journal removal fails. Preserve
the existing error handling and ensure backup deletion occurs only after the
journal is successfully removed, so a crash leaves at most an orphaned backup.
- Around line 783-788: Update createPlatformOAuthProfileRenameDependencies to
accept a shared SecretRedactor and pass it to createPlatformOAuthCredentialStore
instead of allowing the store to create its fallback redactor. Update each
profile-rename entrypoint that calls this dependency factory to supply the same
redactor instance used by the surrounding flow.

In `@tests/oauth-profile-rename-transaction.test.ts`:
- Around line 31-60: Replace the local MemoryCredentialStore and
MemoryMetadataStore definitions with the shared
MemoryProfileRenameCredentialStore and MemoryProfileRenameMetadataStore from the
profile-rename OAuth dependencies helper. Extend the shared credential store
only as needed to support this test’s failOldDelete behavior, then configure
that hook in the test while preserving the existing deletion failure semantics.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 55676347-ff14-4e73-a769-951e0f6a7f70

📥 Commits

Reviewing files that changed from the base of the PR and between 40d4f52 and b14408c.

📒 Files selected for processing (25)
  • README.md
  • docs/cli.md
  • docs/console-api.md
  • src/cli/exit-codes.ts
  • src/cli/migrate-config.ts
  • src/console/console-application-service.ts
  • src/console/console-assets.ts
  • src/console/console-server.ts
  • src/oauth/connection-lifecycle.ts
  • src/oauth/connection-registry.ts
  • src/oauth/local-state-paths.ts
  • src/oauth/profile-rename-transaction.ts
  • src/oauth/remote-oauth-runtime.ts
  • src/oauth/secure-credential-store.ts
  • src/setup/profile-rename-onboarding.ts
  • src/utils/errors.ts
  • tests/cli-exit-codes.test.ts
  • tests/console-application-service.test.ts
  • tests/console-server.test.ts
  • tests/helpers/profile-rename-oauth-dependencies.ts
  • tests/oauth-profile-rename-transaction.test.ts
  • tests/oauth-secure-credential-store.test.ts
  • tests/profile-rename-onboarding.test.ts
  • tests/readme-product-contract.test.ts
  • tests/remote-oauth-runtime.test.ts

Comment thread docs/cli.md Outdated
Comment thread docs/console-api.md
Comment thread src/console/console-server.ts
Comment thread src/oauth/connection-lifecycle.ts
Comment thread src/oauth/connection-registry.ts Outdated
Comment thread src/oauth/profile-rename-transaction.ts
Comment thread src/oauth/profile-rename-transaction.ts Outdated
Comment thread src/oauth/profile-rename-transaction.ts
Comment thread src/oauth/profile-rename-transaction.ts Outdated
Comment thread tests/oauth-profile-rename-transaction.test.ts Outdated
@mohanagy
mohanagy force-pushed the feat/204-native-oauth-profile-rename branch from 9cf1690 to e851344 Compare July 26, 2026 19:57
coderabbitai[bot]
coderabbitai Bot previously requested changes Jul 26, 2026

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

Actionable comments posted: 3

🤖 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/oauth/profile-rename-transaction.ts`:
- Around line 525-539: Move the target-key absence validation before
captureJournal and store.create, so any existing target credential is rejected
as a normal collision before the transaction becomes rollback-capable. Keep
prepareTargetCredentials as the in-transaction invariant check, but ensure the
precondition uses the appropriate target-conflict error rather than
recoveryRequired, preventing rollback from deleting credentials this transaction
did not create.

In `@src/setup/profile-rename-onboarding.ts`:
- Around line 252-313: Remove PROFILE_RENAME_OAUTH_CONNECTION from the public
error contract, including its MiftahErrorCode declaration, ERROR_EXIT_CODES
entry, and HTTP error mapping. Verify no remaining references advertise or
depend on this unreachable outcome, without adding a new throw site.

In `@tests/oauth-profile-rename-transaction.test.ts`:
- Around line 359-441: Add a test covering runProfileRename when credentials
already exist at both oldBinding and the target newBinding before execution.
Assert the rename fails through the expected rollback/recovery path and that the
pre-existing target credential remains intact after failure, exercising
prepareTargetCredentials’ to !== undefined branch and preventing
completeRollback from deleting it.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f11f3802-0dad-444a-b81e-82cc8bec54f0

📥 Commits

Reviewing files that changed from the base of the PR and between b14408c and e851344.

📒 Files selected for processing (30)
  • README.md
  • docs/cli.md
  • docs/console-api.md
  • src/cli/exit-codes.ts
  • src/cli/migrate-config.ts
  • src/console/console-application-service.ts
  • src/console/console-assets.ts
  • src/console/console-server.ts
  • src/oauth/connection-lifecycle.ts
  • src/oauth/connection-registry.ts
  • src/oauth/connection-types.ts
  • src/oauth/local-state-paths.ts
  • src/oauth/profile-rename-transaction.ts
  • src/oauth/remote-oauth-runtime.ts
  • src/oauth/secure-credential-store.ts
  • src/setup/profile-rename-onboarding.ts
  • src/utils/errors.ts
  • tests/cli-exit-codes.test.ts
  • tests/console-application-service.test.ts
  • tests/console-server.test.ts
  • tests/helpers/profile-rename-oauth-dependencies.ts
  • tests/oauth-connection-lifecycle.test.ts
  • tests/oauth-connection-registry.test.ts
  • tests/oauth-connection-types.test.ts
  • tests/oauth-console-threat-model-docs-contract.test.ts
  • tests/oauth-profile-rename-transaction.test.ts
  • tests/oauth-secure-credential-store.test.ts
  • tests/profile-rename-onboarding.test.ts
  • tests/readme-product-contract.test.ts
  • tests/remote-oauth-runtime.test.ts

Comment thread src/oauth/profile-rename-transaction.ts
Comment thread src/setup/profile-rename-onboarding.ts
Comment thread tests/oauth-profile-rename-transaction.test.ts
@mohanagy
mohanagy dismissed stale reviews from coderabbitai[bot] and coderabbitai[bot] July 26, 2026 20:48

Dismissed under the documented CodeRabbit rate-limit exception. This review targets e851344, not the current 6543454 head; all three findings were replied to and resolved. Current-head Linux, macOS, Windows, quality, and Verify checks are green, with no unresolved review threads.

@mohanagy
mohanagy merged commit 5aa2817 into development Jul 26, 2026
12 checks passed
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