# CodexBar Provider System Architecture: A Deep Dive into the Multi-Layer Design

> Explore the five-layer CodexBar provider architecture. Understand how UsageProvider, ProviderDescriptor, ProviderImplementation, ProviderFetchPlan, and ProviderRuntime unify AI services like OpenAI, Claude, and Gemini.

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

---

**CodexBar uses a five-layer provider architecture that separates identity (`UsageProvider` enum), static configuration (`ProviderDescriptor`), runtime behavior (`ProviderImplementation`), data fetching strategies (`ProviderFetchPlan`), and UI lifecycle management (`ProviderRuntime`) to abstract OpenAI, Claude, Gemini, and other AI services into a unified system.**

The steipete/CodexBar repository implements a macOS menu bar application for monitoring AI API usage across multiple services. Understanding the CodexBar provider system architecture reveals how the app cleanly separates concerns between data models, business logic, and UI presentation. This layered design enables rapid addition of new AI providers while maintaining type safety through Swift's concurrency features and immutable state management.

## The Five-Layer Architecture Overview

CodexBar treats every AI service—OpenAI, Claude, Gemini, Cursor, and others—as a **provider**. The architecture divides provider logic into five distinct layers:

1. **Identity** – Stable enumeration for persistence (`UsageProvider`)
2. **Descriptor & Registration** – Static metadata and global registry (`ProviderDescriptor`, `ProviderDescriptorRegistry`)
3. **Implementation** – Runtime behavior contract (`ProviderImplementation`)
4. **Fetching** – Data acquisition strategies (`ProviderFetchPlan`, `ProviderFetchStrategy`)
5. **Runtime** – UI-side lifecycle management (`ProviderRuntime`)

Each layer corresponds to specific source files and protocols, ensuring that static data, network logic, and UI presentation remain decoupled.

### Layer 1: Provider Identity via UsageProvider

The `UsageProvider` enum in [`Sources/CodexBarCore/Providers/Providers.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBarCore/Providers/Providers.swift) defines the canonical list of supported services. It uses a raw `String` representation to ensure values survive across app launches and can be persisted in `WidgetSnapshot`.

```swift
public enum UsageProvider: String, CaseIterable, Sendable, Codable {
    case codex, openai, azureopenai, claude, cursor, gemini, clawrouter
    // ... additional cases
}

```

Each case acts as a stable identifier. When the app stores user preferences (e.g., selected provider), it saves the raw string value, not the descriptor or implementation instance.

### Layer 2: Descriptor and Registration

The `ProviderDescriptor` struct in [`Sources/CodexBarCore/Providers/ProviderDescriptor.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBarCore/Providers/ProviderDescriptor.swift) aggregates all static information required by the app: metadata, branding, token-cost configuration, fetch plans, and CLI settings.

```swift
public struct ProviderDescriptor: Sendable {
    public let id:        UsageProvider
    public let metadata:  ProviderMetadata
    public let branding:  ProviderBranding
    public let tokenCost: ProviderTokenCostConfig
    public let fetchPlan: ProviderFetchPlan
    public let cli:       ProviderCLIConfig
}

```

Descriptors are immutable value types created once per provider. The **ProviderDescriptorRegistry** manages these instances as a thread-safe singleton. On first access (`ensureBootstrapped`), the registry iterates over `UsageProvider.allCases` and populates a dictionary keyed by the enum:

```swift
private static let descriptorsByID: [UsageProvider: ProviderDescriptor] = [
    .codex:   CodexProviderDescriptor.descriptor,
    .openai:  OpenAIAPIProviderDescriptor.descriptor,
    // ... additional mappings
]

```

This design enables fast lookup via `ProviderDescriptorRegistry.descriptor(for:)` and maintains a reverse CLI name map (`cliNameMap`) for command-line alias resolution.

### Layer 3: Provider Implementation Protocol

Concrete providers implement the `ProviderImplementation` protocol defined in [`Sources/CodexBar/Providers/Shared/ProviderImplementation.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBar/Providers/Shared/ProviderImplementation.swift). This protocol defines the contract between the UI layer and provider logic:

- **`presentation(context:)`** – Returns `ProviderPresentation` with detail lines and icons
- **`isAvailable(context:)`** – Determines if the provider can be used (e.g., token present, binaries installed)
- **`sourceMode(context:)`** – Declares preferred fetch mode (`auto`, `web`, `cli`)
- **`makeRuntime()`** – Returns an optional `ProviderRuntime` for UI-side actions
- **`runLoginFlow(context:)`** – Executes provider-specific authentication

Default implementations are no-ops, allowing concrete providers to override only necessary methods. For example, `OpenAIAPIProviderImplementation` and `ClaudeProviderImplementation` provide specific overrides for credential detection and login flows.

### Layer 4: Fetching Pipeline

Data gathering is abstracted through `ProviderFetchPlan` and `ProviderFetchStrategy` in [`Sources/CodexBarCore/Providers/ProviderFetchPlan.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBarCore/Providers/ProviderFetchPlan.swift). A fetch plan is a composite of one or more strategies:

```swift
public protocol ProviderFetchStrategy: Sendable {
    var id:   String               { get }
    var kind: ProviderFetchKind    { get }
    func isAvailable(_ context: ProviderFetchContext) async -> Bool
    func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult
    func shouldFallback(on error: Error, context: ProviderFetchContext) -> Bool
}

```

The `ProviderFetchContext` bundles runtime dependencies: environment variables, settings snapshots, fetcher objects, and token updaters. During a refresh cycle (`descriptor.fetchOutcome(context:)`), the plan:

1. Checks each strategy's `isAvailable` asynchronously
2. Executes the first available strategy
3. Falls back to subsequent strategies if `shouldFallback` returns true
4. Returns a `ProviderFetchOutcome` containing the result and diagnostic attempts

This strategy pattern allows a single provider to try multiple data sources—web scraping, CLI tools (like Antigravity), or OAuth APIs—without hardcoding fallback logic in the UI layer.

### Layer 5: Runtime and UI Integration

The `ProviderRuntime` protocol in [`Sources/CodexBar/Providers/Shared/ProviderRuntime.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBar/Providers/Shared/ProviderRuntime.swift) operates on the **main actor** and handles UI-specific concerns. Instantiated via `ProviderImplementation.makeRuntime()`, it receives a `ProviderRuntimeContext` containing the provider enum, global `SettingsStore`, and `UsageStore`.

Runtime implementations manage:
- Background refresh scheduling
- Menu bar status updates
- Settings change observation
- Action handlers (`forceSessionRefresh`, `openAIWebAccessToggled`)

This separation keeps UI logic (menus, progress bars) distinct from data-fetching logic (`ProviderFetchStrategy`), ensuring that long-running network operations never block the main thread.

## End-to-End Data Flow

The CodexBar provider system architecture orchestrates the following flow during normal operation:

1. **Bootstrap** – `ProviderDescriptorRegistry` initializes on first access, creating immutable descriptors for all `UsageProvider` cases
2. **Menu Construction** – `StatusItemController` queries descriptors for branding and costs, then calls `ProviderImplementation.presentation` for each enabled provider
3. **Refresh Trigger** – The controller creates a `ProviderFetchContext` and calls `descriptor.fetchOutcome(context:)`
4. **Strategy Execution** – The `ProviderFetchPlan` executes available strategies (web scrapers or CLI helpers) until one succeeds
5. **State Update** – Fetched `UsageSnapshot` and `CreditsSnapshot` objects are stored in `UsageStore`
6. **UI Update** – `ProviderRuntime` callbacks trigger menu bar refreshes and visual updates

All moving parts remain stateless except for `UsageStore` and `SettingsStore`, making unit testing straightforward through mocked contexts.

## Adding a New Provider to CodexBar

Extending the CodexBar provider system architecture requires changes across three layers:

```swift
// Step 1: Extend UsageProvider in Sources/CodexBarCore/Providers/Providers.swift
public enum UsageProvider: String, CaseIterable, Sendable, Codable {
    case codex, openai, // ... existing cases
    case myai  // New provider
}

// Step 2: Create descriptor in MyAIProviderDescriptor.swift
public struct MyAIProviderDescriptor {
    public static let descriptor: ProviderDescriptor = {
        let metadata = ProviderMetadata(
            id: .myai,
            displayName: "MyAI",
            sessionLabel: "Session",
            weeklyLabel: "Weekly",
            supportsCredits: true,
            cliName: "myai",
            defaultEnabled: false
        )
        
        let fetchPlan = ProviderFetchPlan(strategies: [
            MyAIWebStrategy(),
            MyAICLIStrategy()
        ])
        
        return ProviderDescriptor(
            id: .myai,
            metadata: metadata,
            branding: ProviderBranding(color: .purple, iconName: "myai"),
            tokenCost: ProviderTokenCostConfig(supportsTokenCost: false),
            fetchPlan: fetchPlan,
            cli: ProviderCLIConfig(name: "myai", versionDetector: nil, aliases: [])
        )
    }()
}

// Step 3: Register in ProviderDescriptorRegistry
private static let descriptorsByID: [UsageProvider: ProviderDescriptor] = [
    // ... existing entries
    .myai: MyAIProviderDescriptor.descriptor
]

```

Finally, implement `ProviderImplementation` and optionally `ProviderRuntime` if the provider requires custom UI handling or login flows.

## Summary

- **CodexBar provider system architecture** uses five distinct layers—Identity, Descriptor, Implementation, Fetching, and Runtime—to separate static data from runtime behavior
- **UsageProvider** provides stable string-based identifiers for persistence in [`Sources/CodexBarCore/Providers/Providers.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBarCore/Providers/Providers.swift)
- **ProviderDescriptorRegistry** offers thread-safe, global access to immutable provider metadata in [`Sources/CodexBarCore/Providers/ProviderDescriptor.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBarCore/Providers/ProviderDescriptor.swift)
- **ProviderImplementation** defines the behavior contract for UI presentation and availability checks in [`Sources/CodexBar/Providers/Shared/ProviderImplementation.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBar/Providers/Shared/ProviderImplementation.swift)
- **ProviderFetchStrategy** enables composable data acquisition with automatic fallback support in [`Sources/CodexBarCore/Providers/ProviderFetchPlan.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBarCore/Providers/ProviderFetchPlan.swift)
- **ProviderRuntime** isolates main-actor UI logic from async data fetching in [`Sources/CodexBar/Providers/Shared/ProviderRuntime.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBar/Providers/Shared/ProviderRuntime.swift)

## Frequently Asked Questions

### What is the difference between ProviderDescriptor and ProviderImplementation in CodexBar?

`ProviderDescriptor` is a value type containing static configuration (names, colors, fetch plans) that never changes during runtime. `ProviderImplementation` is a protocol defining dynamic behavior—such as login flows, availability checks, and UI presentation logic—that varies based on system state and user settings. The descriptor answers "what is this provider," while the implementation answers "how does it behave right now."

### How does CodexBar handle different data sources for the same provider?

Each `ProviderDescriptor` contains a `ProviderFetchPlan` composed of multiple `ProviderFetchStrategy` objects. When refreshing data, the plan iterates through strategies—such as web scraping, CLI execution, or API calls—checking `isAvailable` for each. If a strategy fails and returns `true` from `shouldFallback`, the plan automatically tries the next strategy, allowing seamless fallback from web UI scraping to CLI tools without UI-layer modifications.

### Is the ProviderDescriptorRegistry thread-safe?

Yes. The registry implements a thread-safe singleton pattern using a `store` dictionary protected by a `lock` (likely `NSLock` or actor isolation). The `ensureBootstrapped` method safely initializes descriptors on first access across multiple threads, and all subsequent lookups through `descriptor(for:)` are read-only operations on the immutable descriptor copies.

### How do I add authentication to a custom CodexBar provider?

Implement the `ProviderImplementation` protocol's `runLoginFlow(context:)` method in your concrete implementation. This method receives a `ProviderContext` containing access to settings stores and can present custom UI or handle OAuth flows. Return `true` if the login requires an immediate refresh of usage data. If your provider needs persistent background tasks or menu bar interactions, also implement `makeRuntime()` to return a custom `ProviderRuntime` instance.