Commit Graph
549 Commits
Author SHA1 Message Date
McKayla はな efd93027ce feat: allow editing user avatars (#26652)
Adds an avatar URL field to the admin **Edit user** page, available only
for users whose login type is `password` or `none`.

For identity-provider login types (`github`, `oidc`) the avatar is
synced from the IdP on every login, so the field is hidden and the API
ignores any submitted avatar to avoid confusing overwrites.

The field reuses the same emoji picker + URL input (`IconField`) already
used for template, group, and organization icons.

A follow-up PR will add the same control to the self-service Account
settings page.

<details>
<summary>Implementation plan & decisions</summary>

**Goal:** Let an admin set/clear a user's avatar from the Edit user
page, gated to `password`/`none` login types.

**Backend**
- Add `avatar_url` to `codersdk.UpdateUserProfileRequest`.
- `putUserProfile` applies the submitted avatar only for
`password`/`none`; otherwise it preserves the existing (IdP-synced)
value.
- Regenerated TS types and API docs via `make gen`.

**Frontend**
- `EditUserForm` renders an `IconField` ("Avatar URL") when the login
type allows it.
- `EditUserPage` passes the avatar value and a `canEditAvatar` flag.
- `AccountPage` round-trips `avatar_url` so the shared request type
doesn't wipe avatars on the self-service path.

**Gating** is enforced in both the UI (field hidden) and the backend
(submitted value ignored for IdP login types).

**Tests/stories:** backend `TestUpdateUserProfile` covers apply
(password) and ignore (SSO); `EditUserForm` stories cover the
shown/hidden states with interaction tests.

</details>

---
> Generated by Coder Agents on behalf of @aslilac.
2026-06-26 16:35:03 -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
Steven Masley ad355aeaa9 feat: add INSECURE oidc email fallback flag for IdP brokers (#26751)
<!-- Authored by Coder Agents on behalf of @Emyrk. -->

Adds an opt-in `CODER_DANGEROUS_OIDC_EMAIL_FALLBACK` flag (alias
`--dangerous-oidc-email-fallback`) for IdP brokers that do not issue a
stable `sub` for the same user across connections.
2026-06-26 11:23:42 -05:00
Paweł Banaszewski 0f1e792f3f feat(coderd/database): add AI Gateway key auth lookup and last-used queries (#26505)
Adds DB methods`GetAIGatewayKeyIDByHashedSecret` and `UpdateAIGatewayKeyLastUsedAt`.
`GetAIGatewayKeyIDByHashedSecret` - returns AI Gateway key ID by hashed secret value.
`UpdateAIGatewayKeyLastUsedAt` - updates last used timestamp for given AI Gateway key. 
Used by standalone AI Gateway for authentication and keeping track of currently used keys.
2026-06-26 18:16:01 +02:00
Spike Curtis 0135f29cd8 feat: add CODER_CLUSTER_HOST CLI argument (#26680)
Closes GRU-69

Adds CODER_CLUSTER_HOST enviroment variable and CLI arg.

I ended up not making it hidden since we'll just have to unhide it later and even when hidden it still shows up in some autogenerated stuff. Might as well just go for it.

I also added it to the helm chart.
2026-06-26 08:57:23 -04: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
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
Jon Ayers 4cfed1b3ed feat: plumb time_til_autostop_notify template field (#26439) 2026-06-23 17:32:47 -05:00
Steven Masley 854d280834 chore: add --force-reset-all flag to oidc link repair cli (#26534)
Useful when the issuer is unchanged, but oidc subject claims have
changed.
2026-06-23 11:37:33 -05:00
Jeremy Ruppel 10717572ac feat: show template prerequisites in builder UI (#26523)
Surface base template prerequisites to admins before they create a
template in the Template Builder wizard.

Today, template prerequisites (Docker socket setup, Kubernetes auth, AWS
IAM policies) are only visible in the registry README after import.
Admins hit opaque provisioner errors and have to hunt for docs. This
change extracts the prerequisites from the README and serves them via
the API so the frontend can display them inline.

## How it works

Each base template README uses HTML comment markers (`<!--
prerequisites:start -->` / `<!-- prerequisites:end -->`) to delimit the
prerequisites section. At boot time, the base catalog loader reads the
README, extracts the content between markers via `strings.Index`, and
caches both the full README and the prerequisites string.

The prerequisites are served via a new `prerequisites` field on `GET
/api/v2/templatebuilder/bases`. The full README is included in the
composed template tar bundle and stored as the template version readme.

## Changes

- Add `README.md` with prerequisite markers to
`coderd/templatebuilder/bases/{docker,kubernetes,aws-linux}/`
- New `ExtractPrerequisites()` in `prerequisites.go` using literal
string matching
- `bases.go`: load README at boot, fail loudly if missing, extract
prerequisites
- `compose.go`: include README in `ComposeResult` and tar bundle
- `codersdk`: add `Prerequisites` field to `TemplateBuilderBase`
- Handler: populate prerequisites in bases response, set readme on
template version

<details>
<summary>Implementation notes</summary>

- Prerequisites extraction uses `strings.Index` for exact literal marker
matching; no regex or AST parser needed since we control the markers.
- YAML frontmatter is deliberately retained in the stored README. The
frontend `TemplateDocsPage` already strips it at render time via
`front-matter`.
- The prerequisite markers are HTML comments, invisible in rendered
markdown.
- The `RejectsMissingReadme` test enforces that every base template must
include a README.
- AWS Linux prerequisites span two H2 sections (`## Prerequisites` and
`## Required permissions / policy`), which is why heading-based parsing
was rejected in favor of explicit markers.

*Generated with the assistance of an AI coding agent. Reviewed by
@jeremyruppel.*
</details>

Relates to https://linear.app/codercom/issue/DEVEX-446
2026-06-23 11:36:07 -04:00
Jeremy Ruppel a30631198d feat: template builder backend fixes (DEVEX-287) (#26432)
Part of the Template Builder wizard PR stack.

## Backend fixes

1. **Registry URL scheme fix**: Default
`CODER_TEMPLATE_BUILDER_REGISTRY_URL` was `https://registry.coder.com`
but Terraform module registry addresses must be scheme-less. Changed to
`registry.coder.com`.

2. **Sensitive variable defaults**: Module `.tf.tmpl` files for
claude-code, aider, amazon-q had sensitive `variable` blocks without
`default`, causing `terraform plan` to fail during template import. Also
fixed the `templatebuildermodulegen` script.

3. **Auto-quote string variables**: The backend now accepts raw string
values from callers and wraps them in HCL quotes automatically.
Previously callers were required to send pre-quoted HCL literals, which
is not a reasonable API contract.

---
> [!NOTE]
> Generated by Coder Agents on behalf of @jeremyruppel
2026-06-23 09:17:14 -04: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
Kyle Carberry cd56ab9e33 refactor: remove legacy live-read and injected-history chat context paths (#26585)
This PR makes the agent-pushed pinned snapshot
(`chat_context_resources`) the sole source of workspace context for
chats, completing the "Release 5" cleanup. It removes legacy mechanisms
now superseded by the snapshot that agents push over dRPC
(`PushContextState`) and refresh via `chat-context/refresh`.

Removed:

- **Live-read at turn time.** MCP tool discovery, skill live-body reads,
and the instruction/skill history fallback that dialed the workspace on
every turn.
- **Context injected as message history.** The
`persist_workspace_context` generation action and its decision-loop
guard.
- **The legacy write path.** `POST`/`DELETE
/api/v2/workspaceagents/me/experimental/chat-context`, the agentsdk
`AddChatContext`/`ClearChatContext` methods, and the CLI one-shot
writer.
- **The `chats.last_injected_context` column** and all of its plumbing
(migration `000529`, queries, `db2sdk`, `dbauthz`, audit table, and the
frontend `ContextUsageIndicator` fallback).

Subagent context inheritance no longer copies parent context messages;
children now hydrate the parent's pinned `chat_context_resources` on
create, which yields an identical pin for the same workspace and agent.

What stays (still served by the live agent connection, not the
snapshot): `read_skill_file` supporting-file reads, `read_skill`
supporting-file listing, and MCP tool execution.

> [!NOTE]
> Migration `000529` drops `chats.last_injected_context` and recreates
the `chats_expanded` view without it. The down migration restores both.

<details>
<summary>Decision log (D1-D5)</summary>

- **D1 (subagent inheritance):** Re-point inheritance from the legacy
message copy to a pinned hydrate. Children call
`hydrateChatContextOnCreate` instead of copying parent context messages.
- **D2 (`persist_workspace_context`):** Remove the generation action
entirely along with the decision-loop guard it existed to satisfy, since
context is never injected into history anymore.
- **D3 (legacy HTTP + CLI):** Remove the experimental `chat-context`
POST/DELETE endpoints, the agentsdk methods, and the CLI one-shot. The
dRPC push + `chat-context/refresh` replace them.
- **D4 (frontend fallback):** Remove the `last_injected_context`
fallback in `ContextUsageIndicator`; pinned `resources` are the sole
source.
- **D5 (sequencing):** Ship as a single PR rather than a stacked pair.

</details>

---
Coder Agents generated on behalf of @kylecarbs.
2026-06-22 19:26:34 -06:00
Kyle Carberry c0f854c289 feat: report pinned chat context resources on chat API (#26570)
Surfaces a chat's pinned workspace-context resources on the single-chat
GET and refresh responses, so clients can show *what* context the prompt
was built from, not just whether it drifted.

## What's included
- **codersdk**: `ChatContextResource` (plus `ChatContextResourceKind`
and `ChatContextResourceStatus`) and `ChatContextMCPTool`, and a new
`Chat.Context.Resources` field (metadata only, no bodies). It is
populated only on the single-chat GET/refresh response; list and watch
payloads stay nil to remain lightweight.
- **coderd/x/chatd**: `Server.ContextResources`, which builds the
metadata-only list from the chat's pinned `chat_context_resources` rows.
Non-OK resources (invalid / unreadable / oversize / excluded) are
reported with their status and error so the UI can explain why a
resource was dropped from the prompt instead of silently omitting it.
The shared protojson body decoders are extracted so the prompt and
detail paths reuse them.
- **coderd**: `getChat` and `refreshChatContext` enrich the response
with the resource list. Failures are non-fatal (the chat stays usable
without the detail).

## Scope / what's deferred
This is an incremental split from #26466. This PR reports only the
**resource inventory**. The pinned-context drift *diff* (the per-source
`changes` set and the "View changes" dialog) is intentionally deferred
to a later split; the existing `dirty` bit already signals that context
changed. MCP resources are reported for display only; they are not
injected into the prompt (a future RFC item).

<details>
<summary>Design notes</summary>

- The resource list is the chat's full pinned inventory (instruction
files, skills, and MCP configs/servers), preserving the query's `source
ASC` order. OK-but-empty instruction files, OK skills with no name, and
untracked kinds (reserved plugin/hook/subagent/command) are skipped.
- MCP tool names are reported with the agent's `"<server>__"` prefix
stripped so they read as the server exposes them.
- The detail is computed on read and attached only on the single-chat
GET and refresh responses; list and watch payloads omit it to stay
lightweight.
- `refreshChatContext` enriches its own response (mirroring `getChat`)
so the client reflects a refresh immediately, without a full reload.
</details>

<details>
<summary>Testing</summary>

- `go test ./coderd/x/chatd/ -run
'TestPinnedContextResources|TestContextResources|TestChatContextDirtyFromAgentPush'`
(unit + integration on embedded Postgres) passes. The integration test
exercises the GET and refresh enrichment end-to-end.
- `go build`, `go vet`, `golangci-lint`, and `gofmt` are clean.
- `make gen` regenerated `apidoc`, `swagger.json`,
`docs/reference/api/*`, and `typesGenerated.ts`.
</details>

---
*This PR was created by Coder Agents on behalf of @kylecarbs.*
2026-06-22 10:00:15 -06: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
8b970e7ff3 docs: clarify Agents vs Chats API reference pages (#26021)
## Problem

The REST API reference page at
[`/docs/reference/api/agents`](https://coder.com/docs/reference/api/agents)
is confusing: by the name alone, a reader looking for the *AI Coder
Agents* programmatic API would assume this is the right page. In fact,
those endpoints are for the *workspace agent daemon* (the `coder_agent`
Terraform resource / `workspaceagent` daemon). The actual AI Coder
Agents API is documented at
[`/docs/reference/api/chats`](https://coder.com/docs/reference/api/chats).

Both pages compound the confusion by being rendered with a bare `#
Agents` / `# Chats` heading and no descriptive intro. The sidebar
entries are similarly ambiguous (`Agents` and `Chats` with no
descriptions).

## Root cause

The reference pages are generated by `scripts/apidocgen/generate.sh`
(swag → widdershins → postprocess). The widdershins template
(`scripts/apidocgen/markdown-template/main.dot`) already renders
`data.resource.description` directly under each section heading:

```
<!-- APIDOCGEN: BEGIN SECTION -->
{{= data.tags.section }}# {{= r}}

{{? data.resource.description }}{{= data.resource.description}}{{?}}
```

…but the swag annotations in `coderd/coderd.go` never declared
`@tag.name` / `@tag.description` for any tag, so the descriptions were
always empty.

## Changes

- `coderd/coderd.go`: add `@tag.name Agents` / `@tag.description …` and
`@tag.name Chats` / `@tag.description …` annotations next to the
existing `@title` / `@version` block.
- `docs/manifest.json`: rename the sidebar entry `Agents` → `Workspace
Agents` and add `description` fields to both API sidebar entries (every
other top-level section in the manifest has descriptions; the API
children did not).
- Regenerate `coderd/apidoc/swagger.json`, `coderd/apidoc/docs.go`,
`docs/reference/api/agents.md`, and `docs/reference/api/chats.md` via
`scripts/apidocgen/generate.sh` + `pnpm exec markdownlint-cli2 --fix` +
`pnpm exec markdown-table-formatter` + `scripts/biome_format.sh`
(matching the Makefile's `coderd/apidoc/.gen` pipeline).

Resulting diff is intentionally minimal — 6 files, 35 insertions / 3
deletions.

## After this PR

The Agents page will render:

> # Agents
>
> Workspace agent endpoints. These power the workspace agent daemon
defined by the `coder_agent` Terraform resource (sometimes called the
workspace daemon). This API is NOT the AI Coder Agents API. For
programmatic access to AI Coder Agents (formerly Tasks), see the Chats
API.

The Chats page will render:

> # Chats
>
> Programmatic API for Coder AI Agents (the user-facing "Coder Agents" /
"Chats" product). Experimental. Use these endpoints to create, list, and
manage AI coding agent sessions. For background and migration from the
Tasks API, see the AI Coder docs.

And the sidebar entry for the workspace-agent endpoints becomes
`Workspace Agents` instead of `Agents`.

## Out of scope (potential follow-ups)

- `docs/reference/api/chat.md` is a 7-byte stub — likely dead. Could be
deleted in a follow-up.
- Larger rename of the `Agents` Swagger tag (and/or the `coder_agent`
Terraform resource) to something like `Workspace Agents` /
`workspace_daemon` would more thoroughly fix the naming collision, but
that's a much bigger change.

Created on behalf of @mattvollmer.

---------

Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
Co-authored-by: Matt Vollmer <matthewjvollmer@outlook.com>
Co-authored-by: Atif Ali <atif@coder.com>
2026-06-22 07:43:13 +00: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
Paweł Banaszewski 0dbe0442f0 feat: add CLI commands to manage AI Gateway keys (#25689)
Adds `coder ai-gateway keys` commands:
* `create <name>` creates key with given name
* `list` lists existing keys (alias `ls`)
* `delete <name | id>` removes key matching by name or key id, name has
priority (alias `rm`)
2026-06-18 09:08:17 +00:00
Steven Masley 9d0ab594fb chore: unhide 'scim-use-legacy' flag (#26465) 2026-06-17 16:44:01 +00:00
Jeremy Ruppel 87de6dc23e feat: add base template variables to API (#26425) 2026-06-17 12:22:35 -04:00
Danielle Maywood 8d725969bf chore!: remove coder agents insights page (#26457)
Removes the coder agents PR Insights page (`/agents/settings/insights`) and all of its backend support. The page had previously been hidden and was only reachable via deep link. It had previously been hidden due to the dubious value provided in the current iteration.
2026-06-17 14:02:19 +01:00
Ethan d638b1aaed chore: gate Coder Agents app and port tabs behind experiment (#26395)
The workspace-app and port preview tabs in the Coder Agents right panel
were previously gated behind a `devel` prerelease build check, which
can't be toggled in real deployments.

This replaces that check with a proper `agent-app-tabs` deployment
experiment, registered in `ExperimentsKnown`, so the feature can be
enabled via `CODER_EXPERIMENTS=agent-app-tabs` like any other
experiment. The frontend now reads
`experiments.includes("agent-app-tabs")` from the dashboard instead of
`getPrereleaseFlag(buildInfo) === "devel"`.

Depends on #26208
2026-06-17 17:29:20 +10:00
Steven Masley 0e45ded0ed feat: deployment flag to auto handle changed oidc providers (#26419)
An opt-out flag exists as an escape hatch

closes https://linear.app/codercom/issue/PLAT-343/automatically-reset-user-link-for-affected-users-when-idp-provider
2026-06-16 13:26:04 -07:00
Steven Masley 1d03e63f4f feat: implement package and cli tool for repairing oidc links (#26418) 2026-06-16 12:46:10 -07:00
Kyle Carberry bca0ce04ca feat: integrate agent context snapshots into chats (#26389)
Makes the chat context foundation from #26385 live. That PR added the
storage columns, writer queries, and a dormant
`agentapi.ContextDirtyMarker` trigger with no production callers; this
PR wires them together end to end.

When a workspace agent pushes a context snapshot, bound chats now
hydrate to that snapshot's hash, and a later push with a different hash
flips already-pinned chats to dirty (emitting a `context_dirty` watch
event after the transaction commits). Chat creation pins the agent's
latest snapshot when one already exists. The experimental chat API
exposes this as `Chat.Context` (`*ChatContext` with `dirty`,
`dirty_since`, `error`), and a new `PUT
/api/experimental/chats/{chat}/context` endpoint re-pins the agent's
latest snapshot and clears the dirty marker.

`context_dirty_resources` stays NULL (the resource-level diff is
deferred to the UI phase) and the live per-turn context pull is
unchanged.

The end-to-end test provisions a workspace agent via the echo
provisioner, connects it over the Agent API v2.10, and exercises the
full path: an initial push hydrates a bound chat (clean), a second push
with a different hash marks it dirty, the API reports the dirty state,
and the refresh endpoint clears it.

<details>
<summary>Decision log</summary>

- **API shape — sub-struct.** Dirty state is surfaced as
`codersdk.Chat.Context *ChatContext { Dirty bool; DirtySince *time.Time;
Error string }` rather than flat fields, matching the RFC's named
`ChatContext` type and leaving room for future fields (resource diff,
sources). `db2sdk.Chat` populates it when the chat is context-tracked
(`len(ContextAggregateHash) > 0`), dirty, or carries a snapshot error,
and leaves it nil (`omitempty`) otherwise. `Dirty` mirrors
`context_dirty_since` being set.
- **Marker wiring.** The chat daemon is injected directly as the
`agentapi.ContextDirtyMarker`. It is unconditionally constructed (only
its background worker is gated), so the marker is always non-nil and the
wiring matches every other `api.chatDaemon` call site. `agentapi` still
treats a nil marker as "chatd absent", so `PushContextState` stays a
pure write path for any future caller that does not wire chatd in.
- **Refresh is atomic.** `RefreshChatContext` reads the agent's latest
snapshot and re-pins the chat in one repeatable-read transaction, so a
concurrent push cannot land between the read and the write and leave the
chat pinned to a stale hash with the dirty marker cleared.
- **Hydrate + dirty run inside the push transaction.** The fan-out
shares the push's transaction so a concurrent refresh cannot interleave
with the version gate; `context_dirty` watch events publish only after
commit. The pinned hash on dirtied chats is intentionally left unchanged
— the refresh endpoint re-pins it.
- **Dirtied chats keep their pinned hash.** Drift is advisory: a dirty
chat stays usable, and refreshing is the only path that advances the
pinned hash.
- **Test binds `chats.agent_id` directly.** In production the binding is
set lazily during a chat turn (`chatd.persistBuildAgentBinding`); the
test sets it via `dbgen` so it exercises the context flow rather than
turn resolution.

Plan: `coderd/x/chatd` context integration + E2E (sub-struct API,
create-time + push-time hydration, refresh endpoint;
`context_dirty_resources` and the per-turn pull untouched).

</details>

🤖 Generated by Coder Agents on behalf of @kylecarbs
2026-06-16 17:46:47 +00:00
Sas Swart 2716e2181c feat: purge boundary logs past retention (#24815)
Add a periodic purge job for `boundary_logs` rows past their retention
threshold, following the same pattern as the existing audit log and
connection log purge jobs in `dbpurge`.

Expose a `--boundary-log-retention` deployment flag (env
`CODER_BOUNDARY_LOG_RETENTION`, YAML `retention.boundary_logs`). Default
is `0` (keep indefinitely). When set to a positive duration, `purgeTick`
deletes rows where `captured_at` is older than the threshold in batches
of 10,000, matching other log purge operations. The `boundary_logs`
label is added to the `records_purged_total` Prometheus counter.

Also removes the random-UUID fallback for `OwnerID` in
`dbgen.BoundarySession`. The previous fallback generated a UUID that
could never satisfy the `boundary_sessions_owner_id_fkey` FK constraint,
masking test setup bugs. Callers must now provide a valid user ID or
accept NULL (the legitimate "user deleted" state).
2026-06-16 14:32:54 +02: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
Jeremy Ruppel de31c7c18e feat: add TemplateBuilderCreateTemplate SDK types and client method (#26360)
Adds `POST /api/v2/templatebuilder/compose/template`, a synchronous
endpoint that composes a template from a base and modules, validates it
via a provisioner import job, and creates the template in a single
request.

The handler composes terraform files, bundles them as a tar, inserts the
file with hash-based dedup, creates a template version with an import
job, waits up to 2 minutes for the job to complete, classifies errors
for known failure modes (network-unreachable registry, DNS failures),
then creates the template on success. Canceled and failed jobs return
appropriate error responses.

Also adds `hclwrite.Format` to composed terraform output for canonical
HCL formatting.

Closes https://linear.app/codercom/issue/DEVEX-279

<details>
<summary>Implementation notes</summary>

- SDK types and client method in `codersdk/templatebuilder.go` with
validation tags matching the standard template creation path
(`template_display_name`, `lt=128`)
- `ClassifyProvisionerError` in `coderd/templatebuilder/errors.go`
detects DNS, connection refused, i/o timeout, and TLS handshake failures
and returns actionable messages
- `waitForProvisionerJob` polls with a ramp-up interval schedule (100ms,
200ms, 500ms, then 1s steady) and accepts an `onUpdate` callback for
future SSE streaming
- Audit logging for both template and template version creation
- TOCTOU name uniqueness: early check for fast feedback, DB unique
constraint catch for the race window (returns 409, not 500)
- Swagger annotations for all error responses (400, 404, 409, 504)

</details>

> 🤖 Generated by Coder Agents
2026-06-15 18:12:55 -04:00
Jeremy Ruppel b61b62f4b3 feat: add POST /api/v2/templatebuilder/compose endpoint (#26351)
> [!NOTE]
> This PR was authored by Coder Agents on behalf of @jeremyruppel.

Part 4 of DEVEX-277 (POST /api/v2/templatebuilder/compose).

Adds the HTTP handler, route wiring, and integration tests for the
compose endpoint.

The handler accepts a JSON request with a base template ID and optional
modules with variable overrides, renders them via `Compose`/`BundleTar`,
and returns the tar archive directly with `Content-Type:
application/x-tar`. The registry URL comes from the deployment config
(`CODER_TEMPLATE_BUILDER_REGISTRY_URL`).

RBAC uses `policy.ActionCreate` on
`rbac.ResourceTemplate.AnyOrganization()`.

Integration tests cover: base-only compose, base with modules, unknown
base/module errors, missing base template ID, and feature-disabled 404.
2026-06-15 11:07:16 -04:00
Jeremy Ruppel 9a6e348f5d feat: add GET /api/v2/templatebuilder/modules endpoint (#26117)
Implement `GET /api/v2/templatebuilder/modules`, which returns the
filtered list of modules available for a given base template. Reads from
the bundled catalog via `LoadModules()` and applies OS-compatibility
filtering based on the `base` query param.

Computed variables (e.g. `agent_id`) are excluded from the API response
at the `ToSDK()` conversion boundary since they are wired automatically
by the builder. The `Computed` field is removed from the SDK type. Adds
`CompatibleWithOS()` to `ModuleManifest` for OS filtering.

Returns 400 for unknown base IDs and 404 when the template builder is
disabled.

Depends on #26116

> [!NOTE]
> This PR was authored by Coder Agents on behalf of @jeremyruppel.
2026-06-12 17:53:48 -04:00
Jeremy Ruppel 776fbfa748 feat: add GET /api/v2/templatebuilder/bases endpoint (#26116)
Implement `GET /api/v2/templatebuilder/bases`, which returns the list of
base templates available in the template builder. Reads from the bundled
catalog by cross-referencing `templatebuilder.BaseTemplateIDs()` with
`examples.List()`, enriching each entry with the OS from the `exampleID
-> OS` map.

The endpoint is gated behind the template builder feature flag (returns
404 when disabled) and requires `policy.ActionRead` on
`rbac.ResourceTemplate`.

Depends on #26115

> [!NOTE]
> This PR was authored by Coder Agents on behalf of @jeremyruppel.
2026-06-12 17:40:02 -04: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
George K b5ef700dd6 fix!: only trust x-forwarded-host from configured trusted proxies (#26204)
Subdomain app routing derived the app identity from
httpapi.RequestHost, which returned the client-supplied
X-Forwarded-Host header verbatim. No middleware validated or stripped
that header, so a request from an untrusted peer could forge it. Since
the application_connect cookie is scoped to the wildcard apps domain,
JavaScript in a share=authenticated app could fetch() with a forged
X-Forwarded-Host pointing at a victim's owner-only app; coderd routed
and authorized the request as the victim and returned the private app
response same-origin to the attacker.

Replace RequestHost with httpmw.EffectiveHost, which honors
X-Forwarded-Host only when the original socket peer is a configured
trusted origin, otherwise falling back to the received Host header.
This ties host trust to the same RealIPConfig model already used for
X-Forwarded-For and -Proto. Wire it into HandleSubdomain for both
coderd and wsproxy, and log both the effective host and the raw
received_host.

Add coverage: EffectiveHost unit tests assert the trust decision uses
the socket peer rather than the spoofable forwarded client IP, and a
HandleSubdomain test confirms a forged X-Forwarded-Host from an
untrusted peer never reaches token resolution.

Refs: https://linear.app/codercom/issue/PLAT-259
2026-06-11 10:55:00 -07:00
Danny Kopping 78a6ec293e revert: "fix: avoid an errant license warning banner on new deployments that d…" (#26240)
Reverts coder/coder#26239

We cannot disable a feature which was previously enabled; this is a BC
break.
This is also using `AIGatewayRoutingEnabled` which will be removed in
the next release.
2026-06-11 08:07:32 +00:00
Sas Swart d0e9c5eda5 fix: avoid an errant license warning banner on new deployments that d… (#26239)
Problem: CODER_AI_GATEWAY_ENABLED defaulted to true, which both started
the in-memory gateway and enabled the licensed FeatureAIBridge. As a
result, deployments that never configured AI Gateway saw a spurious "AI
Governance add-on is required" warning whenever they had an older
(non-add-on) Premium license, since the feature was enabled-and-entitled
by default.

Fix: Decouple "external AI Gateway API enabled" from "in-memory daemon
running," so the external/licensed surface is off by default while Coder
Agents retain access by default.
2026-06-11 09:17:26 +02:00
Rowan Smith 77522c3945 feat: cli: add support for supplying ephemeral parameters at workspace creation (#26012)
Resolves the issue of `--prompt-ephemeral-parameters` and
`--ephemeral-parameter` not being available for use in the `coder
create` workspace creation command (they are only available in `coder
start` command). Back when they were [added
originally](https://github.com/coder/coder/pull/15030) it seems to have
been an oversight that they were left out.

The problem this solves:

```
coder create --parameter my_ephemeral_parameter=foo
error: prepare build: ephemeral parameter "my_ephemeral_parameter" can be used only with --prompt-ephemeral-parameters or --ephemeral-parameter flag
```

```
coder create my-test-ws -t general --ephemeral-parameter my_ephemeral_parameter=foo
parsing flags ([create my-test-ws -t general --ephemeral-parameter my_ephemeral_parameter=foo]) for "coder create": unknown flag: --ephemeral-parameter
```

Tested on a template with the following:

```
data "coder_parameter" "my_ephemeral_parameter" {
  name         = "my_ephemeral_parameter"
  type         = "bool"
  description  = "true or false?"
  mutable      = true
  default      = false
  ephemeral    = true
}

resource "coder_env" "debug_ephemeral" {
  agent_id = coder_agent.main.id
  name     = "EPHEMERAL_TEST"
  value    = data.coder_parameter.my_ephemeral_parameter.value
}
```

By running:

```
➜  coder git:(rowan/coder-create-5495) ✗ go run cmd/coder/main.go create --ephemeral-parameter my_ephemeral_parameter=true
> Specify a name for your workspace: ws4
Select a template below to preview the provisioned infrastructure:
?  kasmvnc-ubuntu-coder-dev used by 1 active developer
Select a preset below:
?  Small (2 CPU / 4 GB)
....
...
The ws4 workspace has been created at Jun  3 12:36:38!

➜  coder git:(rowan/coder-create-5495) ✗ coder ssh ws4               
workspace-ws4-5d6994756f-qlwnl% echo $EPHEMERAL_TEST
true
workspace-ws4-5d6994756f-qlwnl% exit
```
2026-06-11 09:06:07 +10:00
Cian JohnstonandCopilot Autofix powered by AI a26c46a3bf fix!: validate HostnameSuffix and SSHConfigOptions' (#26154)
- Adds server-side and client-side validation for
CODER_CONFIGSSH_HOSTNAME_SUFFIX and CODER_SSH_CONFIG_OPTIONS.
- **Server-side breaking change:** invalid values for either of these will cause `coderd` to exit with an error.
- Client-side: `coder config-ssh` will exit with an error if it detects invalid config.
- Adds tests for the above

Local smoke-testing: ran `develop.sh --env-file <path to an env file
containing badness>`. Validated that server startup failed as expected.

> 🤖 Generated by Coder Agents with supervision from a human.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-10 15:48:02 +01: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
Danielle Maywood d751b46a19 fix: scope combined chat source filters (#26137) 2026-06-08 15:05:27 +01:00
Danielle Maywood fa56224eda feat: show shared chats in agents sidebar (#26056) 2026-06-06 00:12:20 +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
Sas Swart 52722b800b chore: rename boundary command to agent-firewall (#25889)
Renames the `coder boundary` CLI subcommand to `coder agent-firewall` as
part of the Boundaries → Agent Firewall rebrand.

`coder boundary` is retained as a hidden, deprecated alias that prints a
deprecation notice to stderr before running. Both commands use separate
builder functions backed by the same boundary base command and license
verification logic.

Closes https://linear.app/codercom/issue/AIGOV-236

<details><summary>Implementation notes</summary>

**Approach:** Two separate `*serpent.Command` objects (not `Aliases`) so
the deprecated `boundary` path can print a stderr warning while
`agent-firewall` stays clean.

**Changes:**
- `enterprise/cli/boundary.go`: Split old `boundary()` into
`buildAgentFirewallCmd()` and `buildBoundaryAliasCmd()`. Error messages
in `verifyLicense` now reference "agent-firewall".
- `enterprise/cli/root.go`: Register both commands.
- `cli/root.go`: Update YAML-only option validation bypass for the new
command name.
- Tests: Rename to `TestAgentFirewallSubcommand`, add
`TestBoundaryAlias`, update license verification tests to use
`agent-firewall`.
- Golden files and CLI reference docs regenerated.
- `docs/ai-coder/agent-firewall/version.md` and `docs/manifest.json`
updated.

</details>

> Generated with [Coder Agents](https://coder.com/agents) by @SasSwart
2026-06-04 11:14:36 +02:00
Ethan 3ab1323bc9 fix!: rename chat stream silence timeout error (#25973)
Renames the Agents chat stream-silence error from `startup_timeout` to
`stream_silence_timeout` now that the timeout applies to any gap between
provider stream parts, not just first-token startup.

Updates the SDK enum, generated API docs/types, chat error copy, and
Agents UI stories/status labels so the user-facing wording describes a
stalled provider response instead of startup delay.

> **Breaking change:** This is a very minor breaking change for the
Coder Agents API: the public chat error kind enum no longer includes
`startup_timeout`, so clients matching that specific value should handle
`stream_silence_timeout` instead.
2026-06-04 18:36:02 +10: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
Paweł Banaszewski f22d4e2cbb feat: add ai_gateway_keys table and related RBAC (#25563)
Adds table to store keys that AI Gateway standalone replicas will use
to authenticate into Coderd.
Also adds RBAC and audit boilerplate.
2026-06-02 09:28:43 +02:00
Danny Kopping c8555e2163 fix: deprecate ai provider seeding env config (#25854)
Environment variables used to configure AI Gateway providers are now deprecated, and we need to reflect this as such.
2026-06-01 15:15:47 +02:00