Commit Graph
877 Commits
Author SHA1 Message Date
McKayla はな 9efbcc7601 chore: make dynamic parameters WorkspaceParametersPage the default (#25095) 2026-06-26 17:55:34 -06:00
Paweł Banaszewski c15d483863 chore: rename 'last_used_at' column (#26749)
Renames the `last_used_at` column  to `last_heartbeat_at` in `ai_gateway_keys` table.  
`ai_gateway_keys` table has not been released yet.  
All references updated.
2026-06-26 18:45:37 +02:00
Paweł Banaszewski 6189d6e386 feat: add /api/v2/aibridge/serve endpoint (#26506)
Adds a new enterprise-only `GET /api/v2/ai-gateway/serve` endpoint that standalone AI Gateway replicas use to connect to `coderd` over a DRPC-over-WebSocket transport, mirroring the existing in-memory path used by the embedded AI Bridge daemon.

- The endpoint upgrades the HTTP connection to a WebSocket, multiplexes it with yamux, and finally serves the three DRPC services (Recorder, MCPConfigurator, Authorizer).
- The `X-AI-Governance-Gateway-Key` header is used for authentication.
    - The key is looked up by its hashed secret
    - Missing or revoked keys return `401`.
- API version negotiation is enforced via a new `aibridged/proto` version (`v1.0`).
    - Incompatible versions return `400`.
- `FeatureAIBridge` entitlement is required.
- Key liveness (`last_used_at`) is recorded immediately on connection and refreshed every 60 seconds while the session remains open.
  - When key liveness detects the key was deleted (no rows where updated) session is closed.

#### Small refactors

* The three DRPC service registrations are extracted into `aibridgedserver.Register`, shared by both the in-memory and WebSocket paths.

* The literal `256 * 1024` used as the yamux-aligned WebSocket read limit is replaced with the named constant `drpcsdk.YamuxDefaultStreamWindowSize` in all call sites.
  * as noted in review comment https://github.com/coder/coder/pull/26506#discussion_r3461905223 order of `SetReadLimit` and `WebsocketNetConn` calls was fixed.
2026-06-26 18:27:37 +02:00
Cian Johnston 387011d725 test: extract AI Gateway test helpers for chatd (#26639)
Extracts test infrastructure for AI Gateway routing into shared helpers
under a new package `coderd/aibridgedtest` so both AGPL and enterprise
tests can use them.

- aibridgedtest.StartTestAIBridgeDaemon` spins up a real in-process 
  aibridged daemon wired to fake upstream providers.
- `chattest.MockAIBridgeTransport` is a mock `aibridge.TransportFactory`
   for the 3 bare-chatd tests that use `newActiveTestServer`.

> 🤖 Generated by Coder Agents under the eyes of a human.
2026-06-26 14:33:26 +01:00
Spike Curtis 98e1ce133c chore: modify replicasync to handle NATS explicitly (#26666)
relates to GRU-69

Modifies replicasync to handle discovering NATS enabled primary replicas explicitly, and passing that info to the NATS Pubsub.

This PR adds a new deployment value to explicitly represent the host or IP that the replica can be reached on. It isn't wired up to the CLI, but piggybacks on the DERP config for now.

We learn the NATS port directly from NATS at runtime, and propagate it thru replicasync to learn all peers for clustering.
2026-06-26 08:36:32 -04:00
Zach 953091c7bc refactor: use sync.WaitGroup.Go in tests (#26671)
Migrate `wg.Add(1); go func() { defer wg.Done(); ... }()` to
`wg.Go(func() { ... })` in tests.

Where the prior pattern passed the loop variable explicitly via a
closure parameter (`go func(id int) { ... }(i)`), drop the parameter and
reference the loop variable directly. Per-iteration loop variables since
Go 1.22 make this safe.
2026-06-25 15:41:09 -06:00
Susana Ferreira 5cae613af1 docs: rename AI Bridge to AI Gateway in swagger summaries (#26704)
Update `@Summary` and `@ID` annotations in
`enterprise/coderd/aibridge.go` from "AI Bridge" to "AI Gateway".
Regenerate swagger docs and API reference via `make gen`.

This was missed in the original API route aliases PR (#26475) which
renamed `@Tags` but not `@Summary` or `@ID` values. The `@ID` must also
change because a test (`assertConsistencyBetweenRouteIDAndSummary`)
enforces that the ID is the kebab-case form of the summary.

Refs https://linear.app/codercom/issue/AIGOV-230

> Generated with the assistance of Coder Agents (@ssncferreira)
2026-06-25 16:56:37 +01:00
Spike Curtis e8bd5004a2 chore: add replica_host and nats_port to replicas table (#26665)
relates to GRU-69

Adds cluster_host and nats_port to replicas table, to explicitly track NATS routes in the cluster.

I decided to make the NATS support explicit and transport the port number over the replicasync so that different Coder Servers can run on different ports. This is not something customers will typically care about, but is very useful for testing, so that they can all run on localhost within one machine.

I've also gone with a design where the NATS pubsub directly tells replicasync the port number _after_ it opens the socket. This is also very useful for testing because it allows us to have the OS assign the port number at runtime, avoiding races where we fail to bind to a free port.
2026-06-24 15:04:04 -04:00
Cian JohnstonandCopilot Autofix powered by AI e8c53f7968 chore: add test to document current behaviour on template ACL revocation (#26104)
Documents a question raised in
https://github.com/coder/coder/pull/26061#discussion_r3361458492 - I
couldn't find the exact answer, so adding a test and accompanying
documentation seemed like the prudent move here.

Obligatory disclosure: an agent wrote this code under my supervision.

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-24 12:57:27 +01:00
Jon Ayers 4cfed1b3ed feat: plumb time_til_autostop_notify template field (#26439) 2026-06-23 17:32:47 -05:00
Yevhenii Shcherbina a06e5a3698 test: index AI budget audit logs by action (#26630)
Fixes
https://linear.app/codercom/issue/AIGOV-436/flake-testgroupaibudgetaudit
2026-06-23 13:57:21 -04:00
Callum StyanandMux d104d0dcb3 fix(enterprise/coderd): propagate license events over Postgres pubsub when NATS is in use (#26536)
Co-authored-by: Mux <mux@coder.com>
2026-06-23 09:58:45 -07:00
Jon Ayers c7ddcce62c fix: only return group member count for workspace acl (#26206) 2026-06-23 11:58:00 -05:00
Susana Ferreira 970bd73691 feat: add /api/v2/ai-gateway API route aliases (#26475)
## Description

Registers `/api/v2/ai-gateway/*` as the new API path for AI Gateway, replacing `/api/v2/aibridge/*`. Both prefixes share the same route builder (`aiBridgeRoutes`) backed by a single in-memory handler, so existing `/aibridge` endpoints continue to work. New endpoints must be registered on the enterprise API handler under `/api/v2/ai-gateway` only.

Swagger annotations now point to `/api/v2/ai-gateway` paths with a backward-compatibility note referencing `/aibridge`. The legacy `/aibridge` routes are skipped in the swagger documentation test.

## Changes

- Store one raw handler (`aiGatewayHandler`) instead of two prefix-stripped handlers
- Register `/ai-gateway` and `/ai-gateway/proxy` route aliases alongside legacy `/aibridge` routes
- Move `/aibridge/keys` to `/ai-gateway/keys`
- Update in-process transport to use `/api/v2/ai-gateway` prefix
- Update SDK client URLs and proxy forwarding URL
- Swap `@Router` and `@Tags` annotations from `aibridge`/`AI Bridge` to `ai-gateway`/`AI Gateway`
- Rename user-facing error messages from "AI Bridge" to "AI Gateway"
- Define consts for route prefixes (`AIGatewayRootPath`, `AIBridgeRootPath`)
- Update tests and comments to use new paths

Note: the following will be addressed in follow-up PRs:
- Frontend API URLs
- Frontend routes and redirects
- Dogfood main.tf updates
- Hand-written documentation URL updates
- aibridge internal comments and nits
- Scale tests path updates

Refs https://linear.app/coder/issue/AIGOV-230

> Generated with the assistance of Coder Agents (@ssncferreira)
2026-06-23 12:15:10 +01:00
Sas Swart adad5bdd49 feat: surface agent firewall correlation in AI Bridge sessions API (#26416)
Add `agent_firewall_session_id` and `agent_firewall_sequence_number`
fields to `AIBridgeThread` in the `GET
/api/v2/aibridge/sessions/{session_id}` response. These fields link each
thread to its agent firewall confinement session so the frontend can
discover the boundary session and compute sequence ranges for
interleaving firewall events within the thread timeline.

The database columns already exist on `aibridge_interceptions`
(migration 000520) and are already selected by
`ListAIBridgeSessionThreads`. This PR surfaces them through the SDK type
and the `db2sdk` conversion.

Depends on #24814

**Naming note:** The RFC uses `boundary_session_id` /
`boundary_sequence_number`, but the codebase standardized on
`agent_firewall_*` naming in the DB migration. The API fields follow the
existing convention.

</details>

> [!NOTE]
> This PR was authored by Coder Agents.
2026-06-22 15:17:37 +02:00
Sas Swart 335d6bda1b feat: add GET /api/v2/agent-firewall/sessions/{id}/logs endpoint (#24816)
Add a `GET /api/v2/agent-firewall/sessions/{id}/logs` endpoint that
returns agent firewall audit logs for a given session, sorted by
sequence number ascending.

The endpoint supports `seq_after` and `seq_before` (exclusive bounds)
and `limit` query parameters. This enables the frontend to fetch exactly
the firewall events that fall between two AI Bridge interceptions within
a thread, as described in FR 4 of the Boundary/Bridge correlation RFC.

Authorization reuses the `boundary_log` RBAC resource (owner and auditor
can read; members cannot). Returns 404 for unauthorized users to avoid
leaking existence information.

The endpoint is enterprise-only, gated behind `FeatureBoundary`
entitlement, matching the session endpoint from #24814.

Depends on #24814

> [!NOTE]
> This PR was authored by Coder Agents.
2026-06-22 13:56:29 +02:00
Ethan bc9cc8cb08 fix(enterprise/coderd): allow deleting external-agent workspaces (#26501)
I stumbled on this while manually testing another workspace change: if a
license expires after an external-agent workspace exists, deleting that
workspace is rejected because `CheckBuildUsage` enforces the
external-agent entitlement for every workspace transition. I assume this
is unintentional from a product POV.

This PR narrows the external-agent entitlement check to start builds.
Creating or rebuilding external-agent workspaces still requires the
feature entitlement, but stop and delete transitions stay available as
cleanup paths.

That matches the managed-agent entitlement precedent (i.e. the usage
billing check) in the same `CheckBuildUsage` path, where license
enforcement is scoped to start transitions instead of trapping users
with resources they can no longer remove.
2026-06-22 12:02:01 +10:00
Sas Swart 491a75294e feat: GET /api/v2/agent-firewall/sessions/{id} (#24814)
Add a GET endpoint at `/api/v2/agent-firewall/sessions/{id}` that
returns agent firewall session metadata (`id`, `workspace_id`,
`owner_id`, `confined_process`, `started_at`). The handler authorizes
against the `boundary_log` resource with `ActionRead` via dbauthz.

The endpoint is enterprise-only, gated behind the `FeatureBoundary`
entitlement.

The `GetBoundarySessionByID` SQL query JOINs through `workspace_agents`
→ `workspace_resources` → `workspace_builds` → `workspaces` to return
`workspace_id` and `workspace_owner_id` directly, avoiding a separate
query.

Also adds an `owner_id` column to the `boundary_logs` table (migration
000526) with a FK to `users(id)` and a backfill from
`boundary_sessions`. This enables user-scoped RBAC authorization for
`InsertBoundaryLogs` via `.WithOwner()`, ensuring workspace agents can
only insert logs for their own owner.

Depends on #24810

**RBAC behaviour:**

| Role    | Result |
|---------|--------|
| Owner   | read   |
| Auditor | read   |
| Member  | 404    |

> [!NOTE]
> This PR was authored by Coder Agents.
2026-06-18 20:50:17 +02:00
George K e702bfa535 test(enterprise/coderd): deflake TestInvalidateTemplatePrebuilds (#26499)
`TestInvalidateTemplatePrebuilds` assumed a stable ordering from preset
invalidation results, but the underlying `UPDATE ... RETURNING` query
does not guarantee row order. Local WIP schema changes were enough to
surface that latent flake when running tests.

Update the test to compare invalidated presets as a set instead of by
slice position. This keeps the behavior under test the same, while
removing dependence on unspecified database row ordering.
2026-06-17 16:33:46 -06:00
Ethan 9f2d88fe24 test(enterprise/coderd): stabilize TestUserSkillAuditDiffTracksContent (#26326)
`GetAuditLogsOffset` orders by `"time" DESC` with no tiebreaker, and
`dbtime.Now` rounds to microseconds, so two audit logs emitted in quick
succession (especially on platforms with coarser clock resolution like
Windows) can land in the same microsecond and Postgres is free to return
them in either order. The test then assumes positional ordering and
breaks.

Sort `rows` by `Action` descending before indexing so `rows[0]` is the
update log and `rows[1]` is the create log regardless of timestamp
collisions.

Closes CODAGT-585
Closes https://github.com/coder/internal/issues/1551
2026-06-18 00:22:19 +10:00
Hugo Dutka 4f74a7adee fix: enable goleak in chatd tests (#26335)
Enable goleak in chatd tests and fix some leaks. Addresses
https://github.com/coder/coder/pull/26109#discussion_r3380039964
2026-06-16 12:35:40 +00:00
Susana Ferreira 12d7ad6100 feat: add ai-gateway-cost-control experiment flag (#26399)
Adds the `ai-gateway-cost-control` experiment flag to gate new cost
control endpoints and upcoming frontend UI behind an explicit opt-in.

Currently AI Gateway cost control supports the following endpoints:
- `GET/PUT/DELETE /api/v2/organizations/{org}/groups/{group}/ai/budget`
- `GET/PUT/DELETE /api/v2/users/{user}/ai/budget`

Note: the group-level endpoints were already released in v2.34.0 and
remain ungated. Only the user-level endpoints are gated behind this
experiment. Future cost control endpoints and UI should use this
experiment for gating until the feature is stable.

> Generated by Coder Agents on behalf of @ssncferreira
2026-06-16 10:33:34 +01:00
Danny Kopping a1330e3a8c refactor: rename Ai* database identifiers to AI* (AIGOV-369) (#26327)
Adds `ai` to sqlc's `gen.go.initialisms` in `coderd/database/sqlc.yaml`
so the generated DB code follows Go's initialism convention. Adds the
matching `ai` -> `AI` case to the dbgen PascalCase helper
(`scripts/dbgen/main.go`) so the corresponding `dbmem` / mock
identifiers stay in sync. `make gen` regenerates the rest; hand-written
call sites that consume DB-generated identifiers
(`enterprise/audit/table.go`, `coderd/database/modelmethods.go`,
`enterprise/coderd/aigatewaykeys.go`, `coderd/database/dbauthz/*`, etc.)
are updated to match.

Scope is deliberately limited to the database layer:

- `coderd/rbac/*` (resource and scope generators) is untouched —
`ResourceAi*` / `ScopeAi*` constants stay on main's casing.
- `codersdk/*` (Go SDK) is untouched — `codersdk.ResourceAi*` /
`codersdk.APIKeyScopeAi*` constants stay on main's casing, so external
Go SDK consumers see no source-level break.
- `Aibridge*` identifiers (one SQL token `aibridge`, not `ai_bridge`)
are out of scope.

On-the-wire values are unchanged: enum strings, RBAC resource type
strings, API key scope strings, and JSON tags all stay the same. The
HTTP/JSON surface is unaffected.

Refs:
[AIGOV-369](https://linear.app/codercom/issue/AIGOV-369/change-ai-references-in-coderddatabasemodelsgo-to-ai)

🤖 Generated with [Coder Agents](https://coder.com)
2026-06-16 09:01:43 +00:00
Hugo Dutka 4debd23cbb fix: chatd refactor (#26270)
Implements the chatd stabilization RFC.

Combines:
- https://github.com/coder/coder/pull/25908
- https://github.com/coder/coder/pull/25923
- https://github.com/coder/coder/pull/26109
- https://github.com/coder/coder/pull/26110
- https://github.com/coder/coder/pull/26111
- https://github.com/coder/coder/pull/26112
2026-06-12 13:33:12 +02:00
Danny Kopping 4a07f61c50 refactor!: remove interceptions API, request logs view, and associated code (#26213)
## Summary

Removes the deprecated `/api/v2/aibridge/interceptions` endpoint and the
Request Logs frontend page, both replaced by the session-based view.

Closes https://linear.app/codercom/issue/AIGOV-266
Closes https://linear.app/codercom/issue/AIGOV-324

## Changes

### Backend
- Remove `GET /api/v2/aibridge/interceptions` HTTP handler and route
- Remove SDK types and client method (`AIBridgeInterception`,
`AIBridgeTokenUsage`, `AIBridgeUserPrompt`, `AIBridgeToolUsage`,
`AIBridgeListInterceptionsResponse`, `AIBridgeListInterceptionsFilter`)
- Remove SQL queries `CountAIBridgeInterceptions` and
`ListAIBridgeInterceptions`
- Remove `searchquery.AIBridgeInterceptions` parser
- Remove dbauthz wrappers, in-memory implementations, metrics, and mocks
for the interceptions list queries
- Remove the `coder aibridge interceptions list` CLI command and golden
files
- Regenerate API docs, swagger, mocks, and metrics

The `/models`, `/clients`, and `/sessions` endpoints stay; the sessions
list page still consumes all three.

### Frontend
- Delete the entire `RequestLogsPage/` directory (page, view, row,
filter, stories, tests)
- Remove the `/aibridge/request-logs` route and its lazy import
- Remove the `getAIBridgeInterceptions` API method,
`paginatedInterceptions` query, and mock interception entities
- `git mv` the shared filter and icon components used by the sessions
pages:
- `RequestLogsPage/RequestLogsFilter/{Client,Model,Provider}Filter.tsx`
→ `AIBridgePage/filters/`
- `RequestLogsPage/icons/AIBridge{Client,Model,Provider}Icon.tsx` →
`AIBridgePage/icons/`
- Drop the `getProviderIconName` hack and the duplicate `anthropic-neue`
icon case now that the FIXME no longer applies

## Commits

1. `refactor: remove interceptions API and request logs view` — the bulk
removal, with explicit renames for the shared filter/icon files.
2. `refactor(site/src/pages/AIBridgePage): drop getProviderIconName
hack` — cleanup of the FIXME that depended on RequestLogsPage existing.

> [!NOTE]
> Generated by Coder Agents on behalf of @dannykopping
2026-06-12 07:50:46 +02:00
Cian Johnston a4c867f11b fix: backfill legacy Bedrock AI provider rows and stale model config strings (#26155)
Fixes CODAGT-548

Adds two idempotent startup backfills run after `newAPI():

- `BackfillBedrockProviderType`: promotes `ai_providers` rows from
`type=anthropic` with Bedrock settings to `type=bedrock`.
- `BackfillChatModelConfigProviderStrings`: fixes stale
`chat_model_configs.provider = "anthropic"` strings on rows whose linked
provider was just promoted.
- `UpdateAIProvider` query now also writes the `type` column, so the
fix persists on any subsequent PATCH.


> 🤖 Generated by Claude with oversight from a human.
2026-06-11 15:31:31 +01:00
Callum StyanandMux 4e2c9f9cae fix: allow lifecycle code path to retry failed stop jobs (#26203)
Co-authored-by: Mux <mux@coder.com>
2026-06-10 16:29:00 -07:00
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
Yevhenii Shcherbina 360611ea15 feat: audit user AI budget override mutations (#25745)
Relates to
https://linear.app/codercom/issue/AIGOV-285/add-user-budget-overrides-table-and-crud-api

Adds audit-log support for `user_ai_budget_override` mutations. Without
it, an admin could quietly change a user's per-user spend cap (e.g. from
`$500` to `$50`), reassign it to a different group, or delete it
entirely with no record of who did it.

Both write (`create-or-update`) and delete actions now generate audit
log entries. Unlike group AI budgets, which only track `spend_limit`,
overrides also track `group_name`: an override can be reassigned to a
different attributed group, so that change needs to show up in the diff.
The raw `spend_limit_micros`, IDs, and timestamps are ignored in favor
of the human-readable `spend_limit` and `group_name`.

Depends on #25439.

## Screenshot

<img width="1343" height="514" alt="image"
src="https://github.com/user-attachments/assets/aee30f58-6e81-435e-9bca-5bc98f49d8d3"
/>
2026-06-10 00:29:06 +00:00
Susana Ferreira 777ac44226 refactor: rename "AI Bridge" to "AI Gateway" in the UI (#26161)
Renames all user-visible "AI Bridge" strings to "AI Gateway" in the
browser UI as part of the AI Bridge to AI Gateway rebrand (AIGOV-233).

Changes:
- Page titles and headings ("AI Bridge Logs" to "AI Gateway Logs",
`pageTitle` calls)
- Help popover title, description, and doc link
- Setup alert title, description, and doc link
- Back-link tooltip on session threads page
- AI add-on help popover text
- License entitlement warning shown in the dashboard banner

Doc links updated from `/ai-coder/ai-bridge` to `/ai-coder/ai-gateway`
(directory already exists).

This PR intentionally does not rename internal identifiers,
file/directory names, API endpoints, routes, CLI help, deployment config
options, or generated types. Those will be tracked separately.

Related to: https://linear.app/codercom/issue/AIGOV-233

> Generated by Coder Agents on behalf of @ssncferreira
2026-06-09 15:11:27 +01:00
Susana Ferreira 01ec5e4577 feat: add key pool failover metrics to aibridge (#25901)
## Description

This PR adds Prometheus metrics for aibridge's API-key failover, giving visibility into key pool health and failover behavior per provider.

The following metrics are introduced:

- **`key_pool_state`** (gauge): number of keys currently in each state (`valid`, `temporary`, `permanent`) per provider, sampled at scrape time.
- **`key_pool_state_transitions_total`** (counter): key state transitions during failover, labeled by `reason` (`rate_limited`, `unauthorized`, `forbidden`).
- **`key_pool_exhaustions_total`** (counter): times a pool ran out of usable keys, labeled by `outcome` (`rate_limited`, `auth_failed`).
- **`key_pool_failover_attempts`** (histogram): keys attempted before success or exhaustion (per interception for bridged requests, per request for passthrough).

## Changes

- Moves `MarkKeyOnStatus` and key-pool error handling onto `*keypool.Pool`.
- Attaches metrics to each provider's key pool at install time, on construction and on provider reload.
- Adds a scrape-time state collector and a `KeyPools()` accessor on the bridge pool to feed it.
- Tracks per-request key attempts in the bridged and passthrough failover paths.
- Adds test coverage for the new metrics across the keypool unit tests, the bridged intercept failover tests, and the passthrough failover test.

Closes https://github.com/coder/internal/issues/1447
Closes https://linear.app/codercom/issue/AIGOV-198/aibridge-key-failover-observability

> [!NOTE]
> Initially generated by Claude Opus 4.7, modified and reviewed by @ssncferreira
2026-06-09 10:49:47 +01:00
Steven Masley 938c2080f3 feat: configurable default org member roles (#25994)
Refs #25936. 
Adds a configurable per-org default member role set. Unioned into each member's effective roles at read time.

<sub>with Coder Agents on behalf of @Emyrk.</sub>
2026-06-05 14:33:13 -05:00
Zach d1f2dec4ff fix: align autostart tests with persisted next_start_at (#26037)
`TestExecutorAutostartOK` and its sibling positive autostart tests
compute the autobuild tick from
`sched.Next(workspace.LatestBuild.CreatedAt)`, but the server persists
`next_start_at` from the build's completion time. When build creation
and completion straddle the schedule's next fire time, the persisted
value advances past the test's tick, the executor's eligibility query
(`next_start_at <= tick`) drops the workspace, and the test fails with
an empty transitions map. This surfaced in flaky test runs.

Add `coderdtest.NextAutostartTick(t, workspace)` which returns
`*workspace.NextStartAt`, and use it across the affected positive
autostart paths in `coderd/autobuild`, `coderd`, and
`enterprise/coderd`.

Generated with assistance from Coder Agents.
2026-06-05 09:55:32 -06:00
Steven Masley bec2115e75 refactor: extract organization-workspace-access role (#25929)
<!-- Authored by Coder Agents on behalf of @Emyrk. -->

Refs
[PLAT-217](https://linear.app/codercom/issue/PLAT-217/rfc-for-gateway-accounts).

Extracts an `organization-workspace-access` role so workspace elevation
can be split off the organization-member floor without changing
behavior.

- New role holds the workspace-side resources currently granted by
`organization-member`.
- The `MinimumImplicitMember` floor preserves the existing behavior
until #26027 shrinks it.
- Prebuilds orchestrator inserts memberships via
`dbauthz.AsSystemRestricted` and no longer needs `OrganizationMember` or
`AssignOrgRole` grants.

<details><summary>Agent context</summary>

- `coderd/rbac/roles.go`: `OrgWorkspaceAccessMemberPerms()` grants
`Workspace`, `WorkspaceDormant`, `File` (Create+Read),
`ProvisionerDaemon` (Create+Read), and `Task`. Deliberate omissions
(`Template`, `Group`, `WorkspaceProxy`, etc.) are documented inline.
- `coderd/rbac/roles_test.go`: `orgWorkspaceAccessUser` is added to
`requiredSubjects`. `UserProvisionerDaemons` is split into
`UserProvisionerDaemonsCreate` and `UserProvisionerDaemonsUpdateDelete`
because the new role grants Create+Read only and the test framework
requires uniform pass/fail per case.
- `codersdk/rbacroles.go`: exposes `RoleOrganizationWorkspaceAccess`.
- `enterprise/coderd/prebuilds/membership.go`:
`InsertOrganizationMember` runs under `dbauthz.AsSystemRestricted`. The
orchestrator never acts with the elevation role; the membership row only
exists so prebuilt workspaces have a valid owner.
- `coderd/database/dbauthz/dbauthz.go`: drops the now-dead
`OrganizationMember` and `AssignOrgRole` permissions from the
prebuilds-orchestrator role and the orchestrator's entry in
`assignRoles`.

</details>

---

<sub>Coder Agents on behalf of @Emyrk.</sub>
2026-06-05 10:30:31 -05:00
Cian Johnston 63cd8a8c01 fix: clamp template port sharing level in SubAgentAPI (#26061)
Fixes an issue where sub-agent apps created via CreateSubAgent would
bypass the check for the template's max port sharing level:

- Clamps dynamically inserted `workspace_apps` to the template max
sharing level in `coderd.agentapi.SubAgentAPI`.
- Emits a warning when clamping occurs.
- Adds unit test coverage for the max sharing level matrix.
- Adds an integration-ish test through the devcontainer sub-agent client
path.

> 🤖 Generated by Coder Agents with guidance from a human.
2026-06-05 16:30:15 +01:00
Garrett Delfosse d5b0e93c6c fix!: reject OIDC login when email_verified claim is non-bool or absent (#25713)
## Problem

The OIDC callback checks `email_verified` via a Go type assertion
(`verifiedRaw.(bool)`). When an IdP returns the claim as a string
(`"false"`), a number, or omits it entirely, the assertion fails
silently and the email is implicitly treated as verified. Several real
IdPs (SAML-to-OIDC bridges, certain Azure AD B2C configurations) emit
string-typed booleans, making this reachable in practice.

## Fix

Add `coerceEmailVerified()` to handle `bool`, `string`
(`"true"`/`"false"`/`"1"`/`"0"` via `strconv.ParseBool`), `float64`,
`json.Number`, and `int`/`int64` variants. Rewrite the check to be
fail-closed: an absent claim, an unrecognized type, or any non-truthy
value is treated as unverified and rejected. The existing
`IgnoreEmailVerified` config option remains as an escape hatch.

Fixes https://linear.app/codercom/issue/PLAT-228

> Generated with [Coder Agents](https://coder.com) by @f0ssel

<details><summary>Implementation plan</summary>

### Production code (`coderd/userauth.go`)
- Added `encoding/json` import
- Added `coerceEmailVerified(v interface{}) (verified bool, ok bool)`
helper near EOF
- Replaced the type-assertion block (lines ~1342-1363) with fail-closed
logic that uses `coerceEmailVerified`

### Unit tests (`coderd/userauth_internal_test.go`, new file)
- Table-driven test covering: `bool`, `string` (`"true"`, `"false"`,
`"1"`, `"0"`, `"TRUE"`, `"t"`, `"f"`, `"invalid"`, `""`), `json.Number`,
`float64`, `int`, `int64`, `nil`, `[]string{}`, `map[string]string{}`

### Integration tests (`coderd/userauth_test.go`,
`coderd/users_test.go`)
- Added 3 new test cases: `EmailVerifiedMissingIgnored` (200),
`EmailVerifiedAsStringTrue` (200), `EmailVerifiedAsStringFalse` (403)
- Updated existing test cases that omitted `email_verified` and expected
success to include `"email_verified": true`

### FakeIDP (`coderd/coderdtest/oidctest/idp.go`)
- `encodeClaims` now defaults `email_verified` to `true` (like `exp`,
`aud`, `iss`) so tests that don't care about the verification flow are
unaffected
</details>
2026-06-04 14:37:19 -04:00
Sas Swart ce2e56785f fix(enterprise/coderd/license): suppress AI Governance seat-count error for not-entitled licenses (#25885)
When a Premium deployment has no AI Governance addon but has accumulated
`ai_seat_state` rows (from prior Gateway testing or Task usage), the
backend emitted an error in the `LicenseBanner`: "Your deployment has N
active AI Governance seats but the license is not entitled to this
feature." This is alarming and inactionable for customers who never
purchased the addon.

Suppress the `EntitlementNotEntitled` case that appended to
`entitlements.Errors`. Customers who purchased AI Governance still see
all their seat-limit warnings (90% threshold, over-limit, grace period)
since those are gated on `entitled` / `grace_period` and are unaffected.

Fixes https://linear.app/codercom/issue/AIGOV-392

> Generated by Coder Agents on behalf of @SasSwart
2026-06-04 11:18:56 +02:00
Jon Ayers 167ac7b879 feat: add nats experiment (#25703) 2026-06-03 15:37:19 -05:00
Paweł Banaszewski 96e3a64b12 feat: add AI Gateway coderd key CRUD endpoints (#25565)
Adds create, list and delete endpoints for AI Gateway keys.
Those keys are used to authenticate into Coderd.
All endpoints require Owner permission.
2026-06-03 13:50:33 +02:00
Yevhenii Shcherbina 1a91d31793 feat: add user AI budget override endpoints (#25439)
Implements https://linear.app/codercom/issue/AIGOV-285
Follow the structure established in
https://github.com/coder/coder/pull/25203

## Summary

Adds the `user_ai_budget_overrides` table and CRUD API at
`/api/v2/users/{user}/ai/budget`. An override sets a custom per-user
spend cap that supersedes group-budget resolution, attributing spend to
a specific group.

## Schema

```sql
CREATE TABLE user_ai_budget_overrides (
    user_id            UUID        PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
    group_id           UUID        NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
    spend_limit_micros BIGINT      NOT NULL CHECK (spend_limit_micros >= 0),
    created_at         TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at         TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```

## Membership lifecycle

The membership invariant — a user must be a member of the attributed
group, including when that group is "Everyone" — would naturally be
expressed as a composite FK on `(user_id, group_id) →
group_members_expanded(user_id, group_id)`. PostgreSQL doesn't allow
foreign keys to reference views, so enforcement is split across two
mechanisms:

- **Write-time check.** A CHECK constraint on the table
(`user_ai_budget_overrides_must_be_group_member`) calls a `STABLE`
function `is_group_member(user_id, group_id)` that queries
`group_members_expanded`. The view surfaces both regular group
memberships and the implicit "Everyone" group memberships from
`organization_members`. Any INSERT or UPDATE that violates the predicate
is rejected with a Postgres `check_violation`, which the handler maps to
a 400. `is_group_member` is defined as a general predicate, reusable by
any future table that needs the same check.

- **Cascade on removal.** Two `BEFORE DELETE` triggers handle membership
loss:
- `trigger_delete_user_ai_budget_overrides_on_group_member_delete` on
`group_members` — covers regular group removals (admin action, OIDC
sync).
- `trigger_delete_user_ai_budget_overrides_on_org_member_delete` on
`organization_members` — covers the "Everyone" group, whose membership
lives in `organization_members`.

The single-column FKs on `users(id)` and `groups(id)` remain to cascade
on user or group deletion (those paths don't pass through
`group_members`).

## Authorization

The dbauthz layer gates each operation against the `User` and (for
writes) `Group` resources:

| Operation | User resource  | Group resource |
|-----------|----------------|----------------|
| `GET`     | `ActionRead`   | —              |
| `PUT`     | `ActionUpdate` | `ActionUpdate` |
| `DELETE`  | `ActionUpdate` | `ActionUpdate` |

For `DELETE`, the dbauthz layer fetches the existing override first to
learn the attributed `group_id`, then runs both checks.

### Role matrix

| Role         | GET | PUT | DELETE |
|--------------|-----|-----|--------|
| Owner        | ✅   | ✅   | ✅      |
| UserAdmin    | ✅   | ✅   | ✅      |
| OrgAdmin     | ✅   | ❌   | ❌      |
| OrgUserAdmin | ✅   | ❌   | ❌      |

Internal discussion:
https://codercom.slack.com/archives/C096PFVBZKN/p1779392747885359

## Audit logs
Audit logs will be addressed in a follow-up PR.
2026-05-29 10:08:25 -04:00
Danny KoppingandClaude Opus 4.7 5b10268827 feat: serve 503 sentinel for disabled providers (#25794)
_Disclosure: created with Coder Agents._

When providers are disabled, we should serve a sentinel error so the
requesting client (Claude Code, Coder Agents, etc) is informed. Coder
Agents can also conditionalize its display to show a helpful error
message.

---------

Signed-off-by: Danny Kopping <danny@coder.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 10:24:16 +02:00
Steven Masley 4591212482 feat: implement SCIM handler for SCIM 2.0 compliance (#25572)
Rewrites the SCIM 2.0 user provisioning handler to be RFC 7644
compliant. Verified against an external IdP Okta.

Behavior is OPT IN
2026-05-28 10:00:37 -05:00
Danny Kopping 12520ee964 feat: add ai provider status and reload freshness metrics (#25770)
Add metrics for `aibridged` and `aibridgeproxyd`'s provider statuses. AI providers can be modified, and possibly misconfigured, at runtime. These metrics help operators understand the state of these provider definitions in case unexpected behaviour is observed.
2026-05-28 14:57:33 +02:00
Danny Kopping 79e007cf30 feat: hot-reload aibridged and aibridgeproxyd providers on DB changes (#25673)
Previously the in-process aibridge daemon and the enterprise aibridgeproxy daemon both snapshotted their provider routing once at boot. Any `ai_providers` or `ai_provider_keys` mutation required a restart for either to pick it up.

Add an `ai_providers_changed` pubsub channel that the CRUD handlers publish on after Create / Update / Delete. Both daemons subscribe:

- **aibridged** rebuilds its `[]aibridge.Provider` snapshot via `BuildProviders` and swaps it into the pool atomically. Inflight requests keep serving against the bridge they already acquired; new acquires build against the new snapshot. Per-provider construction errors stay scoped to the offending row.
- **aibridgeproxyd** rebuilds its routing snapshot from `GetAIProviders` and swaps the host→provider map atomically. The MITM listener picks up new providers without restart.

DB read for aibridgeproxyd uses the existing `AsAIProviderMetadataReader` subject for routing-only access.
2026-05-27 11:58:43 +02:00
Michael Suchacz 8b1705eb65 feat: route chatd provider traffic through aibridge (#25629)
## Summary

Routes chatd model calls backed by concrete AI Provider rows through the
in-process aibridge transport by default, with deployment options to use
direct provider routing when AI Gateway is disabled or chat AI Gateway
routing is disabled.

- Splits model routing into common, direct provider, and AI Gateway
paths behind a single deployment-mode entry point.
- Builds chatd models through explicit request, route, and options data.
Active API key attribution is passed explicitly instead of being hidden
inside generic model construction.
- For AI Gateway BYOK routes, resolves the user's provider key in chatd,
forwards it through provider-specific auth headers, and sets
`X-Coder-AI-Governance-Token` to the `delegated` marker so aibridge
preserves those headers while still stripping Coder-specific metadata.
- Keeps central provider credentials and deployment fallback credentials
out of forwarded provider auth headers, so AI Gateway central policy
remains authoritative.
- Redacts delegated provider auth from default string formatting to
avoid accidental plaintext logging of user BYOK credentials.
- Covers selected chat models, advisor overrides, title and quickgen
paths, subagent overrides, computer use model selection, and an
integration-style chat turn through the aibridge transport path.
- Persists initiating API key IDs on chat and queued user messages,
including subagent child messages, and fails closed for AI
Gateway-routed model builds without an active key.
- Removes unused `api_key_id` indexes while keeping the persistence
columns and foreign keys.
- Keeps the deployment option available through config and env parsing,
but hides it from CLI help and generated docs.
- Stabilizes the subagent poll fallback test so background CreateChat
processing cannot win the state transition under slower CI environments.

## Tests

- `go test ./coderd/x/chatd -run
'TestAIGatewayProviderAuthForUser|TestAIGatewayProviderAuthRedactsFormatting|TestResolveModelRouteForConfigAIGatewayProviderAuth|TestAIGatewayModelForwardsProviderAuth|TestProcessChat_AIGatewayRoutingUsesDelegatedAPIKey|TestAwaitSubagentCompletion'
-count=1`
- `go test ./coderd/aibridged -run
'TestServeHTTP_DelegatedAPIKey|TestServeHTTP_StripCoderToken' -count=1`
- `git diff --check HEAD~1..HEAD`
- `make lint`

> Mux working on behalf of Mike.
2026-05-26 19:31:52 +00:00
Danny Kopping c801dcbbc8 fix: strip route prefix when passing request to aibridged handler (#25671)
We weren't stripping the API base (`/api/v2/aibridge`), leading to 404s
when using the in-memory transport.

Signed-off-by: Danny Kopping <danny@coder.com>
2026-05-26 08:04:26 +00:00
Danny Kopping 4ddda3a9db feat: filter interceptions and sessions by provider name (#25640)
Allows filtering sessions & interceptions by provider name, and adds a test to vaidate that provider name is immutable (at least until #25606 lands).
2026-05-25 16:31:48 +02:00
Michael Suchacz ca1f6b19a2 feat: remove legacy chat provider tables (#25416) 2026-05-22 09:50:01 +02:00
Danny Kopping ddec110b0e refactor: move aibridged out of enterprise to AGPL (#25570)
In order to allow Coder Agents to use AI Gateway in OSS, we need to rehome the `aibridged`\-related code into the AGPL path.

The HTTP API is only registered under enterprise so will still require the AI Governance Add-on to be present in order to use it, whereas Coder Agents uses an in-memory pipe to the same handlers.
2026-05-22 09:11:37 +02:00
Danny Kopping c50b0e84b9 feat!: default CODER_AI_GATEWAY_ENABLED to true (#25575)
`CODER_AI_GATEWAY_ENABLED` / `CODER_AIBRIDGE_ENABLED` is now being defaulted to `true` now that it will be used by Coder Agents.

If you previously had this value disabled explicitly, that value will persist.
2026-05-22 08:57:36 +02:00