Skip to main content
The Clanker backend provides secure credential storage and retrieval, allowing you to sync cloud provider credentials across multiple machines without managing local credential files.

Overview

Instead of configuring AWS, GCP, Azure, Cloudflare, and Kubernetes credentials on every machine, store them once in the Clanker backend and retrieve them with an API key. Benefits:
  • Single source of truth: Update credentials in one place
  • Multi-machine sync: Same credentials on laptop, server, CI/CD
  • Secure storage: Credentials encrypted at rest
  • No local credential files: Reduces credential sprawl
  • Easy rotation: Update once, applies everywhere

Configuration

Set backend credentials in your config file:
.clanker.yaml
backend:
  # API key for authentication (or set CLANKER_BACKEND_API_KEY)
  api_key: "your-api-key-here"
  
  # Environment: testing, staging, or production
  env: "testing"
  
  # Optional: Custom backend URL (overrides env)
  # url: "https://custom-url.example.com"
Or use environment variables:
export CLANKER_BACKEND_API_KEY="your-api-key"
export CLANKER_BACKEND_ENV="testing"  # optional
export CLANKER_BACKEND_URL="https://..."  # optional
Or pass as flags:
clanker ask "test" --api-key "your-key" --backend-env "testing"
See cmd/root.go:40 for backend flags.

Storing credentials

AWS credentials

Store credentials from an existing AWS profile:
clanker credentials store aws --profile dev
This reads from ~/.aws/credentials and uploads:
  • Access key ID
  • Secret access key
  • Region (from profile config)
  • Session token (if present)
Or provide credentials directly:
clanker credentials store aws \
  --access-key-id "AKIA..." \
  --secret-access-key "..." \
  --region "us-east-1"
API endpoint: PUT /api/v1/secrets/aws Request body:
{
  "provider": "aws",
  "credentials": {
    "access_key_id": "AKIA...",
    "secret_access_key": "...",
    "region": "us-east-1",
    "session_token": "..."  // optional
  }
}
See internal/backend/client.go:237.

GCP credentials

Store GCP service account credentials:
clanker credentials store gcp \
  --project-id "my-project" \
  --service-account-json "$(cat service-account.json)"
API endpoint: PUT /api/v1/secrets/gcp Request body:
{
  "provider": "gcp",
  "credentials": {
    "project_id": "my-project",
    "service_account_json": "{\"type\":\"service_account\",...}"
  }
}
See internal/backend/client.go:264.

Azure credentials

Store Azure service principal credentials:
clanker credentials store azure \
  --subscription-id "..." \
  --tenant-id "..." \
  --client-id "..." \
  --client-secret "..."
API endpoint: PUT /api/v1/secrets/azure Request body:
{
  "provider": "azure",
  "credentials": {
    "subscription_id": "...",
    "tenant_id": "...",
    "client_id": "...",
    "client_secret": "..."
  }
}
See internal/backend/client.go:346.

Cloudflare credentials

Store Cloudflare API token:
clanker credentials store cloudflare \
  --api-token "..." \
  --account-id "..." \
  --zone-id "..."  # optional
API endpoint: PUT /api/v1/secrets/cloudflare Request body:
{
  "provider": "cloudflare",
  "credentials": {
    "api_token": "...",
    "account_id": "...",
    "zone_id": "..."  // optional
  }
}
See internal/backend/client.go:291.

Kubernetes credentials

Store kubeconfig content:
clanker credentials store kubernetes \
  --kubeconfig "$(cat ~/.kube/config)" \
  --context "production"  # optional
API endpoint: PUT /api/v1/secrets/kubernetes Request body:
{
  "provider": "kubernetes",
  "credentials": {
    "kubeconfig_content": "apiVersion: v1\nkind: Config\n...",
    "context_name": "production"  // optional
  }
}
See internal/backend/client.go:318.

Retrieving credentials

Clanker automatically retrieves credentials from the backend when you provide an API key.

Automatic retrieval

# Set API key once
export CLANKER_BACKEND_API_KEY="your-key"

# Credentials loaded automatically
clanker ask "list ec2 instances" --aws
Debug output:
[backend] GET /api/v1/cli/credentials/aws
[backend] Using AWS credentials from backend
If backend credentials aren’t available, Clanker falls back to local credentials:
[backend] No AWS credentials available (not found), falling back to local
See cmd/ask.go:638.

Manual listing

List all stored credentials:
clanker credentials list --api-key "your-key"
Output:
Stored Credentials:

  Provider: aws
  Created:  2024-03-15 10:30:00
  Updated:  2024-03-20 14:15:00
  Masked:   access_key_id=AKIA***, region=us-east-1

  Provider: gcp
  Created:  2024-03-16 09:00:00
  Updated:  2024-03-16 09:00:00
  Masked:   project_id=my-project

  Provider: cloudflare
  Created:  2024-03-18 16:45:00
  Updated:  2024-03-18 16:45:00
  Masked:   account_id=abc***
API endpoint: GET /api/v1/cli/credentials Response:
{
  "success": true,
  "data": [
    {
      "provider": "aws",
      "created_at": "2024-03-15T10:30:00Z",
      "updated_at": "2024-03-20T14:15:00Z",
      "masked": {
        "access_key_id": "AKIA***",
        "region": "us-east-1"
      }
    }
  ]
}
See internal/backend/client.go:373.

Deleting credentials

Remove stored credentials:
clanker credentials delete aws --api-key "your-key"
API endpoint: DELETE /api/v1/secrets/aws Response:
{
  "success": true,
  "message": "Credentials deleted successfully"
}
See internal/backend/client.go:396.

Backend environments

Clanker supports multiple backend environments:
EnvironmentURLPurpose
testinghttps://lychaz5ra6.execute-api.us-east-1.amazonaws.com/testingDevelopment and testing
staginghttps://2gjp7z6bxi.execute-api.us-east-1.amazonaws.com/stagingPre-production validation
productionContact supportProduction use (contact for access)
Set the environment:
.clanker.yaml
backend:
  env: "staging"
Or override with a custom URL:
.clanker.yaml
backend:
  url: "https://your-custom-backend.example.com"
See internal/backend/resolver.go for URL resolution.

API client implementation

Client structure

type Client struct {
    apiKey     string
    baseURL    string
    httpClient *http.Client
    debug      bool
}

func NewClient(apiKey string, debug bool) *Client {
    return &Client{
        apiKey:  apiKey,
        baseURL: ResolveBackendURL(),
        httpClient: &http.Client{
            Timeout: 30 * time.Second,
        },
        debug: debug,
    }
}
See internal/backend/client.go:20.

Authentication

All requests include the API key in the X-API-Key header:
req.Header.Set("X-API-Key", c.apiKey)
req.Header.Set("Content-Type", "application/json")
See internal/backend/client.go:70.

Error handling

The client handles common HTTP error codes:
StatusError
401unauthorized: invalid API key
404not found: credential or resource does not exist
400+Parse error message from response body
Example:
if resp.StatusCode == http.StatusUnauthorized {
    return nil, fmt.Errorf("unauthorized: invalid API key")
}

if resp.StatusCode >= 400 {
    var apiResp APIResponse
    if err := json.Unmarshal(respBody, &apiResp); err == nil && apiResp.Error != "" {
        return nil, fmt.Errorf("API error: %s", apiResp.Error)
    }
    return nil, fmt.Errorf("API error: status %d", resp.StatusCode)
}
See internal/backend/client.go:88.

Credential types

AWS credentials

type AWSCredentials struct {
    AccessKeyID     string `json:"access_key_id"`
    SecretAccessKey string `json:"secret_access_key"`
    Region          string `json:"region,omitempty"`
    SessionToken    string `json:"session_token,omitempty"`
}
See internal/backend/types.go:5.

GCP credentials

type GCPCredentials struct {
    ProjectID          string `json:"project_id"`
    ServiceAccountJSON string `json:"service_account_json,omitempty"`
}
See internal/backend/types.go:13.

Azure credentials

type AzureCredentials struct {
    SubscriptionID string `json:"subscription_id"`
    TenantID       string `json:"tenant_id,omitempty"`
    ClientID       string `json:"client_id,omitempty"`
    ClientSecret   string `json:"client_secret,omitempty"`
}
See internal/backend/types.go:32.

Cloudflare credentials

type CloudflareCredentials struct {
    APIToken  string `json:"api_token"`
    AccountID string `json:"account_id,omitempty"`
    ZoneID    string `json:"zone_id,omitempty"`
}
See internal/backend/types.go:19.

Kubernetes credentials

type KubernetesCredentials struct {
    KubeconfigContent string `json:"kubeconfig_content"`
    ContextName       string `json:"context_name,omitempty"`
}
See internal/backend/types.go:26.

Fallback behavior

Clanker always tries backend credentials first, then falls back to local configuration:
  1. Check for backend API key (flag, env var, or config)
  2. If API key exists: Try to retrieve credentials from backend
  3. If backend succeeds: Use backend credentials
  4. If backend fails (not found, unauthorized, etc.): Fall back to local credentials
Example flow (AWS):
backendAPIKey := backend.ResolveAPIKey(apiKeyFlag)

if backendAPIKey != "" {
    backendClient := backend.NewClient(backendAPIKey, debug)
    backendCreds, backendErr := backendClient.GetAWSCredentials(ctx)
    if backendErr == nil {
        // Use backend credentials
        awsClient, err = aws.NewClientWithCredentials(ctx, &aws.BackendAWSCredentials{
            AccessKeyID:     backendCreds.AccessKeyID,
            SecretAccessKey: backendCreds.SecretAccessKey,
            Region:          backendCreds.Region,
        }, debug)
    } else {
        // Fall back to local profile
        awsClient, err = aws.NewClientWithProfileAndDebug(ctx, targetProfile, debug)
    }
}
See cmd/ask.go:638.

Security considerations

Never commit your API key or credentials to version control.Use environment variables or local config files (ensure ~/.clanker.yaml is in .gitignore).

API key protection

# Store API key in environment variable
echo 'export CLANKER_BACKEND_API_KEY="your-key"' >> ~/.zshrc
source ~/.zshrc

# Or use a password manager
clanker ask "test" --api-key "$(security find-generic-password -s clanker-api-key -w)"

Credential rotation

When rotating cloud provider credentials:
  1. Generate new credentials in your cloud provider console
  2. Update backend storage:
    clanker credentials store aws --profile new-profile
    
  3. Verify retrieval:
    clanker credentials list --debug
    
  4. Delete old credentials from cloud provider

Audit trail

The backend tracks creation and update timestamps:
clanker credentials list
Provider: aws
Created:  2024-03-15 10:30:00
Updated:  2024-03-20 14:15:00  ← Last rotation

Troubleshooting

Invalid API key

Error:
failed to get AWS credentials: unauthorized: invalid API key
Solution: Check your API key:
echo $CLANKER_BACKEND_API_KEY
Or verify in config:
grep api_key ~/.clanker.yaml

Credentials not found

Error:
failed to get AWS credentials: not found: credential or resource does not exist
Solution: Store credentials first:
clanker credentials store aws --profile dev
Verify storage:
clanker credentials list

Backend timeout

Error:
request failed: context deadline exceeded
Solution: Check network connectivity and backend URL:
curl -I https://lychaz5ra6.execute-api.us-east-1.amazonaws.com/testing/api/v1/cli/credentials
The default timeout is 30 seconds (see internal/backend/client.go:34).

Wrong backend environment

Error:
API error: status 404
Solution: Verify backend environment:
clanker credentials list --backend-env "testing" --debug
Expected debug output:
[backend] GET /api/v1/cli/credentials

Configuration

Config file and backend setup

Debugging

Debug credential retrieval

Credentials command

CLI reference for credential management

Security

Best practices for credential management

Build docs developers (and LLMs) love