> ## Documentation Index
> Fetch the complete documentation index at: https://docs.clawker.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Credential Forwarding

> SSH, GPG, Git HTTPS, and Claude Code authentication forwarding into containers

Clawker forwards your host credentials into containers so agents can push to Git, sign commits, and authenticate with Claude Code without manual key copying. All credential forwarding is enabled by default.

## Overview

| Credential       | Method                 | Default | Config Key                                 |
| ---------------- | ---------------------- | :-----: | ------------------------------------------ |
| SSH agent        | Socket bridge (muxrpc) |    On   | `security.git_credentials.forward_ssh`     |
| GPG agent        | Socket bridge (muxrpc) |    On   | `security.git_credentials.forward_gpg`     |
| Git HTTPS        | Host proxy (HTTP)      |    On   | `security.git_credentials.forward_https`   |
| `.gitconfig`     | Bind mount (read-only) |    On   | `security.git_credentials.copy_git_config` |
| Claude Code auth | Volume injection       |    On   | `agent.claude_code.use_host_auth`          |

## SSH Agent Forwarding

SSH agent forwarding lets the agent use your host SSH keys for Git operations, without exposing the private keys inside the container.

<Note>
  SSH agent forwarding handles the **authentication** side (your keys). But the **firewall** must also allow SSH traffic to your Git host. By default, no SSH destinations are allowed. You need an explicit rule:

  ```yaml theme={"dark"}
  security:
    firewall:
      rules:
        - dst: github.com
          proto: ssh
          port: 22
          action: allow
  ```

  The `clawker project init` template includes this rule for GitHub. See the [Firewall guide](/firewall#common-setups) for more examples.
</Note>

**How it works:**

1. When a container starts, Clawker spawns a socket bridge daemon on the host
2. The daemon uses `docker exec` to establish a muxrpc connection with a socket server inside the container
3. The container-side server creates a Unix socket at `~/.ssh/agent.sock`
4. `SSH_AUTH_SOCK` is set automatically inside the container
5. Git SSH operations (`git clone git@github.com:...`) use the forwarded agent transparently

**Disable:**

```yaml theme={"dark"}
security:
  git_credentials:
    forward_ssh: false
```

## GPG Agent Forwarding

GPG forwarding lets the agent sign commits with your host GPG key.

**How it works:**

1. The same socket bridge daemon handles GPG forwarding alongside SSH
2. A socket is created at `~/.gnupg/S.gpg-agent` inside the container
3. Your GPG public key is exchanged via the muxrpc protocol
4. The container's `gpg.conf` is configured with `no-autostart` to prevent a local GPG agent from conflicting

**Disable:**

```yaml theme={"dark"}
security:
  git_credentials:
    forward_gpg: false
```

## Git HTTPS Credential Forwarding

HTTPS credential forwarding lets the agent authenticate with Git remotes over HTTPS using your host's credential store (e.g., macOS Keychain, Windows Credential Manager).

**How it works:**

1. The host proxy daemon runs on your machine (default port 18374)
2. Inside the container, a `git-credential-clawker` helper is configured
3. When Git needs HTTPS credentials, the helper sends a request to the host proxy
4. The host proxy queries your host's native `git credential fill` command
5. Credentials are returned to the container without being stored there

**Requirements:**

* The host proxy must be running (`security.enable_host_proxy: true`)
* `forward_https` defaults to follow the `enable_host_proxy` setting

**Disable:**

```yaml theme={"dark"}
security:
  git_credentials:
    forward_https: false
```

<Note>
  Setting `forward_https: true` has no effect if `enable_host_proxy` is `false`. HTTPS credential forwarding requires the host proxy.
</Note>

## Git Config Copying

By default, Clawker copies your host `~/.gitconfig` into the container so Git operations use your name, email, aliases, and other settings.

**Important:** The `credential.helper` lines are **filtered out** during copying. This forces the container to use Clawker's forwarded credential helpers instead of trying to access a host-side credential store that doesn't exist inside the container.

**Disable:**

```yaml theme={"dark"}
security:
  git_credentials:
    copy_git_config: false
```

## Claude Code Authentication

By default, Clawker forwards your Claude Code authentication into the container so the agent is pre-authenticated on startup.

**How it works:**

1. During container creation, Clawker looks for credentials in this order:
   * OS keyring (macOS Keychain, etc.)
   * File fallback (`~/.claude/.credentials.json`)
2. Credentials are injected into the container's config volume
3. Claude Code starts pre-authenticated

**Disable:**

```yaml theme={"dark"}
agent:
  claude_code:
    use_host_auth: false
```

When disabled, Claude Code inside the container will prompt for authentication on first run.

<Warning>
  **Refresh tokens are single-use and rotate — a `/login` after the access token expires is expected, not a bug.** Clawker copies the host credential **once**, at create time, and never syncs the two sides again. Each time Claude Code refreshes an expired access token, Anthropic's OAuth server issues a **new** refresh token and **permanently invalidates the old one**. The host and the container hold independent copies of the *same* refresh token, so whichever side refreshes first "turns in" that shared token; the other side's copy is now stale, and its next refresh is rejected (`invalid_grant`) — forcing a one-time `/login` in that environment.

  This is why a container can start with a perfectly valid credential and still prompt for `/login` later: the host (or another container) turned the shared refresh token in first. After that one `/login`, the container self-heals — it persists its own freshly rotated token in the config volume and won't prompt again.

  Clawker never participates in the token exchange. Refresh and rotation are handled entirely by first-party Claude Code, as Anthropic's terms require — clawker only seeds the initial byte-for-byte copy.
</Warning>

<Note>
  **Which containers are actually exposed — and it's at most once each.** A collision requires a *refresh*, and a refresh only happens once the access token expires. A container that lives **shorter than the access-token lifetime** never refreshes: it uses the inherited (still-valid) access token and exits, so it never collides. The default is a clean win for these ephemeral and short-lived containers — zero-touch auth, no downside.

  Only **long-lived** containers — ones that outlive the inherited access token and must refresh — can hit the stale-token `/login`, and only **once**: that `/login` mints the container its **own** independent credential (its own family), after which it is fully decoupled from the host and never collides again. So this is a one-time event per long-lived container, not recurring churn. If you want a long-lived container to skip even that single prompt, set `use_host_auth: false` and `/login` once up front — it starts on its own family from the beginning.
</Note>

### Config Strategy

The `config.strategy` setting controls how Claude Code's configuration is initialized:

* **`copy`** (default) -- Copies your host `~/.claude/` settings (plugins, preferences, MCP config) into the container
* **`fresh`** -- Starts with a clean Claude Code configuration

```yaml theme={"dark"}
agent:
  claude_code:
    config:
      strategy: "fresh"
```

## Full Configuration Example

```yaml theme={"dark"}
security:
  enable_host_proxy: true
  git_credentials:
    forward_https: true
    forward_ssh: true
    forward_gpg: true
    copy_git_config: true

agent:
  claude_code:
    config:
      strategy: "copy"
    use_host_auth: true
```

## Troubleshooting

### SSH: "Permission denied (publickey)"

1. Verify your SSH agent is running on the host: `ssh-add -l`
2. Check that `forward_ssh` is enabled
3. Try restarting the container -- the socket bridge daemon reconnects automatically

### GPG: "No secret key" when signing commits

1. Verify your GPG key is available on the host: `gpg --list-secret-keys`
2. Check that `forward_gpg` is enabled
3. The container's GPG agent might have auto-started before the forwarded socket was ready. Restart the container to fix.

### Git HTTPS: "Authentication failed"

1. Verify the host proxy is running: check for the process or look at `~/.local/state/clawker/pids/hostproxy.pid`
2. Verify credentials work on the host: `git credential fill` with your repo URL
3. Check that `enable_host_proxy` and `forward_https` are both enabled

### Claude Code: "Please authenticate"

1. Verify you're logged in on the host: `claude auth status`
2. Check that `use_host_auth` is `true` (default)
3. If using the file fallback, verify `~/.claude/.credentials.json` exists
4. **A one-time `/login` after the access token expires is expected** if the shared refresh token was already rotated by the host or another container. Refresh tokens are single-use (see the warning above) — the stale side must re-login once, then self-heals. This is not a misconfiguration. Re-authenticating on the host only fixes containers created *afterward*.
