mirror of
https://github.com/coder/coder.git
synced 2026-09-22 13:10:21 +08:00
`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).
418 lines
16 KiB
SQL
418 lines
16 KiB
SQL
-- name: UpsertDefaultProxy :exec
|
|
-- The default proxy is implied and not actually stored in the database.
|
|
-- So we need to store it's configuration here for display purposes.
|
|
-- The functional values are immutable and controlled implicitly.
|
|
INSERT INTO site_configs (key, value)
|
|
VALUES
|
|
('default_proxy_display_name', @display_name :: text),
|
|
('default_proxy_icon_url', @icon_url :: text)
|
|
ON CONFLICT
|
|
(key)
|
|
DO UPDATE SET value = EXCLUDED.value WHERE site_configs.key = EXCLUDED.key
|
|
;
|
|
|
|
-- name: GetDefaultProxyConfig :one
|
|
SELECT
|
|
COALESCE((SELECT value FROM site_configs WHERE key = 'default_proxy_display_name'), 'Default') :: text AS display_name,
|
|
COALESCE((SELECT value FROM site_configs WHERE key = 'default_proxy_icon_url'), '/emojis/1f3e1.png') :: text AS icon_url
|
|
;
|
|
|
|
-- name: InsertDeploymentID :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('deployment_id', $1);
|
|
|
|
-- name: GetDeploymentID :one
|
|
SELECT value FROM site_configs WHERE key = 'deployment_id';
|
|
|
|
-- name: InsertDERPMeshKey :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('derp_mesh_key', $1);
|
|
|
|
-- name: GetDERPMeshKey :one
|
|
SELECT value FROM site_configs WHERE key = 'derp_mesh_key';
|
|
|
|
-- name: UpsertLastUpdateCheck :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('last_update_check', $1)
|
|
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'last_update_check';
|
|
|
|
-- name: GetLastUpdateCheck :one
|
|
SELECT value FROM site_configs WHERE key = 'last_update_check';
|
|
|
|
-- name: UpsertAnnouncementBanners :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('announcement_banners', $1)
|
|
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'announcement_banners';
|
|
|
|
-- name: GetAnnouncementBanners :one
|
|
SELECT value FROM site_configs WHERE key = 'announcement_banners';
|
|
|
|
-- name: UpsertLogoURL :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('logo_url', $1)
|
|
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'logo_url';
|
|
|
|
-- name: GetLogoURL :one
|
|
SELECT value FROM site_configs WHERE key = 'logo_url';
|
|
|
|
-- name: UpsertApplicationName :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('application_name', $1)
|
|
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'application_name';
|
|
|
|
-- name: GetApplicationName :one
|
|
SELECT value FROM site_configs WHERE key = 'application_name';
|
|
|
|
-- name: GetHealthSettings :one
|
|
SELECT
|
|
COALESCE((SELECT value FROM site_configs WHERE key = 'health_settings'), '{}') :: text AS health_settings
|
|
;
|
|
|
|
-- name: UpsertHealthSettings :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('health_settings', $1)
|
|
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'health_settings';
|
|
|
|
-- name: GetNotificationsSettings :one
|
|
SELECT
|
|
COALESCE((SELECT value FROM site_configs WHERE key = 'notifications_settings'), '{}') :: text AS notifications_settings
|
|
;
|
|
|
|
-- name: UpsertNotificationsSettings :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('notifications_settings', $1)
|
|
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'notifications_settings';
|
|
|
|
-- name: GetPrebuildsSettings :one
|
|
SELECT
|
|
COALESCE((SELECT value FROM site_configs WHERE key = 'prebuilds_settings'), '{}') :: text AS prebuilds_settings
|
|
;
|
|
|
|
-- name: UpsertPrebuildsSettings :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('prebuilds_settings', $1)
|
|
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'prebuilds_settings';
|
|
|
|
-- name: GetRuntimeConfig :one
|
|
SELECT value FROM site_configs WHERE site_configs.key = $1;
|
|
|
|
-- name: UpsertRuntimeConfig :exec
|
|
INSERT INTO site_configs (key, value) VALUES ($1, $2)
|
|
ON CONFLICT (key) DO UPDATE SET value = $2 WHERE site_configs.key = $1;
|
|
|
|
-- name: DeleteRuntimeConfig :exec
|
|
DELETE FROM site_configs
|
|
WHERE site_configs.key = $1;
|
|
|
|
-- name: GetOAuth2GithubDefaultEligible :one
|
|
SELECT
|
|
CASE
|
|
WHEN value = 'true' THEN TRUE
|
|
ELSE FALSE
|
|
END
|
|
FROM site_configs
|
|
WHERE key = 'oauth2_github_default_eligible';
|
|
|
|
-- name: UpsertOAuth2GithubDefaultEligible :exec
|
|
INSERT INTO site_configs (key, value)
|
|
VALUES (
|
|
'oauth2_github_default_eligible',
|
|
CASE
|
|
WHEN sqlc.arg(eligible)::bool THEN 'true'
|
|
ELSE 'false'
|
|
END
|
|
)
|
|
ON CONFLICT (key) DO UPDATE
|
|
SET value = CASE
|
|
WHEN sqlc.arg(eligible)::bool THEN 'true'
|
|
ELSE 'false'
|
|
END
|
|
WHERE site_configs.key = 'oauth2_github_default_eligible';
|
|
|
|
-- name: GetOAuth2DCREnabled :one
|
|
SELECT COALESCE(
|
|
(SELECT value = 'true' FROM site_configs WHERE key = 'oauth2_dcr_enabled'),
|
|
false
|
|
)::bool;
|
|
|
|
-- name: UpsertOAuth2DCREnabled :exec
|
|
INSERT INTO site_configs (key, value)
|
|
VALUES (
|
|
'oauth2_dcr_enabled',
|
|
CASE
|
|
WHEN sqlc.arg(enabled)::bool THEN 'true'
|
|
ELSE 'false'
|
|
END
|
|
)
|
|
ON CONFLICT (key) DO UPDATE
|
|
SET value = CASE
|
|
WHEN sqlc.arg(enabled)::bool THEN 'true'
|
|
ELSE 'false'
|
|
END
|
|
WHERE site_configs.key = 'oauth2_dcr_enabled';
|
|
|
|
-- name: UpsertWebpushVAPIDKeys :exec
|
|
INSERT INTO site_configs (key, value)
|
|
VALUES
|
|
('webpush_vapid_public_key', @vapid_public_key :: text),
|
|
('webpush_vapid_private_key', @vapid_private_key :: text)
|
|
ON CONFLICT (key)
|
|
DO UPDATE SET value = EXCLUDED.value WHERE site_configs.key = EXCLUDED.key;
|
|
|
|
-- name: GetWebpushVAPIDKeys :one
|
|
SELECT
|
|
COALESCE((SELECT value FROM site_configs WHERE key = 'webpush_vapid_public_key'), '') :: text AS vapid_public_key,
|
|
COALESCE((SELECT value FROM site_configs WHERE key = 'webpush_vapid_private_key'), '') :: text AS vapid_private_key;
|
|
|
|
-- name: GetChatSystemPrompt :one
|
|
SELECT
|
|
COALESCE((SELECT value FROM site_configs WHERE key = 'agents_chat_system_prompt'), '') :: text AS chat_system_prompt;
|
|
|
|
-- GetChatSystemPromptConfig returns both chat system prompt settings in a
|
|
-- single read to avoid torn reads between separate site-config lookups.
|
|
-- The include-default fallback preserves the legacy behavior where a
|
|
-- non-empty custom prompt implied opting out before the explicit toggle
|
|
-- existed.
|
|
-- name: GetChatSystemPromptConfig :one
|
|
SELECT
|
|
COALESCE((SELECT value FROM site_configs WHERE key = 'agents_chat_system_prompt'), '') :: text AS chat_system_prompt,
|
|
COALESCE(
|
|
(SELECT value = 'true' FROM site_configs WHERE key = 'agents_chat_include_default_system_prompt'),
|
|
NOT EXISTS (
|
|
SELECT 1
|
|
FROM site_configs
|
|
WHERE key = 'agents_chat_system_prompt'
|
|
AND value != ''
|
|
)
|
|
) :: boolean AS include_default_system_prompt;
|
|
|
|
-- name: UpsertChatSystemPrompt :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('agents_chat_system_prompt', $1)
|
|
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'agents_chat_system_prompt';
|
|
|
|
-- name: GetChatPlanModeInstructions :one
|
|
SELECT
|
|
COALESCE((SELECT value FROM site_configs WHERE key = 'agents_chat_plan_mode_instructions'), '') :: text AS plan_mode_instructions;
|
|
|
|
-- name: UpsertChatPlanModeInstructions :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('agents_chat_plan_mode_instructions', $1)
|
|
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'agents_chat_plan_mode_instructions';
|
|
|
|
-- name: GetChatExploreModelOverride :one
|
|
SELECT
|
|
COALESCE((SELECT value FROM site_configs WHERE key = 'agents_chat_explore_model_override'), '') :: text AS model_config_id;
|
|
|
|
-- name: UpsertChatExploreModelOverride :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('agents_chat_explore_model_override', $1)
|
|
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'agents_chat_explore_model_override';
|
|
|
|
-- name: GetChatGeneralModelOverride :one
|
|
SELECT
|
|
COALESCE((SELECT value FROM site_configs WHERE key = 'agents_chat_general_model_override'), '') :: text AS model_config_id;
|
|
|
|
-- name: UpsertChatGeneralModelOverride :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('agents_chat_general_model_override', $1)
|
|
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'agents_chat_general_model_override';
|
|
|
|
-- name: GetChatTitleGenerationModelOverride :one
|
|
SELECT
|
|
COALESCE((SELECT value FROM site_configs WHERE key = 'agents_chat_title_generation_model_override'), '') :: text AS model_config_id;
|
|
|
|
-- name: UpsertChatTitleGenerationModelOverride :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('agents_chat_title_generation_model_override', $1)
|
|
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'agents_chat_title_generation_model_override';
|
|
|
|
-- name: GetChatCompactionModelOverride :one
|
|
SELECT
|
|
COALESCE((SELECT value FROM site_configs WHERE key = 'agents_chat_compaction_model_override'), '') :: text AS model_config_id;
|
|
|
|
-- name: UpsertChatCompactionModelOverride :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('agents_chat_compaction_model_override', $1)
|
|
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'agents_chat_compaction_model_override';
|
|
|
|
-- name: GetChatDesktopEnabled :one
|
|
SELECT
|
|
COALESCE((SELECT value = 'true' FROM site_configs WHERE key = 'agents_desktop_enabled'), false) :: boolean AS enable_desktop;
|
|
|
|
-- name: UpsertChatDesktopEnabled :exec
|
|
INSERT INTO site_configs (key, value)
|
|
VALUES (
|
|
'agents_desktop_enabled',
|
|
CASE
|
|
WHEN sqlc.arg(enable_desktop)::bool THEN 'true'
|
|
ELSE 'false'
|
|
END
|
|
)
|
|
ON CONFLICT (key) DO UPDATE
|
|
SET value = CASE
|
|
WHEN sqlc.arg(enable_desktop)::bool THEN 'true'
|
|
ELSE 'false'
|
|
END
|
|
WHERE site_configs.key = 'agents_desktop_enabled';
|
|
|
|
-- GetChatAdvisorConfig returns the deployment-wide runtime configuration
|
|
-- for the experimental chat advisor as a JSON blob. Callers unmarshal the
|
|
-- result into codersdk.AdvisorConfig. Returns '{}' when unset so zero
|
|
-- values apply by default.
|
|
-- name: GetChatAdvisorConfig :one
|
|
SELECT
|
|
COALESCE((SELECT value FROM site_configs WHERE key = 'agents_advisor_config'), '{}') :: text AS advisor_config;
|
|
|
|
-- UpsertChatAdvisorConfig stores the deployment-wide runtime configuration
|
|
-- for the experimental chat advisor. Callers marshal codersdk.AdvisorConfig
|
|
-- to JSON before invoking this query.
|
|
-- name: UpsertChatAdvisorConfig :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('agents_advisor_config', $1)
|
|
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'agents_advisor_config';
|
|
|
|
-- name: GetChatComputerUseProvider :one
|
|
SELECT
|
|
COALESCE((SELECT value FROM site_configs WHERE key = 'agents_computer_use_provider'), '') :: text AS provider;
|
|
|
|
-- name: UpsertChatComputerUseProvider :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('agents_computer_use_provider', sqlc.arg(provider))
|
|
ON CONFLICT (key) DO UPDATE SET value = sqlc.arg(provider) WHERE site_configs.key = 'agents_computer_use_provider';
|
|
|
|
-- GetChatDebugLoggingAllowUsers returns the runtime admin setting that
|
|
-- allows users to opt into chat debug logging when the deployment does
|
|
-- not already force debug logging on globally.
|
|
-- name: GetChatDebugLoggingAllowUsers :one
|
|
SELECT
|
|
COALESCE((SELECT value = 'true' FROM site_configs WHERE key = 'agents_chat_debug_logging_allow_users'), false) :: boolean AS allow_users;
|
|
|
|
-- UpsertChatDebugLoggingAllowUsers updates the runtime admin setting that
|
|
-- allows users to opt into chat debug logging.
|
|
-- name: UpsertChatDebugLoggingAllowUsers :exec
|
|
INSERT INTO site_configs (key, value)
|
|
VALUES (
|
|
'agents_chat_debug_logging_allow_users',
|
|
CASE
|
|
WHEN sqlc.arg(allow_users)::bool THEN 'true'
|
|
ELSE 'false'
|
|
END
|
|
)
|
|
ON CONFLICT (key) DO UPDATE
|
|
SET value = CASE
|
|
WHEN sqlc.arg(allow_users)::bool THEN 'true'
|
|
ELSE 'false'
|
|
END
|
|
WHERE site_configs.key = 'agents_chat_debug_logging_allow_users';
|
|
|
|
-- GetChatPersonalModelOverridesEnabled returns whether users may configure
|
|
-- personal chat model overrides. It defaults to false when unset.
|
|
-- name: GetChatPersonalModelOverridesEnabled :one
|
|
SELECT
|
|
COALESCE((SELECT value = 'true' FROM site_configs WHERE key = 'agents_chat_personal_model_overrides_enabled'), false) :: boolean AS enabled;
|
|
|
|
-- UpsertChatPersonalModelOverridesEnabled updates whether users may configure
|
|
-- personal chat model overrides.
|
|
-- name: UpsertChatPersonalModelOverridesEnabled :exec
|
|
INSERT INTO site_configs (key, value)
|
|
VALUES (
|
|
'agents_chat_personal_model_overrides_enabled',
|
|
CASE
|
|
WHEN sqlc.arg(enabled)::bool THEN 'true'
|
|
ELSE 'false'
|
|
END
|
|
)
|
|
ON CONFLICT (key) DO UPDATE
|
|
SET value = CASE
|
|
WHEN sqlc.arg(enabled)::bool THEN 'true'
|
|
ELSE 'false'
|
|
END
|
|
WHERE site_configs.key = 'agents_chat_personal_model_overrides_enabled';
|
|
|
|
-- GetChatTemplateAllowlist returns the JSON-encoded template allowlist.
|
|
-- Returns an empty string when no allowlist has been configured (all templates allowed).
|
|
-- name: GetChatTemplateAllowlist :one
|
|
SELECT
|
|
COALESCE((SELECT value FROM site_configs WHERE key = 'agents_template_allowlist'), '') :: text AS template_allowlist;
|
|
|
|
-- GetChatIncludeDefaultSystemPrompt preserves the legacy default
|
|
-- for deployments created before the explicit include-default toggle.
|
|
-- When the toggle is unset, a non-empty custom prompt implies false;
|
|
-- otherwise the setting defaults to true.
|
|
-- name: GetChatIncludeDefaultSystemPrompt :one
|
|
SELECT
|
|
COALESCE(
|
|
(SELECT value = 'true' FROM site_configs WHERE key = 'agents_chat_include_default_system_prompt'),
|
|
NOT EXISTS (
|
|
SELECT 1
|
|
FROM site_configs
|
|
WHERE key = 'agents_chat_system_prompt'
|
|
AND value != ''
|
|
)
|
|
) :: boolean AS include_default_system_prompt;
|
|
|
|
-- name: UpsertChatIncludeDefaultSystemPrompt :exec
|
|
INSERT INTO site_configs (key, value)
|
|
VALUES (
|
|
'agents_chat_include_default_system_prompt',
|
|
CASE
|
|
WHEN sqlc.arg(include_default_system_prompt)::bool THEN 'true'
|
|
ELSE 'false'
|
|
END
|
|
)
|
|
ON CONFLICT (key) DO UPDATE
|
|
SET value = CASE
|
|
WHEN sqlc.arg(include_default_system_prompt)::bool THEN 'true'
|
|
ELSE 'false'
|
|
END
|
|
WHERE site_configs.key = 'agents_chat_include_default_system_prompt';
|
|
|
|
-- name: GetChatWorkspaceTTL :one
|
|
-- Returns the global TTL for chat workspaces as a Go duration string.
|
|
-- Returns "0s" (disabled) when no value has been configured.
|
|
SELECT
|
|
COALESCE(
|
|
(SELECT value FROM site_configs WHERE key = 'agents_workspace_ttl'),
|
|
'0s'
|
|
)::text AS workspace_ttl;
|
|
|
|
-- name: UpsertChatTemplateAllowlist :exec
|
|
INSERT INTO site_configs (key, value) VALUES ('agents_template_allowlist', @template_allowlist)
|
|
ON CONFLICT (key) DO UPDATE SET value = @template_allowlist WHERE site_configs.key = 'agents_template_allowlist';
|
|
|
|
-- name: UpsertChatWorkspaceTTL :exec
|
|
INSERT INTO site_configs (key, value)
|
|
VALUES ('agents_workspace_ttl', @workspace_ttl::text)
|
|
ON CONFLICT (key) DO UPDATE
|
|
SET value = @workspace_ttl::text
|
|
WHERE site_configs.key = 'agents_workspace_ttl';
|
|
|
|
-- name: GetChatRetentionDays :one
|
|
-- Returns the chat retention period in days. Chats archived longer
|
|
-- than this and orphaned chat files older than this are purged by
|
|
-- dbpurge. Returns 30 (days) when no value has been configured.
|
|
-- A value of 0 disables chat purging entirely.
|
|
SELECT COALESCE(
|
|
(SELECT value::integer FROM site_configs
|
|
WHERE key = 'agents_chat_retention_days'),
|
|
30
|
|
) :: integer AS retention_days;
|
|
|
|
-- name: UpsertChatRetentionDays :exec
|
|
INSERT INTO site_configs (key, value)
|
|
VALUES ('agents_chat_retention_days', CAST(@retention_days AS integer)::text)
|
|
ON CONFLICT (key) DO UPDATE SET value = CAST(@retention_days AS integer)::text
|
|
WHERE site_configs.key = 'agents_chat_retention_days';
|
|
|
|
-- name: GetChatDebugRetentionDays :one
|
|
-- Chat debug run retention window in days. 0 disables.
|
|
SELECT COALESCE(
|
|
(SELECT value::integer FROM site_configs
|
|
WHERE key = 'agents_chat_debug_retention_days'),
|
|
@default_debug_retention_days::integer
|
|
) :: integer AS debug_retention_days;
|
|
|
|
-- name: UpsertChatDebugRetentionDays :exec
|
|
INSERT INTO site_configs (key, value)
|
|
VALUES ('agents_chat_debug_retention_days', CAST(@debug_retention_days AS integer)::text)
|
|
ON CONFLICT (key) DO UPDATE SET value = CAST(@debug_retention_days AS integer)::text
|
|
WHERE site_configs.key = 'agents_chat_debug_retention_days';
|
|
|
|
-- name: GetChatAutoArchiveDays :one
|
|
-- Auto-archive window in days. 0 disables.
|
|
SELECT COALESCE(
|
|
(SELECT value::integer FROM site_configs
|
|
WHERE key = 'agents_chat_auto_archive_days'),
|
|
@default_auto_archive_days::integer
|
|
) :: integer AS auto_archive_days;
|
|
|
|
-- name: UpsertChatAutoArchiveDays :exec
|
|
INSERT INTO site_configs (key, value)
|
|
VALUES ('agents_chat_auto_archive_days', CAST(@auto_archive_days AS integer)::text)
|
|
ON CONFLICT (key) DO UPDATE SET value = CAST(@auto_archive_days AS integer)::text
|
|
WHERE site_configs.key = 'agents_chat_auto_archive_days';
|