Skip to content

Add ServerCredentialStore for server-side credential management#539

Merged
sroussey merged 6 commits into
mainfrom
claude/admiring-maxwell-EZmyY
May 28, 2026
Merged

Add ServerCredentialStore for server-side credential management#539
sroussey merged 6 commits into
mainfrom
claude/admiring-maxwell-EZmyY

Conversation

@sroussey
Copy link
Copy Markdown
Collaborator

Summary

Introduces ServerCredentialStore, a project-scoped credential management system that separates secret storage (in a SecretVault) from metadata persistence (in IKvStorage). This enables secure credential handling in the main process with decryption happening only on the server side, never in the renderer.

Key Changes

  • SecretVault interface — abstraction for platform-specific secret storage (Electron safeStorage, OS keychain, etc.). Implementations handle encryption/decryption and must never log secret values.

  • ServerCredentialStore class — implements ICredentialStore with:

    • User and project scoping via ${userId}/${projectId}/${key} vault IDs
    • Automatic expiry detection and cleanup on get(), has(), and listMetadata()
    • Metadata-first gating: secrets are unreachable without metadata rows
    • Rollback on metadata write failure for new entries (orphaned secrets are unreachable)
    • listMetadata() returns credential info without exposing secret values
    • deleteAll() clears all credentials in the project scope, including expired entries
  • CredentialMetadataRow and CredentialMetadataInfo types — separate persisted metadata (with timestamps and expiry) from API-exposed metadata (no secret values).

  • Comprehensive test suite — covers round-trip storage, expiry handling, scope isolation (by user and project), metadata listing, and failure recovery.

  • Exports — added to packages/storage/src/common.ts for public API access.

Implementation Details

  • Metadata rows are the source of truth; get() and has() check metadata first and auto-delete expired entries
  • Vault IDs are opaque to the caller; the store derives them internally
  • put() preserves existing label and provider if not overridden, and createdAt timestamp across updates
  • deleteAll() iterates all metadata to find scoped entries, ensuring no orphaned secrets remain

https://claude.ai/code/session_018MYdampoJqPM8TETqW9Q86

@github-actions
Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 62.02% 24606 / 39674
🔵 Statements 61.88% 25460 / 41144
🔵 Functions 62.94% 4661 / 7405
🔵 Branches 50.68% 12060 / 23792
File CoverageNo changed files found.
Generated in workflow #2445 for commit ace1570 by the Vitest Coverage Report Action

@sroussey sroussey merged commit ce413c3 into main May 28, 2026
13 checks passed
@sroussey sroussey deleted the claude/admiring-maxwell-EZmyY branch May 28, 2026 01:52
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