Skip to content

fix(secrets): make workspace key rotation atomic#1097

Open
sauraww wants to merge 1 commit into
mainfrom
fix/atomic-workspace-key-rotation
Open

fix(secrets): make workspace key rotation atomic#1097
sauraww wants to merge 1 commit into
mainfrom
fix/atomic-workspace-key-rotation

Conversation

@sauraww

@sauraww sauraww commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Problem

Describe the problem you are trying to solve here

Solution

Provide a brief summary of your solution so that reviewers can understand your code

Environment variable changes

What ENVs need to be added or changed

Pre-deployment activity

Things needed to be done before deploying this change (if any)

Post-deployment activity

Things needed to be done after deploying this change (if any)

API changes

Endpoint Method Request body Response Body
API GET/POST, etc request response

Possible Issues in the future

Describe any possible issues that could occur because of this change

Summary by CodeRabbit

  • Bug Fixes
    • Improved reliability when creating and updating encrypted secrets.
    • Prevented partial secret updates when encryption or decryption fails.
    • Improved consistency during workspace encryption-key rotation.
    • Updated workspace data in the cache after successful key rotation.
  • Tests
    • Added coverage for successful and failed secret re-encryption scenarios.

Copilot AI review requested due to automatic review settings July 14, 2026 17:55
@sauraww
sauraww requested a review from a team as a code owner July 14, 2026 17:55
@semanticdiff-com

semanticdiff-com Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review changes with  SemanticDiff

Changed Files
File Status
  crates/context_aware_config/src/api/secrets/handlers.rs  57% smaller
  crates/service_utils/src/encryption.rs  39% smaller
  crates/superposition/src/workspace/handlers.rs  18% smaller
  crates/service_utils/src/redis.rs  10% smaller

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Workspace encryption and secret updates now use transaction-scoped workspace reloads and locks. Key rotation separates cryptographic processing from database writes, returns updated workspaces, and synchronizes Redis through a shared helper.

Changes

Workspace encryption flow

Layer / File(s) Summary
Encryption rotation core
crates/service_utils/src/encryption.rs
Secret values are re-encrypted before database mutation, failures map to UnexpectedError, and workspace key rotation returns the updated workspace and secret count. Tests cover successful and corrupted-ciphertext cases.
Transactional secret handlers
crates/context_aware_config/src/api/secrets/handlers.rs
Secret creation, updates, and master-key rotation reload or lock workspace rows within transactions and use the updated encryption rotation helper.
Shared Redis cache integration
crates/service_utils/src/redis.rs, crates/superposition/src/workspace/handlers.rs
Workspace Redis updates use a shared best-effort helper across creation, updates, schema migration, and encryption-key rotation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested labels: P0

Suggested reviewers: copilot

Sequence Diagram(s)

sequenceDiagram
  participant Handler
  participant Database
  participant Encryption
  participant Redis
  Handler->>Database: begin transaction and lock workspace
  Database-->>Handler: workspace row
  Handler->>Encryption: rotate workspace key and re-encrypt secrets
  Encryption->>Database: update secrets and workspace
  Database-->>Handler: updated workspace and count
  Handler->>Redis: cache updated workspace
Loading

Poem

I’m a rabbit with keys in my hay,
Locking old rows safely today.
Secrets turn bright, ciphertexts align,
Redis gets the workspace just fine.
Hop, hop—the rotation is done! 🐇

🚥 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 accurately summarizes the main change: making workspace key rotation atomic.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/atomic-workspace-key-rotation

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.

Copilot AI 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.

Pull request overview

This PR strengthens workspace encryption key rotation and secret writes by adding explicit database row locking and refactoring workspace Redis-cache updates into a shared utility, aiming to prevent non-atomic key rotation and writes occurring with stale keys.

Changes:

  • Refactors workspace Redis cache updates into service_utils::redis::put_workspace_in_redis and updates callers accordingly.
  • Makes workspace key rotation more atomic by (a) row-locking the workspace record during rotation, (b) completing crypto before DB mutation, and (c) returning the updated Workspace from the rotation helper for post-commit cache updates.
  • Ensures secret create/update operations acquire a workspace row lock and reload the workspace encryption key from the database inside the same transaction to avoid encrypting with an old key while rotation is in flight.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
crates/superposition/src/workspace/handlers.rs Switches workspace cache updates to shared Redis helper and updates workspace key rotation flow to use atomic helper return value.
crates/service_utils/src/redis.rs Introduces shared put_workspace_in_redis helper for best-effort workspace cache refresh.
crates/service_utils/src/encryption.rs Reworks workspace key rotation to lock the workspace row, precompute crypto before DB writes, and return the updated workspace; adds unit tests for re-encryption helper.
crates/context_aware_config/src/api/secrets/handlers.rs Wraps secret create/update in transactions that acquire a workspace row lock and reload the workspace key from DB; updates master-key rotation to refresh Redis cache post-commit.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@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 `@crates/service_utils/src/redis.rs`:
- Around line 28-60: Update put_workspace_in_redis so concurrent cache writes
are monotonic: atomically compare the snapshot’s last_modified_at with the
cached version and only replace the entry when it is newer, preventing an older
workspace snapshot from overwriting a newer one. If the Redis helpers cannot
support this conditional write, invalidate the workspace key instead of
performing an unconditional stale overwrite.
🪄 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: CHILL

Plan: Pro

Run ID: 851fd65f-c3d0-4a61-b141-f28ec4771fc5

📥 Commits

Reviewing files that changed from the base of the PR and between b71e629 and e060ddc.

📒 Files selected for processing (4)
  • crates/context_aware_config/src/api/secrets/handlers.rs
  • crates/service_utils/src/encryption.rs
  • crates/service_utils/src/redis.rs
  • crates/superposition/src/workspace/handlers.rs

Comment on lines +28 to +60
/// Best-effort update of the canonical workspace cache entry.
pub async fn put_workspace_in_redis(
workspace: &Workspace,
redis_pool: &Option<RedisPool>,
) {
let Some(pool) = redis_pool else {
log::debug!("Redis not configured, skipping workspace cache update");
return;
};

let serialized = match serde_json::to_string(workspace) {
Ok(serialized) => serialized,
Err(error) => {
log::error!("Failed to serialize workspace for Redis: {}", error);
return;
}
};
let key_ttl: i64 = get_from_env_or_default("REDIS_KEY_TTL", 604800);

if let Err(error) = redis_set_data(
pool,
workspace.workspace_schema_name.clone(),
serialized,
Some(Expiration::EX(key_ttl)),
)
.await
{
log::warn!(
"Failed to update Redis cache for workspace '{}': {}",
workspace.workspace_schema_name,
error
);
}

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.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make workspace cache updates monotonic.

Concurrent mutations can commit in order W1, W2 but complete these unconditional Redis writes as W2, W1, leaving the older encryption key cached. Use an atomic version check based on last_modified_at, or invalidate the entry rather than overwriting it with a potentially stale snapshot.

🤖 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 `@crates/service_utils/src/redis.rs` around lines 28 - 60, Update
put_workspace_in_redis so concurrent cache writes are monotonic: atomically
compare the snapshot’s last_modified_at with the cached version and only replace
the entry when it is newer, preventing an older workspace snapshot from
overwriting a newer one. If the Redis helpers cannot support this conditional
write, invalidate the workspace key instead of performing an unconditional stale
overwrite.

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.

2 participants