# How CodexBar Handles Credentials On-Device for Privacy: Secure Keychain Architecture

> Discover how CodexBar secures API keys and tokens on-device using macOS Keychain. Learn about its restricted access controls and automatic storage disabling for enhanced privacy.

- Repository: [Peter Steinberger/CodexBar](https://github.com/steipete/CodexBar)
- Tags: architecture
- Published: 2026-07-07

---

**CodexBar stores all API keys, OAuth tokens, and browser cookies exclusively in the macOS Keychain using generic-password items with restricted SecAccess controls, accessing them without UI prompts while automatically disabling storage for unbundled executables and test environments.**

CodexBar implements a privacy-first credential strategy that ensures sensitive data never touches the filesystem. According to the steipete/CodexBar source code, the architecture centers on a three-layer security model that wraps macOS Security framework calls with centralized access gates and test-specific safety blocks.

## Centralized Access Control with KeychainAccessGate

The `KeychainAccessGate` class in [`Sources/CodexBarCore/KeychainAccessGate.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBarCore/KeychainAccessGate.swift) serves as the single authority for enabling or disabling all Keychain operations. This design prevents accidental credential exposure across different execution contexts.

**Environment-based disabling** allows developers to short-circuit Keychain access by setting `CODEXBAR_DISABLE_KEYCHAIN_ACCESS=1`. When this variable is present, `KeychainAccessGate.isDisabled` returns `true`, causing all reads and writes to return `errSecInteractionNotAllowed` without hitting the Security framework.

**Unbundled executable detection** automatically disables Keychain access when the binary runs outside an `.app` bundle. The `KeychainPromptCoordinator.disableKeychainForUnbundledExecutableIfNeeded()` method calls `KeychainAccessGate.forceDisabledForProcess(reason:)` to avoid repeated system prompts during CLI development.

For unit testing, `KeychainAccessGate.withTaskOverrideForTesting(_:operation:)` temporarily enables or disables access for a single async task:

```swift
func testTokenCachingWithoutRealKeychain() async throws {
    try KeychainAccessGate.withTaskOverrideForTesting(true) {
        let key = KeychainCacheStore.Key.oauth(provider: .claude)
        XCTAssertEqual(KeychainCacheStore.load(key: key), .missing)
    }
}

```

## Test Safety Mechanisms

The `KeychainSecurity` class in [`Sources/CodexBarCore/KeychainSecurity.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBarCore/KeychainSecurity.swift) acts as the sole entry point to `SecItem*` functions. Every public method first consults `KeychainTestSafety.shouldBlockRealKeychainAccess()` to prevent CI pipelines from touching developer Keychains.

The safety check returns `true` when it detects:
- Test binaries (`swiftpm-testing-helper`, `.xctest` bundles, or XCTest environment variables)
- The `CODEXBAR_SUPPRESS_TEST_KEYCHAIN_ACCESS=1` environment variable

```swift
guard !KeychainTestSafety.shouldBlockRealKeychainAccess() else {
    return errSecInteractionNotAllowed   // Prevents real keychain access during tests
}

```

This ensures that continuous integration runs never attempt to read from or write to the macOS Keychain.

## Secure Storage Implementation

The `KeychainCacheStore` class in [`Sources/CodexBarCore/KeychainCacheStore.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBarCore/KeychainCacheStore.swift) handles all credential persistence. Rather than storing raw tokens on disk, it serializes Codable structs into the Keychain as **generic-password items** under the service `com.steipete.codexbar.cache`.

Each entry includes security attributes:
- `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` restricts access to the current device and requires at least one device unlock
- A **SecAccess** object created by `KeychainCacheStore.cacheAccessControl()` that explicitly whitelists only the app bundle and the `CodexBarCLI` helper binary

```swift
// Store an OAuth token
let tokenKey = KeychainCacheStore.Key.oauth(provider: .claude)
KeychainCacheStore.store(key: tokenKey, entry: myTokenStruct)

// Retrieve with structured result
let result = KeychainCacheStore.load(key: tokenKey) as KeychainCacheStore.LoadResult<MyToken>
switch result {
case .found(let token): use(token)
case .missing: requestNewToken()
case .temporarilyUnavailable: // retry later
case .invalid: // wipe and re-store
}

```

If Keychain access is disabled or unavailable, the store silently falls back to an in-memory dictionary (`implicitTestStore`), allowing the app to function without persistence.

## No-UI Access Pattern

To prevent macOS password prompts during background operations, `KeychainNoUIQuery` in [`Sources/CodexBarCore/KeychainNoUIQuery.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBarCore/KeychainNoUIQuery.swift) constructs queries that force immediate failure if the Keychain is locked.

The implementation creates an `LAContext` with `interactionNotAllowed = true` and sets the authentication UI policy to `kSecUseAuthenticationUIFail`:

```swift
let context = LAContext()
context.interactionNotAllowed = true
query[kSecUseAuthenticationContext as String] = context
query[kSecUseAuthenticationUI as String] = kSecUseAuthenticationUIFail

```

When the store loads credentials, it returns a `LoadResult` enum with cases `.found`, `.missing`, `.temporarilyUnavailable` (for locked Keychains), or `.invalid`. This design ensures the app handles locked states gracefully without displaying system dialogs.

## User Prompting and Privacy Controls

When new credentials must be created, `KeychainPromptCoordinator` in [`Sources/CodexBar/KeychainPromptCoordinator.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBar/KeychainPromptCoordinator.swift) displays privacy-focused alerts. The `alertModel(for:)` method constructs messages that end with the localized `keychain_prompt_privacy_note` and include a "Learn more" button linking to the project's keychain documentation.

For CLI usage, you can disable Keychain access entirely:

```bash
export CODEXBAR_DISABLE_KEYCHAIN_ACCESS=1
./CodexBarCLI --operation

```

## Summary

- **Filesystem exclusion**: CodexBar never writes raw credentials to disk; all sensitive data lives in the macOS Keychain as generic-password items.
- **Access restriction**: `KeychainCacheStore` applies `SecAccess` controls limiting Keychain items to the app bundle and `CodexBarCLI` binary.
- **Safety gates**: `KeychainAccessGate` provides centralized enable/disable logic via environment variables and automatic detection of unbundled executables.
- **Test isolation**: `KeychainTestSafety` blocks real Keychain access during unit tests and CI runs unless explicitly overridden.
- **No-UI policy**: `KeychainNoUIQuery` forces `errSecInteractionNotAllowed` for locked Keychains, preventing password prompts during background operations.

## Frequently Asked Questions

### Where does CodexBar store API keys and OAuth tokens?

CodexBar stores all credentials exclusively in the macOS Keychain using the `KeychainCacheStore` class. Each token is saved as a generic-password item with the service label `com.steipete.codexbar.cache` and accessibility set to `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly`. This ensures credentials are encrypted at rest and bound to the specific device.

### How does CodexBar prevent accidental Keychain access during unit testing?

The `KeychainSecurity` wrapper checks `KeychainTestSafety.shouldBlockRealKeychainAccess()` before every `SecItem*` call. This method detects test environments by looking for `swiftpm-testing-helper` binaries, `.xctest` bundles, or the `CODEXBAR_SUPPRESS_TEST_KEYCHAIN_ACCESS` environment variable. When detected, all Keychain operations return `errSecInteractionNotAllowed` without accessing the real Keychain.

### What happens if the macOS Keychain is locked when CodexBar needs credentials?

The `KeychainNoUIQuery` class configures queries with `interactionNotAllowed = true` and the `kSecUseAuthenticationUIFail` policy. If the Keychain is locked, the Security framework returns `errSecInteractionNotAllowed`, which `KeychainCacheStore` translates to the `.temporarilyUnavailable` case. The app can then retry later without displaying a system password prompt.

### Can I run CodexBar CLI without allowing Keychain access?

Yes. Set the environment variable `CODEXBAR_DISABLE_KEYCHAIN_ACCESS=1` before launching the CLI. This forces `KeychainAccessGate.isDisabled` to `true`, causing all credential operations to fail silently with `errSecInteractionNotAllowed`. Alternatively, running the binary as an unbundled executable automatically triggers `KeychainPromptCoordinator.disableKeychainForUnbundledExecutableIfNeeded()`, which disables Keychain access to prevent repeated system prompts.