mirror of
https://github.com/coder/coder.git
synced 2026-09-21 20:51:01 +08:00
ba4779fc87ad1ad3f75e2a0cd2dca36cb06e1b7a
687
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ba4779fc87 |
docs: lead with env vars in admin docs and add configuration reference (#26824)
## What & why Admin/setup docs lead with `coder server --flag` examples, but most operators configure Coder through `CODER_*` environment variables (system service, container, or Helm chart). There is no single page mapping a setting to its env var, CLI flag, YAML key, and default, so searching the docs for an env var name such as `CODER_PG_CONNECTION_URL` returns nothing. This adds a generated configuration reference and begins shifting admin docs to lead with the environment-variable form. ## Changes - **Generated configuration reference** (`docs/admin/setup/configuration-reference.md`): a searchable, per-setting list of every visible deployment option. Each option is a heading (grouped and nested by serpent group) followed by its description and the environment variable, CLI flag, YAML key, and default that apply to it. Generated from `codersdk.DeploymentValues` so it stays in sync. - **Generator + `make gen` wiring** (`scripts/configdocgen/`): new binary plus a Makefile target and `GEN_FILES` entry, mirroring the existing `clidocgen` / `auditdocgen` pattern. Output is host-independent (same env normalization as `clidocgen`). - **Demo conversion** (`docs/admin/users/github-auth.md`): inverted to lead with the `/etc/coder.d/coder.env` env-var form; the CLI-flag form becomes a closing note that links to the reference. H2 slugs preserved. - **Style guide** (`.claude/docs/DOCS_STYLE_GUIDE.md`): documents the env-var-first convention for admin/setup docs. - **Navigation**: manifest entry under Administration → Setup, plus a TIP callout on the setup index. ## Risk Docs + gen pipeline only; no runtime change. The page is regenerated by `make gen`; the `gen` and `check-docs` CI checks pass. ## Follow-up Several other admin pages still lead with flag walls. Recommend sweeping them incrementally in separate PRs rather than expanding scope here. <details> <summary>Implementation notes (provenance, conflict resolution, verification)</summary> - Continues prior work by @aslilac and @bpmct from the `kayla/docs-env-vars-first` branch. Both original commits are cherry-picked here with authorship preserved. - Rebased onto current `main`. Resolved two `Makefile` conflicts where `main` had since added the `feature-stages.md` gen target at the same locations; kept both targets (union) in `GEN_FILES`, `gen/mark-fresh`, and the recipe block. - The original branch's checked-in page predated recent `codersdk.DeploymentValues` changes, so it was **regenerated** against current `main` (adds `CODER_SCIM_USE_LEGACY`, the `Networking / Cluster` section with `CODER_CLUSTER_HOST`, `CODER_BOUNDARY_LOG_RETENTION`, and the AI Gateway description rename). The `gen` CI check enforces this stays current. - Fixed flag-link anchors for short-form flags (`--config`, `--log-filter`): the generator derives the anchor from `FlagShorthand` to match `clidocgen`'s heading (e.g. `#-l---log-filter`). - `linkspector` ignores the AWS Bedrock base URL that appears as an illustrative `<region>` placeholder in an option description, consistent with the existing `openai.com` ignore patterns. </details> <details> <summary>Configuration reference layout (2026-07-08 update)</summary> Reworked the reference from a wide table into a nested, per-setting list so it fits without horizontal scrolling and stops repeating the group name in every heading: - **List, not table.** Each option renders as a heading, its description, and a bullet list of only the configuration methods that apply to it (non-applicable methods are omitted instead of shown as `-`). - **Nested sections.** Sections nest by the serpent group hierarchy, so `Email / Email Authentication` becomes `Email` (h2) with an `Email authentication` (h3) subsection instead of a redundant flat title. - **Shorter, sentence-case headings.** The redundant group prefix is stripped from each option name and the remainder is lowercased to sentence case, preserving acronyms and mixed-case tokens (`URL`, `TLS`, `OAuth2`, `GitHub`) plus a small proper-noun allowlist (`Coder`, `Terraform`, `Honeycomb`, `Anthropic`, `Bedrock`, ...). Example: `AI Gateway Send Actor Headers` becomes `Send actor headers`. - **Deprecated options** sort to the end of each section and lead with an emphasized **Deprecated** marker. Headings stay clean (no `(deprecated)` suffix) so their anchors remain stable. - **Section intros** render from a group's `Description` when the source defines one (e.g. DERP); no hand-maintained prose or links are introduced. All transformations run in pure Go at `make gen` time (no AI at generation time). Generation is idempotent, and `markdownlint` and `golangci-lint` both pass. </details> --- 🤖 Opened by Coder Agents on behalf of @nickvigilante. Continues work by @aslilac and @bpmct. --------- Co-authored-by: Kayla (via Coder Agents) <kayla@coder.com> Co-authored-by: Coder Agents <noreply@coder.com> Co-authored-by: Ben Potter <me@bpmct.net> |
||
|
|
fc24c27dfd |
fix: reserve chat hook dispatch capacity for running turns (#27656)
## Context Follow-up fix from live UAT of the merged chat lifecycle hooks stack (#27430). Its companion UAT fix (#27655) has merged, so this targets `main` directly. ## Why? UAT measured a burst of 1,500 concurrent chat creations against a consumer with 1.2s latency. 255 were admitted and 1,245 got `502 hook_dispatch_failed (over_capacity)`, which is correct fail-closed behavior. The collateral wasn't: the same burst failed 24 `stop` dispatches, parking chats that had already been admitted and had already executed tools. One 256-slot semaphore served every event, so new-work admission could take every slot and kill turns in flight. Callers now classify each dispatch as admission or generation, and admission draws from a 192-slot gate held *before* the shared pool. At least 64 shared slots stay reachable only by dispatches for work a chat already admitted. The dispatcher is per `coderd` replica, so these limits are per replica, not deployment-wide, and the docs say so. **The caller classifies, not the event type.** Event type isn't a reliable proxy in either direction: a subagent spawn dispatches `user_prompt_submit` from inside a running turn, and the edit path dispatches `session_start` at admission time. `CapacityClassUnset` is rejected in `Dispatch`, so a new call site fails closed rather than silently inheriting a share. **Acquisition order is load-bearing.** Admission takes its own gate first. Taking a shared slot first would let admissions queued on the gate occupy the very capacity the reserve protects. `acquireCapacity` is the only path that takes either pool, so the order can't be bypassed. ## What this does not guarantee Nothing bounds how many turns generate concurrently, so the 192/64 split is a judgement call, not a derived ceiling. This stops an *admission* burst from consuming every slot; it does not make the remainder sufficient. A large enough generation load can still exhaust the reserve and error a running chat. The docs say so explicitly rather than promising a guarantee the code doesn't deliver. Generation can now take all 256 slots, so generation traffic starves admission harder than before. That's the intended priority: rejecting a new prompt is recoverable, ending a turn that already ran tools is not. ## Testing Red-green proved both new tests. Removing the release-on-failure path fails `RefusedSharedAcquireReleasesAdmission` deterministically; removing the expired-deadline check fails `ExpiredDeadlineRefusesFreeSlot` in 18/30 runs. That deadline check fixes a real race found in review. `acquire` previously shared one `time.Timer` across both acquires. Because `select` picks a ready case at random, an admission dispatch could take a slot after its capacity deadline had passed. Measured over 300 trials: 135 late acquisitions, worst overshoot 2.1ms. `acquire` now takes an absolute deadline and refuses an expired one before selecting, which measures 0/300. Go: `coderd/x/agenthooks/...` and `coderd/x/chatd/...`, plus `-race -count=3` on the dispatcher. > Mux opened this PR on Mike's behalf. |
||
|
|
df1c0f9710 |
feat: show what a chat lifecycle hook changed (#27655)
## Stack Context Follow-up fixes from live UAT of the merged chat lifecycle hooks stack (#27430). Two PRs: 1. **This PR**: make hook effects visible and correctly attributed in the transcript. 2. [`mike/chat-hooks-uat/dispatch-capacity`]: reserve dispatch capacity so an admission burst can't fail running turns. ## Why? UAT found three ways the transcript misrepresented what a lifecycle hook did. All three are user-visible and share the same surface (`chathooks/effects.go`, `codersdk.ChatMessagePart`, the conversation timeline), so they're reviewed together. **A prompt `input_override` silently discarded attachments.** `ComposeUserPromptContent` replaced the entire submitted part list with one text part, dropping `file` and `file-reference` parts along with their `chat_file_links`. The user saw their attachments vanish with no explanation. The override now replaces submitted *text* parts only and preserves non-text parts in order. A consumer that wants to block attachments uses `deny`, which is the documented mechanism for refusing a submission. **Every user-visible `system` row was labelled "Lifecycle hook".** The timeline keyed the notice off `role === "system"`. That was correct only by accident, because the hook `user_message` was the sole client-visible system row. The backend now emits the notice as a typed `hook-notice` part and the timeline renders on that, so a future system row can't be mislabelled as a policy notice. **Nothing marked a tool call the hook had rewritten.** A consumer could replace tool input via `input_override` and the transcript showed the rewritten input as if the model had produced it. `ChatMessagePart` gains `hook_rewritten`, set from `preflight.Overrides` on the same path that already carries `ToolCallCreatedAt`, and the tool row renders a "Modified by policy" badge. `ToolCall.PolicyProvider` renders the badge itself, at four wrap sites: the `Tool` dispatch wrapper, the `ReadFilesTool` aggregate and its per-file rows, and `ReadFileTimelineBlock` (grouped and single `read_file` rows bypass `Tool`). Renderer props do not include the flag; descendants consume it through the provider context. The badge is emitted by the provider rather than by the shared header because several renderer branches return early without one, including the auth-required `execute` card, a completed `ask_user_question`, and an empty question payload. Those branches would drop the attribution with no type or runtime error, and the gap is not greppable: every renderer file contains a header somewhere, only individual branches do not. Emitting at the provider removes the possibility instead of enumerating the cases. A rewritten call is wrapped in a group labelled by its badge, so one rewritten file inside a merged read is attributed on its own rather than inheriting the group's badge. `HeaderButton` still appends the policy wording to an explicit `ariaLabel`, since an explicit `aria-label` replaces the name computed from descendants. Provider-executed calls are excluded from attribution. Hooks never see them, and duplicate tool-call ID rejection deliberately skips them, so a reused ID would otherwise mark a provider-executed call as policy-rewritten. ## Testing Go: `coderd/x/chatd/...`, `coderd/x/agenthooks/...`, `codersdk/...`, and `coderd -run 'Hook|Chat'`. Frontend: `tsc` plus every `AgentsPage` story; the only failures are `MCP Tool Completed` and `Scroll To Bottom Button Works With Inverse Scroll`, both of which fail on trunk. A registry-wide story asserts every registered renderer shows the badge, verified against three inverted toggles: removing the badge, hiding it with `display:none`, and skipping the provider for one renderer (which names that renderer). Storybook also covers the rewritten subagent spawn, a completed empty question payload, a non-hook system message, and a failed `read_file` guarding the accessible name. > Mux opened this PR on Mike's behalf. |
||
|
|
4245e4e378 |
feat: expose dynamic client registration in deployment settings (#27480)
Adds the admin-controlled OAuth2 Dynamic Client Registration setting landed by #27316 (`GET`/`PUT /api/v2/oauth2-provider/settings`) to the OAuth2 Applications deployment settings page, since it was previously only reachable via the API or `coder oauth2-provider dcr enable|disable`. The page is now tabbed, **Applications** and **Settings**, so DCR has a home that further OAuth2 settings can share (an Initial Access Token setting is a likely next one). The active tab is backed by a `tab` search param, so `?tab=settings` links straight to it, and an unpermitted deep link falls back to **Applications** rather than selecting nothing. On the Settings tab, DCR renders as a titled section with a description, an `Enabled` badge when active, and an Enable/Disable button. Enabling opens a confirmation dialog, since it lets any OAuth2 client self-register against the deployment without prior admin approval (RFC 7591). Disabling is immediate, no confirmation. The control is a button rather than a switch on design feedback: a switch reads as an immediate on/off flip, which conflicts with a confirmation dialog standing in front of it, and it left the only explanation of the risk inside a dialog that disappears. A button carries the confirmation step without misrepresenting what a click costs, the always-visible description explains the setting on the page, and the `Enabled` badge gives the active state a persistent indicator. The layout follows Tracy's mockup on `tj/oauth2-apps-pagination`; the apps-table pagination work that shares that branch is deliberately not included here. Visibility and editability are gated on the same `ResourceDeploymentConfig` RBAC checks the endpoint itself enforces (`viewDeploymentConfig` / `editDeploymentConfig`), not a separate hardcoded check. The view takes the settings values as one optional `settings` prop, absent when the viewer lacks `viewDeploymentConfig`, so "cannot view" is the shape of the prop rather than a flag the caller keeps consistent with the values beside it, and the tab is not rendered at all. Closes https://github.com/coder/coder/issues/27432 ## Where this sits in the request path ```mermaid sequenceDiagram autonumber actor Admin participant View as OAuth2AppsSettingsPageView<br/>(Tabs + Enable/Disable + Dialog) participant Page as OAuth2AppsSettingsPage<br/>(React Query) participant S as coderd Note over Page: On mount Page->>S: GET /api/v2/oauth2-provider/settings S-->>Page: { dynamic_client_registration_enabled } Page-->>View: settings: { dynamicClientRegistrationEnabled, canEdit, ... } Note over Admin,View: Admin opens the Settings tab and enables DCR Admin->>View: click "Enable" View->>View: open confirmation dialog<br/>(no request sent yet) Admin->>View: click Confirm View->>Page: settings.onDynamicClientRegistrationChange(true) Page->>S: PUT /api/v2/oauth2-provider/settings<br/>{dynamic_client_registration_enabled: true} S-->>Page: 200 OK (audited) Page->>S: GET /api/v2/oauth2-provider/settings (refetch) S-->>Page: { dynamic_client_registration_enabled: true } Page-->>View: section shows the "Enabled" badge and a Disable button Note over Admin,View: Admin disables DCR Admin->>View: click "Disable" View->>Page: onDynamicClientRegistrationChange(false)<br/>(no dialog, disable is immediate) Page->>S: PUT ... {dynamic_client_registration_enabled: false} S-->>Page: 200 OK (audited) ``` ## Files changed All 10 files are hand-written; nothing in this PR is `make gen` output. | File | What changed | |---|---| | `site/src/api/api.ts` | New `getOAuth2ProviderSettings`/`putOAuth2ProviderSettings` methods, thin typed wrappers around the two endpoints #27316 added to `main`. | | `site/src/api/api.test.ts` | Covers both methods against the request they issue and the error they propagate. | | `site/src/api/queries/oauth2.ts` | A `getSettings` query and a `putSettings` mutation that invalidates the settings key on success. Both the app and settings keys now derive from a shared `oauth2ProviderKey` constant. | | `site/src/api/queries/oauth2.test.ts` | 4 tests: the key nesting, both delegations, and that a successful update invalidates the settings key without touching app queries. | | `.../OAuth2AppsSettingsPage.tsx` | Wires query and mutation into the page and passes the settings values down as one object, or omits it entirely without `viewDeploymentConfig`. The apps error stays its own prop, since the view gates the applications empty state on it. | | `.../OAuth2AppsSettingsPageView.tsx` | `Tabs` splitting Applications from Settings. The settings tab distinguishes loading, failed, and a value the server omitted rather than rendering nothing, and the header's "Add application" action is scoped to the applications tab. | | `.../OAuth2AppsSettingsPageView.stories.tsx` | 14 stories, covering the tab wiring, both permission boundaries, the header action's scope, and the settings tab's loading, fetch-error, update-error, and value-omitted states. | | `.../DynamicClientRegistrationSetting.tsx` | The section itself: heading, description including what disabling does not undo, `Enabled` badge, a permission explanation when the viewer cannot edit, and one button that confirms only in the enable direction. | | `.../DynamicClientRegistrationSetting.stories.tsx` | 11 stories, including focus surviving an in-flight request and the dialog ignoring a value that changes underneath it. | | `docs/admin/integrations/oauth2-provider.md` | Adds the web UI route to the DCR section, which previously enumerated only the CLI and the management API. | ## Suggested review order Follows the direction data actually flows, from the raw HTTP call up to the rendered section. 1. **`site/src/api/api.ts`**: the two new methods. Confirms they match the `codersdk.OAuth2ProviderSettings` shape #27316 landed and sit next to the existing OAuth2 app methods they mirror. 2. **`site/src/api/queries/oauth2.ts`**: the query/mutation pair. The mutation's `onSuccess` → `invalidateQueries` is the one detail worth double-checking: it's what makes the on-screen state catch up with what was just saved, rather than trusting the PUT payload. 3. **`OAuth2AppsSettingsPage.tsx`**: the container. Check the two separate permission gates (`viewDeploymentConfig` on the query's `enabled` option, `editDeploymentConfig` on the button's editability) match the RBAC the backend enforces. 4. **`OAuth2AppsSettingsPageView.tsx`**: the tabs and the settings tab's four states. The `settings` prop being optional is what hides the tab; the error inside the tab is deliberately separate from the page-level `error`, which gates the applications empty state. 5. **`DynamicClientRegistrationSetting.tsx`**: the section. Two things worth reading closely: the enable path opens the dialog while the disable path calls straight through, and lacking permission uses the native `disabled` attribute while an in-flight request uses `aria-disabled`, so a keyboard user is not blurred mid-flip. 6. **The two story files**: read last, as they exercise everything above without a real server. The dialog stories query `canvasElement.ownerDocument.body` rather than `canvasElement`, since the dialog renders into a portal attached to `<body>`. ## Deliberately not in this PR - **ENG-3116**: the applications list cannot distinguish self-registered clients from admin-created ones. Surfacing that needs a new field on `codersdk.OAuth2ProviderApp`, which is an API addition this PR does not need. - **ENG-3118**: reusing the shared `EnabledBadge` and `SettingsHeader` primitives for this section. Both hinge on what the mockup intends, and the badge in particular is a visible change either here or on the four other pages that share it. ## Screenshots Default (disabled): <img width="1676" height="497" alt="image" src="https://github.com/user-attachments/assets/cfa60266-8678-410e-9577-16ef474491e3" /> Enabling (confirmation dialog): <img width="1661" height="558" alt="image" src="https://github.com/user-attachments/assets/a7d54fdd-f65d-4fec-9ed9-3bfdcfdae5be" /> Enabled: <img width="1666" height="559" alt="image" src="https://github.com/user-attachments/assets/d39251c2-771c-4608-81c2-dda151b35c3d" /> --------- Co-authored-by: Tracy Johnson <tracy@coder.com> |
||
|
|
18128b7b52 |
docs: add standalone AI Gateway docs (#27592)
Documents standalone AI Gateway deployment, Gateway key authentication, monitoring, and the updated embedded vs standalone topology in the AI Gateway docs. --------- Co-authored-by: Cian Johnston <cian@coder.com> |
||
|
|
4987afada7 |
docs: present AI Governance as included with Premium (#27545)
## Summary AI Governance is now included with Premium licenses instead of being sold as a separate per-user add-on. This updates `docs/` to describe the new packaging, removes "Add-On" from AI Governance references, and refreshes the editions architecture diagram. ## Changes - **`docs/ai-coder/ai-governance.md`**: title is now "AI Governance"; rewrote the licensing statements (previously "a separate, per-user license... not included with a Premium subscription and must be purchased separately") to state it is included with Premium. The usage-pool section now attributes the shared Agent Workspace Build pool to Premium deployments. - **Repeated admonition (28 files under `ai-coder/agent-firewall/` and `ai-coder/ai-gateway/`)**: replaced "requires the AI Governance Add-On / as of Coder v2.32, deployments without the add-on..." with "is part of AI Governance, which is included with a Premium license." The v2.32 add-on gate no longer applies; the gate is now Premium vs. Community. - **`docs/ai-coder/index.md`, `security.md`, `tasks.md`, `usage-data-reporting.md`, `admin/licensing/index.md`, `install/releases/esr-2.29-2.34-upgrade.md`, `ai-gateway/ai-gateway-proxy/setup.md`, `ai-gateway/clients/claude-code.md`**: reworded add-on references to Premium inclusion. - **`docs/manifest.json`**: nav title "AI Governance Add-On" → "AI Governance", updated two descriptions, and swapped the 25 `"state": ["ai governance add-on"]` badges to `["premium"]` so the sidebar badge reads "Premium" instead of "AI Governance Add-On". - **`docs/images/single-region-architecture.png`**: refreshed the diagram in the **Community and Premium editions** tab on [Architecture](https://coder.com/docs/admin/infrastructure/architecture). Also deleted the unreferenced `single-region-architecture.svg` copy. ## Follow-ups outside this PR - The `"ai governance add-on"` doc-state badge is defined in `coder/coder.com` (`src/utils/docs/state.ts`). After this merges, no manifest entry uses that key, so it becomes dead config and can be removed there. - `enterprise/coderd/license/license.go:564-572` still warns admins that "The AI Governance add-on is required to use AI Gateway." That backend string will contradict these docs once shipped. ## Verification - `pnpm run lint-docs`: 0 errors across 504 files - `make lint/emdash`: clean - Vale on the changed Markdown files: 0 errors; remaining warnings are pre-existing gerund headings on untouched lines - `docs/manifest.json` validated as JSON - Confirmed the deleted SVG had no references anywhere in the repo --- PR generated with Coder Agents on behalf of @mattvollmer. |
||
|
|
c17bed25e0 |
feat: wire chat lifecycle hooks into chatd (#27429)
Wires chat lifecycle hooks into chatd, gated by the `agent-lifecycle-hooks` experiment. Part of the lifecycle hooks stack (#27401, #27428, #27430). See `docs/admin/setup/chat-lifecycle-hooks.md` for the consumer-facing contract. ## Summary When a hook URL is configured, chatd dispatches `session_start`, `user_prompt_submit`, `pre_tool_use`, `post_tool_use`, `pre_compact`, `post_compact`, and `stop` events to the consumer and applies its responses. ## Design - **Stateless**: Coder stores no hook dispatch or decision state. Delivery is at least once; consumers deduplicate on stable payload identifiers (chat ID, event type, tool-use ID) and answer duplicates with the same decision. - **Admission-time prompt effects**: `user_prompt_submit` dispatches exactly once per submission (create, send, queue, edit, subagent spawn) and folds its effects into the stored prompt as typed message parts: original-or-overridden user parts, then model-only `hook-context`, then a user-visible `hook-notice`. Hook context is stripped from every client-facing conversion; hook notices are excluded from model prompts. The server rejects hook parts in client-submitted content. - **Tool gating**: `pre_tool_use` allow can override tool input; deny becomes a synthetic denied tool result, with any returned model context persisted as a model-only transcript row so it never reaches clients. The denial text identifies an external policy (the deployment's lifecycle hook) as the source and marks the decision as persistent, so the model explains the denial instead of retrying it or misreporting it as an infrastructure failure. - **Fail closed**: a dispatch failure rejects the triggering request or moves the chat to the error state in the same transaction as the affected step, so a runnable state is never published with unapproved content. - **Admission before persistence**: `pre_tool_use` is dispatched for the calls the model produced, before the assistant message is stored. See "Staged tool admission" below. - **Fresh dispatch per tool call**: every non-provider-executed tool call is decided by its own `pre_tool_use` dispatch; Coder never reuses an earlier decision on the consumer's behalf. Retries re-dispatch the same logical event. ## Structure All hook dispatch flows through one seam: entry points build a `chathooks.Chat` (chat identity) and a `chathooks.Message` (event details) and call `Trigger.Trigger`, the only component that talks to the dispatcher. The integration lives in the `coderd/x/chatd/chathooks` subpackage, split by responsibility: - `trigger.go`: the trigger seam; builds the wire envelope per event, normalizes deny into a typed error, and holds the package's single enabled-check. - `effects.go`: pure conversion of hook results into transcript rows and prompt parts. - `errors.go`: failure classification (dispatch error messages, denial mapping, tool-result dispatch-failure scanning). - `tooluse.go`: the tool-call gate (`pre_tool_use` preflight, `post_tool_use` payloads, applying admitted input to the step). Server-bound glue stays in `coderd/x/chatd/hook_server.go`: the chat-parking dispatch error handlers, the step-commit row insertion wrappers, and the dynamic post-tool-use state loader, which depends on chatd validation types. This PR adopts the `codersdk/x/agenthooks` and `coderd/x/agenthooks/dispatch` import paths introduced at the tip of #27401; intermediate commits still reference the pre-move paths and are not individually buildable. ## Staged tool admission `pre_tool_use` originally ran at tool execution time, which is after the assistant message carrying the tool call was already committed. An `input_override` therefore had to rewrite stored message content in place. @hugodutka pointed out that chatd treats message content as immutable, and that the rewrite was a shortcut rather than a requirement. It was also a correctness problem in its own right: the rewrite only updated the database, so the transcript could show one input while a different one had executed. The hook now runs before the step is persisted: ```text provider stream ends (tool calls complete, in memory) -> pre_tool_use dispatch per call -> ONE transaction: assistant row with admitted inputs, synthetic denials, hook rows -> execute ``` The step is inserted once, carrying the input the tool runs with. `UpdateChatMessageContentByID` and `Tx.UpdateMessageContent` are deleted from #27428, so message content stays immutable. Two consequences, both intentional: - **Clients converge rather than wait.** Tool-call parts still stream live, so a rewritten call briefly shows the model's proposed input before the committed message replaces it. The chat store already clears stream state when an assistant message arrives, so the stored input wins with no frontend change and no added latency before tool cards appear. - **A call already in history was already admitted.** Execution consumes the stored input instead of dispatching a second decision, which keeps one dispatch and one set of hook effects per call. A consumer policy change between admission and execution applies to later calls, not to calls already admitted. The per-chat debug endpoint still records the provider's original tool input. Its purpose is to report provider behavior, and it requires an explicit per-chat debug flag; the invariant here covers the transcript. ## Configuration Adds `chat-hook-url`, `chat-hook-secret`, `chat-hook-timeout`, and `chat-hook-enabled` deployment options with startup validation. The flags are hidden from `coder server --help` while the feature is experimental; the setup guide documents them. ## Tool input validation Built-in tool arguments reach a consumer as raw JSON with key spelling preserved, but the tools decode those bytes with Go, which matches struct fields case-insensitively and keeps the last match. A policy reading `path` could therefore authorize one value while the tool executed another, and a lone case variant such as `{"PATH":"/secret"}` was invisible to a policy checking for `path`. Coder now rejects a built-in tool call whose input repeats a key or spells a schema property with different capitalization, before the `pre_tool_use` dispatch, so a consumer is never asked to authorize bytes whose meaning depends on the reader. Rejected calls produce an error result the model can retry; unambiguous calls in the same batch still run. A consumer-authored `input_override` is rechecked after the dispatch and fails the turn closed, because the model cannot correct it. Dynamic and MCP inputs are excluded because the client and the workspace agent execute those calls rather than coderd. Two paths needed more than a schema check. Execution resolves a deprecated tool name to its canonical tool, so validation resolves aliases first. The `edit_files` decoder also reads `search` and `replace`, which its schema does not advertise, so those aliases are now matched exactly and their case variants ignored. A hook denial now returns a structured 403 carrying `kind: "hook_denied"`, mirroring the dispatch-failure response that already carries its own kind. Without it a client cannot tell a policy decision apart from a generic failure, and the chat UI titled a denial "Request failed". Adding a kind needs no migration: `ChatErrorKind` is persisted only inside the JSONB `chats.last_error` column, whose decoder accepts unknown kinds. The hook docs also correct the tool-input convergence window. A batch dispatches sequentially before the assistant row commits, so the original input stays visible for a span that scales with the number of tool calls in the step rather than a single hook timeout. > This PR was written by Mux, an AI coding agent, on Mike's behalf. |
||
|
|
fbac602456 |
feat!: add admin-controlled dynamic client registration toggle (#27316)
`POST /oauth2/register` (RFC 7591 Dynamic Client Registration) has exactly one gate today: `ExperimentOAuth2`, a static, process-lifetime flag that wraps the entire `/oauth2/*` route tree as an all-or-nothing switch. That flag is scheduled for removal at GA, which would leave DCR with zero admin control at all once it is gone. Add a persistent, DCR-specific `oauth2_dcr_enabled` deployment setting, independent of the experiment system, so admin control over DCR survives GA. `POST /oauth2/register` checks the flag and rejects new registrations with an RFC 7591-shaped `403` when disabled; discovery metadata (`GET /.well-known/oauth-authorization-server`) conditionally omits `registration_endpoint`. A new audited `GET`/`PUT /api/v2/oauth2-provider/settings` endpoint lets an owner toggle it live, no restart required. The setting defaults to disabled, matching the canonical design proposal; disabling only stops new self-registrations, clients that already registered continue to authorize and exchange tokens normally. Address issue described in [ENG-3056](https://linear.app/codercom/issue/ENG-3056/oauth2-dcr-admin-configurable-enabledisable). ## Where this sits in the request path ```mermaid sequenceDiagram autonumber participant A as Admin participant S as coderd participant DB as site_configs<br/>(oauth2_dcr_enabled) participant C as OAuth2/MCP Client Note over A,S: Admin toggles DCR (new) A->>S: PUT /api/v2/oauth2-provider/settings<br/>{dynamic_client_registration_enabled: false} S->>S: authorizeContext(ActionUpdate, ResourceDeploymentConfig) S->>DB: UPSERT oauth2_dcr_enabled = false S-->>A: 200 OK (audited) Note over C,S: Client discovery + registration afterward C->>S: GET /.well-known/oauth-authorization-server S->>DB: GetOAuth2DCREnabled (system ctx, every request, no cache) DB-->>S: false S-->>C: 200 metadata, registration_endpoint omitted C->>S: POST /oauth2/register S->>DB: GetOAuth2DCREnabled (system ctx, every request, no cache) DB-->>S: false S-->>C: 403 invalid_request,<br/>"Dynamic client registration is disabled" Note over C,S: A client that registered before the change is unaffected C->>S: GET /oauth2/authorize?client_id=... Note over S: no DCR-enabled check on this path S-->>C: 200 (proceeds normally) C->>S: PUT/DELETE /oauth2/clients/{client_id} (RFC 7592 self-management) Note over S: no DCR-enabled check on this path either S-->>C: 200 (proceeds normally) ``` ## Files changed: manual vs. generated Reviewers should focus on the **manual** files. The **generated** ones are `make gen` output that follows mechanically from the manual changes and don't need direct review. <details> <summary><b>Manual files (26)</b> — click to expand, grouped the same way as "Suggested review order" below</summary> **1. Database** | File | What changed | |---|---| | `coderd/database/queries/siteconfig.sql` | New `GetOAuth2DCREnabled`/`UpsertOAuth2DCREnabled` query pair on the existing generic `site_configs` table. No schema change. | | `coderd/database/dbauthz/dbauthz.go` | RBAC check (`rbac.ResourceDeploymentConfig`) on the two new query methods; extends the `subjectSystemOAuth2` system-actor role with read-only `ResourceDeploymentConfig` access, needed so the public discovery/registration endpoints can read the flag via `dbauthz.AsSystemOAuth2`. | | `coderd/database/dbauthz/dbauthz_test.go` | RBAC assertion coverage for `GetOAuth2DCREnabled`/`UpsertOAuth2DCREnabled` in the method-coverage test suite. | **2. Request gating (the actual feature)** | File | What changed | |---|---| | `coderd/oauth2provider/registration.go` | The actual gate: `CreateDynamicClientRegistration` reads the flag first and returns an RFC 7591-shaped `403` when disabled (defaults disabled if never configured). | | `coderd/oauth2provider/registration_test.go` | New unit test, `TestCreateDynamicClientRegistration_DCREnabled`: calls the handler directly (no HTTP server), covering enabled / explicitly disabled / never-configured. | | `coderd/oauth2provider/metadata.go` | `GetAuthorizationServerMetadata` conditionally omits `registration_endpoint` from discovery metadata when DCR is disabled. | | `coderd/oauth2provider/metadata_test.go` | New unit test, `TestGetAuthorizationServerMetadata_DCREnabled`: same three states, for the discovery handler. | **3. Admin settings endpoint** | File | What changed | |---|---| | `codersdk/oauth2.go` | New `OAuth2ProviderSettings` SDK type plus `Client.OAuth2ProviderSettings`/`PutOAuth2ProviderSettings` methods. | | `coderd/oauth2.go` | New `oauth2ProviderSettings`/`putOAuth2ProviderSettings` admin handlers (audited via `audit.InitRequest`); updates the `GetAuthorizationServerMetadata` call site to pass `api.Database`. | | `coderd/coderd.go` | Registers `GET`/`PUT /api/v2/oauth2-provider/settings`. | | `coderd/oauth2_provider_settings_test.go` | New test file: admin `GET`/`PUT` round-trip, default-disabled-before-any-`PUT`, and `403` for a non-owner on both `GET` and `PUT`. | **4. Audit wiring** | File | What changed | |---|---| | `coderd/database/types.go` | New `database.OAuth2ProviderSettings` audit-only struct (mirrors `NotificationsSettings`). | | `coderd/audit/diff.go` | Adds the new struct to the `Auditable` type union. | | `coderd/audit/request.go` | Adds the new struct to all four dispatch switches (`ResourceTarget`, `ResourceID`, `ResourceType`, `ResourceRequiresOrgID`). | | `codersdk/audit.go` | New API-facing `ResourceTypeOAuth2ProviderSettings` constant and its `FriendlyString` case. | | `enterprise/audit/table.go` | Field-level audit action map (`ActionTrack`/`ActionIgnore`) for the new struct. | | `coderd/database/migrations/000546_audit_oauth2_provider_settings.up.sql` | Adds `oauth2_provider_settings` to the `resource_type` Postgres enum, required for the audit wiring above (`resource_type` is a real enum, not a Go-only value). | | `coderd/database/migrations/000546_audit_oauth2_provider_settings.down.sql` | No-op (`ALTER TYPE ... ADD VALUE` can't be reverted). | **5. Test-suite ripple from the disabled-by-default flip** | File | What changed | |---|---| | `coderd/oauth2provider/oauth2providertest/helpers.go` | New shared test helper, `EnableDCR`, since DCR now defaults to disabled and many pre-existing tests need it turned on to register a client. | | `coderd/oauth2_test.go` | Adds `TestOAuth2DynamicClientRegistrationDisabled` (registers a client, disables DCR, verifies new registration is rejected while the existing client's self-management, authorize, and token exchange all keep working); calls `EnableDCR` in every pre-existing test that registers a client. | | `coderd/oauth2_error_compliance_test.go` | Calls `EnableDCR` in every test that registers a client, so RFC-error-format assertions aren't masked by the new disabled-by-default gate. | | `coderd/oauth2_metadata_validation_test.go` | Same: `EnableDCR` added to every registration-dependent test. | | `coderd/oauth2_security_test.go` | Same. | | `coderd/oauth2provider/validation_test.go` | Same (near-duplicate of `oauth2_metadata_validation_test.go` in a different package). | | `coderd/oauth2provider/provider_test.go` | Same. | | `coderd/mcp/mcp_e2e_test.go` | Same, for the MCP end-to-end dynamic-registration flow test. | </details> <details> <summary><b>Generated files (12)</b> — from <code>make gen</code>, no need to review directly</summary> `coderd/apidoc/docs.go`, `coderd/apidoc/swagger.json`, `coderd/database/dbmetrics/querymetrics.go`, `coderd/database/dbmock/dbmock.go`, `coderd/database/dump.sql`, `coderd/database/models.go`, `coderd/database/querier.go`, `coderd/database/queries.sql.go`, `docs/admin/security/audit-logs.md`, `docs/reference/api/enterprise.md`, `docs/reference/api/schemas.md`, `site/src/api/typesGenerated.ts`. </details> ## Suggested review order ### 1. Database Establishes the persisted setting and its RBAC rule; everything else builds on `GetOAuth2DCREnabled`/`UpsertOAuth2DCREnabled`. 1. `coderd/database/queries/siteconfig.sql` — the two new queries. Same boolean-encoding pattern as the existing `oauth2_github_default_eligible` key right above them in the same file. 2. `coderd/database/dbauthz/dbauthz.go` — the RBAC wrapper for those two queries, plus the `subjectSystemOAuth2` role extension (search this file for `ResourceDeploymentConfig`, it appears in both spots). 3. `coderd/database/dbauthz/dbauthz_test.go` — asserts the RBAC checks from (2) actually fire. ### 2. Request gating (the actual feature) Where `POST /oauth2/register` and discovery metadata change behavior. 1. `coderd/oauth2provider/registration.go` — the primary gate. Read this first; it's the feature. 2. `coderd/oauth2provider/registration_test.go` — its new unit test, exercising the gate's three states directly against the handler. 3. `coderd/oauth2provider/metadata.go` — the same gating pattern applied to the discovery `GET` endpoint. 4. `coderd/oauth2provider/metadata_test.go` — its new unit test. ### 3. Admin settings endpoint How an owner flips the setting live. 1. `codersdk/oauth2.go` — the `OAuth2ProviderSettings` SDK type and `Client` methods first; this is the public contract everything below implements against. 2. `coderd/oauth2.go` — the `GET`/`PUT` handlers themselves. 3. `coderd/coderd.go` — route registration, to see where those handlers get wired in. 4. `coderd/oauth2_provider_settings_test.go` — round-trip and permission tests. ### 4. Audit wiring Plumbing required so step 3's `PUT` is auditable; mechanical except for (3). 1. `coderd/database/types.go` — the audit-only struct; everything else in this layer exists to plumb it through. 2. `coderd/audit/diff.go` — adds it to the `Auditable` type union (the compiler enforces this one). 3. `coderd/audit/request.go` — the four dispatch switches; the one part of this layer worth reading closely. 4. `codersdk/audit.go` — the API-facing resource type constant. 5. `enterprise/audit/table.go` — the field-action map. 6. `coderd/database/migrations/000546_audit_oauth2_provider_settings.{up,down}.sql` — read last; a consequence of needing a new `resource_type` enum value for (1)-(5), not a design decision of its own. ### 5. Test-suite ripple from the disabled-by-default flip 1. `coderd/oauth2provider/oauth2providertest/helpers.go` — the new `EnableDCR` helper. Read first to understand the fix pattern before seeing it applied repeatedly. 2. `coderd/oauth2_test.go` — next, since it also contains the new `TestOAuth2DynamicClientRegistrationDisabled`, not just `EnableDCR` call sites. 3. The rest, in any order, they're mechanical repeats of the same one-line addition: `coderd/oauth2_error_compliance_test.go`, `coderd/oauth2_metadata_validation_test.go`, `coderd/oauth2_security_test.go`, `coderd/oauth2provider/validation_test.go`, `coderd/oauth2provider/provider_test.go`, `coderd/mcp/mcp_e2e_test.go`. ## Explicitly out of scope Per the design proposal: rate limiting on `POST /oauth2/register` (tracked separately), retroactively affecting already-registered clients when DCR is disabled (this only gates new self-registration), and an Initial Access Token requirement (a separate, follow-up ticket). |
||
|
|
1a6a8be96c |
feat: log tailnet tunnels to the connection log (#27423)
Co-authored-by: Chris DiGiamo <cd@anthropic.com> Co-authored-by: Chris DiGiamo <cdigiamo@anthropic.com> |
||
|
|
09a69e624a |
feat: search users by display name (#27398)
Free-text member search previously matched only username and email, so typing a person's display name returned no results even though the UI shows the display name as the primary label. This broadens the free-text `@search` filter to also match `users.name`. The change is in three queries: `GetUsers`, `PaginatedOrganizationMembers`, and `GetGroupMembersByGroupIDPaginated`. This covers every server-filtered surface: the Users page, the Organization Members page, the Group Members page, and the `UserAutocomplete` / `WorkspaceUserAutocomplete` pickers (which query `GetUsers` with `q`). The org member picker (`MemberAutocomplete`) filters client-side via cmdk, so display name is added to its `keywords`. Explicit filters (`name:`, `username`/`email`) and pagination counts are unchanged; the group members count still comes from the filtered `COUNT(*) OVER()` in the same query. Refs DEVEX-484 Refs DEVEX-565 <details> <summary>Implementation plan</summary> ## Problem Member search (both the global Users page and the Organization Members page) matches only on `username` and `email`. It does not match on the user's display name (`users.name`), even though the Organization Members table shows `name` as the primary title. So typing a person's full name in the search box returns nothing. Today a bare search term (`alice`) is routed to the SQL `@search` filter, which only checks `email`/`username`. Display name is only matched if the user explicitly types `name:alice`, which is undiscoverable. ## Design decision Include `name` in the free-text `@search` condition in the affected SQL queries. A bare term then matches `email OR username OR name`, using the same case-insensitive substring `ILIKE` already in place. This keeps the existing explicit `name:` filter working. Tradeoff: this broadens the meaning of free-text `search` globally (anything using these queries now also matches display name). This is the intended behavior, confirmed against DEVEX-565 (display name search in the user picker). ## Affected files Backend: - `coderd/database/queries/users.sql` (`GetUsers`) - `coderd/database/queries/organizationmembers.sql` (`PaginatedOrganizationMembers`) - `coderd/database/queries/groupmembers.sql` (`GetGroupMembersByGroupIDPaginated`) - `coderd/database/queries.sql.go` regenerated via `make gen` Frontend: - `site/src/components/UserAutocomplete/UserAutocomplete.tsx` (add `name` to client-side cmdk keywords) Tests: - `coderd/coderdtest/users.go` (shared `UsersFilter` helper): added a `DisplayNameSearch` case and extended search-based expectations to include `name`. Exercised by `TestGetUsersFilter`, `TestGetOrgMembersFilter`, and `TestGetGroupMembersFilter`. Docs: - `docs/admin/users/index.md`: documented that free-text search matches username, email, and display name. ## Frontend surface coverage | Surface | Sends | Backend | Query | |---|---|---|---| | Users page | `q` | `GET /users` | `GetUsers` | | Organization Members page | `q` | paginated members | `PaginatedOrganizationMembers` | | Group Members page | `q` | `groupMembers` | `GetGroupMembersByGroupIDPaginated` | | User pickers (server-filtered) | `q` | `GET /users` | `GetUsers` | | Org member picker (client-filtered) | local cmdk | n/a | keyword change | ## Out of scope - Trigram/similarity (fuzzy) matching; keeps `ILIKE` substring semantics. - Sort/pagination ordering (still `LOWER(username)`). </details> --- _Created by Coder Agents on behalf of @aqandrew._ |
||
|
|
85984ff142 |
feat: add enable/disable support for user secrets (#27537)
Users can now disable a secret to stop it from being injected into workspaces without deleting it, and re-enable it later. Disabled secrets stay visible and editable everywhere they already appear. An enabled secret must have at least one injection target; a secret with no target can be stored only while disabled. Existing target-less secrets are migrated to disabled to preserve current behavior. Support spans the REST API, SDK, CLI, dashboard, and audit log. |
||
|
|
8ea2586189 |
feat: add chat lifecycle hook dispatch backend (#27401)
Adds the chat lifecycle hook wire contract and dispatch plumbing, first PR of the lifecycle hooks stack (followed by #27428, #27429, #27430). - `codersdk/x/agenthooks`: event and response wire types, JWT creation and verification with the shared secret (HS256, request body digest, expiry and not-before freshness checks), and an HTTP handler helper so consumers only implement the events they use. The `codersdk/x` location marks the consumer SDK as experimental. - `coderd/x/agenthooks/dispatch`: a stateless dispatcher that signs and posts hook events, enforces a concurrency cap under one configured timeout that bounds both the capacity wait and both post attempts, retries one connection failure with the same JWT, sends a distinctive `coderd-agenthooks/<version>` User-Agent, and records Prometheus metrics. Delivery is at least once; consumers own durable decision state, audit records, and deduplication keyed by the stable payload identifiers. Nothing is persisted by Coder. - Response bodies decode strictly: unknown fields, duplicate JSON keys (including inside `input_override`), and trailing data fail the dispatch closed as protocol errors instead of silently reading as allow. - `coderd/util/xnet`: shared timeout and connection error classification used by the dispatcher retry logic. Transient HTTP/2 stream aborts count as connection errors, so the documented single retry also applies to h2 consumers, which is the shape Go's default transport negotiates against any TLS consumer. Deterministic protocol failures stay terminal. Only the struct form of a stream error is matched, because `net/http` bundles its own HTTP/2 types and `h2_error.go` bridges only that shape. - `scripts/agenthooks-server`: a reference consumer that logs events and demonstrates consumer-owned pre-tool decision deduplication. It requires an explicitly configured JWT audience rather than deriving one from the request, and its startup output names the mode it is running in so an operator can see that the example policy flags need `-log-only=false`. - `scripts/apitypings`: generate TypeScript types for the hook wire contract. Dispatch failures log without the error's stack frames, since a failed dispatch is an expected, operator-visible condition. Nothing dispatches these events yet; chatd wiring lands in #27429. > This PR was written by Mux, an AI coding agent, on Mike's behalf. |
||
|
|
c351280a37 |
feat: add Prometheus metrics for AI Governance cost control (#27490)
## Description Adds Prometheus metrics for AI budget cost control, emitted by the aibridged server under the `cost_control` subsystem (full names are prefixed `coder_ai_gateway_`). - `blocked_requests_total` (counter) — labels: `group_id` - `blocked_users` (gauge) — labels: `group_id` - `unpriced_requests_total` (counter) — labels: `provider`, `model` - `enforcement_duration_seconds` (histogram) — labels: `outcome` ## Changes - Add `GetOverBudgetUsersPerGroup` query (plus dbauthz/dbmetrics/dbmock wiring) to count over-budget users per effective group. - Add a background collector that refreshes the `blocked_users` gauge on an interval, started only when Prometheus is enabled. - Wire `Metrics` through the aibridged server, coderd API, `cli/server.go`, and the enterprise AI gateway handler; recording is nil-safe when metrics are unset. Closes https://linear.app/codercom/issue/AIGOV-296/add-prometheus-metrics-for-cost-control > [!NOTE] > Initially generated by Claude Opus 4.7, modified and reviewed by @ssncferreira |
||
|
|
00d134ebfd | chore: remove classic parameter UI (#25014) | ||
|
|
025ded0536 | docs: remove beta labels from user secrets (#27510) | ||
|
|
92d45a0411 |
docs: document SCIM 2.0 handler opt-in and legacy flag (#27469)
Documents the SCIM 2.0 handler introduced in #25572 and how to opt in. Adds a "SCIM 2.0 handler" subsection to the SCIM section of `docs/admin/users/oidc-auth/index.md`: - The handler follows RFC 7644 and supports user provisioning/deprovisioning and user listing. - Opt in with `CODER_SCIM_USE_LEGACY=false` (also `--scim-use-legacy` / `scimUseLegacy`); requires a server restart. - Behavior notes: delete/deactivate suspends (never hard-deletes), reactivation goes through dormant, usernames are immutable. - Notes it will eventually become the default behavior. Behavior details were verified against `enterprise/coderd/scimroutes.go`, `enterprise/coderd/scim/`, and the `SCIM Use Legacy` option in `codersdk/deployment.go`. `make lint/markdown` and `make lint/emdash` pass. --- Generated by Coder Agents on behalf of @Emyrk. --------- Co-authored-by: Nick Vigilante <nickvigilante@users.noreply.github.com> |
||
|
|
0f1eafa17e |
docs(docs/admin): document wildcard hostname suffixes (#27482)
Documents wildcard hostname suffixes such as `*-apps.example.com`, which the existing application hostname parser and Helm chart already support. Explains the generated application hostname and the DNS and TLS wildcard required for each supported form. Also adds the suffix form to the installation summary. Validated with the repository's documentation linters and pre-commit hook, the hostname-pattern unit test, and an end-to-end workspace application on Coder v2.35.2. |
||
|
|
3c7a1d33e3 |
feat: add persisted whole-chat summary with background generation (#26657)
Adds a persisted whole-chat summary that backs the chat summary popover. A new nullable `chats.summary` column is populated in the background after a successful root-chat turn and pushed to clients via a new `chat_summary_change` watch event (distinct from `summary_change`, which is bound to `last_turn_summary`), so the popover reads `chat.summary` straight off the loaded `Chat` with no extra query. This is the data source for the popover and per-chat cost UI built in #26649; the popover can consume `chat.summary` once this lands (the field is nullable, so merge order does not matter). ## How it works - **Generation** runs in the existing successful-turn finalize hook, detached from the request so the user's turn is never blocked. A cadence gate generates the first summary after one completed turn, then regenerates every three turns, using the `chats.summary_generated_at` freshness marker. Generation reads compaction-aware history, renders it to a bounded plain-text transcript (short transcripts are skipped), and asks for a 1-3 sentence summary via structured output. Failures never clear an existing summary. - **Staleness** is guarded by `history_version` (mirroring `last_turn_summary`), so a background write racing a newer turn loses while worker lifecycle transitions cannot reject a fresh write. - **Model selection** uses the chat's configured model. ## Deferred to follow-ups - **Cost accounting**: the `chat_messages.cost_source` discriminator and summary/title usage recording were removed from this PR so summary persistence is not blocked by hidden accounting rows advancing `history_version`. Title usage recording stays on main's `InsertChatMessages` path. - **Model override**: deployment-wide summary generation model selection is split into #26803; the base feature always uses the chat model. ## Notes - Migration `000540` adds `chats.summary` and `chats.summary_generated_at`, and recreates `chats_expanded` to expose the new columns. - Root chats only; shared viewers pick up the summary on their next refetch (live watch events are owner-only). Refs #26649 --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
f5e0c1a860 |
fix: correct invalid inline HTML in hand-written docs (#27298)
## What
Fixes three classes of invalid inline HTML in hand-written docs, all of
which
render incorrectly (or only render by accident) today. Found via a
systematic,
markdown-aware audit of every `.md` under `docs/` (ignores code blocks,
inline
code, comments, and autolinks), so this is a complete sweep of the
hand-written
surface, not a spot fix.
## Changes
1. **`<kdb>` → `<kbd>` (72 tags).** The keyboard element is `<kbd>`;
`<kdb>` is
a typo that is not a real element, so renderers drop/mangle it and the
keystrokes lose their styling. Corrected across the IDE access guides
(`cursor.md`, `windsurf.md`, `antigravity.md`). The correct `<kbd>` is
already used in the JetBrains Gateway guide.
2. **Unclosed `<div class="tabs">` in `docs/admin/users/idp-sync.md`.**
The
"Provider-Specific Guides" section opened a `.tabs` container (rendered
as
the `DocsTabs` component) that was never closed, so the wrapper leaked
over
the rest of the page. Added the missing `</div>` before `## Next Steps`,
matching the three other tab sections in the same file.
3. **`<Image>` → `<img>` (6 tags).** `<Image>` is not a registered docs
component — it renders only because the HTML5 parser rewrites the legacy
`<image>` tag to `<img>`. Converted to lowercase `<img>` for correctness
and
clarity; rendering is unchanged. (`organizations.md`, `idp-sync.md`,
`add-envbuilder.md`.)
## Scope / what is intentionally not here
- **Generated reference docs.** The audit also found swallowed
placeholders in
generated pages (`<server>` in `reference/api/{chats,schemas}.md`;
`<glob>`/`<host>` in `agent-firewall`; `<region>` in `server`). Those
are
fixed at the generator source (codersdk comments / CLI flag help) and
tracked
in DOCS-551.
- **`<b>Resource<b>`** in the generated audit-logs table was fixed
separately in
#27293 (merged) and is not duplicated here.
- **`<children></children>`** is an intentional, renderer-implemented
docs
component (child-page card grid) with no HTML equivalent, so it is left
as-is.
It is well-formed; a follow-up CI checker will still verify its
open/close
balance.
A follow-up adds CI enforcement so invalid inline HTML can't regress.
<details>
<summary>Verification</summary>
Run against the changed files:
- `markdownlint-cli2` — 0 errors
- `markdown-table-formatter --check` — no changes needed
- `typos --config .github/workflows/typos.toml` — clean
- Re-running the audit scanner: hand-written `unclosed`, `<kdb>`, and
capitalized-component findings all drop to 0 (only the generated-doc
placeholders tracked in DOCS-551 remain).
</details>
## Linear
DOCS-581:
https://linear.app/codercom/issue/DOCS-581/audit-and-fix-all-invalid-html-across-the-docs
> This PR was created with AI assistance (Coder Agents).
|
||
|
|
2b2a5c963a | Revert "fix(coderd): explain default GitHub app org visibility on login rejection" (#27388) | ||
|
|
48e9bb3391 |
fix(coderd): explain default GitHub app org visibility on login rejection (#27374)
## Problem On a fresh deployment with no custom GitHub OAuth app, Coder falls back to the default Coder-managed GitHub app. That app can only see organization memberships in organizations where it has been installed. If `CODER_OAUTH2_GITHUB_ALLOWED_ORGS` is set but the app isn't installed in the allowed organizations, the membership list comes back empty and every login, including the first admin login, is rejected with a bare "You aren't a member of the authorized Github organizations!" with no hint about the actual cause. This leaves fresh deployments in an apparently broken state. ## Fix * Append a remediation hint to the login rejection when the default provider is configured, pointing at the [app installation page](<https://github.com/apps/coder/installations/select_target>) and at configuring a custom GitHub OAuth app. * Log a startup warning when the default provider is combined with `CODER_OAUTH2_GITHUB_ALLOWED_ORGS`, listing the allowed orgs and the install URL. * Document the installation requirement next to the `CODER_OAUTH2_GITHUB_ALLOWED_ORGS` step in the GitHub auth docs. Access-control behavior is unchanged; the org check still rejects logins as before, it just explains why and how to fix it. ## Testing * New `TestUserOAuth2Github/NotInAllowedOrganizationDefaultProvider` asserts the hint appears when `DefaultProviderConfigured` is set; the existing `NotInAllowedOrganization` subtest asserts it does not leak into the custom-app path. Fixes coder/coder#17752 |
||
|
|
3227cac217 |
feat: add manual chat compaction via /compact (#27081)
Adds a user-triggered `/compact` action for Coder Agents chats: typing
`/compact` in the composer (or picking it from the `/` trigger menu)
summarizes the conversation so far to free up context window space.
## How it works
- New `POST /api/experimental/chats/{chat}/compact` endpoint
(owner-only, RBAC `ActionUpdate`, excluded from the public API reference
via `x-apidocgen skip`). It marks the chat with a durable one-shot
`chats.compaction_requested_at` signal and moves it `waiting -> running`
via a new `RequestCompaction` state transition; no message row is
inserted. AI Gateway attribution needs no per-request key: generation
preparation resolves the owner's synthetic API key (#27170) like any
other turn.
- `RequestCompaction` hands off chat ownership (clears
`worker_id`/`runner_id`) so a worker acquisition hint is published;
since the transition changes no history, the previous runner could
otherwise miss the request under reordered pubsub delivery.
- The background chat worker picks the chat up like any other turn. A
pending manual request takes precedence over turn completion in the
generation decision, and forces compaction even below the automatic
threshold (and when compaction is disabled via threshold=100). The
commit step consumes the request marker in the same transaction; any
transition that ends the turn clears stale markers.
- The summary triplet reuses the automatic-compaction path, now tagged
with a `source` (`automatic` | `manual`) that is plumbed through
streamed progress parts, persisted tool JSON, and the UI label
("Summarized (manual)").
- Validation order: busy chats reject with 409 (state-machine conflict),
empty/already-compacted chats with 409 "nothing to compact", archived
chats with 400; the owner usage-limit check runs last so no-op requests
surface the specific conflict instead of a limit error.
- Web UI: the `/` trigger menu now has a built-in "Commands" group
listing `/compact`; submit intercepts exactly `/compact` and calls the
endpoint instead of sending a message. A personal or workspace skill
named `compact` takes precedence over the built-in command; while skill
collisions are still resolving, an exact `/compact` submission is
blocked with a retryable hint instead of leaking as message text.
History and queued-message edits are never intercepted. After
compaction, the context usage indicator resets to its unknown state
until the next assistant response reports fresh usage, instead of
showing the stale pre-compaction number.
- codersdk: `ExperimentalClient.CompactChat`.
Worker-path execution (rather than compacting synchronously in the
handler) reuses the existing lock fencing, live "Summarizing..."
streaming, retry accounting, restart resilience, and debug-run
observability. Rationale documented in `coderd/x/chatd/ARCHITECTURE.md`.
## Testing
- State machine: transition-matrix coverage for `RequestCompaction`,
marker lifecycle tests (carried by lease renewals/queue appends, cleared
by terminal transitions, consumed by commit), ownership handoff +
acquisition hint assertions.
- Worker: decision-ordering and forced-compaction unit tests;
active-server end-to-end test (manual compact below threshold produces a
`source=manual` summary, returns to `waiting`, no assistant follow-up;
busy chat rejected).
- API: success, archived, non-owner, RBAC-denied, empty-chat, no-daemon
cases; usage-limit ordering (at-limit owners still get
state/nothing-to-compact conflicts for no-op requests, with marker
rollback).
- Frontend: Storybook play tests for the Commands menu group, submit
intercept, skill-name collision, queued-edit passthrough, and
manual/automatic tool rendering; unit tests for command availability
resolution and the post-compaction context usage reset.
> This PR was created by Mux, an AI coding agent, working on Mike's
behalf.
|
||
|
|
77582be805 |
fix: close <b> tag in generated audit log table header (#27293)
## What The audit log resource table header in `docs/admin/security/audit-logs.md` was emitted as `<b>Resource<b>`: a second opening `<b>` instead of a closing `</b>`. Because the bold element never closes, Markdown/HTML renderers can bold content well beyond the header cell. The page is generated (`<!-- Code generated by 'make docs/admin/security/audit-logs.md'. DO NOT EDIT -->`), so the fix belongs in the generator, `scripts/auditdocgen/main.go`, with the doc regenerated from it. ## Changes - `scripts/auditdocgen/main.go`: emit a closing `</b>` instead of a second `<b>` in the table header row. - `docs/admin/security/audit-logs.md`: regenerated with `make docs/admin/security/audit-logs.md`; only the header cell changes. ## Verification <details> <summary>Regenerated doc and local checks</summary> Header cell before (unclosed tag): ```text | <b>Resource<b> | ... ``` Header cell after (balanced tag): ```text | <b>Resource</b> | ... ``` - `make docs/admin/security/audit-logs.md` regenerates the page from the fixed generator and changes only the header cell (single line; the table stays aligned). - Local `make pre-commit` passed with `GEN_SKIP_GOLDEN=1` (this workspace has no Docker daemon for the golden-file gen step, which this change does not touch): `gen`, `fmt`, `lint/go`, `lint/ts`, `lint/markdown`, `lint/typos`, `lint/emdash`, and the slim binary build all green. </details> ## Linear DOCS-580: https://linear.app/codercom/issue/DOCS-580/fix-unclosed-b-tag-in-generated-audit-logs-table-header --- This PR was created using AI (Coder Agents) on behalf of @nickvigilante, who is accountable for its contents. See the [AI Contribution guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING). |
||
|
|
f7481c5d08 |
feat: Add full text search over chat messages (#27126)
Closes CODAGT-721 Closes CODAGT-722 Closes CODAGT-723 Closes CODAGT-724 Closes CODAGT-725 This PR adds the database and API pieces necessary to support full-text chat message search. - Adds required chat schema for full-text search - Adds dbpurge job to populate search_tsv in the background - Adds `search` parameter to GetChats query - Adds `search` filter to `searchquery.Chats` - Wires chat search filter into chats API > Implemented by Coder Agents, reviewed and tested by a human. |
||
|
|
c84aa564ba |
docs: normalize code-fence languages for Shiki compatibility (#27161)
Normalizes non-standard code-fence language tags across `docs/**` so a strict highlighter (Shiki, used by Fumadocs) won't fail the build on an unrecognized language, and unifies redundant synonym tags onto one canonical form per language. The current renderer (Speed-Highlight) detects the language from the code content, not the fence label, so this drift wasn't visible until now. ## Changes - `hcl` -> `tf` (199 fences, including indented ones nested in numbered/bulleted lists). Shiki ships `hcl` and `terraform` as two distinct grammars (not aliases); every `hcl`-tagged fence in `docs/**` is actually Terraform resource/data/provider syntax, so the more specific `terraform` grammar is correct for all of them. `tf` is Shiki's own alias for that grammar, and it's also what GitHub's own markdown renderer resolves to the same HCL/Terraform highlighting. - `pwsh`/`powershell` -> `ps1`. Both `ps` and `ps1` are registered PowerShell aliases in Shiki, but on GitHub's renderer only `.ps1` is a registered file extension (`.ps` isn't), so `ps1` renders identically to `powershell` there today while bare `ps` would silently lose highlighting. - `env` -> `dotenv` (a dedicated Shiki grammar for `KEY=VALUE` files) - `text`/`output`/`none`/`url` -> `txt`. Same built-in plain-text fallback either way, just shorter. - `Dockerfile` -> `dockerfile` (lowercase) - `bash`/`shell` -> `sh` (732 fences). Shiki and GitHub both alias all three to a single shell grammar; this was already the style guide's stated preference, just not enforced across the existing corpus until now. - `markdown` -> `md` (4 fences). Alias of the same grammar in both Shiki and GitHub. - `jsonc` -> `json` (1 fence). The block has no comments or trailing commas, so it doesn't need the comments-capable grammar. - `ts` -> `tsx` (2 fences, `docs/about/contributing/frontend.md`). Verified the actual content tokenizes identically under both grammars, and a sibling block in the same file already needs `tsx` for real JSX, so unifying to one tag is safe for this file. Documented a caveat: `tsx` mis-tokenizes the legacy angle-bracket type-assertion syntax (`<Type>value`), which is invalid in real `.tsx` files anyway, so use `value as Type` instead. - `yml` -> `yaml` (1 fence) - Updated `docs/.style/style-guide/formatting.md` to document all canonical tags `promql` (2 fences) and `caddyfile` (2 fences) are left as-is. Shiki doesn't bundle a grammar for either, so they need a custom grammar registration when the site adopts Shiki, rather than degrading to `txt`. Tracked as follow-up work under DOCS-118 and [DOCS-544](https://linear.app/codercom/issue/DOCS-544/vendor-a-local-promql-grammar-for-shiki-syntax-highlighting) (promql). Does not touch `offlinedocs/`. Linear: [DOCS-476](https://linear.app/codercom/issue/DOCS-476/normalize-docs-code-fence-languages-de-risk-shikifumadocs) <details> <summary>How the fence tags were verified</summary> Each tag was tested against a real `shiki@latest` highlighter instance (`codeToHtml`/`codeToTokens`) and cross-checked against GitHub's `@wooorm/starry-night` grammar sources (the renderer that actually displays these `.md` files today, in repo browsing and PR diffs), since that's what determines whether brevity is safe before Shiki adoption: ```text FAIL env -- Language `env` is not included in this bundle. FAIL Dockerfile -- Language `Dockerfile` is not included in this bundle. FAIL promql -- Language `promql` is not included in this bundle. FAIL caddyfile -- Language `caddyfile` is not included in this bundle. FAIL pwsh -- Language `pwsh` is not included in this bundle. FAIL output -- Language `output` is not included in this bundle. ``` `hcl` doesn't error in Shiki, since it's a real grammar, but that's exactly the trap: it was silently rendering every fence with the generic HCL grammar instead of the Terraform-specific one. Every `hcl`-tagged fence in `docs/**` was manually checked against `origin/main` and is genuinely Terraform content. For `ts`/`tsx`, tokenizing the actual doc content confirmed identical output under both grammars; a synthetic test with the legacy angle-bracket cast syntax confirmed `tsx` degrades on that specific construct, which the style guide now calls out. The first normalization pass only matched fence tags at column 0 (`^```tag$`), missing tags indented inside numbered/bulleted lists. A follow-up pass caught the remaining occurrences at any indentation level. </details> --- *This PR description and the underlying changes were prepared with Coder Agents assistance.* |
||
|
|
199b5936c1 |
fix(docs): replace invalid </br> tags and format swallowed placeholder URL (#27174)
## Summary Fixes two classes of invalid/broken HTML in hand-written docs. Both are visible problems in today's rendered docs, independent of any docs-engine work. 1. **`</br>` is not a real HTML tag.** `br` is a void element with no closing form; browsers error-correct `</br>`, but it is invalid HTML. Replaced all 15 usages with `<br />` across: - `docs/admin/templates/extending-templates/dynamic-parameters.md` - `docs/admin/users/idp-sync.md` - `docs/tutorials/best-practices/organizations.md` 2. **Browser-swallowed placeholder URL.** In `docs/ai-coder/github-to-tasks.md`, `https://<your-coder-url>/settings/external-auth` was unformatted, so HTML renderers parse `<your-coder-url>` as an unknown tag and drop it. The live docs currently render the broken text `re-authenticate at https:///settings/external-auth`. Wrapped in backticks, matching every other instance in the same file. Table realignment noise in the diff is from `fmt/markdown` (`<br />` is one character wider than `</br>`). A repo-wide grep confirms no remaining `</br>` and no other unformatted `https://<placeholder>` URLs in prose (other hits are inside code fences or already backticked). The equivalent placeholder issues in **generated** reference docs (CLI help strings, swagger annotations) are intentionally out of scope and tracked separately in [DOCS-551](https://linear.app/codercom/issue/DOCS-551/backtick-placeholder-syntax-in-generated-reference-docs-cli-help). Tracking issue: [DOCS-550](https://linear.app/codercom/issue/DOCS-550/fix-invalid-br-tags-and-browser-swallowed-placeholder-url-in-hand) --- Created by Coder Agents on behalf of @nickvigilante. |
||
|
|
d66e4d794f | feat: add configurable reasoning effort to Coder agents (#26974) | ||
|
|
f7632451f4 |
feat(docs): add Coder.BrandNames Vale rule, enforce HashiCorp casing (#25501)
Lands the first concrete rule under the `Coder` style: `Coder.BrandNames`, a bundled `substitution` rule that enforces canonical brand casing in prose. HashiCorp is the first entry; [DOCS-188](https://linear.app/codercom/issue/DOCS-188) extends it with GitHub, OpenTofu, Kubernetes, Terraform, JetBrains, and VS Code. ## What changes Four commits, ordered so each is independently valid: 1. **`docs: fix HashiCorp casing in prose and sidebar`** ([06d769dad1](https://github.com/coder/coder/pull/25501/commits/06d769dad179cf85c535b058df3b6bafdc1f9565)). 5 Markdown files plus 2 `docs/manifest.json` entries. Drives the corpus violation count to zero. 2. **`feat(docs/.style/styles/Coder): add Coder.BrandNames Vale rule`** ([e00fc780a7](https://github.com/coder/coder/pull/25501/commits/e00fc780a7a20dcf82105d997af5cfcddd4b1855)). New `BrandNames.yml` with the HashiCorp swap at `level: error`, plus a new `### Brand names` subsection in `docs/.style/style-guide.md`. 3. **`docs(.style/styles/Coder/README.md): scrub planned-rules notes obsoleted by Coder.BrandNames`** ([af8833b9f5](https://github.com/coder/coder/pull/25501/commits/af8833b9f58dd617732ae533bbf53eb4fc2e816a)). Removes the README's "intentionally empty for now" lead-in and the obsolete HashiCorp casing bullet from the planned-coverage list. 4. **`docs: apply semantic line breaks and fix Vale findings on PR-touched files`** ([e9f11df188](https://github.com/coder/coder/pull/25501/commits/e9f11df1886fdf0d5efc5e8a2cab95fecbd898f9)). Pre-review pass on every Markdown file this PR modifies. Full sembr and Vale-warning cleanup on the style-guide infrastructure (`style-guide.md`, `Coder/README.md`); sembr applied to the HashiCorp swap paragraph only on the five product docs, per scoping discussion with @nickvigilante. ## Severity rationale `error` from day one. HashiCorp's brand owner publishes a canonical casing; any other casing in prose is wrong, not a judgment call. Matches the `error = low FPs x high gravity` framework. False-positive rate is effectively zero because Vale's `substitution` rule skips inline code, fenced code blocks, and URLs by default, so `hashicorp/kubernetes` (Terraform provider source) and `developer.hashicorp.com` stay untouched. ## Verification - `make lint/markdown`: 0 errors across 487 files. - `make lint/prose`: 1 error, 1 warning, 1 suggestion in 468 files. All three findings are the intentional `Coder.DemoError`, `Coder.DemoWarning`, and `Coder.DemoSuggestion` annotations on `docs/.style/style-guide/demo/demo.md` (added on main as part of the [DOCS-425](https://linear.app/codercom/issue/DOCS-425) inline-annotation demo), not real findings. `Coder.BrandNames` fires zero times against the cleaned-up corpus. - `make pre-commit-light`: passed (7s). - Self-test: ran the rule against an unmodified `docs/` and confirmed it flags the 7 prose instances the cleanup commit fixes, then re-ran against the post-cleanup state and confirmed zero alerts. ## Known future conflict When [#26632](https://github.com/coder/coder/pull/26632) ([DOCS-434](https://linear.app/codercom/issue/DOCS-434)) merges, the monolithic `docs/.style/style-guide.md` is split into the `docs/.style/style-guide/` multi-page structure. The `### Brand names` subsection added in commit 2 will need to land in `docs/.style/style-guide/word-choice.md` (which already references the rule), and the `link:` in `docs/.style/styles/Coder/BrandNames.yml` will need to update from `style-guide.md#brand-names` to `style-guide/word-choice.md#brand-names`. Resolution path documented in an inline comment on this PR. <details> <summary>Implementation plan and decision log</summary> ### Why bundle into Coder.BrandNames rather than one file per brand Vale's convention (mirrored by `Google.WordList` with ~70 swaps in a single file) is to bundle `substitution` rules when they share severity, message template, and link. All brand-name rules share that shape: `error`, `Use '%s' instead of '%s'`, link to the style guide section. Bundling reduces "add a brand" to a one-line YAML diff and keeps `CODEOWNERS` and blame coherent. Per-rule performance is irrelevant at this scale; Vale's per-rule overhead is sub-millisecond and dwarfed by Markdown parsing. ### Why the cleanup lands first Commits are ordered cleanup-then-rule so each commit is a known-good state: - After commit 1: corpus is HashiCorp-clean, but no rule exists yet. - After commit 2: rule exists and lints a clean corpus. Reversing the order would land the rule at commit 1 (firing 7 errors on uncleaned content) and resolve them at commit 2. Under `--no-exit` the CI job still passes, but the inline annotations on commit 1 would be misleading. ### Why HashiCorp first instead of all brands at once Proof-of-concept value. HashiCorp is the smallest cleanup (7 prose lines plus 2 sidebar lines = 9 lines), zero FPs, zero ambiguity. Once the loop (rule plus cleanup plus style-guide section) is proven, [DOCS-188](https://linear.app/codercom/issue/DOCS-188) appends the other brands as additional commits to the same bundle. ### Brand-token sensitivity The `swap:` table only matches: - `Hashicorp` (capital H, lowercase rest), the actual wrong form in the corpus. - `HASHICORP` (all caps), defensive; doesn't appear in current corpus but cheap to include. `hashicorp` (all lowercase) is **not** in the swap table. The lowercase form appears 49 times in URLs (`developer.hashicorp.com`, `registry.terraform.io/providers/hashicorp/...`, `github.com/hashicorp/...`) and 6 times as Terraform provider sources (`source = "hashicorp/kubernetes"`), all of which are correct lowercase by convention. Vale's substitution rule scope ensures URLs and code blocks are skipped, but skipping the rule entirely for `hashicorp` (lowercase) is the explicit decision; if a prose typo of lowercase "hashicorp" ever shows up, we'd catch it through `Vale.Spelling` ([DOCS-187](https://linear.app/codercom/issue/DOCS-187)) instead. ### Self-reference in the style guide The `### Brand names` section's example table needed `Hashicorp` and `HashiCorp` as literal demonstration tokens. Wrapping them in backticks (`` `Hashicorp` ``, `` `HashiCorp` ``) keeps Vale from flagging the wrong-case example as a real violation. This is correct typography too: demonstration tokens get code formatting. ### Manifest.json Vale doesn't lint JSON, so the two `docs/manifest.json` entries are fixed by direct edit rather than tool enforcement. The sidebar `path` (`./admin/integrations/vault.md`) is unchanged; the title change does not affect the page URL on coder.com. No redirect needed in `coder/coder.com:redirects.json`. ### Pre-mortem - **Generated docs noise**: `Coder.BrandNames` does not fire on auto-generated `docs/reference/` content because no codersdk identifier matches the swap pattern. Zero risk. - **Future-additions friction**: adding GitHub to the swap table is one YAML line and a cleanup commit. The bundling shape pays off here. - **Disable footgun**: if a contributor needs to write the wrong casing on purpose (quoting an external bug report verbatim, for example), they can wrap the literal in backticks (already correct typography) or use the per-line Vale skip comment. </details> Closes [DOCS-34](https://linear.app/codercom/issue/DOCS-34). --- *Filed via [Coder Agents](https://coder.com/docs/ai-coder/agents) on Nick's behalf.* |
||
|
|
83acdaebd1 |
docs: add DOCKER_HOST guidance for non-default Docker socket paths (#26807)
## What Add `DOCKER_HOST` guidance for non-default Docker socket paths to two pages: - `docs/install/docker.md`: expands the **Cannot connect to the Docker daemon** troubleshooting section with the `DOCKER_HOST` fix and how to persist it to your shell startup file. - `docs/admin/templates/troubleshooting.md`: adds a concise **Cannot connect to the Docker daemon** entry that cross-references the install guide for the full steps. ## Why `install/docker.md` previously documented only the default socket path (`/var/run/docker.sock`). When Docker runs through a tool that uses a per-user socket, such as rootless Docker on Linux, or Colima, Podman, or Rancher Desktop on macOS, the daemon exposes its socket at a non-default path, so the Coder server cannot connect until `DOCKER_HOST` is set. The guidance frames Colima as one example, notes that default socket paths vary by tool, and persists the setting in a shell-agnostic way. Generated by Coder Agents on behalf of @nickvigilante. |
||
|
|
d51762440b | feat: add custom AI provider icons and instance-based model picker grouping (#27026) | ||
|
|
7b19ec3933 |
feat: improve the image management experience with template builder (#27018)
Makes it easier to pick the right workspace image, both in the template builder and in the docs. - Template builder: the Docker and Kubernetes bases now expose a `container_image` variable in the wizard (freeform text, defaults to `codercom/example-base:ubuntu`), and their prerequisites explain why image choice matters, with tradeoffs between `codercom/example-base:ubuntu` (minimal) and `codercom/example-universal:ubuntu` (catch-all), plus pointers to [coder/images](https://github.com/coder/images) and the image management docs. - Docs: reworked [image management](https://coder.com/docs/@ben%2Fdevrel-201-image-guidance-prereqs/admin/templates/managing-templates/image-management) into a clearer maturity ladder (minimal → golden → project-specific → developer customization), with pullable image references in every example, `codercom/oss-dogfood` as a project-specific example, and Dev Containers + [mise](https://mise.jdx.dev/) as ways to customize without new images. Companion PR for the starter templates: coder/registry#943 Part of DEVREL-201. 🤖 Generated with Coder Agents using Claude, on behalf of @bpmct (wizard variable by @jeremyruppel in #27024) --------- Co-authored-by: Jeremy Ruppel <jeremyruppel@users.noreply.github.com> |
||
|
|
79fc8541ed |
docs: update template creation docs for template builder (#26993)
## Summary
Update documentation across 9 files to present the template builder as
the primary template creation method, replacing the old starter
templates flow as the default entry point.
The template builder is a guided wizard that lets admins select base
infrastructure, add registry modules, configure variables, and produce
validated Terraform without writing HCL.
## Changes
**Primary docs (significant rewrites):**
- `docs/admin/templates/creating-templates.md`: Added "Using the
template builder" as the first section with full 5-step wizard
documentation, screenshots, airgap/registry notes, and alternative
creation links. Moved CLI starter template flow to its own section.
Fixed "You can the" typo.
- `docs/get-started/index.md`: Rewrote Steps 4-6 to use the builder with
the Docker base template instead of the Coder Quickstart (which is not a
builder base template). Generalized workspace parameter instructions.
- `docs/start/first-template.md`: Rewrote to use the builder. Removed
old starter templates references, TODO notes, typo, and commented-out
sections.
**Secondary docs (targeted edits):**
- `docs/admin/templates/index.md`: Replaced starter templates section
with builder-first "Create a template" section.
- `docs/admin/templates/managing-templates/index.md`: Renamed "Starter
templates" to "Creating templates" pointing to the builder.
- `docs/install/airgap.md`: Added "Template builder" section documenting
`CODER_DISABLE_TEMPLATE_BUILDER` and
`CODER_TEMPLATE_BUILDER_REGISTRY_URL`.
- `docs/tutorials/template-from-scratch.md`: Added TIP callout
recommending the builder. Fixed `coder templates create` -> `coder
templates push` inconsistency.
- `docs/admin/integrations/devcontainers/envbuilder/add-envbuilder.md`:
Updated Dashboard tab to reference the builder and "Upload an existing
template" alternative.
- `docs/about/screenshots.md`: Updated caption and image reference for
template builder.
**Screenshots added:**
- `templatebuilder_01_bases.png` (base selection step)
- `templatebuilder_02_modules.png` (module selection step)
- `templatebuilder_03_module_customization.png` (module settings step)
- `templatebuilder_04_customizations.png` (template customizations step)
<details>
<summary>Implementation plan</summary>
# Plan: Update docs/ for Template Builder Launch
## Summary
The Template Builder is a new guided wizard at `/templates/new/builder`
that lets admins create templates by selecting a base infrastructure
template, composing it with registry modules, configuring variables, and
producing a validated Terraform bundle without writing HCL. The docs
need to be updated to present this as the primary/recommended template
creation path, while preserving the existing paths (upload, CLI,
duplicate) as alternatives.
## Key behavioral facts from the code
- **Route**: `/templates/new/builder` (new), `/templates/new` (old,
still exists)
- **Entry point**: The "New Template" button on the Templates page links
to `/templates/new/builder` when the builder is enabled; otherwise falls
back to `/starter-templates`
- **5-step wizard**:
1. **Select base infrastructure** (e.g., Docker, AWS EC2, Kubernetes)
2. **Base template parameters** (optional, skipped if base has none)
3. **Select modules** (IDE, AI Agent, Source Control, etc.;
multi-select, grouped by category)
4. **Module settings** (optional, skipped if no configurable variables)
5. **Template customizations** (name, display name, description, icon,
organization)
- **Alternative creation links** are shown on step 1: "Start from
scratch", "Upload an existing template", "Browse community templates",
"Use template agent skill"
- **Disabled via**: `CODER_DISABLE_TEMPLATE_BUILDER` env var /
`--disable-template-builder` flag. When disabled, redirects to old
`/templates/new` flow
- **Registry URL override**: `CODER_TEMPLATE_BUILDER_REGISTRY_URL`
(default: `registry.coder.com`)
- **Requires outbound access** to `registry.coder.com` for `terraform
init` at compose time
- **Modules are bundled** with the Coder release binary; the builder
does not fetch metadata from the registry at runtime
- **Sensitive variables** (secrets) are not collected by the builder;
they are deferred to workspace creation time
- **Module conflicts** show a warning but do not block creation
- **One-way**: No re-entry into the builder for existing templates; edit
HCL directly after creation
## Files to update
### Tier 1: Primary creation flow docs (significant rewrites)
#### 1. `docs/admin/templates/creating-templates.md`
**Current state**: Documents three creation paths: "From a starter
template" (primary), "From an existing template", "From scratch
(advanced)".
**Changes**:
- Add a new section **"Using the template builder"** as the first and
primary section (before "From a starter template").
- Describe the 5-step wizard flow: select base infrastructure, configure
base parameters, select modules, configure module settings, set template
customizations.
- Mention that the builder is enabled by default and requires outbound
access to `registry.coder.com`.
- Note that sensitive variables are collected from developers at
workspace creation, not during template building.
- Add a callout about disabling the builder for airgapped deployments
(`CODER_DISABLE_TEMPLATE_BUILDER`).
- Note the `CODER_TEMPLATE_BUILDER_REGISTRY_URL` option for self-hosted
registry mirrors.
- Keep existing "From a starter template", "From an existing template",
and "From scratch" sections largely intact, but reframe them as
alternative paths.
- Update the "From a starter template" Web UI instructions to note the
new entry point routing (the "New Template" button now goes to the
builder when enabled).
- Fix existing typo: "You can the [Coder CLI]" should be "You can use
the [Coder CLI]".
#### 2. `docs/start/first-template.md`
**Current state**: Beginner tutorial walking through creating a template
from the Docker starter template via the old flow. Has a typo (`s` at
end of line 32), commented-out workspace creation section, and TODO
notes.
**Changes**:
- Rewrite steps 2 and 3 to use the Template Builder as the primary path.
- Step 2: Navigate to **Templates**, select **New Template**, which
opens the Template Builder.
- Step 3: Walk through the builder wizard steps (select Docker base,
optionally select modules like code-server, configure template
name/description, create).
- Remove the typo on line 32 (`s`).
- Keep the "Modify your template" section (step 6) intact since it
covers post-creation editing which is unchanged.
- Remove or update the reference to "Starter Templates" as a separate
page since the builder subsumes that entry point.
#### 3. `docs/get-started/index.md`
**Current state**: Quickstart guide. Step 4 says "Select **Templates** →
**New Template**" then pick "Coder Quickstart" from starter templates.
**Changes**:
- Update Step 4 to describe using the Template Builder.
- The flow becomes: Select **Templates** → **New Template** → builder
opens → select **Coder Quickstart** as the base template → optionally
add modules → set name/description → **Create Template**.
- Update the "What just happened?" explanation to mention the builder
composed and validated the Terraform.
- Screenshot reference `create-quickstart-template.png` will need a new
screenshot (note this in the PR; screenshots are out of scope for this
change but should be flagged).
### Tier 2: Secondary references (targeted edits)
#### 4. `docs/admin/templates/index.md`
**Current state**: Overview page mentioning starter templates as the
primary creation path.
**Changes**:
- Update the "Starter templates" section to mention the Template Builder
as the recommended way to create templates, with starter templates
serving as base templates within the builder.
- Update the link to point to the builder section: `[Create a template
with the template
builder](./creating-templates.md#using-the-template-builder)`.
- Update the screenshot reference and caption. The "Starter Templates"
page screenshot may no longer be the first thing admins see.
#### 5. `docs/admin/templates/managing-templates/index.md`
**Current state**: Documents starter templates, editing, updating,
deleting.
**Changes**:
- Update the "Starter templates" section to mention the Template Builder
as the primary creation path, with starter templates available as base
templates within it.
- Update the image reference from `starter-templates.png` if it shows
the old flow.
#### 6. `docs/tutorials/template-from-scratch.md`
**Current state**: Detailed tutorial for writing a template from scratch
with Terraform.
**Changes**:
- Add a brief note at the top recommending the Template Builder for
users who want to create templates without writing Terraform, with a
link to
`docs/admin/templates/creating-templates.md#using-the-template-builder`.
- In section "7. Create the template in Coder" → "Dashboard" tab, update
the UI steps. The "Upload template" option is now accessed via the old
creation flow at `/templates/new` (or through the "Upload an existing
template" link in the builder's alternatives).
- Fix the inconsistency where text says `coder templates create` but the
code block uses `coder templates push`.
#### 7.
`docs/admin/integrations/devcontainers/envbuilder/add-envbuilder.md`
**Current state**: Documents creating envbuilder templates via
Dashboard, CLI, and Registry tabs.
**Changes**:
- In the Dashboard tab, update the instructions. The "Create Template"
button now opens the builder by default. Users need to use the "Upload
an existing template" alternative link or navigate to `/templates/new`
directly.
- Update "From scratch" reference since that option is now an
alternative link in the builder.
- The CLI and Registry tabs remain unchanged.
#### 8. `docs/install/airgap.md`
**Current state**: Documents air-gapped installations. No mention of
Template Builder.
**Changes**:
- Add a note in the relevant section about the Template Builder
requiring outbound access to `registry.coder.com`.
- Document `CODER_DISABLE_TEMPLATE_BUILDER` for fully air-gapped
deployments.
- Document `CODER_TEMPLATE_BUILDER_REGISTRY_URL` for deployments using a
self-hosted registry mirror.
#### 9. `docs/about/screenshots.md`
**Current state**: Contains a caption "Template administrators can
either create a new Template from scratch or choose a Starter Template".
**Changes**:
- Update the caption to mention the Template Builder as the primary
creation method.
- Screenshot reference may need updating (flag for new screenshot).
### Tier 3: Minor/link-only updates
#### 10. `docs/admin/users/organizations.md`
- If it references the old "Create Template" screen with an org picker,
add a note that the Template Builder also includes organization
selection in its final step.
#### 11. `docs/ai-coder/tasks.md`
- If it mentions creating templates, add a passing reference to the
Template Builder as an option.
## Files NOT to update
- `docs/reference/api/templatebuilder.md`: Auto-generated API reference.
Already correct.
- `docs/reference/api/schemas.md`: Auto-generated. Already correct.
- `docs/reference/cli/server.md`: Auto-generated. Already has
`--disable-template-builder` and `--template-builder-registry-url`.
- `docs/reference/cli/templates_create.md`: Already deprecated.
- `docs/reference/cli/templates.md`: No changes needed.
## Implementation order
1. `docs/admin/templates/creating-templates.md` (primary creation docs,
most content)
2. `docs/get-started/index.md` (quickstart)
3. `docs/start/first-template.md` (beginner tutorial)
4. `docs/admin/templates/index.md` (overview)
5. `docs/admin/templates/managing-templates/index.md` (managing
overview)
6. `docs/install/airgap.md` (airgap note)
7. `docs/tutorials/template-from-scratch.md` (from-scratch tutorial)
8. `docs/admin/integrations/devcontainers/envbuilder/add-envbuilder.md`
(envbuilder)
9. `docs/about/screenshots.md` (screenshot captions)
10. Minor link/reference updates in tier 3 files
## Style notes
- Follow the Diataxis framework; keep tutorials as tutorials, reference
as reference.
- Use present tense, active voice, second person.
- Bold for UI elements: **Templates**, **New Template**, **Create
Template**.
- No emdash/endash.
- Do not add screenshots; flag where new screenshots are needed as
comments/TODOs.
- Run `make fmt/markdown` and `make lint/markdown` after all changes.
- Verify all pages are already in `docs/manifest.json` (no new pages
being added, only existing pages being updated).
</details>
> 🤖 Generated by Coder Agents
|
||
|
|
b21e0717d5 |
feat: remove chat chain mode (#26980)
Removes OpenAI Responses "chain mode" from chatd. Closes CODAGT-445.
- Deletes `chatopenai/responses.go` (chain detection, activation, prompt filtering, response ID extraction) and its tests.
- Deletes the `ChainBroken` classification in `chaterror` and the chatloop retry bookkeeping that disabled chain mode mid-generation.
- Drops the `chain_broken` label from the `coderd_chatd_stream_retries_total` metric.
- Stops reading and writing `chat_messages.provider_response_id`
- Deletes the dead `ClearChatMessageProviderResponseIDsByChatID` query. Dropping the column is a follow-up migration.
- Deletes three chatloop hooks no caller sets (`ReloadMessages`, `DisableChainMode`, `PrepareMessages`), the dead `const AgentChatContextSentinelPath`, and stale chain-mode comments.
🤖 Generated by Coder Agents on behalf of @johnstcn.
|
||
|
|
89b0a66079 |
docs: add top-level Get started section and move the Quickstart (#26821)
Add a top-level "Get started" docs section to the nav and move the Quickstart to /docs/get-started, with inbound link updates and the install page TIP pointing to the Quickstart. Filed via Coder Agents on Nick's behalf. |
||
|
|
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. |
||
|
|
32217259b7 |
feat: cap tool output to fit the model context window (#26637)
## Problem
Local tool results were persisted and replayed to the model verbatim,
with no size cap. A single oversized result, most often a multi-megabyte
response from an MCP tool, overflows the prompt on the next request.
Every retry rebuilds the same history and fails the same way, leaving
the chat wedged in `error`. Auto-compaction is reactive (token usage is
only known after a response), so it can't catch a single result that
blows the very next request.
## Fix
Cap every locally-executed tool result at its single choke point,
`executeSingleTool` in `chatloop`, so the cap covers built-in tools,
**global (deployment-pinned) MCP**, **workspace MCP**, and provider
runners uniformly. Because this runs before the result is published to
the live stream and before it is committed, the SSE preview, the
persisted message, and the model replay all see the same bounded output.
The budget is token-aware: a single tool result may use at most half the
model's context window (`~4 bytes/token`), with a `16KB` floor and a
`64KB` default when the window is unknown. Truncation keeps the head and
tail of the output and replaces the middle with a marker telling the
model how much was removed and to narrow its query; it is UTF-8 safe and
never exceeds the budget. Binary media `Data` is passed through
untouched (only the text payload is bounded).
A `coderd_chatd_tool_result_truncated_total{provider,model,tool_name}`
counter and a warning log record each truncation.
## Out of scope
- Provider-executed results (e.g. web search) arrive via the stream, not
`executeSingleTool`.
- Dynamic/external tool results submitted through the `/tool-results`
API are validated as JSON elsewhere.
- Cumulative growth across many results is still handled by context
compaction; this change only bounds any single result.
<details>
<summary>Implementation notes</summary>
- New `coderd/x/chatd/chatloop/tooltruncate.go`:
`toolResultByteBudget(contextLimitTokens)` and
`truncateToolResultText(text, maxBytes)` (pure, unit-tested).
- `chatloop.go`: added `ContextLimit` to `ExecuteLocalToolsOptions`;
threaded a computed byte budget through `executeTools` into
`executeSingleTool`, where `resp.Content` is capped for the text,
media-text, and error branches.
- `generation.go`: passes `ContextLimit: prepared.ContextLimitFallback`
(the model's configured context limit).
- `metrics.go`: new `ToolResultTruncatedTotal` counter +
`RecordToolResultTruncated`.
- Tunable knobs live as constants in `tooltruncate.go`
(`toolResultContextDivisor = 2`, `bytesPerTokenEstimate`,
`minToolResultBytes`, `defaultToolResultBytes`).
Verified: `go build ./coderd/x/chatd/...`, `go test
./coderd/x/chatd/chatloop/...`, and the `chatd` test binary compiles.
</details>
---
Resolves CODAGT-678
Generated by Coder Agents on behalf of @kylecarbs.
|
||
|
|
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> |
||
|
|
6da322d59f | feat: add Prometheus metrics to NATS pubsub for parity with PGPubsub (#26441) | ||
|
|
2f6f8b9520 | feat: add workspace autostop reminder template (#26429) | ||
|
|
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. |
||
|
|
ed908ed019 |
fix(docs): repoint 7 broken external and anchor links (DOCS-415) (#26572)
Closes [DOCS-415](https://linear.app/codercom/issue/DOCS-415). ## TL;DR Repoints 7 broken links across 5 docs files that the 2026-06-22 weekly `check-docs.yml` Linkspector run flagged. Two other links from the same run (the dead `nix` ref and the dead `reflectoring.io` ref in `CONTRIBUTING.md`) were already folded into [#26341](https://github.com/coder/coder/pull/26341). ## Why Broken external and anchor links degrade reader trust, leak SEO juice, and make the docs look stale. The weekly `check-docs` job exists precisely to catch this kind of rot before customers do; the surfacing-to-fix turnaround on these 7 is one PR. Run that surfaced them: [actions/runs/27948011619 job 82697664858](https://github.com/coder/coder/actions/runs/27948011619/job/82697664858). ## Scope | File | Line(s) | Old target | New target | Why | |------|---------|-----------|------------|-----| | `docs/tutorials/best-practices/organizations.md` | 62 | anchor `#update-template-metadata-by-id` | `#update-template-settings-by-id` | API endpoint renamed in [#19228](https://github.com/coder/coder/pull/19228) (Aug 2025). New heading at line 1105 of `docs/reference/api/templates.md`. | | `docs/install/registry-mirror-artifactory.md` | 197 | JFrog `terraform-registry` | `terraform-opentofu-and-terraform-backend-repositories` | JFrog consolidated their Terraform / OpenTofu / Backend docs into a single page. | | `docs/admin/templates/extending-templates/modules.md` | 76, 206 | JFrog `set-up-a-terraform-module/provider-registry` and `terraform-registry` | same consolidated JFrog page (root, no anchor) | Same JFrog consolidation. Anchor dropped, see decision log. | | `docs/admin/integrations/dx-data-cloud.md` | 84 | `https://help.getdx.com/en/` | `https://docs.getdx.com/` | DX migrated their help center to a separate docs domain. | | `docs/about/contributing/frontend.md` | 37, 71 | `https://reactrouter.com/en/main` | `https://reactrouter.com/` | React Router dropped the `/en/main` prefix. | ## Validation - All 7 replacement URLs return HTTP 200 (manual `curl -L -o /dev/null -w '%{http_code}'` per URL; linkspector's puppeteer crashed in the agent env, so it was run case-by-case) - `make lint/markdown lint/emdash` clean locally - Pre-commit hook (`scripts/githooks/pre-commit` -> `make pre-commit-light`) clean - No `/docs/` route changes; pure markdown content ## Not triggering `/coder-agents-review` Docs-only markdown edit, no CI or build config changes; per `AGENTS.md` the bot review is reserved for product / CI changes. `doc-check` handles this category. ## Pre-mortem | Concern | Mitigation | |---|---| | Replacement URL also turns out to be broken later | All 7 verified HTTP 200 today; next weekly `check-docs` run will catch any future regression. | | JFrog anchor drop on `modules.md` (76, 206) loses navigation context | Verified the consolidated JFrog page has no clean section anchor for the original target; linking the root page is the honest fix. If JFrog ships a better TOC anchor later, a follow-up can reattach. | | Anchor rename in `organizations.md` was actually a different rename | Confirmed via PR #19228 (Aug 2025) which is the exact rename that produced `## Update template settings by ID`. | <details> <summary>Decision log</summary> **Why drop the anchor on the JFrog `modules.md` links (76 + 206)**: JFrog's new consolidated page (`/terraform-opentofu-and-terraform-backend-repositories`) doesn't expose the original `set-up-a-terraform-module/provider-registry` section as a fragment-link target. The honest fix is to link the page root; readers can scroll. The `registry-mirror-artifactory.md:197` reference uses the same root link for symmetry. **Why DX `docs.getdx.com` over `help.getdx.com`**: DX's help center at `help.getdx.com/en/` now returns 404. They moved to a separate `docs.getdx.com` domain with a different content structure. Linking the docs root is the closest analog to the original "browse our docs" intent. **Why React Router root over `/en/main`**: React Router unified their docs under the root URL. The `/en/main` prefix is no longer routable. The root URL is the canonical successor. </details> <details> <summary>CI: <code>audit-docs-paths</code> failure (pre-existing, unrelated)</summary> The `audit-docs-paths` job in `.github/workflows/weekly-docs.yaml` fails on this PR because its `Fetch redirects.json` step issues an unauthenticated `curl` to a file in private `coder/coder.com` and gets a 404 (exit code 22). Same failure on every recent PR in this repo. Tracked in [DOCS-409](https://linear.app/codercom/issue/DOCS-409) and fixed in [#26571](https://github.com/coder/coder/pull/26571), which authenticates the fetch through the Contents API. My changes are docs-content only (5 markdown files, 7 line changes) and don't touch the TS/TSX paths or `redirects.json` that the audit examines, so this is a pre-existing CI break, not a regression introduced here. </details> --- *Generated by Coder Agents on @nickvigilante's behalf.* |
||
|
|
401aa58eeb | feat: add schema changes for autostop notification (#26417) | ||
|
|
e458692cb8 |
refactor(docs): convert absolute coder/coder blob/tree/main links to relative (DOCS-351) (#26341)
Closes [DOCS-351](https://linear.app/codercom/issue/DOCS-351). > [!WARNING] > **DO NOT MERGE** until [DOCS-349](https://linear.app/codercom/issue/DOCS-349) ([coder.com#877](https://github.com/coder/coder.com/pull/877)) has shipped to production and baked for at least one Vercel cycle. > > Without DOCS-349, the relative links in this PR resolve to broken docs-route URLs (`/docs/helm/coder/values.yaml` -> 404) instead of GitHub URLs tagged with the displayed docs version. DOCS-349 fixes the rewriter to classify these as GitHub blob/tree URLs with the page's resolved ref. ## TL;DR Converts 121 absolute `https://github.com/coder/coder/(blob|tree)/main/<path>` links across 39 docs markdown files to relative paths. After this lands AND DOCS-349 deploys, every one of these links will follow the displayed docs version (mainline tag on bare URLs, explicit tag on `/@vX.Y.Z/`, `main` on `/@main/`) instead of always pointing to `main`. ## Why Today a reader on `/docs/@v2.30.0/install/docker` follows a `compose.yaml` link and arrives at `main`'s `compose.yaml`, which doesn't necessarily match what the docs page describes. Helm values, Terraform templates, and source-code references in particular drift across versions. The fix is to let the coder.com rewriter substitute the page's resolved ref into the URL; that only works on relative links. ## Example payoff (post-DOCS-349) | URL | Today (absolute, always `main`) | After (relative + rewriter) | |---|---|---| | `/docs/install/docker` | `https://github.com/coder/coder/blob/main/compose.yaml` | `https://github.com/coder/coder/blob/v2.34.1/compose.yaml` (today's mainline) | | `/docs/@v2.30.0/install/docker` | same as above | `https://github.com/coder/coder/blob/v2.30.0/compose.yaml` | | `/docs/@main/install/docker` | same as above | `https://github.com/coder/coder/blob/main/compose.yaml` | ## Scope - **121 conversions** across **39 files**. - Verb breakdown: `tree/main` (directories) and `blob/main` (files), both flipped to relative paths. - Line anchors (`#L23-L24`) and query strings preserved verbatim. - Conversion is mechanical: relative path computed from the doc file's directory to the target via `os.path.relpath`. Any path starting at the same directory or below gets a `./` prefix; otherwise `../` chains. ## Rebased on main The branch was rebased onto `main` after the DOCS-350 hotfix ([#26339](https://github.com/coder/coder/pull/26339)) merged. The hotfix repointed 3 `docs-backend-contrib-guide` refs in `backend.md` to `main`, which then needed the same `main` -> relative conversion this PR is doing for the other 121 links. The conflict was resolved by reapplying the mechanical conversion to `backend.md` after taking the hotfix's content. Net result: those 3 links land here as relative, same as everything else. New HEAD `3f501cb622`. ## Inline fix folded in: dead `nix` link - `docs/about/contributing/CONTRIBUTING.md:7` -> `../../../nix` The original absolute URL `https://github.com/coder/coder/tree/main/nix` already returned 404 today. Repointed to `flake.nix` (modern Nix entrypoint, what the prose "Nix environment" semantically refers to). Closes [DOCS-357](https://linear.app/codercom/issue/DOCS-357) here since the `check-docs` Linkspector job surfaced it during rebase; cheaper to fix inline than in a separate single-line PR. ## Out of scope (filed separately) - [DOCS-350](https://linear.app/codercom/issue/DOCS-350): 3 dead `docs-backend-contrib-guide` branch refs in `backend.md` ([#26339](https://github.com/coder/coder/pull/26339), merged). - [DOCS-352](https://linear.app/codercom/issue/DOCS-352): 10 SHA-pinned `(blob|tree)/<sha>` links pending intent review. - [DOCS-355](https://linear.app/codercom/issue/DOCS-355): code-server analog (4 absolute `(blob|tree)/main` links in `coder/code-server`). - [DOCS-356](https://linear.app/codercom/issue/DOCS-356): 2 upstream content bugs in `coder/code-server/docs/CONTRIBUTING.md` (independent of this PR). ## Not triggering `/coder-agents-review` Docs-only edit; per `AGENTS.md` the bot review is reserved for product/CI changes. ## Pre-mortem | Concern | Mitigation | |---|---| | Merging before DOCS-349 deploys regresses ~120 currently-working links into 404s on coder.com | Clear DO-NOT-MERGE banner; tracked as blocker in Linear. | | Relative path computed incorrectly (off-by-one `..`) | Verified all 114 newly-relative non-md/non-image paths resolve to existing files in the repo (only exception is the pre-existing dead `nix` link above). | | Line anchors stripped during conversion | Preserved by the substitution regex; verified `#L<n>-L<m>` cases in `airgap.md` and `speed-up-templates.md`. | | Future code reorgs change file locations | Relative links will start pointing to nothing. Same failure mode as absolute links pointing to renamed files; can be caught with a future link-checker job. | ## Validation ``` $ grep -rE 'github\.com/coder/coder/(blob|tree)/main' docs --include="*.md" | wc -l 0 $ git diff --stat origin/main | tail -1 39 files changed, 118 insertions(+), 118 deletions(-) ``` 114 newly-relative paths verified to resolve to existing repo files (Python `os.path.exists` check on each computed target). <details> <summary>Decision log + planning context</summary> **Why relative over `(blob|tree)/{{currentDocsVersion}}/...` templating**: relative paths require zero markdown-system support and zero upstream churn beyond this one PR. Templating would require a preprocessor on `coder.com` side AND a convention upstream authors have to remember; relative paths just work in a plain editor and `github.com`'s own renderer too. **Why `./` prefix on same-directory targets**: makes the conversion grep-able later (`grep -E '\((\.\./|\./)'`). **Why preserve `#L<n>-L<m>` anchors verbatim**: the anchor is meaningful to the linked file's content, not to the URL form; keeping it as-is preserves authorial intent. If the file later changes such that the line range drifts, that's a different problem the SHA-pin audit ([DOCS-352](https://linear.app/codercom/issue/DOCS-352)) will surface. </details> --- *Generated by Coder Agents on @nickvigilante's behalf.* ## Drive-by external link fix folded in `docs/about/contributing/CONTRIBUTING.md:296` cited `https://reflectoring.io/meaningful-commit-messages/` which is returning HTTP 503 (the host appears to be down site-wide right now). `check-docs` Linkspector flagged it after the rebase. Replaced with `https://cbea.ms/git-commit/` (Chris Beams' canonical "If applied, this commit will..." article, confirmed 200), which is the original source of the rule the prose recites anyway. |
||
|
|
2f0bb657e2 | docs: note Database Encryption coverage for user secrets (#26435) | ||
|
|
45dcd7edfc |
docs: document coder exp sync list in startup coordination guides (#26454)
Follow-up to #26443. Documents the new `coder exp sync list` command in the startup coordination guides. **troubleshooting.md:** - New "List All Units" section after "Check Unit Status" with example output - Added `coder exp sync list` to the "Workspace startup script hangs" checklist, since users debugging hanging scripts may not know which unit to query **usage.md:** - New "Inspect Unit State" section covering `list`, `status`, and `ping` - Updated "Test your changes" checklist to reference `coder exp sync list` > Generated by Coder Agents on behalf of @SasSwart |
||
|
|
f1ce1013c4 |
chore: export AI Gateway metrics under new branding + keep old as alias (#26413)
> AI Tools where used in this request. Registers `coder_aibridged_*` and `coder_aibridgeproxyd_*` metrics under new prefixes: `coder_ai_gateway_*` and `coder_ai_gateway_proxy_*`. Old prefix is still exported. Will be removed in later release. Also updated the `metricsdocgen` static fixture. Added 4 previously-undocumented metrics `key_pool_state`, `key_pool_state_transitions_total`, `key_pool_exhaustions_total`, `key_pool_failover_attempts` added the `client` label to the existing interception, prompt, and token counter samples. Updated AI Gateway documentation. |
||
|
|
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) |
||
|
|
210261b143 |
feat: add chat context pinning storage and push trigger (#26385)
Foundation for the Workspace Context Sources RFC (phase 3). The agent push (#25983) and coderd snapshot storage (#26145) already persist per-agent context snapshots; this PR lands the **chat-side storage** plus the **`agentapi` push trigger** that a follow-up will use to read them. It does **not** touch `chatd` and changes no behavior — nothing wires an implementation yet. ## What changed - Adds four nullable columns to `chats` — `context_aggregate_hash`, `context_dirty_since`, `context_dirty_resources`, and `context_error` — and rebuilds the `chats_expanded` view. - Adds three queries — `SetChatContextSnapshot`, `HydrateAgentChatsContext`, `MarkChatsContextDirtyByAgent` — with `dbauthz` wrappers and `audit` entries. They are store-interface methods covered by a Postgres test (`TestChatContextHydration`). - Adds the `agentapi.ContextDirtyMarker` interface and invokes it inside the `PushContextState` transaction, publishing collected events only after commit. ## Intentionally inert There are **no production callers** of the three queries and **no implementation** wired for `ContextDirtyMarker`, so the push trigger is dormant. This is deliberate: the PR is the durable storage/query foundation only. The actual integration — the `chatd` implementation that hydrates/dirties chats and backs a refresh endpoint, consuming the pinned context in prompt building, the rich SDK types + UI, and retiring the live per-turn pull — lands as a single follow-up PR. Splitting this way keeps the schema/query layer reviewable on its own and keeps the integration whole in one place. Refs #25983, #26145. <details> <summary>Decision log</summary> - **Columns over a side table.** The four `chats` columns are the durable model (accepting the one-time `chats_expanded` view/CTE churn). `last_injected_context` is deliberately left untouched — it is load-bearing for the live per-turn context pull. - **Keep `agentapi`, drop `chatd`.** The earlier revision wired the hydrate/dirty implementation through `chatd` and added a `PUT /chats/{chat}/context` refresh endpoint. Those were removed so this PR is pure foundation; `agentapi` defines the trigger + interface (it does not import `chatd`), and the `chatd` implementation arrives with the full integration. - **No new experiment flag.** The columns are dark and unread by prompt building. - **Authz.** The new query wrappers authorize chat updates under the chat RBAC object / `ResourceChat`, consistent with the existing system chat mutators. </details> --- 🤖 Generated by Coder Agents on behalf of @kylecarbs. |
||
|
|
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 |
||
|
|
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 |