`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).
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)
Relates to
https://linear.app/codercom/issue/AIGOV-285/add-user-budget-overrides-table-and-crud-api
Adds audit-log support for `user_ai_budget_override` mutations. Without
it, an admin could quietly change a user's per-user spend cap (e.g. from
`$500` to `$50`), reassign it to a different group, or delete it
entirely with no record of who did it.
Both write (`create-or-update`) and delete actions now generate audit
log entries. Unlike group AI budgets, which only track `spend_limit`,
overrides also track `group_name`: an override can be reassigned to a
different attributed group, so that change needs to show up in the diff.
The raw `spend_limit_micros`, IDs, and timestamps are ignored in favor
of the human-readable `spend_limit` and `group_name`.
Depends on #25439.
## Screenshot
<img width="1343" height="514" alt="image"
src="https://github.com/user-attachments/assets/aee30f58-6e81-435e-9bca-5bc98f49d8d3"
/>
> Mux updated this PR on behalf of Mike.
## Stack Context
This PR is the storage, permissions, API, and SDK layer for experimental
personal skills. #25362 has landed on `main`, so this branch is
restacked directly on `main`.
Stack order:
1. #25363 storage, permissions, API, and SDK
2. #25365 API test coverage
3. #25366 chattool and chatd integration
4. #25066 settings UI and docs
5. #25386 personal skills slash menu
## What?
Adds the `user_skills` database table, generated queries, RBAC resources
and scopes, audit resource handling, experimental user-scoped CRUD
endpoints, SDK types, and generated API/site types.
Follow-up review and restack fixes:
- Enforce a bounded personal skill description in parser and database
constraints.
- Return `403 Forbidden` for unauthorized create and update attempts.
- Return explicit conflict responses when soft-deleted users are
targeted.
- Keep user admins out of personal skills, while site owners can read
and delete but not create or update.
- Document trigger-raised constraint names and keep schema constants
covered by tests.
- Reuse `UserSkillMetadata` in the full `UserSkill` SDK response type.
- Generate user skill IDs in Go instead of relying on a database
default.
- Rebase on latest `main` and renumber the user skills migration to
`000502_user_skills`.
## Why?
Personal skills need durable user-owned storage with owner
authorization, limited site-owner moderation, and a hidden API surface
before chatd can consume them.
## Validation
- `make gen`
- `go test ./coderd/database -run '^TestUserSkillSchemaConstants$'
-count=1`
- `go test ./coderd/database/dbauthz -run
'^TestMethodTestSuite/TestUserSkills$' -count=1`
- `go test ./coderd -run '^TestPatchUserSkill$' -count=1`
- `go test ./codersdk ./coderd/database/db2sdk`
- `make lint`
- pre-commit hook on `97fd58108d`
Emit user secret audit log entries for create/update/delete operations.
Reads stay un-audited, matching every other resource.
Audit log entries record changes in user secret name, environment
variable name, file path, and value. The secret value column is marked
`ActionSecret` so the diff records the change without showing the
ciphertext or plaintext.
Closes a TOCTOU window on delete to ensure no phantom audit logs for a
delete of a non-existent secret. Secret update accepts a small TOCTOU
window matching the other audited resources (templates, workspaces,
chats). The two-query pattern is wrapped in a transaction so audit state
can't leak from a failed mutation.
The main goal of this PR is to remove Workspace Apps and Workspace Agents from the auto-generated audit log documentation, that incorrectly claims they are audited resources (no longer true with the addition of the connection log).
Though I believe we haven't touched any codepaths for returning audit logs, this PR also adds a test that ensures we continue to return *existing* connection, disconnect and open events correctly from the audit log API.
This PR provides two commands:
* `coder prebuilds pause`
* `coder prebuilds resume`
These allow the suspension of all prebuilds activity, intended for use
if prebuilds are misbehaving.
- Update go.mod to use Go 1.24.1
- Update GitHub Actions setup-go action to use Go 1.24.1
- Fix linting issues with golangci-lint by:
- Updating to golangci-lint v1.57.1 (more compatible with Go 1.24.1)
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <claude@anthropic.com>
This commit adds new audit resource types for workspace agents and
workspace apps, as well as connect/disconnect and open/close actions.
The idea is that we will log new audit events for connecting to the
agent via SSH/editor.
Likewise, we will log openings of `coder_app`s.
This change also introduces support for filtering by `request_id`.
Updates #15139
Joins in fields like `username`, `avatar_url`, `organization_name`,
`template_name` to `workspaces` via a **view**.
The view must be maintained moving forward, but this prevents needing to
add RBAC permissions to fetch related workspace fields.
* chore: add /v2 to import module path
go mod requires semantic versioning with versions greater than 1.x
This was a mechanical update by running:
```
go install github.com/marwan-at-work/mod/cmd/mod@latest
mod upgrade
```
Migrate generated files to import /v2
* Fix gen
* feat: Implement view for workspace builds to include rbac info
* Removes the need to fetch the workspace to run an rbac check.
* chore: Use workspace build as RBAC object
* chore: Use golang templates instead of sqlc files
* added AuditableGroup type
* added json tags
* Anonymizing gGroup struct
* adding support on the FE for nested group diffs
* added type for GroupMember
* Update coderd/database/modelmethods.go
Co-authored-by: Steven Masley <Emyrk@users.noreply.github.com>
* Update coderd/database/modelmethods.go
Co-authored-by: Steven Masley <Emyrk@users.noreply.github.com>
* fetching group members in group.delete
* passing through right error
* broke out into util function and added tests
Co-authored-by: Steven Masley <Emyrk@users.noreply.github.com>
* adding workspace_build resource
* added migration
* added migration for audit_actions
* fix keyword
* got rid oof diffs for workspace builds
* adding workspace name to string
* renamed migrations
* fixed lint
* pass throough AdditionalFields and fix tests
* no need to pass through each handler
* cleaned up migrations