Skip to content

feat(credential_store): add GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND env var#359

Merged
jpoehnelt merged 6 commits intomainfrom
fix/keyring-backend-selection
Mar 10, 2026
Merged

feat(credential_store): add GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND env var#359
jpoehnelt merged 6 commits intomainfrom
fix/keyring-backend-selection

Conversation

@jpoehnelt
Copy link
Member

Summary

Adds gogcli-style backend selection for encryption key storage, inspired by gogcli.

GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND

Value Behavior
keyring (default) OS keyring with .encryption_key file fallback
file .encryption_key file only — for Docker/CI/headless

Key changes

  • Never delete .encryption_key — it always serves as a durable fallback for ephemeral keyring environments
  • When generating new keys with backend=keyring, save to both keyring and file
  • Extract KeyringProvider trait + resolve_key() for testability
  • 25 unit tests covering both backends and all edge cases
  • Updated env var help, README, AGENTS.md

Root cause (#344)

The previous fix for #344 deleted .encryption_key when the OS keyring succeeded. In Docker containers, the keyring is ephemeral, so the key was permanently lost on container restart. This PR preserves the file and adds an explicit env var to bypass the keyring entirely.

Fixes #344

Add gogcli-style backend selection for encryption key storage:
- keyring (default): OS keyring with file fallback
- file: .encryption_key file only (Docker/CI/headless)

Never delete .encryption_key — it always serves as a durable fallback
for environments where the keyring is ephemeral. When generating new
keys with backend=keyring, save to both keyring and file.

Extracts KeyringProvider trait + resolve_key() for testability.
25 tests covering both backends and all edge cases.

Fixes #344
@changeset-bot
Copy link

changeset-bot bot commented Mar 10, 2026

🦋 Changeset detected

Latest commit: 80ce461

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@googleworkspace/cli Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@googleworkspace-bot googleworkspace-bot added area: docs area: core Core CLI parsing, commands, error handling, utilities labels Mar 10, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new environment variable to provide explicit control over the encryption key storage backend, addressing a critical issue where keys were lost in ephemeral environments. By ensuring the .encryption_key file is always preserved as a durable fallback and refining the key resolution process, the changes significantly enhance the reliability and flexibility of credential management, particularly for headless and containerized deployments. The update also includes substantial refactoring for testability and comprehensive new unit tests.

Highlights

  • Keyring Backend Selection: Introduced the GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND environment variable, allowing users to explicitly choose between keyring (OS keyring with file fallback) and file (file-only) for encryption key storage.
  • Durable Key Fallback: Ensured that the .encryption_key file is never deleted, establishing it as a permanent and durable fallback for encryption keys, especially crucial for ephemeral environments like Docker or CI/CD.
  • Key Generation Strategy: Modified key generation to save new keys to both the OS keyring and the .encryption_key file when the keyring backend is active, enhancing resilience.
  • Improved Testability: Refactored the key resolution logic by extracting a KeyringProvider trait and a resolve_key() function, significantly improving the testability of the credential store.
  • Comprehensive Testing: Added 25 new unit tests to cover all new key resolution scenarios, including both backend types and various edge cases, ensuring robust behavior.
  • Documentation Updates: Updated relevant documentation files, including AGENTS.md, README.md, and CLI usage help, to reflect the new environment variable and the updated key storage behavior.
Changelog
  • .changeset/fix-keyring-fallback.md
    • Added a new changeset entry documenting the GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND environment variable and the fix for credential key loss.
  • AGENTS.md
    • Updated the environment variable documentation to include GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND.
  • README.md
    • Updated the interactive authentication section to mention the .encryption_key file as an alternative key storage location when GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file.
  • src/auth_commands.rs
    • Updated the authentication success message to include information about the new GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND for headless environments.
  • src/credential_store.rs
    • Refactored the key management logic to introduce KeyringProvider trait, KeyringBackend enum, read_key_file, generate_random_key, and resolve_key functions.
    • Ensured the .encryption_key file is never deleted and handles different backend selections.
    • Added extensive unit tests for the new key resolution logic.
  • src/main.rs
    • Added GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND to the printed CLI usage help.
  • src/token_storage.rs
    • Updated a comment to reflect that encryption uses AES-256-GCM generally, rather than specifically mentioning the OS keyring as the sole key derivation source.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Generative AI Prohibited Use Policy, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@googleworkspace-bot googleworkspace-bot added the cla: yes This human has signed the Contributor License Agreement. label Mar 10, 2026
@google-cla google-cla bot added cla: no This human has *not* signed the Contributor License Agreement. and removed cla: yes This human has signed the Contributor License Agreement. labels Mar 10, 2026
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a robust solution for managing encryption keys by adding the GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND environment variable and ensuring the .encryption_key file is always preserved as a fallback. The refactoring of the key retrieval logic into a testable KeyringProvider trait and resolve_key function, along with the comprehensive new test suite, significantly improves the code's maintainability and correctness.

I have one high-severity concern regarding a potential race condition during initial key generation if multiple processes run simultaneously. Please see the detailed comment in src/credential_store.rs.

Note: Security Review did not run due to the size of the PR.

@github-actions github-actions bot added the gemini: reviewed Gemini Code Assist has reviewed the latest changes label Mar 10, 2026
Use create_new(true) (O_EXCL on Unix, CREATE_NEW on Windows) when
generating a new encryption key file. If another process wins the
race, read their key instead. Platform-independent.
@github-actions github-actions bot removed the gemini: reviewed Gemini Code Assist has reviewed the latest changes label Mar 10, 2026
@github-actions
Copy link
Contributor

/gemini review

@codecov
Copy link

codecov bot commented Mar 10, 2026

Codecov Report

❌ Patch coverage is 95.60440% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.21%. Comparing base (5e7d120) to head (80ce461).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/credential_store.rs 96.12% 14 Missing ⚠️
src/auth_commands.rs 50.00% 1 Missing ⚠️
src/main.rs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #359      +/-   ##
==========================================
+ Coverage   62.17%   63.21%   +1.04%     
==========================================
  Files          38       38              
  Lines       14797    15102     +305     
==========================================
+ Hits         9200     9547     +347     
+ Misses       5597     5555      -42     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

When two processes race to create the encryption key file, the loser
now syncs the winner's key back into the keyring. Without this, the
keyring and file could permanently diverge.
@github-actions
Copy link
Contributor

/gemini review

…ing, and race paths

- save_key_file_exclusive: creates new file, rejects existing
- save_key_file: overwrites existing
- ensure_key_dir: creates nested dirs
- KeyringBackend: file/FILE/invalid parsing
- Race loser: syncs winner key to keyring
- Race loser: corrupt file gets overwritten
@github-actions
Copy link
Contributor

/gemini review

1. Warn on unrecognized KEYRING_BACKEND values instead of silent default
2. fsync after key file writes for crash durability
3. Zeroize decoded key material from heap after copy
4. Warn if key file has overly permissive Unix permissions (mode & 077)
5. Log which keyring backend was selected to stderr
6. Expose keyring_backend in 'gws auth status' JSON output
@github-actions
Copy link
Contributor

/gemini review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: auth area: core Core CLI parsing, commands, error handling, utilities area: distribution area: docs area: skills cla: no This human has *not* signed the Contributor License Agreement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

credential_store: .encryption_key file persists on disk even when OS keyring succeeds

3 participants