Files
coder/aibridge
Nick Vigilante cfb03f52db fix: update stale docs URLs across non-TS files (#25750)
Closes [DOCS-256](https://linear.app/coder/issue/DOCS-256). Sibling to
[DOCS-253](https://linear.app/coder/issue/DOCS-253) (#25740).

Updates docs URL references across the non-TypeScript surface of
`coder/coder` to match the current docs site structure. Source-of-truth
for redirects is `coder/coder.com/redirects.json` (parent ticket
[DOCS-209](https://linear.app/coder/issue/DOCS-209)).

## What changed

| Area | Files | URL mapping |
|---|---|---|
| Top-level README | `README.md` | `/docs/workspaces` ->
`/docs/user-guides/workspace-management`, `/docs/templates` ->
`/docs/admin/templates`, `/docs/ides` ->
`/docs/user-guides/workspace-access` |
| Docs source | `docs/admin/security/0001_user_apikeys_invalidation.md`
| `/docs/admin/audit-logs` -> `/docs/admin/security/audit-logs` |
| Docs source | `docs/install/cloud/azure-vm.md` |
`/docs/coder-oss/latest/install` -> `/docs/install` |
| Dogfood | `dogfood/coder/guide.md` | `/docs/ides` ->
`/docs/user-guides/workspace-access` |
| Helm | `helm/coder/values.yaml` | `/docs/admin/workspace-proxies` ->
`/docs/admin/networking/workspace-proxies` |
| Enterprise coderd | `enterprise/coderd/coderd.go` |
`/docs/admin/encryption` -> `/docs/admin/security/database-encryption`
(error message) |
| Release tooling | `scripts/release/main_internal_test.go` |
`/docs/admin/upgrade` -> `/docs/install/upgrade` (test fixture, matches
`generate_release_notes.sh`) |
| AI bridge | `aibridge/client.go` | repinned to current `main` SHA on
renamed `docs/ai-coder/ai-gateway/monitoring.md`, line range `#L47-L57`
|
| Example templates | 12 `examples/templates/*/README.md`,
`examples/parameters/*`,
`examples/parameters-dynamic-options/README.md`,
`examples/workspace-tags/README.md`, `examples/parameters/main.tf`,
`examples/examples.gen.json` (regenerated) | `/docs/workspaces` ->
`/docs/user-guides/workspace-management`, `/docs/templates/parameters`
-> `/docs/admin/templates/extending-templates/parameters`,
`/docs/templates/dev-containers` ->
`/docs/admin/integrations/devcontainers`, `/docs/dotfiles` ->
`/docs/user-guides/workspace-dotfiles`,
`/docs/about/architecture#agents` ->
`/docs/admin/infrastructure/architecture#agents` |
| Live notification templates (DB) | New migration
`000510_fix_dormancy_notification_docs_urls.up.sql` and `.down.sql` plus
the four regenerated SMTP/webhook goldens under
`coderd/notifications/testdata/rendered-templates/` |
`/docs/templates/schedule#dormancy-threshold-enterprise` ->
`/docs/admin/templates/managing-templates/schedule#dormancy-threshold`,
`/docs/templates/schedule#dormancy-auto-deletion-enterprise` ->
`/docs/admin/templates/managing-templates/schedule#dormancy-auto-deletion`
|

The migration uses `REPLACE(body_template, ...)` scoped by template id
and `LIKE '%/docs/templates/schedule%'`, so it works regardless of which
intermediate state (`000232`, `000262`, `000305`, or `000311`) is
currently in the row.

## What did not change

Historical SQL migrations `000232`, `000262`, `000305`, and `000311` are
not modified because migrations are immutable history. The 18 remaining
stale URL references in those files are superseded at runtime by
migration `000510`. This decision matches the pattern used in the A1
sister PR (#25740).

## Verification

- `go test ./coderd/database/migrations/... -count=1` (UP+DOWN)
- `go test ./coderd/notifications/ -run TestNotificationTemplates_Golden
-update -count=1` to regenerate the four `.golden` files
- `go test ./scripts/release/ -run Test_removeMainlineBlurb -count=1`
- `make pre-commit` (gen + fmt + lint + slim build) ran clean as part of
the commit hook

I also fixed a pre-existing emdash on line 35 of
`examples/templates/azure-linux/README.md` that the lint flagged once
the file entered my diff. The line was already in `main`, but `make gen`
rewrites `examples/examples.gen.json` whenever a `README.md` changes, so
the line came back as a `+` in the diff against `origin/main` and the
`lint/emdash` step refused it.

<details>
<summary>Pre-mortem</summary>

| Risk | Mitigation |
|---|---|
| Migration overwrites future template edits | Used `REPLACE` instead of
full body overwrite. `WHERE id IN (...) AND body_template LIKE
'%/docs/templates/schedule%'` further scopes the write |
| Goldens drift from migrated body | Regenerated goldens via `-update`
after the migration was in place, so the goldens reflect the
post-migration state |
| Down migration leaves stale URLs | Down migration reverses the REPLACE
so a rollback restores the prior URLs |
| Fragment loss when redirect strips fragment | Verified the destination
`schedule.md` contains `## Dormancy threshold` and `## Dormancy
auto-deletion` anchors |
| Terraform parse breakage in `examples/parameters/main.tf` | Only
comments changed; Terraform parser is unaffected |
| Test fixtures in `scripts/release` diverging from
`generate_release_notes.sh` | Updated to match the script, which already
emits `/docs/install/upgrade` |

</details>

---

Generated by Coder Agent on behalf of @nickvigilante.
2026-06-10 13:40:50 -04:00
..

aibridge

aibridge provides an HTTP handler that intercepts AI client requests bound for upstream AI providers (Anthropic, OpenAI, Copilot). It records token usage, prompts, and tool invocations per user. Optionally supports centralized MCP tool injection with allowlist/denylist filtering.

The handler is mounted by a host process. Today that host is coderd, which mounts the handler at /api/v2/aibridge/<provider>/*. Running aibridge as a separate process is planned for the future.

Architecture

┌─────────────────┐     ┌───────────────────────────────────────────┐
│    AI Client    │     │                    aibridge               │
│  (Claude Code,  │────▶│  ┌─────────────────┐    ┌─────────────┐   │
│   Cursor, etc.) │     │  │  RequestBridge  │───▶│  Providers  │   │
└─────────────────┘     │  │  (http.Handler) │    │  (Anthropic │   │
                        │  └─────────────────┘    │   OpenAI)   │   │
                        │                         └──────┬──────┘   │
                        │                                │          │
                        │                                ▼          │    ┌─────────────┐
                        │  ┌─────────────────┐    ┌─────────────┐   │    │  Upstream   │
                        │  │    Recorder     │◀───│ Interceptor │─── ───▶│    API      │
                        │  │ (tokens, tools, │    │ (streaming/ │   │    │ (Anthropic  │
                        │  │  prompts)       │    │  blocking)  │   │    │   OpenAI)   │
                        │  └────────┬────────┘    └──────┬──────┘   │    └─────────────┘
                        │           │                    │          │
                        │           ▼             ┌──────▼──────┐   │
                        │  ┌ ─ ─ ─ ─ ─ ─ ─ ┐      │  MCP Proxy  │   │
                        │  │    Database   │      │   (tools)   │   │
                        │  └ ─ ─ ─ ─ ─ ─ ─ ┘      └─────────────┘   │
                        └───────────────────────────────────────────┘

Components

  • RequestBridge: The main http.Handler that routes requests to providers
  • Provider: Defines bridged routes (intercepted) and passthrough routes (proxied)
  • Interceptor: Handles request/response processing and streaming
  • Recorder: Interface for capturing usage data (tokens, prompts, tools)
  • MCP Proxy (optional): Connects to MCP servers to list tool, inject them into requests, and invoke them in an inner agentic loop

Request Flow

  1. Client sends request to /anthropic/v1/messages or /openai/v1/chat/completions
  2. Actor extraction: Request must have an actor in context (via AsActor()). The host is responsible for authenticating the caller before invoking the handler.
  3. Upstream call: Request forwarded to the AI provider
  4. Response relay: Response streamed/sent to client
  5. Recording: Token usage, prompts, and tool invocations recorded

With MCP enabled: Tools from configured MCP servers are centrally defined and injected into requests (prefixed bmcp_). Allowlist/denylist regex patterns control which tools are available. When the model selects an injected tool, the gateway invokes it in an inner agentic loop, and continues the conversation loop until complete.

Passthrough routes (/v1/models, /v1/messages/count_tokens) are reverse-proxied directly.

Observability

Prometheus Metrics

Create metrics with NewMetrics(prometheus.Registerer):

Metric Type Description
interceptions_total Counter Intercepted request count
interceptions_inflight Gauge Currently processing requests
interceptions_duration_seconds Histogram Request duration
passthrough_total Counter Non-intercepted requests forwarded to the upstream
prompts_total Counter User prompt count
tokens_total Counter Token usage (input, output, cache read/write, provider extras)
injected_tool_invocations_total Counter Injected MCP tool invocations performed by the handler
non_injected_tool_selections_total Counter Client-defined tool selections returned by the model
circuit_breaker_state Gauge Circuit breaker state per provider/endpoint (0=closed, 0.5=half, 1=open)
circuit_breaker_trips_total Counter Times the circuit breaker transitioned to open
circuit_breaker_rejects_total Counter Requests rejected due to an open circuit breaker

Recorder Interface

Implement Recorder to persist usage data to your database:

  • aibridge_interceptions - request metadata (provider, model, initiator, timestamps)
  • aibridge_token_usages - input/output and cache read/write token counts per response
  • aibridge_user_prompts - user prompts
  • aibridge_tool_usages - tool invocations (injected and client-defined)
  • aibridge_model_thoughts - model reasoning content (thinking, reasoning summaries, commentary)
type Recorder interface {
    RecordInterception(ctx context.Context, req *InterceptionRecord) error
    RecordInterceptionEnded(ctx context.Context, req *InterceptionRecordEnded) error
    RecordTokenUsage(ctx context.Context, req *TokenUsageRecord) error
    RecordPromptUsage(ctx context.Context, req *PromptUsageRecord) error
    RecordToolUsage(ctx context.Context, req *ToolUsageRecord) error
    RecordModelThought(ctx context.Context, req *ModelThoughtRecord) error
}

Supported Routes

Each provider instance is mounted under /api/v2/aibridge/<name>, where <name> is the provider's configured name. For example, with an Anthropic provider named my-anthropic, its /messages endpoint would be reachable at /api/v2/aibridge/my-anthropic/v1/messages.

If a name is not set, the route path defaults to the provider's type: anthropic, openai, or copilot. The table below uses the default names.

(/*) denotes a route that handles both the exact path and any subpaths. A trailing /* denotes subpaths only.

Provider Route Type
Anthropic /anthropic/v1/messages Bridged (intercepted)
Anthropic /anthropic/v1/messages/count_tokens Passthrough
Anthropic /anthropic/v1/models(/*) Passthrough
Anthropic /anthropic/api/event_logging/* Passthrough
OpenAI /openai/v1/chat/completions Bridged (intercepted)
OpenAI /openai/v1/responses Bridged (intercepted)
OpenAI /openai/v1/responses/* Passthrough
OpenAI /openai/v1/conversations(/*) Passthrough
OpenAI /openai/v1/models(/*) Passthrough
Copilot /copilot/chat/completions Bridged (intercepted)
Copilot /copilot/responses Bridged (intercepted)
Copilot /copilot/models(/*) Passthrough
Copilot /copilot/agents/* Passthrough
Copilot /copilot/mcp/* Passthrough
Copilot /copilot/.well-known/* Passthrough