Skip to main content
Browse docs
By Audience
Getting Started
Configuration
Use Cases
IDE Integration
Third-Party Integrations
Engineering Cache
Console
API Reference
Gateway
Workflow Guides
Templates
Providers and SDKs
Industry Guides
Advanced Guides
Browse by Role
Deployment Guides
In-Depth Guides
Tutorials
FAQ

Configuration-as-Code for AI Policies

AI governance policies are infrastructure. They should live in Git, go through code review, and promote through environments the same way application code does. This guide covers structuring YAML policy configs, validating in CI, reviewing diffs, and promoting changes from dev through staging to production.

Use this page when

  • You are structuring YAML policy configs in a Git repository with per-team, per-environment layout
  • You need to set up CI validation with kt policy lint and CODEOWNERS-based review
  • You want to define an environment promotion workflow (dev → staging → prod) for policy configs

Primary audience

  • Primary: Technical Engineers
  • Secondary: AI Agents, Technical Leaders

Policy Configuration Structure

Anatomy of a Policy Config

Every Keeptrusts gateway consumes a policy-config.yaml:

pack:
name: payments-team-prod
version: 0.1.0
enabled: true
description: "Payments team production gateway"

providers:
targets:
- id: openai-primary
provider: openai
model: gpt-4o-mini
base_url: https://api.openai.com
secret_key_ref:
store: OPENAI_API_KEY
- id: anthropic-fallback
provider: anthropic
provider_type: anthropic
format: anthropic
model: claude-sonnet-4-20250514
base_url: https://api.anthropic.com
secret_key_ref:
store: ANTHROPIC_API_KEY

policies:
chain:
- prompt-injection
- pii-detector
- spend-limit
- audit-logger

policy:
pii-detector:
action: redact
spend-limit:
max_daily_usd: 250
audit-logger:
retention_days: 365

Repository Layout

Organize configs by team and environment:

ai-policies/
├── README.md
├── templates/
│ ├── standard-guardrails.yaml
│ ├── healthcare-hipaa.yaml
│ └── finance-sox.yaml
├── teams/
│ ├── payments/
│ │ ├── dev/
│ │ │ └── policy-config.yaml
│ │ ├── staging/
│ │ │ └── policy-config.yaml
│ │ └── prod/
│ │ └── policy-config.yaml
│ └── data-science/
│ ├── dev/
│ │ └── policy-config.yaml
│ └── prod/
│ └── policy-config.yaml
└── shared/
├── blocked-patterns.yaml
└── approved-models.yaml

Validation with kt policy lint

Local Validation

Validate configs before committing:

# Validate a single config
kt policy lint --file teams/payments/prod/policy-config.yaml

# Validate all configs in the repository
find teams -name 'policy-config.yaml' -exec kt policy lint --file {} \;

Exit codes: 0 = valid, 2 = lint failure. The validator checks:

  • YAML syntax
  • Supported top-level document shape
  • Valid policy and provider field names
  • Provider configuration completeness

CI Validation Pipeline

# .github/workflows/policy-validate.yml
name: Validate AI Policies
on:
pull_request:
paths:
- 'teams/**'
- 'templates/**'
- 'shared/**'

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

- name: Install kt CLI
run: |
curl -fsSL https://get.keeptrusts.dev/cli | sh
echo "$HOME/.keeptrusts/bin" >> $GITHUB_PATH

- name: Validate all configs
run: |
errors=0
for config in $(find teams -name 'policy-config.yaml'); do
echo "::group::${config}"
if ! kt policy lint --file "${config}"; then
errors=$((errors + 1))
fi
echo "::endgroup::"
done
if [[ ${errors} -gt 0 ]]; then
echo "::error::${errors} config(s) failed validation"
exit 1
fi

- name: Validate templates
run: |
for tmpl in templates/*.yaml; do
kt policy lint --file "${tmpl}"
done

Diff-Based Review

Meaningful Diffs

YAML configs produce clean diffs. A policy change looks like:

policies:
- name: spend-limit
type: spend_limit
action: block
- max_daily_usd: 250
+ max_daily_usd: 500

+ - name: model-allowlist
+ type: input_filter
+ action: block
+ allowed_models:
+ - gpt-4o
+ - gpt-4o-mini
+ - claude-sonnet-4-20250514

CODEOWNERS for Policy Review

Enforce review by the governance team for production configs:

# .github/CODEOWNERS
teams/*/prod/ @org/governance-team @org/platform-team
teams/*/staging/ @org/platform-team
templates/ @org/governance-team

PR Review Checklist

Automate a review checklist on policy PRs:

# .github/workflows/policy-review.yml
name: Policy Review Checks
on:
pull_request:
paths:
- 'teams/**/prod/**'

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

- name: Diff summary
run: |
echo "## Policy Changes" >> $GITHUB_STEP_SUMMARY
echo '```diff' >> $GITHUB_STEP_SUMMARY
git diff origin/main -- teams/*/prod/ >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY

- name: Check for spend limit changes
run: |
if git diff origin/main -- teams/*/prod/ | grep -q 'max_daily_usd'; then
echo "::warning::Spend limit change detected — requires finance team approval"
fi

- name: Check for provider changes
run: |
if git diff origin/main -- teams/*/prod/ | grep -q 'providers:'; then
echo "::warning::Provider config change detected — requires security review"
fi

Environment Promotion

Promotion Workflow: Dev → Staging → Prod

# 1. Develop and test in dev
vi teams/payments/dev/policy-config.yaml
kt policy lint --file teams/payments/dev/policy-config.yaml
kt gateway run --listen 0.0.0.0:41002 --policy-config teams/payments/dev/policy-config.yaml

# 2. Promote to staging
cp teams/payments/dev/policy-config.yaml teams/payments/staging/policy-config.yaml
# Update gateway name
sed -i 's/payments-team-dev/payments-team-staging/' teams/payments/staging/policy-config.yaml
git add teams/payments/staging/ && git commit -m "Promote payments policy to staging"

# 3. After staging validation, promote to prod
cp teams/payments/staging/policy-config.yaml teams/payments/prod/policy-config.yaml
sed -i 's/payments-team-staging/payments-team-prod/' teams/payments/prod/policy-config.yaml
git add teams/payments/prod/ && git commit -m "Promote payments policy to prod"

Automated Promotion with CI

# .github/workflows/promote.yml
name: Promote Policy Config
on:
workflow_dispatch:
inputs:
team:
description: "Team name"
required: true
from_env:
description: "Source environment"
required: true
type: choice
options: [dev, staging]
to_env:
description: "Target environment"
required: true
type: choice
options: [staging, prod]

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

- name: Copy config
run: |
src="teams/${{ inputs.team }}/${{ inputs.from_env }}/policy-config.yaml"
dst="teams/${{ inputs.team }}/${{ inputs.to_env }}/policy-config.yaml"
cp "${src}" "${dst}"
# Update gateway name to match target environment
sed -i "s/${{ inputs.team }}-${{ inputs.from_env }}/${{ inputs.team }}-${{ inputs.to_env }}/" "${dst}"

- name: Validate promoted config
run: kt policy lint --file "teams/${{ inputs.team }}/${{ inputs.to_env }}/policy-config.yaml"

- name: Create PR
uses: peter-evans/create-pull-request@v6
with:
title: "Promote ${{ inputs.team }} policy: ${{ inputs.from_env }} → ${{ inputs.to_env }}"
branch: "promote/${{ inputs.team }}-${{ inputs.from_env }}-to-${{ inputs.to_env }}"
body: |
Automated policy promotion for **${{ inputs.team }}**.
Source: `${{ inputs.from_env }}` → Target: `${{ inputs.to_env }}`

Environment-Specific Config Variables

Use the API's config-variable system to handle environment-specific secrets without duplicating configs:

# Set provider keys per environment
curl -X PUT "${API_URL}/v1/config-variables/OPENAI_API_KEY" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-d '{"value": "sk-dev-...", "scope": "dev"}'

curl -X PUT "${API_URL}/v1/config-variables/OPENAI_API_KEY" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-d '{"value": "sk-prod-...", "scope": "prod"}'

The policy config references a config-variable-backed secret, for example:

secret_key_ref:
store: OPENAI_API_KEY

The gateway resolves the correct value at runtime. Secrets stay out of Git.

For AI systems

  • Canonical terms: policy-config.yaml, kt policy lint, config variables, secret_key_ref, environment promotion, CODEOWNERS, CI validation
  • CLI commands: kt policy lint --file <path>, kt gateway run --listen <host:port> --policy-config <path>
  • API endpoints: PUT /v1/config-variables/{name}
  • Config structure: pack, providers.targets[], policies.chain, policy
  • Related pages: GitOps for AI Policy, Golden Paths, Secret Management

For engineers

  • Organize configs as teams/<name>/<env>/policy-config.yaml for clean Git diffs and scoped CODEOWNERS
  • Run kt policy lint --file <path> locally before committing; exit code 0 means valid
  • Add CI workflow that lints all changed policy-config.yaml files on PR using find + lint loop
  • Use provider config with block-form secret_key_ref.store references so secrets stay outside YAML
  • Promote configs between environments by copying the file and updating the gateway name
  • Validate: after merge, confirm the gateway auto-reloads by checking /readyz or config version in console

For leaders

  • Treating policies as code enforces change management discipline — every change is reviewed, versioned, and auditable
  • CODEOWNERS ensures governance and security teams approve production policy changes
  • Environment promotion (dev → staging → prod) reduces the blast radius of misconfigurations
  • CI validation catches broken configs before they reach any environment
  • Secrets stay out of Git entirely via config-variable resolution at runtime

Next steps