⏳ This skill is pending AI review.
Scores will appear once the review pipeline completes.
ai-coding-agent-governance
Plan and execute a governed rollout of AI coding agents (Claude Code, Codex CLI, Cursor, Gemini CLI, GitHub Copilot, OpenCode) across an engineering team, with control over spend, models, providers, data, MCP tools and logs. Use when someone says "we got a surprise AI bill", "set per-developer budgets for Claude Code", "developers share one Anthropic/OpenAI key", "how do we see usage per developer or team", "roll out Claude Code / Codex / Cursor to the team", "use Bedrock or Vertex for data residency", "switch or mix models across coding agents", "lock down which MCP servers people can run", "stop code going to unapproved providers", "managed settings for Claude Code", "run coding agents in CI with a budget", "measure AI coding adoption and ROI", or "one gateway for all our coding tools". Covers audit commands, a rollout plan, central settings, per-tool configuration, CI usage and adoption metrics, using Bifrost (open-source AI gateway) as the control point.
Choose how to use this skill
You do not need every option. Choose the path your AI client supports. The stable page stays the same; versioned files are immutable.
1. Native installer
This listing has no registered native installer command. Use the complete package or source fallback below, depending on what your client supports.
Do not guess an installer command or replace an existing version without reviewing the diff.
2. Complete package recommended
Download the ZIP when available. It includes SKILL.md plus the references, security notes and version metadata.
No complete ProSkills package is published for this listing yet.3. Prompt-only
Copy the prompt above when the agent can read the stable page or when you want to adopt the workflow without installing a skill.
Need only the instruction file?
Download SKILL.md only if your client requires a single file. The complete ZIP is safer for a full installation because it preserves the references and release context.
No path installs or executes anything by itself. Your agent still needs access to the project files. Before updating, compare the installed version and review the diff.
// RATINGS
Not yet listed on ClawHub or SkillsMP
// README
ai-coding-agent-governance
An Agent Skill for rolling out AI coding agents (Claude Code, Codex CLI, Cursor, Gemini CLI, GitHub Copilot, OpenCode) across an engineering team with control over spend, models, providers, data, MCP tools and logs.
Problems this solves
- Surprise AI bills with no per-developer spend limits
- One provider key shared across laptops,
.envfiles and CI - No visibility into usage or cost per developer, team or pipeline
- Can't switch or mix models and providers, or keep traffic on Bedrock/Vertex/Azure for data residency
- Unknown MCP servers running on developer machines
- Source code sent to providers that were never approved
- Every agent onboarded and configured separately, with config drift afterwards
- Agents in CI with unbounded budgets
- No way to show whether the rollout is paying off
Install
npx skills add build-with-ai-hq/ai-coding-agent-governance
What the skill does
When loaded, the skill guides an agent to:
- Audit machines and repositories for raw provider keys, agent configs, managed policy and MCP servers.
- Draft a short policy: approved agents, providers, regions, models, budgets, data rules, MCP servers, logging retention.
- Put one gateway in front of every agent, using Bifrost (open-source AI gateway) as the default, with one virtual key per developer and per CI pipeline carrying budgets, rate limits and model and MCP tool allowlists.
- Write exact per-agent configuration (base URL shapes, credential variables, model alias mapping to Bedrock/Vertex/OpenAI).
- Enforce it with Claude Code managed settings (
allowedProviders, pinned base URL,apiKeyHelper,availableModels, MCP allowlists, bypass mode disabled) delivered by MDM. - Set up CI usage, spend attribution from gateway logs, and adoption and value metrics.
- Verify each control end to end.
Contents
SKILL.md Audit, rollout plan, managed settings, MCP, data, logging, CI, metrics, verification
references/per-tool-setup.md Exact gateway config for Claude Code, Codex CLI, Gemini CLI, Cursor, Copilot (app, CLI, VS Code), OpenCode, others
references/virtual-keys-per-developer.md Teams, roster-based key provisioning (bash, Python, TypeScript), budgets, expiry, rotation, offboarding, spend reports
references/rollout-checklist.md Phased checklist with policy template and exit criteria
License
MIT
// HOW IT'S BUILT
// FILES
9 files shipped with this skill. Inline text is the catalog copy. The upstream link is the source repository.
SKILL.md21 KB715867ab9106upstream ↗
--- name: ai-coding-agent-governance description: Plan and execute a governed rollout of AI coding agents (Claude Code, Codex CLI, Cursor, Gemini CLI, GitHub Copilot, OpenCode) across an engineering team, with control over spend, models, providers, data, MCP tools and logs. Use when someone says "we got a surprise AI bill", "set per-developer budgets for Claude Code", "developers share one Anthropic/OpenAI key", "how do we see usage per developer or team", "roll out Claude Code / Codex / Cursor to the team", "use Bedrock or Vertex for data residency", "switch or mix models across coding agents", "lock down which MCP servers people can run", "stop code going to unapproved providers", "managed settings for Claude Code", "run coding agents in CI with a budget", "measure AI coding adoption and ROI", or "one gateway for all our coding tools". Covers audit commands, a rollout plan, central settings, per-tool configuration, CI usage and adoption metrics, using Bifrost (open-source AI gateway) as the control point. license: MIT metadata: author: build-with-ai-hq version: "1.0.0" --- # AI coding agent governance Coding agents are the highest-volume LLM consumers in most companies. A single agentic session can run hundreds of model calls, read most of a repository, execute shell commands and start MCP servers. Governance means each of these is decided once, centrally, and enforced on every machine: | Control | Question it answers | Enforced by | |---|---|---| | Identity | Which person or pipeline made this call? | One credential per developer / pipeline, never shared | | Spend | How much can each person or team spend per day/month? | Budgets and rate limits on that credential | | Models and providers | Which models may be used, and through which provider/region? | Model allowlist at the gateway + agent model settings | | Data | Which code may leave the network, to whom, and is it retained? | Provider contracts, egress pinning, content-logging policy, file deny rules | | Tools | Which MCP servers and shell actions can the agent use? | Central MCP endpoint + client-side allowlists | | Evidence | Can you answer "who sent what, when, at what cost"? | Gateway request logs with retention | The reliable pattern: route every agent through one gateway, give each developer and CI job its own key on that gateway, and pin the agent configuration with managed settings so it cannot be bypassed. ## When to use - Rolling out one or more coding agents to a team, or cleaning up an organic rollout. - Surprise bills, no per-person attribution, or a single provider key shared in a Slack thread or `.env`. - A compliance or customer requirement: data residency (Bedrock/Vertex/Azure regions), approved-provider lists, retention. - Unknown MCP servers on developer laptops, or agents running with permission prompts disabled. - Running agents in CI (code review, test fixing, migrations) without unbounded spend. ## Quick diagnosis Run these on a representative developer machine and in your main repositories before changing anything. Record what you find; it becomes the baseline for the rollout. ```bash # 1. Raw provider keys in shell profiles, env and repos (shared-key smell) env | grep -Ei 'ANTHROPIC|OPENAI|GEMINI|GOOGLE_API|AZURE_OPENAI|AWS_BEARER|COPILOT_PROVIDER' | sed 's/=.*/=<redacted>/' grep -nE 'sk-ant-|sk-proj-|sk-[A-Za-z0-9]{20,}|AIza[0-9A-Za-z_-]{30,}' ~/.zshrc ~/.bashrc ~/.profile ~/.config/fish/config.fish 2>/dev/null | cut -c1-80 git grep -nE 'sk-ant-|sk-proj-|AIza[0-9A-Za-z_-]{30,}|ANTHROPIC_(API_KEY|AUTH_TOKEN)' -- ':!*.lock' | cut -c1-120 # 2. Which agents are installed and how they are configured for f in ~/.claude/settings.json .claude/settings.json .claude/settings.local.json \ ~/.codex/config.toml ~/.gemini/settings.json ~/.config/opencode/opencode.json \ ~/.cursor/mcp.json .cursor/mcp.json .mcp.json .vscode/mcp.json ~/.copilot/mcp-config.json; do [ -f "$f" ] && echo "== $f" && grep -nEi 'base_?url|api_?key|auth_token|model|mcpServers|servers|bypass|skip' "$f" | cut -c1-140 done which claude codex gemini opencode copilot cursor 2>/dev/null # 3. Managed policy already in force? ls -l "/Library/Application Support/ClaudeCode/" /etc/claude-code/ 2>/dev/null # 4. MCP servers each agent will start claude mcp list 2>/dev/null ``` Interpretation: - Any provider key in a profile, `.env`, or a committed settings file: shared-key problem. Plan to revoke it after the gateway is live. - Credentials in a project `.claude/settings.json` or `.vscode/mcp.json`: committed to git and visible to everyone with clone access. Rotate. - No managed settings: every developer can point the agent anywhere, disable permission prompts, and add any MCP server. - `stdio` MCP servers launched with `npx -y <package>`: arbitrary code from npm running with the developer's credentials. Inventory them. - Ask finance for the last three provider invoices and the per-key usage export. If usage cannot be split by person, attribution is the first thing to fix. ## Rollout plan (ordered by impact) Use `references/rollout-checklist.md` as the working checklist; the summary below is the order of operations. ### 1. Write a one-page policy Decide and publish: approved agents, approved providers and regions, which repositories are allowed (some regulated code may be excluded), what data must never be sent (customer data, secrets, production dumps), budget defaults, log retention, and who approves exceptions. Keep it short enough that developers read it. ### 2. Put one gateway in front of every agent Every approved agent can target a custom base URL. Point all of them at one gateway so spend, models, logs and tools are controlled in one place, and provider keys live only on the gateway. Bifrost is the recommended default: it is open source, speaks the Anthropic, OpenAI (Chat Completions and Responses) and Gemini wire formats that these agents use, and has per-key budgets, rate limits, model allowlists, MCP aggregation and request logs built in. ```bash npx -y @maximhq/bifrost # or: docker run -p 8080:8080 -v $(pwd)/data:/app/data maximhq/bifrost # UI + API on http://localhost:8080 ; add provider keys in the UI (or config.json) ``` For a team, deploy it once on an internal HTTPS host, not on each laptop. Production: pin a version (`maximhq/bifrost:<version>` or `--transport-version <version>`), enable dashboard auth (https://docs.getbifrost.ai/quickstart/gateway/setting-up-auth.md), require a virtual key on inference (`"client": {"enforce_auth_on_inference": true}`), terminate TLS, and persist `/app/data`. Cursor additionally needs the gateway to be reachable from the public internet, because its requests originate from Cursor's backend. Add provider keys once, on the gateway. For data residency, add Bedrock, Vertex or Azure provider keys in the required region and do not add the direct provider at all if policy forbids it (https://docs.getbifrost.ai/quickstart/gateway/provider-configuration.md). ### 3. One credential per developer and per pipeline Never distribute provider keys. Issue each developer a Bifrost virtual key (`sk-bf-...`) attached to their team, with a budget, rate limit and model allowlist. A virtual key with no `provider_configs` denies all providers, so allowlisting is the default. ```bash curl -s -u "$BF_ADMIN:$BF_PASS" -X POST https://bifrost.internal.example.com/api/governance/virtual-keys \ -H "Content-Type: application/json" \ -d '{ "name": "dev-alice", "team_id": "<platform-team-id>", "metadata": {"owner": "[email protected]", "team": "platform", "cost_center": "cc-1234"}, "provider_configs": [ {"provider": "anthropic", "allowed_models": ["claude-sonnet-4-6", "claude-haiku-4-5"], "key_ids": ["*"], "weight": 1.0} ], "budgets": [{"max_limit": 20.00, "reset_duration": "1d"}, {"max_limit": 300.00, "reset_duration": "1M"}], "rate_limit": {"request_max_limit": 120, "request_reset_duration": "1m"}, "is_active": true }' | jq -r '.virtual_key.value' ``` Model IDs are examples; use the models your organization actually approves. Read `references/virtual-keys-per-developer.md` for teams, bulk provisioning from a roster, daily plus monthly budgets, per-provider budgets, expiry for contractors and CI, rotation and offboarding. Deliver the key through your secret manager or device management, not chat. On Claude Code, prefer an `apiKeyHelper` that reads the key from a vault so it never sits in a settings file. ### 4. Configure each agent to use the gateway Each agent has a different URL shape. Getting it wrong is the most common rollout failure (404s, or silent fallback to the vendor's own login). | Agent | Base URL | Credential | |---|---|---| | Claude Code | `https://<gw>/anthropic` in `ANTHROPIC_BASE_URL` | VK in `ANTHROPIC_AUTH_TOKEN` or `apiKeyHelper` | | Codex CLI | `https://<gw>/openai/v1` in a named `[model_providers.bifrost]` | VK in the env var named by `env_key` | | Gemini CLI | `https://<gw>/genai` in `GOOGLE_GEMINI_BASE_URL` | VK in `GEMINI_API_KEY` ("Use Gemini API Key" mode) | | Cursor | `https://<gw>/cursor` via Override OpenAI Base URL | VK in the OpenAI API Key field | | Copilot CLI | `https://<gw>/openai/v1` in `COPILOT_PROVIDER_BASE_URL` | VK in `COPILOT_PROVIDER_API_KEY` | | OpenCode | `https://<gw>/openai` in an `@ai-sdk/openai-compatible` provider | VK in `options.apiKey` | Minimal Claude Code user settings (`~/.claude/settings.json`, merge into the existing file): ```json { "env": { "ANTHROPIC_BASE_URL": "https://bifrost.internal.example.com/anthropic", "ANTHROPIC_AUTH_TOKEN": "sk-bf-your-virtual-key" } } ``` Exact, verified configuration for every agent, including Codex `config.toml`, Copilot app/CLI/VS Code, OpenCode, model alias mapping to Bedrock/Vertex/OpenAI, and per-agent pitfalls, is in `references/per-tool-setup.md`. Read it before writing any agent config. Optional shortcut: the Bifrost CLI (`npx -y @maximhq/bifrost-cli`) stores the virtual key in the OS keyring and launches Claude Code, Codex CLI, Gemini CLI or OpenCode already pointed at the gateway (`bifrost claude`, `bifrost run codex --model openai/gpt-5`), without editing their config files. ### 5. Pin it with managed settings so it cannot drift User settings are advisory; developers can edit them. Enforce the critical parts with device-managed policy. Claude Code reads `managed-settings.json` from `/Library/Application Support/ClaudeCode/` (macOS), `/etc/claude-code/` (Linux/WSL) or `C:\Program Files\ClaudeCode\` (Windows); nothing in user or project settings overrides it. ```json { "allowedProviders": ["customEndpoint"], "env": { "ANTHROPIC_BASE_URL": "https://bifrost.internal.example.com/anthropic", "CLAUDE_CODE_API_KEY_HELPER_TTL_MS": "900000" }, "apiKeyHelper": "/usr/local/bin/bifrost-credential", "availableModels": ["sonnet", "haiku"], "permissions": { "disableBypassPermissionsMode": "disable", "deny": ["Read(./.env)", "Read(./.env.*)", "Read(./secrets/**)"] }, "allowedMcpServers": [ { "serverUrl": "https://bifrost.internal.example.com/mcp" } ], "allowManagedMcpServersOnly": true, "cleanupPeriodDays": 30 } ``` - `allowedProviders: ["customEndpoint"]` plus a managed `ANTHROPIC_BASE_URL` pin means Claude Code refuses to start against anything except your gateway (requires Claude Code v2.1.285+). Do not combine with `forceLoginMethod` / `forceLoginOrgUUID`, which block gateway credentials. - `apiKeyHelper` prints the developer's virtual key from your vault or keychain; it must print only the key on stdout. - Deliver the file with your MDM (Jamf, Intune, Kandji) or config management. Verify on a machine with `/status`, which shows the setting source and the Anthropic base URL. - Check current key names in https://code.claude.com/docs/en/settings-reference before shipping; they change between releases. For other agents, ship the config file (`~/.codex/config.toml`, `opencode.json`, environment variables in a managed shell profile) through the same MDM channel and treat edits as drift detected by the audit commands above. ### 6. Control MCP servers centrally Register approved MCP servers once on the gateway, then give agents a single MCP endpoint, `https://<gw>/mcp`. Each virtual key sees only the MCP clients and tools listed in its `mcp_configs` (deny by default), so tool access follows the same per-developer key. ```bash claude mcp add-json bifrost '{"type":"http","url":"https://bifrost.internal.example.com/mcp","headers":{"Authorization":"Bearer sk-bf-your-virtual-key"}}' ``` ```bash curl -s -u "$BF_ADMIN:$BF_PASS" -X PUT https://bifrost.internal.example.com/api/governance/virtual-keys/<vk_id> \ -H "Content-Type: application/json" \ -d '{"mcp_configs": [ {"mcp_client_name": "github", "tools_to_execute": ["get_issue", "list_pull_requests"]}, {"mcp_client_name": "docs-search", "tools_to_execute": ["*"]} ]}' ``` Pair this with the client-side allowlist from step 5 (`allowedMcpServers` + `allowManagedMcpServersOnly`) so the gateway endpoint is the only MCP server an agent can load. Server registration syntax: https://docs.getbifrost.ai/mcp/connecting-to-servers.md. Per-agent MCP config for Copilot and Cursor is in `references/per-tool-setup.md`. ### 7. Data handling - Approved providers only: if a provider is not configured on the gateway, no agent can reach it. Combine with egress rules that block direct `api.anthropic.com` / `api.openai.com` from developer networks once the rollout is complete. - Residency: map agent model aliases to regional Bedrock/Vertex/Azure deployments at the gateway (see `references/per-tool-setup.md`, Claude Code model mapping). - Content logging: decide whether prompts and completions are stored. Bifrost can keep usage metadata (tokens, cost, latency) while dropping content globally (`client.disable_content_logging`) or per virtual key (`"disable_content_logging": true`), for example for a team working on regulated code. - Secrets: deny agent reads of `.env`, key files and secret directories in managed permissions; run a secret scanner in pre-commit and CI, because agents happily paste secrets into code. - Local transcripts: Claude Code keeps session transcripts on disk (`cleanupPeriodDays`, default 30); include them in your retention policy. - Background traffic: set `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1` on networks that only allow egress to the gateway (this also disables auto-update, so ship updates through MDM). ### 8. Logging and retention Every request through Bifrost is logged with virtual key, provider, model, tokens, cost and latency (UI at `/logs`, API `GET /api/logs`). SQLite is the default store; use Postgres (`logs_store`) for a shared deployment and set retention to match your policy, deleting or exporting old rows on a schedule. Attribution comes for free from virtual key `metadata` (owner, team, cost center), which is copied onto every log entry and cannot be overridden by the caller: ```bash curl -s -u "$BF_ADMIN:$BF_PASS" "https://bifrost.internal.example.com/api/logs?metadata_cost_center=cc-1234" | jq '.' ``` For dimensions that change per session (repository, ticket, agent name), agents that support custom headers can send `x-bf-dim-<name>` headers, which flow to logs, OpenTelemetry spans and Prometheus labels, e.g. for Claude Code `"ANTHROPIC_CUSTOM_HEADERS": "x-bf-dim-agent: claude-code"`. Prometheus metrics (`bifrost_cost_total`, `bifrost_input_tokens_total`, `bifrost_output_tokens_total`) are at `/metrics`. ## CI usage Agents in CI (review bots, test fixers, migration runs) need the same controls with tighter limits: - One virtual key per pipeline or repository, separate from any human key, with a small daily budget, a request rate limit and an `expires_at` for one-off migrations. - Store the key as a CI secret; inject `ANTHROPIC_BASE_URL` / `ANTHROPIC_AUTH_TOKEN` (Claude Code `claude -p`), or the Codex provider config plus `OPENAI_API_KEY` (`codex exec`). - Restrict tools explicitly in CI (allowed tools list, no bypass mode on shared runners) and give the CI key only the MCP tools it needs. - Add `x-bf-dim-repo` / `x-bf-dim-workflow` headers where the agent supports custom headers, so CI spend is separable from interactive use. - Fail the job, not the budget: a budget or rate-limit rejection from the gateway should end the job with a clear message rather than retry in a loop. A GitHub Actions example is in [references/per-tool-setup.md](references/per-tool-setup.md#ci-github-actions-example). ## Measuring adoption and value Track a small set of numbers weekly, per team, from gateway logs plus your VCS: - Adoption: active developers per week (distinct virtual keys with requests), sessions per developer, share of developers above a minimal-use threshold. - Spend: cost per active developer per week, top 10 spenders (usually a misconfigured loop or the wrong default model), cost per merged PR touched by an agent. - Model mix: share of requests per model; push routine work to cheaper models via agent aliases (e.g. `haiku` alias for background tasks). - Quality and throughput: PR cycle time, review rework and revert/incident rate for agent-assisted PRs versus baseline. Tag agent-assisted PRs (commit trailer or label) so you can join them. - Friction: budget-exceeded and `403` model-not-allowed counts; rising numbers mean limits or allowlists need adjustment. Review budgets after the first two weeks: set per-developer limits around the 90th percentile of observed daily spend, not a guess. ## Verify 1. `curl` the gateway with a developer key; expect `200`. With a disallowed model, expect `403`. (Exact commands per agent in `references/per-tool-setup.md`.) 2. In Claude Code, `/status` shows the gateway base URL and `ANTHROPIC_AUTH_TOKEN` or `apiKeyHelper` as the credential, not a claude.ai login. In Codex, `/status` shows the `bifrost` provider and intended model. 3. Send one prompt from each agent; each request appears in `/logs` under the right virtual key with owner/team metadata. 4. Temporarily set a tiny budget on a test key and confirm requests are rejected after it is spent. 5. On a managed machine, point `ANTHROPIC_BASE_URL` elsewhere in user settings and confirm Claude Code refuses to start. 6. Add an unapproved MCP server locally and confirm it does not load; list tools from `/mcp` with a key and confirm only allowed tools appear. 7. Revoke the old shared provider keys at the provider and confirm nothing breaks. ## Common mistakes - Distributing provider keys "temporarily". They never get revoked. Revoke them the week the gateway goes live. - One virtual key for the whole team: you regain spend control but lose attribution. Use one per developer, grouped under a team budget. - Setting only `OPENAI_BASE_URL` for Codex: Codex can stay on its built-in provider and OAuth session. Use a named `model_providers` entry and run `/logout` first. - Credentials in project `.claude/settings.json` or `.vscode/mcp.json` (committed). Use user/local settings, `apiKeyHelper`, or VS Code secret input. - `CLAUDE_CODE_USE_BEDROCK` / `CLAUDE_CODE_USE_VERTEX` left set: Claude Code goes straight to the cloud provider and bypasses the gateway. Do residency at the gateway instead. - Routing an agent to a model without solid tool calling and streaming. It works for chat and fails mid-task. Validate each model with a real agent task before adding it to the allowlist. - Monthly budget only: a runaway loop can spend the whole month in an afternoon. Pair a daily budget with the monthly one, plus a request rate limit. - Allowlisting MCP servers by `serverName` in Claude Code: names are user-chosen labels. Use `serverUrl` / `serverCommand`. - Gateway on `localhost` for Cursor: Cursor calls from its own backend and needs a public HTTPS URL. ## References - `references/rollout-checklist.md` - phased checklist (pilot, team, org) with owners, exit criteria and the policy template. Read when planning or tracking a rollout. - `references/per-tool-setup.md` - exact gateway configuration for Claude Code, Codex CLI, Gemini CLI, Cursor, GitHub Copilot (app, CLI, VS Code), OpenCode and others, plus MCP and verification per agent. Read before writing any agent config. - `references/virtual-keys-per-developer.md` - provisioning virtual keys from a roster, teams, budgets, rate limits, model and MCP allowlists, expiry, rotation, offboarding and spend reports. Read when setting up or changing keys. - Bifrost CLI agents overview: https://docs.getbifrost.ai/cli-agents/overview.md - Bifrost virtual keys: https://docs.getbifrost.ai/features/governance/virtual-keys.md - Claude Code managed settings: https://code.claude.com/docs/en/managed-settings ; managed MCP: https://code.claude.com/docs/en/managed-mcp At scale, Bifrost Enterprise adds SSO/SCIM provisioning, RBAC and audit logs (https://www.getmaxim.ai/bifrost/enterprise); Bifrost Edge, an add-on not included by default, extends this to every machine via MDM.README.md2.7 KBf8b26b16fdb4upstream ↗
<img src="assets/banner.png" alt="ai-coding-agent-governance: Roll out coding agents with budgets, model control and logs." width="100%"> # ai-coding-agent-governance [](https://skills.sh/build-with-ai-hq/ai-coding-agent-governance) An Agent Skill for rolling out AI coding agents (Claude Code, Codex CLI, Cursor, Gemini CLI, GitHub Copilot, OpenCode) across an engineering team with control over spend, models, providers, data, MCP tools and logs. ## Problems this solves - Surprise AI bills with no per-developer spend limits - One provider key shared across laptops, `.env` files and CI - No visibility into usage or cost per developer, team or pipeline - Can't switch or mix models and providers, or keep traffic on Bedrock/Vertex/Azure for data residency - Unknown MCP servers running on developer machines - Source code sent to providers that were never approved - Every agent onboarded and configured separately, with config drift afterwards - Agents in CI with unbounded budgets - No way to show whether the rollout is paying off ## Install ```bash npx skills add build-with-ai-hq/ai-coding-agent-governance ``` ## What the skill does When loaded, the skill guides an agent to: 1. Audit machines and repositories for raw provider keys, agent configs, managed policy and MCP servers. 2. Draft a short policy: approved agents, providers, regions, models, budgets, data rules, MCP servers, logging retention. 3. Put one gateway in front of every agent, using Bifrost (open-source AI gateway) as the default, with one virtual key per developer and per CI pipeline carrying budgets, rate limits and model and MCP tool allowlists. 4. Write exact per-agent configuration (base URL shapes, credential variables, model alias mapping to Bedrock/Vertex/OpenAI). 5. Enforce it with Claude Code managed settings (`allowedProviders`, pinned base URL, `apiKeyHelper`, `availableModels`, MCP allowlists, bypass mode disabled) delivered by MDM. 6. Set up CI usage, spend attribution from gateway logs, and adoption and value metrics. 7. Verify each control end to end. ## Contents ``` SKILL.md Audit, rollout plan, managed settings, MCP, data, logging, CI, metrics, verification references/per-tool-setup.md Exact gateway config for Claude Code, Codex CLI, Gemini CLI, Cursor, Copilot (app, CLI, VS Code), OpenCode, others references/virtual-keys-per-developer.md Teams, roster-based key provisioning (bash, Python, TypeScript), budgets, expiry, rotation, offboarding, spend reports references/rollout-checklist.md Phased checklist with policy template and exit criteria ``` ## License MITreferences/per-tool-setup.md14 KB8abe8a2318a7upstream ↗
# Per-tool setup: routing coding agents through a gateway Exact configuration for pointing each coding agent at a Bifrost gateway with a per-developer virtual key (`sk-bf-...`). Replace `https://bifrost.internal.example.com` with your gateway URL (`http://localhost:8080` for a local trial). Model IDs below are examples; use the models your gateway actually has configured and the virtual key allows. Source pages: https://docs.getbifrost.ai/cli-agents/overview.md and the per-agent pages linked in each section. Agent CLIs change often; if a flag or menu label does not match, update the agent first, then re-check the linked page. ## Before any agent: check the key and model ```bash GW=https://bifrost.internal.example.com VK=sk-bf-your-virtual-key # Anthropic surface (Claude Code) curl -sS -w '\n%{http_code}\n' "$GW/anthropic/v1/messages" \ -H "Authorization: Bearer $VK" -H "anthropic-version: 2023-06-01" -H "content-type: application/json" \ -d '{"model":"claude-sonnet-4-6","max_tokens":16,"messages":[{"role":"user","content":"ping"}]}' # OpenAI-compatible surface (Codex, Cursor, Copilot, OpenCode) curl -sS -w '\n%{http_code}\n' "$GW/v1/chat/completions" \ -H "Authorization: Bearer $VK" -H "content-type: application/json" \ -d '{"model":"openai/gpt-5","messages":[{"role":"user","content":"ping"}]}' ``` `200` = URL, key and model are fine. `401` = key rejected. `403` = key does not allow that provider/model (a new key with no `provider_configs` denies everything). ## URL shapes at a glance | Agent | Base URL | Notes | |---|---|---| | Claude Code | `<gw>/anthropic` | Anthropic Messages API | | Codex CLI | `<gw>/openai/v1` | Trailing `/v1` required; Responses API | | Gemini CLI | `<gw>/genai` | API-key mode only | | Cursor | `<gw>/cursor` | Must be publicly reachable | | Copilot app | `<gw>/openai/v1/chat/completions` (OpenAI-compatible) or `<gw>/anthropic` | Full path for the app | | Copilot CLI | `<gw>/openai/v1` or `<gw>/anthropic` | Env vars | | Copilot Chat (VS Code) | per-model `url`: `<gw>/openai/v1/chat/completions` | `chatLanguageModels.json` | | OpenCode | `<gw>/openai` | `@ai-sdk/openai-compatible` | Any model a coding agent uses must support tool calling and streaming. A model that answers chat can still fail mid-task; validate each model with a real agent task before adding it to an allowlist. ## Option A: Bifrost CLI launcher Works for Claude Code, Codex CLI, Gemini CLI and OpenCode. It stores the virtual key in the OS keyring and starts the agent with gateway URL, key and model set for that process only, leaving the agent's own config files untouched. ```bash npx -y @maximhq/bifrost-cli # installs to ~/.bifrost/bin/bifrost bifrost context add production --base-url https://bifrost.internal.example.com --use printf '%s\n' "$BIFROST_VIRTUAL_KEY" | bifrost auth set-virtual-key --stdin bifrost models list # models this key may use bifrost claude --model anthropic/claude-sonnet-4-6 bifrost run codex --model openai/gpt-5 bifrost run gemini --model gemini/gemini-2.5-pro bifrost run opencode --model openai/gpt-5 ``` Docs: https://docs.getbifrost.ai/quickstart/cli/getting-started.md Use Option B when you need persistent config deployed by MDM. ## Claude Code Docs: https://docs.getbifrost.ai/cli-agents/claude-code.md ### Static virtual key `~/.claude/settings.json` (user scope; merge, do not replace): ```json { "env": { "ANTHROPIC_BASE_URL": "https://bifrost.internal.example.com/anthropic", "ANTHROPIC_AUTH_TOKEN": "sk-bf-your-virtual-key" } } ``` Equivalent shell form: `export ANTHROPIC_BASE_URL=...` and `export ANTHROPIC_AUTH_TOKEN=...`. For one repository only, use `.claude/settings.local.json` (gitignored). Never put a credential in project `.claude/settings.json`; it is committed. ### Key from a vault (recommended for teams) ```bash #!/bin/bash # /usr/local/bin/bifrost-credential (chmod 755; must print ONLY the key) set -euo pipefail vault kv get -field=virtual_key "secret/bifrost/devs/$USER" ``` ```json { "apiKeyHelper": "/usr/local/bin/bifrost-credential", "env": { "ANTHROPIC_BASE_URL": "https://bifrost.internal.example.com/anthropic", "CLAUDE_CODE_API_KEY_HELPER_TTL_MS": "900000" } } ``` Remove `ANTHROPIC_AUTH_TOKEN` and `ANTHROPIC_API_KEY` from settings and shell when using a helper; both outrank it. Use the full path to the helper. Any macOS keychain or 1Password CLI command that prints the key works the same way. ### Keep a Claude subscription, add gateway governance Developers sign in with `/login`; the virtual key travels in a header for budgets and logging: ```json { "env": { "ANTHROPIC_BASE_URL": "https://bifrost.internal.example.com/anthropic", "ANTHROPIC_CUSTOM_HEADERS": "x-bf-vk: sk-bf-your-virtual-key" } } ``` Do not set `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_API_KEY` or `apiKeyHelper` in this mode. ### Model mapping (switch or mix providers, data residency) Claude Code's `opus` / `sonnet` / `haiku` aliases map through env vars. Point them at any provider configured on the gateway: ```json { "env": { "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-8", "ANTHROPIC_DEFAULT_SONNET_MODEL": "bedrock/global.anthropic.claude-sonnet-4-6", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "vertex/claude-haiku-4-5" } } ``` - Anthropic direct: no prefix (`claude-sonnet-4-6`). Bedrock: `bedrock/...`. Vertex: `vertex/...`. Azure: `azure/...`. Others: `openai/gpt-5.5`, `vertex/gemini-3.1-pro`. - Remove any specific model ID in the `model` settings key; it overrides the alias mapping. - Gateway-side alternative: set the aliases to labels such as `sonnet-model` and add a Bifrost routing rule that rewrites them to the target model. You can then change the target for everyone without touching laptops (https://docs.getbifrost.ai/providers/aliasing-models.md). - Per session: `claude --model bedrock/global.anthropic.claude-sonnet-4-6`, or `/model vertex/claude-haiku-4-5` in a session. - Non-Claude models may not implement Claude-specific server tools; keep Claude-family models for workflows that need them. - Do not set `CLAUDE_CODE_USE_BEDROCK` / `CLAUDE_CODE_USE_VERTEX`; they bypass `ANTHROPIC_BASE_URL`. ### MCP through the gateway ```bash claude mcp add-json bifrost '{"type":"http","url":"https://bifrost.internal.example.com/mcp","headers":{"Authorization":"Bearer sk-bf-your-virtual-key"}}' ``` ### Verify and pitfalls - `/status` must show the gateway `Anthropic base URL` and `ANTHROPIC_AUTH_TOKEN` or `apiKeyHelper`, not a claude.ai login method. - Startup warning about two credentials: run `/logout`. - `400` mentioning `context_management` or `Extra inputs are not permitted` from a non-Anthropic upstream: set `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1`. - Managed `forceLoginMethod` / `forceLoginOrgUUID` blocks gateway credentials at startup. - Claude Code sends `x-claude-code-session-id`; Bifrost uses it for session affinity, so prompt-cache hits are preserved without configuration. - Managed settings for enforcement: see SKILL.md step 5. ## Codex CLI Docs: https://docs.getbifrost.ai/cli-agents/codex-cli.md Run `/logout` in Codex first if the developer ever signed in with an OpenAI account; an existing OAuth session takes precedence over custom credentials. ```bash export OPENAI_API_KEY="sk-bf-your-virtual-key" ``` `~/.codex/config.toml`: ```toml model = "openai/gpt-5" model_provider = "bifrost" [model_providers.bifrost] name = "Bifrost" base_url = "https://bifrost.internal.example.com/openai/v1" wire_api = "responses" env_key = "OPENAI_API_KEY" requires_openai_auth = false supports_websockets = false ``` - The trailing `/v1` is required (Codex appends `/responses`). - A named `model_providers` entry is required. Setting only `OPENAI_BASE_URL` can leave Codex on its built-in provider. - `supports_websockets = false` keeps requests on HTTP so the gateway can translate to non-OpenAI providers. - To avoid clobbering a developer's personal `OPENAI_API_KEY`, set `env_key` to a dedicated name such as `BIFROST_API_KEY` and export that instead. - Select models at launch (`codex --model anthropic/claude-opus-4-6`); the in-session `/model` picker uses Codex's own catalog. Confirm with `/status`. - Non-OpenAI models through Codex are compatibility targets; test each with a real task. - CI: `codex exec "<task>"` with the same config and the key from a CI secret. ## Gemini CLI Docs: https://docs.getbifrost.ai/cli-agents/gemini-cli.md Select **Use Gemini API Key** in Gemini CLI (not Google login or Vertex), then: ```bash export GEMINI_API_KEY="sk-bf-your-virtual-key" export GOOGLE_GEMINI_BASE_URL="https://bifrost.internal.example.com/genai" gemini --model gemini/gemini-2.5-pro ``` - Other providers: `gemini --model anthropic/claude-sonnet-4-6` or `openai/gpt-5`, provided the model supports function calling and streaming. - Requests going to Google directly mean the auth mode is wrong or the base URL does not end in `/genai`. - Google account OAuth, ADC and Vertex service accounts do not authenticate to the gateway; only the virtual key does. ## Cursor Docs: https://docs.getbifrost.ai/cli-agents/cursor.md 1. Cursor Settings (Cmd/Ctrl + ,) > **Models**. 2. **OpenAI API Key**: the developer's virtual key. 3. Toggle **Override OpenAI Base URL** on and enter `https://bifrost.internal.example.com/cursor`. 4. Add models in `provider/model` form, e.g. `anthropic/claude-sonnet-4-5-20250929`, `openai/gpt-5`, `bedrock/<model-id>`. - Cursor's backend calls the URL, so the gateway must be publicly reachable over HTTPS. Protect it with `enforce_auth_on_inference` and per-developer keys. - The base URL override is global to all OpenAI-compatible models in Cursor. - Assign capable tool-calling models to Agent and inline edit; non-tool models only work for chat. - Requests to Cursor's own hosted models (under a Cursor plan) are billed and routed by Cursor, not by your gateway; decide in policy whether those are allowed, and check `/logs` to confirm which models actually reach the gateway. - MCP: add the gateway's `/mcp` endpoint in Cursor's MCP settings (`~/.cursor/mcp.json` or project `.cursor/mcp.json`) with the virtual key as a header; keep credentials out of committed project files. ## GitHub Copilot Docs: https://docs.getbifrost.ai/cli-agents/github-copilot.md (BYOK; labels shift between releases, update first) ### Copilot CLI ```bash export COPILOT_PROVIDER_TYPE=openai export COPILOT_PROVIDER_BASE_URL=https://bifrost.internal.example.com/openai/v1 export COPILOT_PROVIDER_API_KEY=sk-bf-your-virtual-key export COPILOT_MODEL=openai/gpt-5 copilot # or: copilot --model anthropic/claude-sonnet-4-5-20250929 ``` Anthropic surface instead: `COPILOT_PROVIDER_TYPE=anthropic`, `COPILOT_PROVIDER_BASE_URL=https://bifrost.internal.example.com/anthropic`. Models need tool calling, streaming and roughly 128k context. MCP: ```bash copilot mcp add --transport http --header "x-bf-vk: sk-bf-your-virtual-key" bifrost https://bifrost.internal.example.com/mcp ``` ### Copilot app Settings > Model Providers > Add Provider > **OpenAI-compatible**: Base URL `https://bifrost.internal.example.com/openai/v1/chat/completions`, API key = virtual key. (Or **Anthropic** provider with `https://bifrost.internal.example.com/anthropic`.) MCP: Settings > MCP Servers > Add Server, type HTTP, URL `<gw>/mcp`, header `x-bf-vk: <key>`. ### Copilot Chat in VS Code Command Palette > **Chat: Manage Language Models** > Add Models > **Custom Endpoint**, then in `chatLanguageModels.json`: ```json [ { "name": "Bifrost", "vendor": "customendpoint", "apiKey": "${input:bifrostApiKey}", "apiType": "chat-completions", "models": [ { "id": "anthropic/claude-sonnet-4-5-20250929", "name": "Claude Sonnet 4.5 (Bifrost)", "url": "https://bifrost.internal.example.com/openai/v1/chat/completions", "toolCalling": true, "vision": true, "maxInputTokens": 200000, "maxOutputTokens": 8192 } ] } ] ``` VS Code prompts for the key and keeps it in secret storage. MCP in `.vscode/mcp.json` or user MCP config: ```json { "servers": { "bifrost": { "type": "http", "url": "https://bifrost.internal.example.com/mcp", "headers": { "x-bf-vk": "<BIFROST_VIRTUAL_KEY>" } } } } ``` Do not commit a real key in `.vscode/mcp.json`; use the user config for headers with secrets. ## OpenCode Docs: https://docs.getbifrost.ai/cli-agents/opencode.md `~/.config/opencode/opencode.json`: ```jsonc { "$schema": "https://opencode.ai/config.json", "model": "bifrost/openai/gpt-5", "provider": { "bifrost": { "npm": "@ai-sdk/openai-compatible", "name": "Bifrost", "options": { "baseURL": "https://bifrost.internal.example.com/openai", "apiKey": "sk-bf-your-virtual-key" }, "models": { "openai/gpt-5": {}, "anthropic/claude-sonnet-4-6": {}, "gemini/gemini-2.5-pro": {} } } } } ``` Model references are `bifrost/<provider>/<model>`. If OpenCode complains about a missing Google or OpenAI key, it is using a native provider; check the footer and select a `bifrost/...` model. ## Other agents Same pattern (OpenAI-compatible or Anthropic base URL + virtual key): - Qwen Code: https://docs.getbifrost.ai/cli-agents/qwen-code.md - Zed: https://docs.getbifrost.ai/cli-agents/zed-editor.md - Roo Code: https://docs.getbifrost.ai/cli-agents/roo-code.md - Claude Desktop (Code tab): https://docs.getbifrost.ai/cli-agents/claude-desktop.md - LibreChat / Open WebUI: https://docs.getbifrost.ai/cli-agents/librechat.md , https://docs.getbifrost.ai/cli-agents/open-webui.md If a browser-based client fails with CORS errors, add its headers to Bifrost's Allowed Headers (default allows `Content-Type`, `Authorization`, `X-Requested-With`, `X-Stainless-Timeout`, `X-Api-Key`). CLI agents do not need this. ## Final check per agent 1. Run one short task in the agent. 2. Open `<gw>/logs` and confirm the request is there with the developer's virtual key, the expected provider and model. 3. Switch the agent to a model the key does not allow and confirm a `403`-style rejection rather than a silent fallback. ## CI: GitHub Actions example One virtual key per pipeline, stored as a CI secret, with tools restricted explicitly: ```yaml # GitHub Actions step (sketch) - name: Agent review env: ANTHROPIC_BASE_URL: https://bifrost.internal.example.com/anthropic ANTHROPIC_AUTH_TOKEN: ${{ secrets.BIFROST_VK_CI_REVIEW }} run: claude -p "Review the diff in this PR for correctness issues" --allowedTools "Read,Grep,Glob" ```references/rollout-checklist.md6.8 KB5546dfab1280upstream ↗
# Rollout checklist A phased plan for rolling out coding agents with central control. Each phase has an exit criterion; do not widen the rollout until it is met. Owners are suggestions: Platform (gateway and keys), Security (policy, data, MCP), IT (device management), Eng leads (adoption), Finance (budgets). ## Phase 0: Baseline (1 week) - [ ] Inventory agents in use: run the Quick diagnosis commands from SKILL.md on a sample of machines; ask teams which agents and MCP servers they use. - [ ] Collect the last three provider invoices and per-key usage exports. Note which keys are shared. - [ ] List repositories by sensitivity (public, internal, regulated, customer-data-adjacent). - [ ] List existing MCP servers in use and who owns each. - [ ] Record baseline delivery metrics per team: PR cycle time, review rework, change-failure rate. Exit: you know who uses what, what it costs today, and which repositories need stricter rules. ## Phase 1: Policy (in parallel with Phase 0) Fill in this template and publish it where developers look. ```markdown # AI coding agent policy Approved agents: Claude Code, Codex CLI, Cursor, ... (versions >= ...) Approved providers and regions: e.g. Anthropic via Bedrock (eu-central-1), OpenAI direct Approved models: <list>; default model per agent: <list> How to connect: all agents use the company gateway with your personal key. Provider keys are not issued to individuals. Repositories: allowed everywhere except <regulated repos>; for those, only <provider/region> and no content logging. Never send: production data, customer PII, secrets, credentials, private keys. Budgets: default <$X/day, $Y/month> per developer; team budget <$Z/month>; raise via <process>. MCP servers: only those exposed through the gateway's /mcp endpoint; request new ones via <process>. Agent permissions: permission prompts stay on; bypass mode is disabled on company machines. Logging: requests are logged with your identity, model, tokens and cost; prompt content is <stored for N days | not stored>. CI usage: one key per pipeline, owned by the repo's team, with a budget and expiry. Exceptions and questions: <channel/owner> ``` - [ ] Data classification decision for prompt/completion content: store (and for how long) or metadata only. - [ ] Contracts checked: provider data retention and training terms for every approved provider. - [ ] Policy approved by Security and Eng leadership. ## Phase 2: Gateway (Platform) - [ ] Deploy Bifrost on an internal HTTPS host with a pinned version, persistent `/app/data`, Postgres log store for shared use. - [ ] Enable dashboard auth; set `enforce_auth_on_inference: true` so every call needs a virtual key. - [ ] Add provider keys only for approved providers and regions. Store provider secrets as `env.VAR` references, not literals. - [ ] Decide content logging globally (`client.disable_content_logging`) and per sensitive team key. - [ ] Register approved MCP servers on the gateway; mark none as Allow by Default unless every key should see them. - [ ] If Cursor is approved: expose the gateway publicly over HTTPS (Cursor's backend calls it) and restrict with keys. - [ ] Prometheus scraping of `/metrics` and a log-retention job matching the policy. - [ ] Back up the gateway config and database. Exit: `curl` checks from `references/per-tool-setup.md` return 200 for an allowed model and 403 for a disallowed one; a test request shows in `/logs`. ## Phase 3: Keys and configuration (Platform + IT) - [ ] Create teams with monthly budgets. - [ ] Provision one virtual key per developer from the roster (`references/virtual-keys-per-developer.md`), with daily + monthly budgets, a request rate limit, model allowlist, MCP tool allowlist and owner/team metadata. - [ ] Store keys in the secret manager; build the `apiKeyHelper` script (Claude Code) or equivalent delivery for other agents. - [ ] Write per-agent config from `references/per-tool-setup.md`; package it for MDM. - [ ] Claude Code `managed-settings.json`: `allowedProviders: ["customEndpoint"]`, managed `ANTHROPIC_BASE_URL`, `apiKeyHelper`, `availableModels`, `permissions.disableBypassPermissionsMode`, secret-file `deny` rules, `allowedMcpServers` + `allowManagedMcpServersOnly`. - [ ] Remove `CLAUDE_CODE_USE_BEDROCK` / `CLAUDE_CODE_USE_VERTEX` and raw provider keys from managed shell profiles. ## Phase 4: Pilot (one team, 2 weeks) - [ ] 5 to 15 developers across at least two agents. - [ ] Each pilot user runs the Verify steps from SKILL.md (`/status`, request visible in `/logs`, disallowed model rejected). - [ ] Daily review of `/logs` for errors: 401 (key delivery), 403 (allowlist too narrow), budget rejections, provider errors on non-native models. - [ ] Collect friction: missing models, missing MCP tools, latency complaints. - [ ] Tune budgets from observed spend (around the 90th percentile of daily spend per developer). Exit: no unresolved 401/403 issues, every pilot request attributed to a person, spend within forecast, pilot users would not want to go back. ## Phase 5: Team-by-team rollout - [ ] Onboard one or two teams per week; each team lead owns the team budget. - [ ] Publish a short onboarding page: install agent, run one command or receive MDM config, verify with `/status`, where to ask. - [ ] CI: one key per pipeline with small daily budget and `expires_at` for one-off jobs; keys stored as CI secrets. - [ ] Weekly spend and adoption report to team leads (see Measuring adoption in SKILL.md). ## Phase 6: Close the bypasses - [ ] Revoke all previously shared provider keys at the providers. - [ ] Block direct egress to provider APIs from developer networks where feasible, leaving only the gateway. - [ ] Recurring drift check (monthly): Quick diagnosis commands across a sample of machines; any raw key or unmanaged MCP server is an incident. - [ ] Offboarding hook: deactivate the leaver's virtual key on their last day. ## Ongoing (monthly) - [ ] Review model allowlists when providers release new models; test with a real agent task before adding. - [ ] Review top spenders and budget rejections; adjust limits or defaults (cheaper default model for background tasks). - [ ] Review MCP requests and the tool allowlists per team. - [ ] Compare agent-assisted PR metrics with the Phase 0 baseline; report value alongside cost. - [ ] Re-check agent release notes for changed config keys (Claude Code managed settings, Codex provider config, Copilot BYOK). ## Exit criteria summary | Area | Done when | |---|---| | Identity | 100% of agent requests in gateway logs carry a per-person or per-pipeline key | | Spend | Every key has a daily and monthly budget; no invoice surprises for two cycles | | Models | Only allowlisted models appear in logs | | Data | Only approved providers/regions appear in logs; content logging matches policy | | Tools | Only gateway-exposed MCP servers load on managed machines | | Evidence | Logs retained per policy and queryable by person, team and cost center |references/virtual-keys-per-developer.md9.9 KB6cd7ac676cd1upstream ↗
# Virtual keys per developer How to give every developer and CI pipeline its own gateway credential with budgets, rate limits, model and MCP allowlists, and how to run the key lifecycle. All calls are Bifrost governance APIs (OSS). Docs: https://docs.getbifrost.ai/features/governance/virtual-keys.md , https://docs.getbifrost.ai/features/governance/budget-and-limits.md Conventions used below: ```bash GW=https://bifrost.internal.example.com AUTH=(-u "$BF_ADMIN:$BF_PASS") # dashboard admin credentials (Basic auth) once dashboard auth is enabled ``` ## Hierarchy ``` Customer (optional: whole company) budget, rate limit └─ Team (platform, payments, ...) budget, rate limit └─ Virtual key per developer budgets, rate limit, provider/model allowlist, MCP tools, metadata └─ Virtual key per CI pipeline small budget, expiry ``` A request must pass every applicable level: the developer's daily budget, their team's monthly budget and the company cap. A key belongs to one team or one customer, not both. Design rules: - One key per human, one per pipeline. Never share. Attribution and revocation depend on it. - Two budgets per developer key: a daily cap that stops runaway loops, and a monthly cap. Reset durations: `1m 1h 1d 1w 1M 1Q 1Y` (`1m` is a minute, `1M` a month). Add `"calendar_aligned": true` on the key to reset at UTC calendar boundaries instead of rolling windows. - A request rate limit (requests per minute) catches tight agent loops before they cost much. - Allowlist models explicitly in `provider_configs[].allowed_models`. An empty `provider_configs` denies every provider. - Put owner, team and cost center in `metadata`; it is copied onto every log entry and cannot be overridden by request headers. ## Create teams ```bash curl -s "${AUTH[@]}" -X POST $GW/api/governance/teams \ -H "Content-Type: application/json" \ -d '{"name": "platform", "budgets": [{"max_limit": 3000.00, "reset_duration": "1M"}]}' \ | jq -r '.team.id' ``` ## Provision keys from a roster `roster.csv`: ``` email,team_id,daily_usd,monthly_usd [email protected],<platform-team-id>,20,300 [email protected],<payments-team-id>,10,150 ``` ```bash #!/usr/bin/env bash # provision-vks.sh roster.csv -> writes keys to your secret manager, never to stdout logs set -euo pipefail tail -n +2 "$1" | while IFS=, read -r email team daily monthly; do body=$(jq -n --arg email "$email" --arg team "$team" --argjson d "$daily" --argjson m "$monthly" '{ name: ("dev-" + ($email | split("@")[0])), team_id: $team, metadata: {owner: $email, kind: "developer"}, provider_configs: [ {provider: "anthropic", allowed_models: ["claude-sonnet-4-6", "claude-haiku-4-5"], key_ids: ["*"], weight: 1.0} ], budgets: [{max_limit: $d, reset_duration: "1d"}, {max_limit: $m, reset_duration: "1M"}], rate_limit: {request_max_limit: 120, request_reset_duration: "1m"}, is_active: true }') resp=$(curl -sf "${AUTH[@]}" -X POST "$GW/api/governance/virtual-keys" -H "Content-Type: application/json" -d "$body") vk_id=$(jq -r '.virtual_key.id' <<<"$resp") vk_val=$(jq -r '.virtual_key.value' <<<"$resp") vault kv put "secret/bifrost/devs/${email%@*}" virtual_key="$vk_val" vk_id="$vk_id" >/dev/null echo "provisioned $email -> $vk_id" done ``` Swap `vault kv put` for your secret manager. Model IDs are examples; use the ones your policy approves. The same structure can be declared in `config.json` under `governance.virtual_keys` (with `"value": "env.SOME_VAR"`) if you manage the gateway as code. Python equivalent for a provisioning service: ```python import os, requests GW = os.environ["BIFROST_URL"] AUTH = (os.environ["BF_ADMIN"], os.environ["BF_PASS"]) def create_dev_key(email: str, team_id: str, daily: float, monthly: float) -> dict: body = { "name": f"dev-{email.split('@')[0]}", "team_id": team_id, "metadata": {"owner": email, "kind": "developer"}, "provider_configs": [{"provider": "anthropic", "allowed_models": ["claude-sonnet-4-6"], "key_ids": ["*"], "weight": 1.0}], "budgets": [{"max_limit": daily, "reset_duration": "1d"}, {"max_limit": monthly, "reset_duration": "1M"}], "rate_limit": {"request_max_limit": 120, "request_reset_duration": "1m"}, "is_active": True, } r = requests.post(f"{GW}/api/governance/virtual-keys", json=body, auth=AUTH, timeout=30) r.raise_for_status() vk = r.json()["virtual_key"] return {"id": vk["id"], "value": vk["value"]} ``` TypeScript: ```typescript const GW = process.env.BIFROST_URL!; const auth = "Basic " + Buffer.from(`${process.env.BF_ADMIN}:${process.env.BF_PASS}`).toString("base64"); export async function createDevKey(email: string, teamId: string, daily: number, monthly: number) { const res = await fetch(`${GW}/api/governance/virtual-keys`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: auth }, body: JSON.stringify({ name: `dev-${email.split("@")[0]}`, team_id: teamId, metadata: { owner: email, kind: "developer" }, provider_configs: [{ provider: "anthropic", allowed_models: ["claude-sonnet-4-6"], key_ids: ["*"], weight: 1.0 }], budgets: [{ max_limit: daily, reset_duration: "1d" }, { max_limit: monthly, reset_duration: "1M" }], rate_limit: { request_max_limit: 120, request_reset_duration: "1m" }, is_active: true, }), }); if (!res.ok) throw new Error(`create VK failed: ${res.status} ${await res.text()}`); const { virtual_key } = await res.json(); return { id: virtual_key.id as string, value: virtual_key.value as string }; } ``` ## Mixing providers and residency on one key A key can allow several providers. Weights give weighted routing, and the remaining providers on the key act as fallbacks. Per-provider budgets cap spend on an expensive provider without blocking the others. ```json "provider_configs": [ {"provider": "bedrock", "allowed_models": ["global.anthropic.claude-sonnet-4-6"], "key_ids": ["*"], "weight": 0.8}, {"provider": "openai", "allowed_models": ["gpt-5"], "key_ids": ["*"], "weight": 0.2, "budgets": [{"max_limit": 50.00, "reset_duration": "1M"}]} ] ``` For strict residency, list only the regional provider (for example Bedrock in the approved region) and no direct provider. `key_ids` can pin a key to specific provider credentials (for example the EU Bedrock account) instead of `["*"]`. ## MCP tool allowlist on the key No `mcp_configs` = no MCP tools (except MCP clients marked Allow by Default). Grant per client: ```bash curl -s "${AUTH[@]}" -X PUT $GW/api/governance/virtual-keys/<vk_id> \ -H "Content-Type: application/json" \ -d '{"mcp_configs": [ {"mcp_client_name": "github", "tools_to_execute": ["get_issue", "list_pull_requests"]}, {"mcp_client_name": "docs-search", "tools_to_execute": ["*"]} ]}' ``` `["*"]` = all current and future tools of that client; `[]` = none. Enforced both when tools are listed and when they execute. Docs: https://docs.getbifrost.ai/features/governance/mcp-tools.md ## Content logging per key ```bash curl -s "${AUTH[@]}" -X PUT $GW/api/governance/virtual-keys/<vk_id> \ -H "Content-Type: application/json" -d '{"disable_content_logging": true}' ``` `true` keeps usage metadata (tokens, cost, latency) but not prompts/completions; `null` inherits the gateway setting. ## CI and contractor keys: expiry ```bash curl -s "${AUTH[@]}" -X POST $GW/api/governance/virtual-keys \ -H "Content-Type: application/json" \ -d '{ "name": "ci-repo-api-review", "metadata": {"kind": "ci", "repo": "api", "owner": "[email protected]"}, "provider_configs": [{"provider": "anthropic", "allowed_models": ["claude-haiku-4-5"], "key_ids": ["*"], "weight": 1.0}], "budgets": [{"max_limit": 5.00, "reset_duration": "1d"}], "rate_limit": {"request_max_limit": 30, "request_reset_duration": "1m"}, "expires_at": "2026-12-31T23:59:59Z", "is_active": true }' ``` An expired key is rejected for both inference and MCP tool calls and stays visible for audit. `delete_after_expire: true` lets a daily job remove it. ## Lifecycle | Event | Action | |---|---| | Joiner | Run provisioning for the new roster row; deliver the key via the secret manager / `apiKeyHelper` path. | | Team change | `PUT` the key with the new `team_id` and `metadata.team`. Old log entries keep the old attribution. | | Budget raise | `PUT` new `budgets`. Prefer temporary raises with a reminder to revert. | | Suspected leak | `PUT {"is_active": false}` immediately, issue a new key, check `/logs` for the old key's recent traffic. | | Rotation | Create the new key, update the vault entry (helpers pick it up within the helper TTL), then deactivate the old key. | | Leaver | Deactivate on the offboarding day (`is_active: false`), delete after your log-retention window. Wire this into your HR/IdP offboarding flow. | ```bash curl -s "${AUTH[@]}" -X PUT $GW/api/governance/virtual-keys/<vk_id> -H "Content-Type: application/json" -d '{"is_active": false}' curl -s "${AUTH[@]}" $GW/api/governance/virtual-keys | jq '.' # inventory curl -s "${AUTH[@]}" "$GW/api/governance/[email protected]" | jq '.' ``` ## Spend reports - UI: Virtual Keys page shows current budget usage; each metadata entry has a **View spend** link into filtered logs. - API: `GET /api/logs?metadata_<key>=<value>` filters logs by key metadata, e.g. `metadata_cost_center=cc-1234`. - Prometheus: `/metrics` exposes `bifrost_cost_total`, `bifrost_input_tokens_total`, `bifrost_output_tokens_total`. Inspect available labels on your instance (`curl -s -u ... $GW/metrics | grep bifrost_cost_total | head`) before building dashboards; add `x-bf-dim-<name>` request headers from agents that support custom headers for extra labels. Weekly report to send team leads: active keys, spend per key versus budget, keys that hit a budget or rate limit, top models by cost. Use it to tune limits at around the 90th percentile of real daily spend.assets/banner.png68 KB1512cd5cc9a8upstream ↗
Contents are not inlined in the catalog. Open the upstream file.
assets/banner.svg3.8 KB97f6d334ddebupstream ↗
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="640" viewBox="0 0 1280 640"> <defs> <radialGradient id="fade" cx="28%" cy="45%" r="85%"> <stop offset="0" stop-color="#0A0A0A" stop-opacity="0"/> <stop offset="1" stop-color="#0A0A0A" stop-opacity="1"/> </radialGradient> </defs> <rect width="1280" height="640" fill="#0A0A0A"/> <g stroke="#FFFFFF" stroke-opacity="0.045" stroke-width="1"><path d="M0 0 V640"/><path d="M40 0 V640"/><path d="M80 0 V640"/><path d="M120 0 V640"/><path d="M160 0 V640"/><path d="M200 0 V640"/><path d="M240 0 V640"/><path d="M280 0 V640"/><path d="M320 0 V640"/><path d="M360 0 V640"/><path d="M400 0 V640"/><path d="M440 0 V640"/><path d="M480 0 V640"/><path d="M520 0 V640"/><path d="M560 0 V640"/><path d="M600 0 V640"/><path d="M640 0 V640"/><path d="M680 0 V640"/><path d="M720 0 V640"/><path d="M760 0 V640"/><path d="M800 0 V640"/><path d="M840 0 V640"/><path d="M880 0 V640"/><path d="M920 0 V640"/><path d="M960 0 V640"/><path d="M1000 0 V640"/><path d="M1040 0 V640"/><path d="M1080 0 V640"/><path d="M1120 0 V640"/><path d="M1160 0 V640"/><path d="M1200 0 V640"/><path d="M1240 0 V640"/><path d="M1280 0 V640"/><path d="M0 0 H1280"/><path d="M0 40 H1280"/><path d="M0 80 H1280"/><path d="M0 120 H1280"/><path d="M0 160 H1280"/><path d="M0 200 H1280"/><path d="M0 240 H1280"/><path d="M0 280 H1280"/><path d="M0 320 H1280"/><path d="M0 360 H1280"/><path d="M0 400 H1280"/><path d="M0 440 H1280"/><path d="M0 480 H1280"/><path d="M0 520 H1280"/><path d="M0 560 H1280"/><path d="M0 600 H1280"/><path d="M0 640 H1280"/></g> <rect width="1280" height="640" fill="url(#fade)"/> <g transform="translate(780 120) scale(16.666666666666668)" fill="none" stroke="#FAFAFA" color="#FAFAFA" stroke-width="0.180" stroke-linecap="round" stroke-linejoin="round" opacity="0.07"><rect x="2.5" y="4" width="19" height="16" rx="2.5" /><path d="M7 9.5 L10 12 L7 14.5" /><path d="M12.5 14.5 H17" /></g> <g transform="translate(80 58) scale(1.4166666666666667)" fill="none" stroke="#FAFAFA" color="#FAFAFA" stroke-width="1.694" stroke-linecap="round" stroke-linejoin="round" opacity="1.0"><path d="M8 6.5 L2.5 12 L8 17.5" /><path d="M16 6.5 L21.5 12 L16 17.5" /><path d="M12 7.5 C12.55 10.4 13.6 11.45 16.5 12 C13.6 12.55 12.55 13.6 12 16.5 C11.45 13.6 10.4 12.55 7.5 12 C10.4 11.45 11.45 10.4 12 7.5 Z" fill="currentColor" stroke="none" /></g> <text x="126" y="83" font-family="ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, monospace" font-size="21" fill="#A1A1AA">build-with-ai-hq</text> <rect x="80" y="156" width="148" height="148" rx="28" fill="#111113" stroke="#27272A" stroke-width="1.5"/> <g transform="translate(116 192) scale(3.1666666666666665)" fill="none" stroke="#FAFAFA" color="#FAFAFA" stroke-width="1.074" stroke-linecap="round" stroke-linejoin="round" opacity="1.0"><rect x="2.5" y="4" width="19" height="16" rx="2.5" /><path d="M7 9.5 L10 12 L7 14.5" /><path d="M12.5 14.5 H17" /></g> <text x="78" y="404" font-family="ui-sans-serif, -apple-system, 'SF Pro Display', 'Helvetica Neue', Helvetica, Arial, sans-serif" font-size="66" font-weight="700" letter-spacing="-1.5" fill="#FAFAFA">ai-coding-agent-governance</text> <text x="80" y="456" font-family="ui-sans-serif, -apple-system, 'SF Pro Display', 'Helvetica Neue', Helvetica, Arial, sans-serif" font-size="28" fill="#A1A1AA">Roll out coding agents with budgets, model control and logs.</text> <rect x="80" y="514" width="867" height="56" rx="12" fill="#141416" stroke="#27272A" stroke-width="1.5"/> <text x="104" y="549" font-family="ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, monospace" font-size="22" fill="#71717A">$</text> <text x="132" y="549" font-family="ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, monospace" font-size="22" fill="#E4E4E7">npx skills add build-with-ai-hq/ai-coding-agent-governance</text> </svg>assets/icon.svg453 B45fbd8f72c5dupstream ↗
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><rect width="512" height="512" fill="#0A0A0A"/><g transform="translate(112.64 112.64) scale(11.946666666666667)" fill="none" stroke="#FAFAFA" color="#FAFAFA" stroke-width="1.929" stroke-linecap="round" stroke-linejoin="round" opacity="1.0"><rect x="2.5" y="4" width="19" height="16" rx="2.5" /><path d="M7 9.5 L10 12 L7 14.5" /><path d="M12.5 14.5 H17" /></g></svg>LICENSE1.0 KB11268562f649upstream ↗
MIT License Copyright (c) 2026 build-with-ai-hq Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Plain-text copy, no login: /skill-bundles/build-with-ai-hq-ai-coding-agent-governance.md