Skip to content

Conversation

@codefromthecrypt
Copy link
Collaborator

@codefromthecrypt codefromthecrypt commented Dec 21, 2025

Summary

When OPENAI_API_KEY is set via environment variable, Goose still prompted for keychain access because secondary config lookups (e.g. OPENAI_CUSTOM_HEADERS) fell through to keyring.

This adds get_secrets(primary, maybe_secret) that checks if the primary key exists in environment variables. If it does, all lookups use env-only mode, completely bypassing keyring. Secondary keys also fall back to get_param() automatically.

Type of Change

  • Bug fix

AI Assistance

  • This PR was created or reviewed with AI assistance

Testing

Verified no keychain access occurs by temporarily adding a panic in the keyring path - all tests still passed.

Related Issues

Relates to #4018

@michaelneale
Copy link
Collaborator

well worthwhile when running local unit tests for productivity.

When OPENAI_API_KEY is set via environment variable, Goose still
prompted for keychain access because secondary config lookups
(e.g. OPENAI_CUSTOM_HEADERS) fell through to keyring.

Add get_secrets(primary, maybe_secret) that uses env-only mode when
the primary key exists in environment variables. This prevents
unnecessary keychain prompts during normal usage and cargo test.

Tests updated to use file-based storage or set required env vars.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
@codefromthecrypt codefromthecrypt marked this pull request as ready for review December 21, 2025 08:02
Copilot AI review requested due to automatic review settings December 21, 2025 08:02
pub const CONFIG_YAML_NAME: &str = "config.yaml";

#[cfg(test)]
const TEST_KEYRING_SERVICE: &str = "goose-test";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

FYI, this may look scary, but AFAICT tests never actually used the keyring.

The tests calling Config::new(..., TEST_KEYRING_SERVICE) only exercised set_param/get_param - which read/write YAML config, not secrets. The keyring service parameter was passed but never touched.

So, removing this is mainly to prevent us from accidentally depending on keyring access in tests that don't actually need it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

heh. wow.

Copy link
Contributor

Copilot AI left a comment

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 fixes an issue where cargo test would trigger keychain access prompts even when OPENAI_API_KEY was set via environment variables. The fix introduces a new get_secrets() method that detects when the primary secret key is in the environment and, if so, bypasses keyring entirely for both primary and secondary keys.

Key Changes

  • Added Config::get_secrets() method that uses env-only mode when the primary key is in environment variables
  • Updated OpenAI and LiteLLM providers to use the new unified secrets lookup
  • Modified tests to use file-based secrets instead of keyring for better isolation

Reviewed changes

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

Show a summary per file
File Description
crates/goose/src/config/base.rs Adds get_secrets() method for unified secret lookup with env-only mode; refactors tests to use new_test_config() helper with file-based secrets
crates/goose/src/providers/openai.rs Migrates from separate get_secret() calls to unified get_secrets() approach
crates/goose/src/providers/litellm.rs Migrates from separate get_secret() calls to unified get_secrets() approach
crates/goose/src/providers/factory.rs Sets API keys in environment for tests to prevent keyring access
crates/goose/src/config/signup_tetrate/tests.rs Updates to use new_with_file_secrets() constructor

.and_then(|v| Ok(serde_json::from_value(v.clone())?))
}

/// Get secrets. If primary is in env, use env for all keys. Otherwise use secret storage.
Copy link
Collaborator

Choose a reason for hiding this comment

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

so - prefer env? (what if you only want it in secrets? is that not needed here?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah if we want to add an ignore-env mode we'd want to propagate it down here in a future change.


let config = crate::config::Config::global();
let api_key: String = config.get_secret("OPENAI_API_KEY")?;
let secrets = config.get_secrets("OPENAI_API_KEY", &["OPENAI_CUSTOM_HEADERS"])?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

ah I think I get it - we still have get_secret which works as before (required in secrets?) vs get_secrets which will use env if it finds the key there (and then get all other values as well).

Copy link
Collaborator

Choose a reason for hiding this comment

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

actually I think this is simiar to original get_secret but this is for convenience.. yeah I like it.

@michaelneale
Copy link
Collaborator

yeah I think I like this, makes sense. Also will make it easier for goose to operate on itself as won't be unignorable dialogs popping up when you set it on a task.

@codefromthecrypt codefromthecrypt merged commit 5942e9c into block:main Dec 22, 2025
24 checks passed
@codefromthecrypt codefromthecrypt deleted the fix/lazy-keyring-eval branch December 22, 2025 02:43
@codefromthecrypt
Copy link
Collaborator Author

missed a couple spots.. hopefully these are the last #6231

cronus42 pushed a commit to cronus42/goose that referenced this pull request Dec 22, 2025
Signed-off-by: Adrian Cole <adrian@tetrate.io>
wpfleger96 added a commit that referenced this pull request Dec 23, 2025
* main: (155 commits)
  remove Tool Selection Strategy preview (#6250)
  fix(cli): correct bash syntax in terminal integration functions (#6181)
  fix : opening a session to view it modifies session history order in desktop (#6156)
  test: fix recipe and audio tests to avoid side effects (#6231)
  chore: Update gemini versions in test_providers.sh (#6246)
  feat: option to stream json - jsonl really (#6228)
  feat: add mcp app renderer (#6095)
  docs: update skills extension to support .agents/skills directories (#6199)
  Add YouTube short to Chrome DevTools MCP tutorial (#6244)
  docs: Caveats for privacy information in logs documentation (#6218)
  move goose issue solver to opus (#6233)
  feat: improved UX for tool calls via execute_code (#6205)
  Blog: Code Mode Doesn't Replace MCP (#6227)
  fix: prevent keychain requests during cargo test (#6219)
  test: fix test_max_turns_limit slow execution and wrong message type (#6221)
  Skills vs MCP blog (#6220)
  Add blog post: Does Your AI Agent Need a Plan? (#6209)
  fix(ui): enable MCP UI to send a prompt message when an element is clicked (#6207)
  docs: param option for recipe deeplink/open (#6206)
  docs: edit in place or fork session (#6203)
  ...
cronus42 pushed a commit to cronus42/goose that referenced this pull request Dec 26, 2025
Signed-off-by: Adrian Cole <adrian@tetrate.io>
cronus42 pushed a commit to cronus42/goose that referenced this pull request Dec 29, 2025
Signed-off-by: Adrian Cole <adrian@tetrate.io>
Signed-off-by: James Loope <cronus@stolenshoe.com>
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