Complete guide to authenticating with HTTP APIs and CLI tools in TermStack.
- Overview
- HTTP API Authentication
- CLI Tool Authentication
- Security Best Practices
- Environment Variables Support
- Troubleshooting
TermStack supports multiple authentication methods for both HTTP APIs and CLI tools. Authentication is handled through:
- HTTP APIs: Headers with template interpolation
- CLI Tools: Environment variable inheritance and custom env vars
- Template Engine: Dynamic credential insertion using Tera templates
| Method | HTTP APIs | CLI Tools | Example Use Case |
|---|---|---|---|
| Bearer Tokens | ✅ | N/A | GitHub API, REST APIs |
| API Keys | ✅ | N/A | Custom APIs, SaaS platforms |
| Basic Auth | ✅ | N/A | Jenkins, private APIs |
| Environment Variables | ✅ | ✅ | All scenarios (recommended) |
| Credential Files | N/A | ✅ | kubectl, aws, gh CLI |
| Custom Headers | ✅ | N/A | Custom auth schemes |
HTTP authentication is handled through the headers field in your data source configuration. All header values support template interpolation using {{ variable }} syntax.
Most common for modern APIs (GitHub, GitLab, REST APIs)
version: v1
app:
name: "API Browser"
globals:
# Token stored in globals (use with caution - see security section)
api_token: "ghp_your_github_token_here"
start: main
pages:
main:
title: "API Data"
data:
adapter: http
url: "https://api.github.com/user/repos"
method: GET
headers:
Authorization: "Bearer {{ api_token }}"
Accept: "application/json"
items: "$[*]"globals:
# SECURE: Read token from environment variable
api_token: "{{ env.GITHUB_TOKEN }}"
pages:
repos:
data:
adapter: http
url: "https://api.github.com/user/repos"
headers:
Authorization: "Bearer {{ api_token }}"Usage:
export GITHUB_TOKEN="ghp_your_token_here"
termstack config.yamlpages:
login:
# First page gets token
data:
adapter: http
url: "https://api.example.com/login"
method: POST
body: '{"username": "user", "password": "pass"}'
items: "$"
next:
page: protected_data
context:
auth_token: "$.token"
protected_data:
# Second page uses token from login
data:
adapter: http
url: "https://api.example.com/data"
headers:
Authorization: "Bearer {{ login.token }}"Common in SaaS APIs (Stripe, SendGrid, custom APIs)
globals:
api_key: "{{ env.API_KEY }}"
pages:
data:
data:
adapter: http
url: "https://api.example.com/v1/data"
headers:
X-API-Key: "{{ api_key }}"
Content-Type: "application/json"headers:
Authorization: "ApiKey {{ api_key }}"
# OR
Authorization: "Token {{ api_key }}"globals:
primary_key: "{{ env.PRIMARY_API_KEY }}"
secondary_key: "{{ env.SECONDARY_API_KEY }}"
pages:
primary_data:
data:
adapter: http
headers:
X-API-Key: "{{ primary_key }}"
secondary_data:
data:
adapter: http
headers:
X-API-Key: "{{ secondary_key }}"Used in enterprise systems (Jenkins, internal APIs, legacy systems)
globals:
# Generate: echo -n "username:password" | base64
basic_auth: "{{ env.BASIC_AUTH_TOKEN }}"
pages:
protected:
data:
adapter: http
url: "https://jenkins.example.com/api/json"
headers:
Authorization: "Basic {{ basic_auth }}"Generate base64 token:
# Linux/macOS
echo -n "username:password" | base64
# Result: dXNlcm5hbWU6cGFzc3dvcmQ=
export BASIC_AUTH_TOKEN="dXNlcm5hbWU6cGFzc3dvcmQ="globals:
username: "{{ env.API_USERNAME }}"
password: "{{ env.API_PASSWORD }}"
# Note: Encoding must be done externally
pages:
data:
data:
adapter: http
url: "https://api.example.com/data"
headers:
Authorization: "Basic {{ env.ENCODED_CREDENTIALS }}"For custom authentication schemes (proprietary APIs, multi-factor auth)
globals:
session_id: "{{ env.SESSION_ID }}"
tenant_id: "{{ env.TENANT_ID }}"
pages:
data:
data:
adapter: http
headers:
X-Session-ID: "{{ session_id }}"
X-Tenant-ID: "{{ tenant_id }}"
X-Request-ID: "{{ request_id }}"headers:
X-API-Key: "{{ env.API_KEY }}"
X-API-Secret: "{{ env.API_SECRET }}"
X-Timestamp: "{{ timestamp }}"
X-Signature: "{{ signature }}"pages:
data:
data:
adapter: http
url: "https://api.example.com/data"
headers:
Authorization: "Bearer {{ api_token }}"
# Add debug header in development
X-Debug-Mode: "{{ env.DEBUG | default(value='false') }}"CLI tools typically authenticate using environment variables and credential files. TermStack processes inherit the parent environment and can override specific variables.
How it works:
- TermStack inherits all environment variables from the shell that launches it
- CLI commands executed by TermStack receive this environment
- Tools like
kubectl,aws,ghread their standard config files automatically
version: v1
app:
name: "Kubernetes Browser"
start: namespaces
pages:
namespaces:
title: "Namespaces"
data:
adapter: cli
command: "kubectl"
args: ["get", "namespaces", "-o", "json"]
items: "$.items[*]"
# No auth config needed - inherits from ~/.kube/configAuthentication:
- kubectl reads
~/.kube/configautomatically - Uses the active context
- Respects
KUBECONFIGenvironment variable
Setup:
# kubectl is already configured
kubectl config current-context
# Run TermStack - automatically authenticated
termstack kubernetes.yamlpages:
s3_buckets:
data:
adapter: cli
command: "aws"
args: ["s3", "ls", "--output", "json"]
# Inherits ~/.aws/credentials and ~/.aws/configAuthentication:
- Reads
~/.aws/credentials - Reads
~/.aws/config - Respects
AWS_PROFILE,AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY
Setup:
# Configure AWS CLI
aws configure
# Or use profiles
aws configure --profile production
# Run TermStack
AWS_PROFILE=production termstack aws.yamlpages:
repos:
data:
adapter: cli
command: "gh"
args: ["repo", "list", "--json", "name,description"]
items: "$[*]"
# Inherits authentication from gh CLIAuthentication:
# Login once
gh auth login
# TermStack automatically authenticated
termstack github-cli.yamlOverride or add environment variables for specific commands.
pages:
custom_kube:
data:
adapter: cli
command: "kubectl"
args: ["get", "pods", "-o", "json"]
env:
KUBECONFIG: "/custom/path/to/kubeconfig.yaml"pages:
production_s3:
data:
adapter: cli
command: "aws"
args: ["s3", "ls", "--output", "json"]
env:
AWS_PROFILE: "production"
AWS_REGION: "us-west-2"pages:
custom_cli:
data:
adapter: cli
command: "custom-tool"
args: ["list"]
env:
TOOL_CONFIG_PATH: "{{ env.HOME }}/.config/tool/config.yaml"
TOOL_API_KEY: "{{ env.TOOL_API_KEY }}"
TOOL_DEBUG: "true"globals:
environment: "production"
region: "us-west-2"
pages:
data:
data:
adapter: cli
command: "aws"
args: ["s3", "ls"]
env:
AWS_PROFILE: "{{ environment }}"
AWS_REGION: "{{ region }}"Many CLI tools use credential files for authentication. Here's how they work with TermStack:
File Location: ~/.kube/config (or $KUBECONFIG)
Structure:
apiVersion: v1
clusters:
- cluster:
server: https://cluster.example.com
name: my-cluster
contexts:
- context:
cluster: my-cluster
user: my-user
name: my-context
current-context: my-context
users:
- name: my-user
user:
token: eyJhbGc...TermStack Usage:
# Automatically uses current-context
pages:
pods:
data:
adapter: cli
command: "kubectl"
args: ["get", "pods", "-o", "json"]
# Or override config file
pages:
custom_pods:
data:
adapter: cli
command: "kubectl"
args: ["get", "pods", "-o", "json"]
env:
KUBECONFIG: "/path/to/other/config"File Location: ~/.aws/credentials
Structure:
[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
[production]
aws_access_key_id = AKIAI44QH8DHBEXAMPLE
aws_secret_access_key = je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEYTermStack Usage:
# Uses [default] profile
pages:
s3:
data:
adapter: cli
command: "aws"
args: ["s3", "ls"]
# Use specific profile
pages:
prod_s3:
data:
adapter: cli
command: "aws"
args: ["s3", "ls"]
env:
AWS_PROFILE: "production"Authentication: Token stored by gh auth login
Setup:
gh auth loginTermStack Usage:
pages:
repos:
data:
adapter: cli
command: "gh"
args: ["repo", "list", "--json", "name"]
items: "$[*]"✅ Use Environment Variables
globals:
api_key: "{{ env.API_KEY }}"export API_KEY="secret-key"
termstack config.yaml✅ Use Credential Files
# Let tools read their standard config
kubectl, aws, gh, etc. read ~/.kube/config, ~/.aws/credentials✅ Add Secrets to .gitignore
echo "*.secrets.yaml" >> .gitignore
echo ".env" >> .gitignore
echo "secrets/" >> .gitignore✅ Use Read-Only/Scoped Tokens
# GitHub: Use personal access token with minimal scopes
# AWS: Use IAM role with read-only permissions
# Kubernetes: Use ServiceAccount with viewer role✅ Rotate Credentials Regularly
# GitHub tokens: Regenerate every 90 days
# AWS keys: Rotate every 90 days
# Kubernetes tokens: Use short-lived tokens✅ Use Shell Wrappers
#!/bin/bash
# run-termstack.sh
export API_KEY=$(cat ~/.secrets/api-key.txt)
export GITHUB_TOKEN=$(cat ~/.secrets/github-token.txt)
termstack config.yaml❌ Store Secrets in YAML Files
# BAD - visible in file, committed to git
globals:
api_key: "sk-1234567890abcdef"
password: "MyPassword123"❌ Commit Secrets to Git
# BAD - secrets exposed in git history
git add config.yaml # Contains hardcoded secrets
git commit -m "Add config"❌ Use Root/Admin Credentials
# BAD - excessive permissions
globals:
aws_access_key: "AKIAIOSFODNN7EXAMPLE" # Admin user❌ Share Config Files with Secrets
# BAD - secrets exposed to others
slack upload config.yaml # Contains API keys❌ Log or Display Secrets
# BAD - secrets in logs
pages:
debug:
title: "API Key: {{ api_key }}" # Visible on screenTermStack supports environment variable interpolation using the {{ env.VAR }} syntax in templates.
globals:
variable: "{{ env.ENV_VAR_NAME }}"Environment variables work in:
-
✅ Globals section
globals: api_key: "{{ env.API_KEY }}"
-
✅ HTTP URLs
url: "{{ env.API_BASE_URL }}/endpoint"
-
✅ HTTP Headers
headers: Authorization: "Bearer {{ env.AUTH_TOKEN }}"
-
✅ CLI Arguments
args: ["--api-key", "{{ env.API_KEY }}"]
-
✅ CLI Environment Variables
env: CUSTOM_VAR: "{{ env.SOURCE_VAR }}"
Use Tera's default filter to provide fallback values:
globals:
# Use DEBUG env var, default to 'false' if not set
debug_mode: "{{ env.DEBUG | default(value='false') }}"
# Use custom API base, default to production
api_base: "{{ env.API_BASE | default(value='https://api.example.com') }}"
# Use custom timeout, default to 30s
timeout: "{{ env.TIMEOUT | default(value='30s') }}"version: v1
app:
name: "Secure API Browser"
globals:
# All secrets from environment
github_token: "{{ env.GITHUB_TOKEN }}"
api_base: "{{ env.GITHUB_API_BASE | default(value='https://api.github.com') }}"
timeout: "{{ env.API_TIMEOUT | default(value='30s') }}"
start: repos
pages:
repos:
title: "Repositories"
data:
adapter: http
url: "{{ api_base }}/user/repos"
headers:
Authorization: "Bearer {{ github_token }}"
User-Agent: "TermStack"
timeout: "{{ timeout }}"
items: "$[*]"Usage:
# Required
export GITHUB_TOKEN="ghp_your_token_here"
# Optional (uses defaults if not set)
export GITHUB_API_BASE="https://api.github.com"
export API_TIMEOUT="60s"
termstack config.yamlSymptom: API returns 401 status code
Causes:
- Invalid or expired token
- Token not properly set in environment
- Missing
Authorizationheader
Solutions:
# Verify environment variable is set
echo $GITHUB_TOKEN
# Check token is valid (GitHub example)
curl -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/user
# Verify YAML syntax
termstack --validate config.yamlSymptom: API returns 403 status code
Causes:
- Valid token but insufficient permissions
- Rate limiting
- API key doesn't have required scopes
Solutions:
# GitHub: Check token scopes at https://github.com/settings/tokens
# AWS: Check IAM permissions
# Custom API: Verify API key has required permissionsSymptom: Error: "Variable 'env.VAR_NAME' not found"
Solutions:
# Verify variable is set
printenv | grep VAR_NAME
# Set the variable
export VAR_NAME="value"
# Add to shell profile for persistence
echo 'export VAR_NAME="value"' >> ~/.bashrc
source ~/.bashrcSymptom: Error: "command not found: kubectl"
Solutions:
# Verify command exists
which kubectl
# Install if missing
# kubectl: https://kubernetes.io/docs/tasks/tools/
# aws: https://aws.amazon.com/cli/
# gh: https://cli.github.com/Symptom: kubectl returns "Unauthorized" or aws returns "InvalidAccessKeyId"
Solutions:
# kubectl: Verify config
kubectl config view
kubectl config current-context
# aws: Verify credentials
aws configure list
aws sts get-caller-identity
# gh: Verify authentication
gh auth statusSymptom: Command runs but shows wrong data (wrong cluster, wrong AWS account)
Solutions:
# kubectl: Switch context
kubectl config use-context <context-name>
# aws: Use specific profile
AWS_PROFILE=production termstack config.yaml
# Or set in YAML
env:
AWS_PROFILE: "production"See GitHub API Browser for a complete working example.
See AWS CLI Integration for comprehensive AWS examples.
# HTTP Bearer Token
headers:
Authorization: "Bearer {{ env.TOKEN }}"
# HTTP API Key
headers:
X-API-Key: "{{ env.API_KEY }}"
# HTTP Basic Auth
headers:
Authorization: "Basic {{ env.BASIC_AUTH }}"
# CLI with environment
data:
adapter: cli
command: "kubectl"
args: ["get", "pods"]
env:
KUBECONFIG: "/path/to/config"- Templates & Context Guide - Template syntax and variable usage
- GitHub API Cookbook - Complete GitHub integration example
- AWS CLI Cookbook - Complete AWS integration example
- Documentation Hub - Central documentation index