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

# Headless CI/CD

> Use CCS in GitHub Actions and CI/CD pipelines without browser access

## Overview

Headless mode enables CCS to run in non-interactive environments:

* **CI/CD Pipelines** - GitHub Actions, GitLab CI, Jenkins
* **Automated Scripts** - Cron jobs, batch processing
* **Server-Side Tasks** - Background workers, scheduled analysis
* **No Browser Required** - 7-day session tokens eliminate OAuth flow

## How It Works

### Session Persistence

When you authenticate via browser, CCS:

1. Obtains OAuth token (expires in \~1 hour)
2. Stores refresh token (expires in 7 days)
3. Creates session file (`~/.ccs/cliproxy/sessions.json`)
4. Automatically refreshes tokens 5min before expiry

**Result:** Once authenticated locally, CI/CD can run for 7 days without re-auth.

### Headless Delegation Mode

```bash theme={null}
ccs -p "Your prompt here"
```

This mode:

* **Skips interactive prompts** - Returns error if auth needed
* **Uses default profile** - Or specify with first arg: `ccs codex -p "prompt"`
* **Outputs to stdout** - JSON-parsable responses
* **Exits with code** - 0 = success, non-zero = failure

## Prerequisites

* CCS installed on CI runner
* Initial authentication done (see Step 1)
* Session files accessible to CI environment

<Steps>
  <Step title="Authenticate Locally First">
    **You cannot authenticate directly in CI** - OAuth requires browser.

    On your local machine:

    ```bash theme={null}
    ccs codex --auth
    ```

    This creates session files in `~/.ccs/cliproxy/`:

    * `sessions.json` - Session metadata
    * `gemini-{account}.json` - OAuth tokens

    **Session expiry:** 7 days from last token refresh.
  </Step>

  <Step title="Copy Session Files to CI">
    **Option A: GitHub Actions Secrets**

    Base64-encode session files:

    ```bash theme={null}
    # On local machine
    tar -czf ccs-sessions.tar.gz -C ~/.ccs/cliproxy sessions.json gemini-*.json
    base64 ccs-sessions.tar.gz > ccs-sessions.b64
    ```

    Add to GitHub secrets:

    * Go to repo Settings → Secrets → Actions
    * Create secret `CCS_SESSIONS` with contents of `ccs-sessions.b64`

    **Option B: GitLab CI Variables**

    ```bash theme={null}
    # Add as file-type CI/CD variable
    cat ccs-sessions.tar.gz | base64
    ```

    Settings → CI/CD → Variables → Add variable:

    * Key: `CCS_SESSIONS`
    * Type: File
    * Value: (paste base64 content)

    **Option C: Self-Hosted Runner**

    Copy directly to runner home:

    ```bash theme={null}
    scp -r ~/.ccs/cliproxy runner-host:~/.ccs/
    ```
  </Step>

  <Step title="Restore Sessions in CI">
    **GitHub Actions:**

    ```yaml theme={null}
    - name: Setup CCS Sessions
      run: |
        mkdir -p ~/.ccs/cliproxy
        echo "${{ secrets.CCS_SESSIONS }}" | base64 -d | tar -xzf - -C ~/.ccs/cliproxy
    ```

    **GitLab CI:**

    ```yaml theme={null}
    before_script:
      - mkdir -p ~/.ccs/cliproxy
      - base64 -d $CCS_SESSIONS | tar -xzf - -C ~/.ccs/cliproxy
    ```

    **Docker:**

    ```dockerfile theme={null}
    COPY ccs-sessions.tar.gz /tmp/
    RUN mkdir -p ~/.ccs/cliproxy && \
        tar -xzf /tmp/ccs-sessions.tar.gz -C ~/.ccs/cliproxy && \
        rm /tmp/ccs-sessions.tar.gz
    ```
  </Step>

  <Step title="Use Headless Mode in Pipeline">
    **GitHub Actions Example:**

    ```yaml theme={null}
    name: AI Code Review

    on:
      pull_request:
        types: [opened, synchronize]

    jobs:
      review:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4

          - name: Install CCS
            run: npm install -g @kaitranntt/ccs@latest

          - name: Setup Sessions
            run: |
              mkdir -p ~/.ccs/cliproxy
              echo "${{ secrets.CCS_SESSIONS }}" | base64 -d | tar -xzf - -C ~/.ccs/cliproxy

          - name: Run AI Review
            run: |
              ccs codex -p "Review the changes in this PR and suggest improvements" > review.txt

          - name: Post Comment
            uses: actions/github-script@v7
            with:
              script: |
                const fs = require('fs');
                const review = fs.readFileSync('review.txt', 'utf8');
                github.rest.issues.createComment({
                  issue_number: context.issue.number,
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  body: review
                });
    ```

    **GitLab CI Example:**

    ```yaml theme={null}
    review:
      stage: test
      image: node:20
      before_script:
        - npm install -g @kaitranntt/ccs@latest
        - mkdir -p ~/.ccs/cliproxy
        - base64 -d $CCS_SESSIONS | tar -xzf - -C ~/.ccs/cliproxy
      script:
        - ccs codex -p "Analyze code quality and suggest refactorings" | tee review.txt
      artifacts:
        paths:
          - review.txt
    ```
  </Step>

  <Step title="Handle Session Expiry">
    Sessions expire after 7 days. Options:

    **Option A: Scheduled Re-Authentication**

    Create GitHub Action that runs weekly:

    ```yaml theme={null}
    name: Refresh CCS Sessions

    on:
      schedule:
        - cron: '0 0 * * 0'  # Every Sunday
      workflow_dispatch:  # Manual trigger

    jobs:
      refresh:
        runs-on: self-hosted  # Must have browser
        steps:
          - name: Re-authenticate
            run: |
              ccs codex --auth  # Opens browser on self-hosted runner

          - name: Archive Sessions
            run: |
              cd ~/.ccs/cliproxy
              tar -czf ccs-sessions.tar.gz sessions.json gemini-*.json
              base64 ccs-sessions.tar.gz

          - name: Update Secret
            env:
              GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
            run: |
              gh secret set CCS_SESSIONS < ccs-sessions.tar.gz
    ```

    **Option B: Check and Exit Gracefully**

    ```bash theme={null}
    #!/bin/bash
    ccs codex -p "Test prompt" || {
      echo "[!] CCS session expired. Please re-authenticate locally and update CI secrets."
      exit 1
    }
    ```

    **Option C: Remote Proxy (Recommended)**

    Use [Remote Proxy](/tutorials/remote-proxy-deployment) to centralize auth on server.
  </Step>

  <Step title="Configure Environment Variables">
    Control behavior via env vars:

    ```yaml theme={null}
    env:
      CCS_DEBUG: "0"                    # Disable debug logs in CI
      CCS_SKIP_PREFLIGHT: "1"           # Skip API key validation
      CCS_WEBSEARCH_SKIP: "1"           # Disable WebSearch hook
      CCS_PROXY_FALLBACK_ENABLED: "0"   # Fail fast if proxy down
      DISABLE_TELEMETRY: "1"            # Disable telemetry
      DISABLE_ERROR_REPORTING: "1"      # Disable error reporting
    ```

    **Security tip:** Never log `CCS_PROXY_AUTH_TOKEN` in CI output.
  </Step>

  <Step title="Parse Responses (Advanced)">
    Headless mode outputs raw AI response. Parse for CI:

    ```bash theme={null}
    # Extract JSON from response
    ccs codex -p "Analyze this code and return JSON: {score: number, issues: string[]}" | \
      jq -r '.score'

    # Fail pipeline if score below threshold
    SCORE=$(ccs albb -p "Rate code quality 1-10" | grep -oE '[0-9]+' | head -1)
    if [ "$SCORE" -lt 7 ]; then
      echo "[X] Code quality score $SCORE below threshold"
      exit 1
    fi
    ```

    **Example: Extract action items**

    ```bash theme={null}
    ccs codex -p "List action items from meeting notes in MEETING.md" | \
      grep -E '^\s*[-*]' > action-items.txt
    ```
  </Step>
</Steps>

## Complete CI/CD Examples

### GitHub Actions: Automated Documentation

```yaml theme={null}
name: Generate Docs

on:
  push:
    branches: [main]
    paths:
      - 'src/**'

jobs:
  docs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install CCS
        run: npm install -g @kaitranntt/ccs@latest

      - name: Setup Sessions
        run: |
          mkdir -p ~/.ccs/cliproxy
          echo "${{ secrets.CCS_SESSIONS }}" | base64 -d | tar -xzf - -C ~/.ccs/cliproxy

      - name: Generate API Docs
        run: |
          ccs codex -p "Generate API documentation from src/ directory in Markdown format" > docs/api.md

      - name: Commit Docs
        run: |
          git config user.name "AI Docs Bot"
          git config user.email "bot@example.com"
          git add docs/api.md
          git commit -m "docs: update API documentation [skip ci]" || exit 0
          git push
```

### GitLab CI: Code Quality Gate

```yaml theme={null}
stages:
  - test
  - review

quality-check:
  stage: test
  image: node:20
  before_script:
    - npm install -g @kaitranntt/ccs@latest
    - mkdir -p ~/.ccs/cliproxy
    - base64 -d $CCS_SESSIONS | tar -xzf - -C ~/.ccs/cliproxy
  script:
    - |
      ccs albb -p "Analyze code in src/ and rate quality 1-10. Output only the number." > score.txt
      SCORE=$(cat score.txt | grep -oE '[0-9]+' | head -1)
      echo "Code quality score: $SCORE/10"
      if [ "$SCORE" -lt 7 ]; then
        echo "Quality gate failed: score below 7"
        exit 1
      fi
  only:
    - merge_requests
```

### Jenkins: Nightly Analysis

```groovy theme={null}
pipeline {
  agent any

  triggers {
    cron('H 2 * * *')  // 2 AM daily
  }

  environment {
    CCS_DEBUG = '0'
    CCS_SKIP_PREFLIGHT = '1'
  }

  stages {
    stage('Setup') {
      steps {
        sh 'npm install -g @kaitranntt/ccs@latest'
        sh 'mkdir -p ~/.ccs/cliproxy'
        withCredentials([file(credentialsId: 'ccs-sessions', variable: 'SESSIONS_FILE')]) {
          sh 'tar -xzf $SESSIONS_FILE -C ~/.ccs/cliproxy'
        }
      }
    }

    stage('Analyze') {
      steps {
        sh '''
          ccs codex -p "Analyze codebase for security vulnerabilities and performance issues" > report.md
        '''
      }
    }

    stage('Publish') {
      steps {
        archiveArtifacts artifacts: 'report.md'
        emailext(
          subject: "Nightly Code Analysis Report",
          body: readFile('report.md'),
          to: 'dev-team@example.com'
        )
      }
    }
  }
}
```

## Environment Variables Reference

| Variable                     | Default | Purpose                  |
| ---------------------------- | ------- | ------------------------ |
| `CCS_DEBUG`                  | `0`     | Enable verbose logging   |
| `CCS_SKIP_PREFLIGHT`         | `0`     | Skip API key validation  |
| `CCS_WEBSEARCH_SKIP`         | `0`     | Disable WebSearch hook   |
| `CCS_PROXY_FALLBACK_ENABLED` | `1`     | Fallback to local proxy  |
| `CCS_UNIFIED_CONFIG`         | `1`     | Use unified config mode  |
| `CCS_MIGRATE`                | `0`     | Trigger auto-migration   |
| `DISABLE_TELEMETRY`          | unset   | Disable Claude telemetry |
| `DISABLE_ERROR_REPORTING`    | unset   | Disable error reporting  |
| `DISABLE_BUG_COMMAND`        | unset   | Disable bug reporting    |

## Troubleshooting

### Authentication Required

**Symptom:** `[X] Auth error: No valid session found`

**Causes:**

* Sessions not restored correctly
* Session files expired (>7 days)
* Wrong provider in headless command

**Solutions:**

```bash theme={null}
# Verify session files exist
ls -la ~/.ccs/cliproxy/sessions.json

# Check session age
stat -c %y ~/.ccs/cliproxy/sessions.json

# Re-authenticate locally and update CI secrets
```

### Token Refresh Failed

**Symptom:** `[X] Network error: UND_ERR_SOCKET`

**Causes:**

* Firewall blocking OAuth endpoints
* Proxy configuration issues
* Network timeout

**Solutions:**

```bash theme={null}
# Increase timeout
export CCS_PROXY_TIMEOUT=5000

# Check network access
curl -I https://auth.openai.com
```

### Session File Permissions

**Symptom:** `[X] Config error: EACCES`

**Cause:** Session files not readable by CI user

**Solution:**

```bash theme={null}
chmod 600 ~/.ccs/cliproxy/sessions.json
chmod 600 ~/.ccs/cliproxy/*.json
```

### Prompt Too Long

**Symptom:** Command line argument limit exceeded

**Solution:** Use file redirection:

```bash theme={null}
# Instead of:
ccs codex -p "$(cat large-file.txt)"

# Use:
ccs codex -p "Analyze the content of large-file.txt" < large-file.txt
```

## Security Best Practices

### Protect Session Files

* **Encrypt in transit:** Use secrets/variables, not environment variables
* **Rotate regularly:** Re-authenticate every 7 days maximum
* **Scope access:** Limit CI job permissions to necessary secrets
* **Audit logs:** Monitor secret access in GitHub/GitLab audit logs

### Minimize Token Exposure

```yaml theme={null}
# ❌ BAD: Token in logs
- run: echo "Token: ${{ secrets.CCS_SESSIONS }}"

# ✅ GOOD: Silent extraction
- run: echo "${{ secrets.CCS_SESSIONS }}" | base64 -d | tar -xzf - -C ~/.ccs/cliproxy
```

### Use Self-Hosted Runners

For sensitive repos, use self-hosted runners:

* Sessions stay on your infrastructure
* No need to upload to GitHub secrets
* Easier re-authentication workflow

## Next Steps

<CardGroup cols={2}>
  <Card title="Remote Proxy" icon="server" href="/tutorials/remote-proxy-deployment">
    Centralize auth on server, avoid session expiry issues
  </Card>

  <Card title="Token Management" icon="key" href="/tutorials/token-management">
    Deep dive into session persistence and refresh logic
  </Card>
</CardGroup>
