# How Container Authenticates with Registries Using macOS Keychain

> Learn how container uses macOS Keychain for secure registry authentication. Discover how it isolates secrets and automates credential retrieval for seamless image pulls.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: how-to-guide
- Published: 2026-06-21

---

**Container stores registry credentials in the macOS Keychain via the `KeychainHelper` wrapper, isolating secrets from plain-text files and enabling secure authentication through `login`, `logout`, and automatic retrieval during image pulls.**

The `apple/container` tool leverages macOS native security infrastructure to handle OCI registry authentication. By using the **Keychain** as the credential store, Container ensures that registry passwords never reside in unencrypted configuration files, instead relying on the system's secure enclave for storage and retrieval.

## The Keychain-Based Authentication Architecture

Container isolates registry credentials from its configuration by storing them as **generic password items** in the macOS Keychain. This architecture centers on the `KeychainHelper` class provided by the internal `ContainerPersistence` package, which acts as a thin wrapper around Apple's Security framework.

All Keychain entries are namespaced using a unique **security domain** defined by `Constants.keychainID` in [`Sources/ContainerCommands/Registry/Constants.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Registry/Constants.swift). This prevents credential collisions with other applications and ensures that Container only accesses its own registry secrets.

## Registry Login Workflow

The authentication flow begins with the `container registry login` command, implemented in [`Sources/ContainerCommands/Registry/RegistryLogin.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Registry/RegistryLogin.swift).

### Credential Prompting and Validation

The command parses optional `--username` and `--password-stdin` flags. If a username is not provided, `KeychainHelper.userPrompt(hostname:)` retrieves any previously saved username for that host or prompts the user interactively. For passwords, `KeychainHelper.passwordPrompt()` reads input securely from the terminal with echo disabled.

Before storage, Container validates the credentials by creating a `RegistryClient` with `BasicAuthentication(username:password:)` and invoking `ping()` to verify connectivity and authentication against the registry.

### Secure Storage with KeychainHelper

Upon successful validation, the credentials persist via `keychain.save(hostname:username:password:)`, which stores them as a generic password item keyed by the hostname.

```swift
// Simplified workflow from RegistryLogin.swift
let keychain = KeychainHelper(securityDomain: Constants.keychainID)

if username.isEmpty {
    username = try keychain.userPrompt(hostname: server)
}
if password.isEmpty {
    password = try keychain.passwordPrompt()
    print() // newline after hidden password entry
}

// Validate before storing
let client = RegistryClient(
    host: host,
    scheme: scheme.rawValue,
    port: url.port,
    authentication: BasicAuthentication(username: username, password: password),
    retryOptions: .default
)

try await client.ping()
try keychain.save(hostname: server, username: username, password: password)

```

## Registry Logout and Credential Management

Container provides explicit commands to manage stored credentials without manual Keychain manipulation.

### Removing Stored Credentials

The `container registry logout` command resolves the registry hostname and calls `keychain.delete(hostname:)`, removing the entry from the Keychain entirely. This implementation resides in [`Sources/ContainerCommands/Registry/RegistryLogout.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Registry/RegistryLogout.swift).

```swift
// From RegistryLogout.swift
let keychain = KeychainHelper(securityDomain: Constants.keychainID)
let host = Reference.resolveDomain(domain: registry)
try keychain.delete(hostname: host)

```

### Listing Saved Registries

The `container registry list` command invokes `KeychainHelper.list()` to display all stored registry entries for the configured security domain. This allows users to audit which registries have active credentials without exposing the underlying passwords. The implementation is located in [`Sources/ContainerCommands/Registry/RegistryList.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Registry/RegistryList.swift).

## How KeychainHelper Works

`KeychainHelper` abstracts macOS Security framework interactions through a concise API:

- **`init(securityDomain:)`** – Initializes the helper with `Constants.keychainID` to namespace all entries.
- **`userPrompt(hostname:)`** – Retrieves stored usernames or prompts interactively.
- **`passwordPrompt()`** – Reads passwords securely from `/dev/tty` with no echo.
- **`save(hostname:username:password:)`** – Creates or updates generic password items.
- **`delete(hostname:)`** – Removes specific entries by hostname.
- **`list()`** – Returns all stored credentials for the security domain.

Because credentials live in the system Keychain, they benefit from macOS security policies including automatic screen-lock protection, Touch ID integration, and hardware-backed encryption.

## End-to-End Authentication Flow

1. **Initial Login** – When running `container registry login my.registry.com`, the tool creates a `KeychainHelper` instance, prompts for missing credentials, validates them via `RegistryClient.ping()`, and stores them via `keychain.save()`.

2. **Subsequent Operations** – Commands like `container run` or `container pull` automatically retrieve credentials from the Keychain using `keychain.load(hostname:)` (internal to the image-pull logic) without requiring user interaction.

3. **Explicit Logout** – Running `container registry logout` ensures stale credentials are removed via `keychain.delete()`, preventing unauthorized access to private registries.

## Summary

- Container uses the macOS Keychain via `KeychainHelper` to store credentials securely outside of configuration files.
- The `login` command validates credentials using `RegistryClient.ping()` before persisting them with `keychain.save(hostname:username:password:)`.
- Credentials are namespaced using `Constants.keychainID` to prevent collisions with other applications.
- The `logout` command removes entries using `keychain.delete(hostname:)`, while `list` displays stored registries without exposing passwords.
- Image pull operations automatically retrieve credentials from the Keychain, enabling seamless authentication workflows.

## Frequently Asked Questions

### How does Container store registry credentials securely?

Container stores credentials as generic password items in the macOS Keychain using the `KeychainHelper` class from the `ContainerPersistence` package. This ensures secrets are encrypted and protected by the user's login password or Touch ID, rather than being stored in plain-text configuration files like `~/.docker/config.json`.

### What happens when I run container registry login?

The `RegistryLogin` command creates a `KeychainHelper` instance with `Constants.keychainID` and prompts for credentials if not provided via `--username` or `--password-stdin`. It validates the credentials using `RegistryClient.ping()` before calling `keychain.save(hostname:username:password:)` to persist them in the Keychain under the Container security domain.

### How can I check which registries have stored credentials?

Run `container registry list`, which invokes `KeychainHelper.list()` to retrieve all stored entries for the Container security domain. This displays all registries that currently have saved credentials without exposing the actual passwords, as implemented in [`Sources/ContainerCommands/Registry/RegistryList.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Registry/RegistryList.swift).

### Is the Keychain integration mandatory for Container?

Yes, the Keychain is the mandatory credential store for registry authentication in Container. The tool does not support unencrypted credential files, ensuring that all registry passwords benefit from macOS security policies including automatic locking and hardware-backed encryption through the Secure Enclave.