Skip to main content

Secrets Management

This guide covers secure handling of secrets in Aragora deployments.

Overview

Aragora requires various secrets for operation:

SecretPurposeRequired
ANTHROPIC_API_KEYClaude API accessYes (or OPENAI)
OPENAI_API_KEYGPT API accessYes (or ANTHROPIC)
OPENROUTER_API_KEYFallback providerRecommended
SUPABASE_KEYDatabase accessProduction
STRIPE_SECRET_KEYBillingIf billing enabled
STRIPE_WEBHOOK_SECRETWebhook verificationIf billing enabled
ARAGORA_JWT_SECRETToken signingProduction
GOOGLE_OAUTH_CLIENT_SECRETGoogle OAuth client secretIf Google OAuth enabled
ARAGORA_SSO_CLIENT_SECRETSSO client secret (OIDC/SAML)If SSO enabled

Local Development

Using .env Files

Create a .env file in the project root (never commit this file):

# .env (gitignored)
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_KEY=eyJ...

Load automatically with:

aragora serve  # Auto-loads .env

Using direnv

For automatic environment loading per directory:

# Install direnv
brew install direnv # macOS
sudo apt install direnv # Ubuntu

# Create .envrc
echo 'dotenv' > .envrc
direnv allow

Kubernetes Deployment

Option 1: Kubernetes Secrets (Basic)

Create secrets manually:

kubectl create secret generic aragora-secrets \
--from-literal=ANTHROPIC_API_KEY=sk-ant-... \
--from-literal=OPENAI_API_KEY=sk-... \
--from-literal=SUPABASE_KEY=eyJ... \
--from-literal=ARAGORA_JWT_SECRET=$(openssl rand -base64 32)

Reference in deployment:

# deploy/k8s/deployment.yaml
spec:
containers:
- name: aragora
envFrom:
- secretRef:
name: aragora-secrets

Option 2: Sealed Secrets (GitOps)

For storing encrypted secrets in Git:

# Install kubeseal
brew install kubeseal

# Create sealed secret
kubectl create secret generic aragora-secrets \
--from-literal=ANTHROPIC_API_KEY=sk-ant-... \
--dry-run=client -o yaml | \
kubeseal --format=yaml > deploy/k8s/sealed-secrets.yaml

The sealed secret can be safely committed to Git.

Option 3: External Secrets Operator

For pulling secrets from external providers:

# deploy/k8s/external-secret.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: aragora-secrets
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: aragora-secrets
data:
- secretKey: ANTHROPIC_API_KEY
remoteRef:
key: aragora/api-keys
property: anthropic
- secretKey: OPENAI_API_KEY
remoteRef:
key: aragora/api-keys
property: openai

Option 4: HashiCorp Vault

Direct Vault integration:

# deploy/k8s/deployment.yaml
spec:
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "aragora"
vault.hashicorp.com/agent-inject-secret-env: "secret/data/aragora/env"
vault.hashicorp.com/agent-inject-template-env: |
{{- with secret "secret/data/aragora/env" -}}
export ANTHROPIC_API_KEY="{{ .Data.data.anthropic_key }}"
export OPENAI_API_KEY="{{ .Data.data.openai_key }}"
{{- end }}

Helm Chart Configuration

Using the Aragora Helm chart:

# values.yaml
secrets:
# Option 1: Inline (not recommended for production)
anthropicApiKey: ""
openaiApiKey: ""

# Option 2: Existing secret reference (recommended)
existingSecret: "aragora-secrets"

# Keys in the existing secret
existingSecretKeys:
anthropicApiKey: ANTHROPIC_API_KEY
openaiApiKey: OPENAI_API_KEY
supabaseKey: SUPABASE_KEY

Install with:

helm install aragora deploy/helm/aragora \
--set secrets.existingSecret=aragora-secrets

AWS Deployment

AWS Secrets Manager

# Create secret
aws secretsmanager create-secret \
--name aragora/api-keys \
--secret-string '{"ANTHROPIC_API_KEY":"sk-ant-...","OPENAI_API_KEY":"sk-..."}'

# In ECS task definition
{
"secrets": [
{
"name": "ANTHROPIC_API_KEY",
"valueFrom": "arn:aws:secretsmanager:us-east-1:123456789:secret:aragora/api-keys:ANTHROPIC_API_KEY::"
}
]
}

AWS Parameter Store

# Create parameters
aws ssm put-parameter \
--name /aragora/anthropic-api-key \
--value "sk-ant-..." \
--type SecureString

# In ECS task definition
{
"secrets": [
{
"name": "ANTHROPIC_API_KEY",
"valueFrom": "arn:aws:ssm:us-east-1:123456789:parameter/aragora/anthropic-api-key"
}
]
}

GCP Deployment

Google Secret Manager

# Create secret
echo -n "sk-ant-..." | gcloud secrets create anthropic-api-key --data-file=-

# Grant access
gcloud secrets add-iam-policy-binding anthropic-api-key \
--member="serviceAccount:aragora@project.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"

In Cloud Run:

spec:
template:
spec:
containers:
- env:
- name: ANTHROPIC_API_KEY
valueFrom:
secretKeyRef:
key: latest
name: anthropic-api-key

Secret Rotation

Automatic Rotation

For production, implement secret rotation:

# Example rotation script
import boto3
from datetime import datetime, timedelta

def rotate_api_key():
# 1. Generate new key from provider
new_key = provider.create_api_key()

# 2. Update secret store
client = boto3.client('secretsmanager')
client.update_secret(
SecretId='aragora/api-keys',
SecretString=json.dumps({'ANTHROPIC_API_KEY': new_key})
)

# 3. Restart pods to pick up new secret
# Or use reloader: https://github.com/stakater/Reloader

Using Reloader

Auto-restart pods on secret changes:

# Deploy Reloader
helm repo add stakater https://stakater.github.io/stakater-charts
helm install reloader stakater/reloader

# Annotate deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"

Security Best Practices

1. Principle of Least Privilege

Grant minimal permissions:

# Kubernetes RBAC
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: aragora-secrets-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["aragora-secrets"]
verbs: ["get"]

2. Audit Logging

Enable secret access logging:

# AWS CloudTrail
aws cloudtrail create-trail \
--name secrets-audit \
--s3-bucket-name audit-logs

# GCP Audit Logs (automatic)
gcloud logging read "resource.type=secretmanager.googleapis.com"

3. Network Policies

Restrict secret access by network:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: aragora-secrets-access
spec:
podSelector:
matchLabels:
app: aragora
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/8 # Internal only

4. Secret Scanning

Prevent accidental commits:

# .pre-commit-config.yaml
repos:
- repo: https://github.com/Yelp/detect-secrets
rev: v1.4.0
hooks:
- id: detect-secrets
args: ['--baseline', '.secrets.baseline']

5. Environment Isolation

Separate secrets per environment:

secrets/
├── dev/
│ └── aragora-secrets
├── staging/
│ └── aragora-secrets
└── prod/
└── aragora-secrets

Troubleshooting

Secret Not Found

# Check secret exists
kubectl get secret aragora-secrets -o yaml

# Check pod can access
kubectl exec -it aragora-pod -- env | grep API_KEY

Permission Denied

# Check service account
kubectl get serviceaccount aragora -o yaml

# Check role binding
kubectl get rolebinding -l app=aragora

Secret Not Updating

# Force pod restart
kubectl rollout restart deployment/aragora

# Or use Reloader (see above)

Quick Reference

EnvironmentRecommended Method
Local dev.env file
CI/CDGitHub/GitLab secrets
KubernetesExternal Secrets Operator
AWSSecrets Manager
GCPSecret Manager
ProductionVault + rotation