Project 21: “The Headless Server Setup” — Remote Development

Attribute Value
File KIRO_CLI_LEARNING_PROJECTS.md
Main Programming Language Linux Shell
Coolness Level Level 3: Genuinely Clever
Difficulty Level 2: Intermediate
Knowledge Area Remote Development

What you’ll build: Install Kiro on a headless VM and authenticate via device flow.

Why it teaches Remote Dev: This is the standard pattern for server-based work.

Success criteria:

  • Headless login succeeds without a local browser.

Real World Outcome

You’ll have Kiro running on a headless Linux server (no GUI, no browser) and successfully authenticated via OAuth device flow. This enables server-side automation, CI/CD integration, and remote development workflows:

Successful headless authentication:

# On remote server (no GUI)
$ ssh user@dev-server.company.com

$ kiro auth login

Kiro CLI Authentication (Device Flow)
═══════════════════════════════════════
No browser detected. Using device code flow.

1. Visit this URL on any device with a browser:
   https://anthropic.com/device

2. Enter this code: ABCD-EFGH

3. Authorize the Kiro CLI application

Waiting for authorization... (timeout in 10 minutes)

[User visits URL on laptop, enters code, approves]

✓ Authentication successful!
✓ Token stored in: ~/.kiro/credentials.json
✓ Expires in: 30 days

$ kiro "show me system info"

Kiro: [Returns server specs, uptime, disk usage]

# Headless session is now active

Automated server setup:

$ cat setup-headless-kiro.sh

#!/bin/bash
# Install and configure Kiro on headless servers

# Install Kiro CLI
curl -fsSL https://kiro.dev/install.sh | bash

# Verify installation
kiro --version

# Authenticate (device flow)
echo "Follow the URL and enter the code displayed"
kiro auth login

# Verify authentication
kiro auth whoami

# Set up SSH agent forwarding (optional)
echo 'Host dev-server' >> ~/.ssh/config
echo '  ForwardAgent yes' >> ~/.ssh/config

echo "✓ Kiro headless setup complete"

CI/CD integration example:

# .github/workflows/deploy.yml
name: Deploy with Kiro

on: [push]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Install Kiro
        run: curl -fsSL https://kiro.dev/install.sh | bash

      - name: Authenticate Kiro (service account)
        env:
          KIRO_API_KEY: ${{ secrets.KIRO_API_KEY }}
        run: kiro auth login --api-key $KIRO_API_KEY

      - name: Run deployment
        run: kiro "deploy to staging using docker-compose"

This setup unlocks server-side AI automation where Kiro can manage infrastructure, run deployments, and execute maintenance tasks—all without human intervention.


The Core Question You’re Answering

“How do I use an AI coding assistant on remote servers that don’t have browsers or GUIs?”

Before you start coding, consider: Most cloud services use OAuth for authentication, which requires opening a browser to authorize. But servers don’t have browsers—they’re headless. The device flow (RFC 8628) solves this by splitting authentication into two steps: the headless device gets a code, and you enter that code on any browser-enabled device. This project teaches you to bridge local and remote authentication workflows.


Concepts You Must Understand First

Stop and research these before coding:

  1. OAuth Device Authorization Grant (Device Flow)
    • What is the device code flow (RFC 8628)?
    • How does it differ from standard OAuth (authorization code flow)?
    • What are the steps: device code request → user authorization → token polling?
    • How do you handle timeout (user doesn’t authorize within 10 minutes)?
    • Book Reference: “OAuth 2 in Action” by Justin Richer - Ch. 12 (Device Flow)
  2. SSH Agent Forwarding
    • What is ForwardAgent yes in SSH config?
    • How do you forward local credentials to remote sessions?
    • What are the security risks of agent forwarding?
    • When should you use ProxyJump vs ForwardAgent?
    • Book Reference: “SSH Mastery” by Michael W. Lucas - Ch. 6 (Agent Forwarding)
  3. Headless Environment Detection
    • How do you detect if a display is available ($DISPLAY, $WAYLAND_DISPLAY)?
    • How do you check if xdg-open or open commands work?
    • What’s the difference between TTY and pseudo-TTY sessions?
    • How do you determine if running in a CI/CD environment?
    • Book Reference: “The Linux Programming Interface” by Michael Kerrisk - Ch. 62 (Terminals)
  4. Credential Storage and Rotation
    • Where should tokens be stored (~/.kiro/credentials.json)?
    • What file permissions are required for security (600)?
    • How do you handle token expiration and refresh?
    • Should you use environment variables vs config files?
    • Book Reference: “Building Secure and Reliable Systems” by Google - Ch. 9 (Secrets Management)

Questions to Guide Your Design

Before implementing, think through these:

  1. Authentication Method Selection
    • How do you detect if a browser is available vs requiring device flow?
    • Should you support both interactive and non-interactive modes?
    • How do you handle service accounts (API keys) vs user accounts (OAuth)?
    • What happens if the device code expires before authorization?
  2. Token Management
    • Where do you store the access token (file, keyring, environment)?
    • How do you secure the token file (permissions, encryption)?
    • Do you need a refresh token for long-running servers?
    • How often should you validate the token (every command, daily)?
  3. CI/CD Integration
    • How do you authenticate in GitHub Actions/GitLab CI without interaction?
    • Should you use service accounts or personal access tokens?
    • How do you rotate tokens in CI without manual intervention?
    • What’s the fallback if authentication fails mid-pipeline?
  4. Debugging Headless Issues
    • How do you debug authentication failures without a GUI?
    • Should you log to a file (~/.kiro/debug.log) or stderr?
    • How do you test device flow locally before deploying to servers?
    • What telemetry do you need (auth attempts, failures, timeouts)?

Thinking Exercise

Manual Device Flow Authentication Walkthrough

Before writing code, trace the OAuth device flow step by step:

Step 1: Kiro detects headless environment

$ kiro auth login

# Check for browser availability
if ! command -v xdg-open &>/dev/null && [ -z "$DISPLAY" ]; then
  # Headless mode detected
  use_device_flow=true
fi

Step 2: Request device code from Anthropic

POST https://anthropic.com/oauth/device/code
Content-Type: application/json

{
  "client_id": "kiro-cli",
  "scope": "kiro.read kiro.write"
}

Response:
{
  "device_code": "DEVICE-12345",
  "user_code": "ABCD-EFGH",
  "verification_uri": "https://anthropic.com/device",
  "expires_in": 600,
  "interval": 5
}

Step 3: Display instructions to user

1. Visit: https://anthropic.com/device
2. Enter code: ABCD-EFGH
3. Authorize Kiro CLI

Step 4: Poll for authorization

# Poll every 5 seconds for up to 600 seconds
while [ $elapsed -lt 600 ]; do
  POST https://anthropic.com/oauth/token
  {
    "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
    "device_code": "DEVICE-12345",
    "client_id": "kiro-cli"
  }

  # Response if pending:
  { "error": "authorization_pending" }

  # Response if approved:
  {
    "access_token": "kiro_...",
    "token_type": "Bearer",
    "expires_in": 2592000
  }

  sleep 5
done

Step 5: Store token securely

echo "$access_token" > ~/.kiro/credentials.json
chmod 600 ~/.kiro/credentials.json

Questions while tracing:

  • What happens if the user never authorizes (timeout)?
  • How do you handle polling errors (network failures)?
  • Should you provide a QR code for mobile authorization?
  • How do you test this without hitting real OAuth servers?

The Interview Questions They’ll Ask

Prepare to answer these:

  1. “Explain the OAuth device flow (RFC 8628) and how it differs from the standard authorization code flow. When would you use each?”
  2. “How would you securely store OAuth tokens on a headless server? What file permissions and encryption methods would you use?”
  3. “What strategies would you use to detect if a system is headless vs has a GUI available?”
  4. “How do you implement token refresh in a long-running server application without user interaction?”
  5. “Explain the security implications of SSH agent forwarding. When is it safe to use, and what are the alternatives?”
  6. “How would you design a CI/CD integration for an OAuth-authenticated tool without exposing tokens in logs?”

Hints in Layers

Hint 1: Detect Headless Environment Check for display availability before attempting browser launch:

if [ -z "$DISPLAY" ] && ! command -v xdg-open &>/dev/null; then
  echo "No browser detected. Using device code flow."
  device_flow=true
fi

Hint 2: Use curl for OAuth API Calls Request device code:

response=$(curl -s -X POST https://anthropic.com/oauth/device/code \
  -H "Content-Type: application/json" \
  -d '{"client_id": "kiro-cli", "scope": "kiro.read kiro.write"}')

device_code=$(echo "$response" | jq -r '.device_code')
user_code=$(echo "$response" | jq -r '.user_code')
verification_uri=$(echo "$response" | jq -r '.verification_uri')

Hint 3: Poll with Exponential Backoff Don’t hammer the server every second:

interval=5
max_attempts=120  # 10 minutes / 5 seconds
for i in $(seq 1 $max_attempts); do
  token_response=$(curl -s -X POST https://anthropic.com/oauth/token \
    -d "grant_type=device_code&device_code=$device_code&client_id=kiro-cli")

  if echo "$token_response" | jq -e '.access_token' &>/dev/null; then
    echo "✓ Authentication successful!"
    break
  fi
  sleep $interval
done

Hint 4: Secure Token Storage

mkdir -p ~/.kiro
echo "$access_token" > ~/.kiro/credentials.json
chmod 600 ~/.kiro/credentials.json  # Owner read/write only

Books That Will Help

Topic Book Chapter
OAuth Device Flow “OAuth 2 in Action” by Justin Richer Ch. 12 (Device Flow), Ch. 6 (Client Types)
SSH Configuration “SSH Mastery” by Michael W. Lucas Ch. 6 (Agent Forwarding), Ch. 11 (SSH for Automation)
Headless Systems “The Linux Programming Interface” by Michael Kerrisk Ch. 62 (Terminals), Ch. 34 (Process Groups)
Secrets Management “Building Secure and Reliable Systems” by Google Ch. 9 (Secrets), Ch. 6 (Least Privilege)
CI/CD Integration “Continuous Delivery” by Jez Humble Ch. 14 (Advanced Version Control)

Common Pitfalls & Debugging

Problem 1: “Device code flow times out before user authorizes”

  • Why: 10-minute timeout is too short, or user didn’t see the prompt
  • Fix: Send timeout reminder:
    echo "⏰ 5 minutes remaining. Visit https://anthropic.com/device"
    
  • Quick test: Wait 11 minutes without authorizing, verify graceful timeout

Problem 2: “Token file is world-readable, exposing credentials”

  • Why: Default file creation umask allows group/other read
  • Fix: Force secure permissions:
    (umask 077 && echo "$token" > ~/.kiro/credentials.json)
    
  • Quick test: ls -l ~/.kiro/credentials.json should show -rw-------

Problem 3: “Authentication works locally but fails in CI/CD”

  • Why: CI runs as different user with no home directory
  • Fix: Use environment variables in CI:
    export KIRO_TOKEN="$KIRO_API_KEY"
    kiro auth login --token-stdin <<< "$KIRO_TOKEN"
    
  • Quick test: Unset $HOME and verify token is read from env

Problem 4: “SSH agent forwarding doesn’t work”

  • Why: ForwardAgent not enabled or SSH key not added to agent
  • Fix:
    # On local machine
    ssh-add ~/.ssh/id_rsa
    ssh-add -l  # Verify key is added
    
    # In ~/.ssh/config
    Host dev-server
      ForwardAgent yes
    
  • Quick test: SSH to server, run ssh-add -l, verify keys are listed

Problem 5: “Device flow polling hammers the OAuth server (rate limit)”

  • Why: Polling every second instead of respecting interval from response
  • Fix: Use the interval field from device code response:
    interval=$(echo "$response" | jq -r '.interval // 5')
    sleep $interval
    
  • Quick test: Monitor network requests, verify polling rate matches interval

Definition of Done

  • Kiro detects headless environment automatically (no $DISPLAY)
  • Device code flow is initiated when browser is unavailable
  • User sees clear instructions: URL to visit and code to enter
  • Polling respects the interval from OAuth response (no rate limiting)
  • Token is stored in ~/.kiro/credentials.json with 600 permissions
  • Authentication timeout (10 minutes) is handled gracefully
  • Token refresh is implemented for long-running sessions
  • CI/CD integration works via environment variable (KIRO_API_KEY)
  • SSH agent forwarding is documented as an alternative
  • Installation script (setup-headless-kiro.sh) is provided
  • Documentation explains how to troubleshoot headless auth failures