feat: workspace settings as one document, applied server-side (#11740)

* feat: add the workspace settings document, applied server-side

* feat: add wmill workspace get-settings and apply-settings

* fix: warn on unknown settings keys and refuse ee-only ones in the dry run

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: stub the settings document endpoint in the push settings unit tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: refuse mistyped settings and keep 4xx status on a partial settings apply

Reject non-boolean switches and non-string Slack credentials instead of
coercing them into destructive writes, preserve the failing endpoint's
status when wrapping a partial-apply error, keep the stored Slack secret
when an older CLI pushes one encrypted under another workspace's key,
print the plan's warnings in apply-settings, and state the admin check
load_settings_document leaves to its caller.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: compare handler arguments verbatim and type-check scalar settings

A handler's extra_args is stored as sent and its nulls reach the job as
arguments, so an added or removed null argument is now a change. Scalar
settings are type-checked in the plan so a dry run never reports a change
the endpoint then refuses, and an error handler's omitted mute flags
compare as the false the endpoint stores.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: require a 32-bit integer rate limit in the settings plan

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: data table permissions and protection rules in the settings document

The settings document gains datatable_permissions, each data table's roles
named by their Postgres role name and written by catalog id, and
protection_rules, keyed by rule name. Both are managed only when stated and
stated as a whole. The dev workspace lock stays with the dev workspace
setting.

wmill sync carries datatable_permissions only with includeDatatablePermissions
(or --include-datatable-permissions), since tenants and roles are specific to
a workspace and an instance. protection_rules stay out of settings.yaml:
protection-rules.yaml syncs them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: validate the default data table role and plan permissions against a replaced datatable

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-10-14 12:35:08 +00:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 17fefcf777
commit 9a5ec223bc
25 changed files with 2449 additions and 136 deletions
+6 -1
View File
@@ -187,6 +187,8 @@ export interface PushObjOptions {
wsSpecific?: boolean;
/** encryption_key push: non-interactive flag and explicit re-encryption choice */
keyPushOpts?: PushWorkspaceKeyOptions;
/** settings push: whether settings.yaml manages the data tables' roles */
includeDatatablePermissions?: boolean;
/** TypeScript runtime a bare `.ts` denotes, for raw-app runnables */
defaultTs?: "bun" | "deno";
/** schedule push into a fork: the file's `enabled` is the parent's */
@@ -218,6 +220,7 @@ export async function pushObj(
permissionedAsContext,
wsSpecific,
keyPushOpts,
includeDatatablePermissions,
defaultTs,
enabledOwnedByParent,
} = opts;
@@ -285,7 +288,9 @@ export async function pushObj(
} else if (typeEnding === "workspace_dependencies") {
await pushWorkspaceDependencies(workspace, p, befObj, newObj);
} else if (typeEnding === "settings") {
await pushWorkspaceSettings(workspace, p, befObj, newObj);
await pushWorkspaceSettings(workspace, p, befObj, newObj, {
includeDatatablePermissions,
});
} else if (typeEnding === "encryption_key") {
await pushWorkspaceKey(workspace, p, befObj, newObj, keyPushOpts);
} else {