# How to Handle Registry Authentication and Credentials in the Apple Container CLI

> Learn to handle registry authentication and credentials in the Apple Container CLI. Securely store credentials in macOS Keychain for seamless push and pull operations after a simple login. Explore the KeychainHelper class.

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

---

**The Apple Container CLI stores registry credentials securely in macOS Keychain via the `KeychainHelper` class, enabling automatic authentication for push and pull operations after an initial `container registry login`.**

The `apple/container` project provides a native macOS container management solution that handles registry authentication and credentials through system Keychain integration rather than insecure file-based storage. This approach ensures that sensitive authentication data remains encrypted and accessible only to the user, while supporting both interactive and automated workflows.

## Authentication Flow

The authentication system follows a strict flow: credentials are verified against the registry, stored in Keychain, and then retrieved automatically for subsequent operations.

### Logging In (`container registry login`)

The login implementation resides in **[Sources/ContainerCommands/Registry/RegistryLogin.swift](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Registry/RegistryLogin.swift)**. The command accepts `--username` and `--password-stdin` flags, or prompts interactively when omitted.

The process executes as follows:

1. **Host Resolution**: The code calls `Reference.resolveDomain` to determine the target registry host and automatically detects the scheme (`http`, `https`, or `auto`) based on system DNS configuration.
2. **Credential Verification**: A `RegistryClient` is instantiated with a `BasicAuthentication` payload containing the provided credentials. The client performs a **ping** request to verify validity before storage.
3. **Secure Storage**: Upon successful verification, the system calls `KeychainHelper.save(hostname:username:password:)` to persist credentials under the security domain defined by `Constants.keychainID`.

### Logging Out (`container registry logout`)

To remove credentials, the logout command—implemented in **[Sources/ContainerCommands/Registry/RegistryLogout.swift](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Registry/RegistryLogout.swift)**—resolves the registry host and invokes `KeychainHelper.delete(hostname:)`. This operation requires no network request; it simply removes the entry from the macOS Keychain.

### Automatic Credential Retrieval

When executing registry-related operations like `container image push` or `pull`, the system instantiates `KeychainHelper` (observed in **[Sources/Services/MachineAPIService/Client/MachineClient.swift](https://github.com/apple/container/blob/main/Sources/Services/MachineAPIService/Client/MachineClient.swift)** at line 336 and **[Sources/Services/ContainerImagesService/Server/ImagesService.swift](https://github.com/apple/container/blob/main/Sources/Services/ContainerImagesService/Server/ImagesService.swift)** at line 426) with `Constants.keychainID`.

The helper calls `userPrompt(hostname:)` or `passwordPrompt()` only when no stored credentials exist. If valid credentials are found, they are returned transparently, bypassing manual input.

## Configuration and Defaults

The default registry domain is defined in the system configuration under the `[registry]` table:

```toml
[registry]
domain = "docker.io"

```

Users can override this default by creating or editing `~/.config/container/config.toml`. This configuration affects how image references are resolved when no explicit host is specified (e.g., `myapp:latest` resolves to `my-registry.example.com/library/myapp:latest`).

## Security Guarantees

The authentication architecture provides three critical security layers:

- **Keychain Isolation**: Credentials are stored under a unique security domain (`Constants.keychainID`), preventing cross-application access or exposure to other processes.
- **Memory Safety**: Passwords remain in memory only until verified and saved; the code never writes secrets to logs, environment variables, or temporary files.
- **Phishing Mitigation**: When executing `container registry login` without the `--username` flag, the system prompts the user to confirm the hostname via the Keychain dialog, preventing credential theft through spoofed registry endpoints.

## Practical Examples

### Interactive Login

```bash

# Prompts for username and password via secure dialog

container registry login my-registry.example.com

```

### Non-Interactive Login (CI/CD)

```bash
echo "$MY_PASSWORD" | container registry login \
    --username myuser --password-stdin my-registry.example.com

```

### Automatic Push Using Stored Credentials

```bash

# Uses credentials previously saved in Keychain

container image push my-registry.example.com/myuser/myapp:latest

```

### Logout and Credential Removal

```bash
container registry logout my-registry.example.com

```

### Override Default Registry

Create `~/.config/container/config.toml`:

```toml
[registry]
domain = "my-registry.example.com"

```

## Key Implementation Files

- **[Sources/ContainerCommands/Registry/RegistryLogin.swift](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Registry/RegistryLogin.swift)**: Parses login options, implements `BasicAuthentication` verification, and persists credentials via `KeychainHelper`.
- **[Sources/ContainerCommands/Registry/RegistryLogout.swift](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Registry/RegistryLogout.swift)**: Handles credential deletion from Keychain without network operations.
- **[Sources/Services/MachineAPIService/Client/MachineClient.swift](https://github.com/apple/container/blob/main/Sources/Services/MachineAPIService/Client/MachineClient.swift)** (line 336): Demonstrates `KeychainHelper` instantiation for machine-related registry operations.
- **[Sources/Services/ContainerImagesService/Server/ImagesService.swift](https://github.com/apple/container/blob/main/Sources/Services/ContainerImagesService/Server/ImagesService.swift)** (line 426): Shows credential retrieval for image push/pull operations.
- **[docs/container-system-config.md](https://github.com/apple/container/blob/main/docs/container-system-config.md)**: Documents the `[registry]` configuration table and default domain settings.

## Summary

- **Keychain Storage**: All credentials are stored in macOS Keychain under `Constants.keychainID`, not in plaintext files.
- **Login Flow**: The [`RegistryLogin.swift`](https://github.com/apple/container/blob/main/RegistryLogin.swift) implementation verifies credentials via ping before saving them using `KeychainHelper.save()`.
- **Automatic Retrieval**: [`ImagesService.swift`](https://github.com/apple/container/blob/main/ImagesService.swift) and [`MachineClient.swift`](https://github.com/apple/container/blob/main/MachineClient.swift) automatically retrieve stored credentials, bypassing prompts when available.
- **Configuration**: Default registries are configured via `~/.config/container/config.toml` under the `[registry]` table.
- **Security**: The system prevents credential logging and requires explicit user consent for unknown hosts.

## Frequently Asked Questions

### Where exactly are my registry credentials stored?

Credentials are stored in the macOS Keychain under a unique security domain defined by `Constants.keychainID`. According to the source code in [`RegistryLogin.swift`](https://github.com/apple/container/blob/main/RegistryLogin.swift), the `KeychainHelper.save(hostname:username:password:)` method writes them to the user's default keychain, making them accessible only to the Container CLI process and requiring macOS user authentication for access.

### How do I handle registry authentication in CI/CD pipelines without interactive prompts?

Use the `--password-stdin` flag combined with `--username` for non-interactive authentication. As implemented in [`RegistryLogin.swift`](https://github.com/apple/container/blob/main/RegistryLogin.swift), the command accepts piped input: `echo "$PASSWORD" | container registry login --username $USER --password-stdin $HOST`. This bypasses the interactive `userPrompt()` and `passwordPrompt()` calls while still securely storing credentials in Keychain for subsequent push operations.

### Can I use multiple private registries simultaneously?

Yes. The `KeychainHelper` stores credentials keyed by hostname (via `Reference.resolveDomain`), allowing distinct entries for `docker.io`, `ghcr.io`, and private registries. When executing operations like `container image push`, the system extracts the domain from the image reference and retrieves the matching credentials automatically, as seen in the [`ImagesService.swift`](https://github.com/apple/container/blob/main/ImagesService.swift) implementation.

### What happens if I deny Keychain access when prompted?

If you deny Keychain access, the `KeychainHelper` cannot retrieve or save credentials, causing the authentication flow to fail. The system will either prompt again (for interactive sessions) or fail with an authentication error (for non-interactive sessions). You must grant Keychain access for the `Constants.keychainID` security domain to enable credential persistence.