Commit Graph

27 Commits

Author SHA1 Message Date
hugocasa e8e0701a36 feat(api): add endpoint to update token label (#9474)
* feat(api): add endpoint to update token label

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(api): prevent renaming the session token label

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(api): restrict token-label edits to user tokens, not just session

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): edit token label in the edit modal instead of inline

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(api): reject relabeling tokens to reserved system-token names

Centralize the is_user_token classifier in windmill-common and reuse it
to reject labels colliding with system-token namespaces (ephemeral*,
debugger-token, mcp-oauth-*), not just session.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(api): match ephemeral label case-insensitively and cap label length

Align the canonical is_user_token, the SQL guard and the frontend mirror on
a case-insensitive `ephemeral` match (so a token can't be relabeled to a
casing the backend allows but the UI hides), reject labels over the
VARCHAR(1000) column limit with a 400, and add unit tests for is_user_token.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 17:33:31 +00:00
Ruben Fiszel 2ddf93de96 fix(auth): enforce monotonic privilege on user token lifecycle endpoints (#9371) 2026-05-30 11:45:42 +00:00
Ruben Fiszel b125eca762 feat(service-accounts): allow choosing role at creation time (#9307)
* [ee] feat(service-accounts): allow choosing role at creation time

Previously, service accounts were hardcoded to operator and could not be
used as the CLI sync user since they had no write access. They also only
counted as 0.5 seat each.

This change:
- Extends `NewServiceAccount` to accept optional `is_admin` / `operator`
  (defaults to `operator=true` for backward compatibility).
- Exposes a role picker in `AddUser.svelte` when creating a service
  account (Operator / Developer / Admin).
- Lets admins update a service account's role from the user list (it
  used to be locked to "Operator" with a tooltip).
- Updates the OpenAPI spec + regenerates the frontend client.

A developer/admin service account counts as 1 seat under the existing
seat-cap logic (operators stay at 0.5).

Companion PR on windmill-ee-private updates the `INSERT INTO usr` to
honour the chosen role.

Fixes WIN-1985

* [ee] feat(service-accounts): wm_deployers opt-in for Dev role

When creating a service account with role=Developer, surface a toggle
"Add to wm_deployers" (recommended). Members of wm_deployers can deploy
on behalf of other users — the typical setup when the service account is
used as the CLI sync / CI deploy identity.

- `NewServiceAccount` gains an optional `add_to_deployers` flag.
- Frontend defaults the toggle to on but only shows it under Developer
  (admins have it implicitly; operators can't deploy).
- Tooltip links to docs.windmill.dev "Run on behalf of".

Companion EE PR updates the handler to INSERT into usr_to_group for
wm_deployers when the flag is set.

Refs WIN-1985

* chore: update ee-repo-ref to 974ed42067d9f63acb42332b671b8c01ffd4b625

This commit updates the EE repository reference after PR #589 was merged in windmill-ee-private.

Previous ee-repo-ref: f7dbc3cc2ba21c396f4828881e3b9d9ab6f50c69

New ee-repo-ref: 974ed42067d9f63acb42332b671b8c01ffd4b625

Automated by sync-ee-ref workflow.

* [ee] fix(service-accounts): unhardcode role in superadmin user list

Two review issues from the merged #9307 / #589:

1. P1 — The global Users tab in #superadmin-settings still pinned every
   service account to "Operator". Now it shows the actual role
   (Admin / Operator / Developer), derived from the SA's usr row.

   - `list_users_as_super_admin`: replaced `true as operator_only` with
     the real `operator` value, and added `is_workspace_admin` from the
     row (NULL for password users since their admin status is
     per-workspace).
   - `global_whoami`: when the email belongs to a service account, look
     up its real `operator` / `is_admin` instead of pinning to operator.
   - `SuperadminSettingsInner.svelte`: drop the hardcoded "Operator"
     badge; render Admin / Operator / Developer using the new fields,
     matching the workspace-level view.

2. P2 — Regenerate the bundled `openapi-deref.{yaml,json}` so the
   `createServiceAccount` body (now exposing `is_admin`, `operator`,
   `add_to_deployers`) and the new `GlobalUserInfo.is_workspace_admin`
   field show up at runtime in `/api/openapi.{yaml,json}`.

Bumps `ee-repo-ref.txt` to the EE follow-up that adds the offline
seat-cap check on `create_service_account`.

Refs WIN-1985

* chore: update ee-repo-ref to b7a6068c1f3dc845e012959268b2426f0de4d697

This commit updates the EE repository reference after PR #590 was merged in windmill-ee-private.

Previous ee-repo-ref: 0b1307c21d1bfd6fb43a03c2ba39d2a8bf8e6470

New ee-repo-ref: b7a6068c1f3dc845e012959268b2426f0de4d697

Automated by sync-ee-ref workflow.

---------

Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-05-25 16:18:38 +00:00
Ruben Fiszel e5286f4607 feat: include service accounts in instance settings users list (#9157)
* feat: include service accounts in instance settings users list

Service accounts (workspace-scoped, no password row) now appear in the
superadmin users list with a Bot icon, workspace badge, and a link to
manage them in the workspace settings. Role is locked to Operator.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: update sqlx offline cache

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: use composite key for users each block

Service accounts can share emails across workspaces, so key by email + workspace_id to avoid Svelte each_key_duplicate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 06:46:48 +00:00
Ruben Fiszel d666e8431c feat: read-only flag on API tokens (#9144)
* feat: read-only flag on API tokens, orthogonal to scopes

Add a per-token `read_only` boolean set at creation time. When true, the
token can only call HTTP methods classified as Read (GET/HEAD/OPTIONS).
Mutating methods and job-run actions are rejected with 403, regardless of
which scopes are attached. Surfaced as a prominent toggle in the standard
token-creation flow and a discreet `2xs` toggle in MCP mode (where users
often want write access, so we don't bias them toward enabling it).

MCP enforcement: read-only tokens hide all script/flow/hub tools from
`list_tools` and only see endpoint tools whose method is GET, and the
runner rejects `call_tool` on anything mutating.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: review fixes for read-only token flag

- Exempt /api/mcp/* and /mcp/* paths from the read-only middleware check.
  MCP transport runs over POST (streamable HTTP / SSE), so otherwise the
  middleware would 403 every MCP request before the runner could enforce
  read-only at the tool-call level.
- Tighten is_endpoint_read_only to GET only, matching the read_only_hint
  that create_endpoint_annotations actually emits.
- Add unit test for check_read_only_for_route covering GET/HEAD/OPTIONS,
  mutating methods, and run paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: bump ee-repo-ref to read-only-trigger-toggle

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(frontend): make read-only toggle discreet in both modes

Match the MCP-mode treatment in standard mode: text-tertiary, 2xs, shared
"Read-only" label. The tooltip switches per mode so the explanation still
fits the context.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(frontend): gate read-only toggle behind Limit token permissions

The read-only toggle now only shows when the user has limited the token's
scopes (standard mode) or in MCP mode (which always picks an MCP scope).
Turning the limit off also resets read-only so it doesn't silently stick.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(frontend): hide incompatible MCP tools when read-only is on

When the read-only toggle is on in MCP mode:
- Endpoint badges and the custom-mode endpoint MultiSelect filter to GET.
- Already-selected non-GET endpoints are pruned from the scope.
- The scripts/flows preview is replaced with a note explaining they're
  hidden (the runner already rejects script/flow runs for read-only).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(frontend): place read-only toggle at top of limited scope area

The previous gate required at least one scope to be picked before the
read-only toggle appeared, which made it look missing while the user was
still building their scope list. Move the toggle inside ScopesPicker:
- Standard mode: sits directly under the "Limit token permissions" toggle
  whenever Limit is on, before the scope selector.
- MCP mode: sits at the top of the MCP scope block.

readOnly is now $bindable on ScopesPicker so CreateToken still owns the
value. The auto-reset on un-limit moves into ScopesPicker too.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(frontend): nest read-only toggle inside the scope list card

Place the read-only toggle at the top of the scope list (between the
Selected Scopes summary and the bordered domain list) via a new optional
topSlot snippet on ScopeSelector. Keeps ScopeSelector decoupled from
read-only specifics; ScopesPicker fills the slot.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to 9bc8160be50b3e57a60daf4e1b71c389a6e02b8a

This commit updates the EE repository reference after PR #571 was merged in windmill-ee-private.

Previous ee-repo-ref: f53d26e6685dfd60bfa67686fbd7358169cfd130

New ee-repo-ref: 9bc8160be50b3e57a60daf4e1b71c389a6e02b8a

Automated by sync-ee-ref workflow.

* fix: address CI review for read-only token flag

- P1 (Codex): narrow the MCP middleware exemption from "any /api/mcp/*"
  to just the streamable HTTP transport endpoints
  (/api/mcp/gateway, /api/mcp/w/{ws}/{mcp,sse,list_tools}). Without this,
  a read-only token could POST /api/mcp/gateway/oauth/server/approve and
  mint a follow-on non-read-only MCP token via the OAuth code/token
  exchange.
- P2 (Claude/cubic): fix test comment/assertion mismatch — the run-path
  assertion now exercises GET (which is what the RUN_PATH_ACTIONS
  elevation comment describes) in addition to POST. Add a regression
  assertion for /api/mcp/gateway/oauth/server/approve.
- P2 (cubic): short-circuit script/flow/hub-script/resource fetches in
  MCP list_tools when read_only is on — they would only be discarded
  below, so skipping the DB and resource fan-out is pure win.
- P2 (cubic): when scopes are pre-supplied via the CreateToken prop, the
  ScopesPicker isn't rendered, which previously hid the read-only
  toggle entirely. Render it next to the pre-supplied scopes display.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-05-13 15:26:21 +00:00
hugocasa e74f06cb56 fix: handle singlestepflow zombies and stop filtering them from runs page (#9055)
* fix: handle singlestepflow zombies and stop filtering them from runs page

* fix: support singlestepflow in batch_rerun_jobs

Previous PR added singlestepflow to list_selected_job_groups so the BatchReRun
pane shows them, but batch_rerun_jobs_inner still joined on kind = 'script' /
'flow' with j.runnable_id (which is NULL for SingleStepFlow), so the rows were
silently filtered out — user sees the option, click Re-run, gets zero successes.

Mirror the norm_kind CTE projection from list_selected_job_groups inside
batch_rerun_jobs_inner: pull the wrapped runnable type and pinned script hash
from raw_flow.modules[id='a'], cast back to JOB_KIND so the existing handler
dispatch works unchanged. Path-based schema fallback so input_transforms still
resolve at rerun time.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: project singlestepflow in batch-rerun schema lookups

Codex review pointed out two follow-on regressions from the previous fix:

(1) list_selected_job_groups returned schemas with script_hash=null and
schema=null for singlestepflow rows because the inner schemas subquery still
joined runnable metadata via j.runnable_id (NULL for SingleStepFlow). The
BatchReRun pane consumes every selected.schemas entry through
mergeSchemasForBatchReruns / buildExtraLibForBatchReruns, both of which
assume real schema objects.

(2) When use_latest_version=true, batch_rerun_handle_job re-fetched
latest_schema from v2_job filtering jb.kind='script' or 'flow' — neither
matched singlestepflow, so schema came back NULL and every input_transforms
entry silently no-op'd.

Both queries now project singlestepflow rows via raw_flow.modules[id='a'] —
norm_kind for dispatch and effective_hash for the schemas join, plus a
path-based latest-schema fallback so flow-wrapped SSF (no version pinning)
and any SSF whose pinned hash has been deleted still resolve.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test: add batch_rerun integration tests, fix SSF hash hex parsing

Adds 11 integration tests against /jobs/run/batch_rerun_jobs and
/jobs/list_selected_job_groups (both endpoints had zero CI coverage).
Tests cover the full 4-kind × 3-mode matrix: regular Script and Flow
(baseline regression for the SQL refactor), script-wrapped and flow-
wrapped SingleStepFlow (regression for the bugs this PR fixes), and a
mixed-kind batch.

Writing the tests caught a real bug in the previous commit: ScriptHash
serializes as a 16-char hex string in raw_flow.modules[a].value.hash
(per the custom Serialize impl in windmill-types/scripts.rs), not as
an integer. The earlier `(m->'value'->>'hash')::bigint` cast worked
on the hand-inserted SQL fixture I'd used for live testing (which
embedded the hash as a raw integer) but failed in production where
all SSF jobs are pushed via JobPayload::SingleStepFlow's serialized
form. Replaced with `('x' || lpad(hex, 16, '0'))::bit(64)::bigint` —
preserves the twos-complement bit pattern so both positive and
negative i64 hashes round-trip correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Update SQLx metadata

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-05-07 08:00:38 +00:00
Ruben Fiszel 1174d7d77f refactor: replace SELECT * with explicit column lists (#9010)
* refactor: replace SELECT * with explicit column lists

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

* chore: update sqlx offline query cache

* chore: update sqlx offline query cache

* chore: update sqlx offline query cache with EE support

* chore: update sqlx offline query cache, no deletions

* chore: update sqlx offline query cache after rebase

* fix: correct column names in explicit script query lists

- concurrency_limit → concurrent_limit (matches DB column name)
- runnable_settings → runnable_settings_handle (matches DB column name)

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

* fix: add missing delete_after_secs column to script queries

Also add integration test covering all explicit-column export queries.

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

* test: add workspace export integration test covering all explicit-column queries

Covers tarball_workspace (folder, script, resource, resource_type, variable,
schedule, usr, group_) and the mcp_oauth_client SELECT query from windmill-mcp.

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

* test: add tarball export integration test covering all explicit-column queries

Single test creates one of each entity type and exercises every runtime-checked
explicit-column query in tarball_workspace. Uses archive_type=tar to avoid
zip feature-gate in CI.

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

* docs: fix stale verification step and CI contradiction in update-sqlx skill

- Regenerate current_files.txt after EE cache restoration so step 4 reports accurate diff
- Scope "Never use SQLX_OFFLINE=true" to local prepare (CI legitimately uses it)

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

* docs: remove Co-Authored-By from commit skill template

* refactor: extract SCRIPT_COLUMNS const to single source of truth

Replaces 5 duplicated 44-column lists with a shared const in windmill-types.

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-04 08:39:45 +00:00
Ruben Fiszel 392888d113 docs: add SAFETY comments to all dynamic SQL call sites (#9009)
* docs: add SAFETY comments to all dynamic SQL call sites

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

* docs: address review feedback on SAFETY comments

- Fix missed comment for obo_triggers loop in offboarding.rs
- Fix variable name in comment (table -> table_name) in offboarding.rs
- Fix api-settings comment to reference inline VALID_NAME regex, not validate_dbname()
- Add SAFETY comments to batch_execute calls in api-settings
- Fix db.rs comment: PG_SCHEMA is env var, not compile-time constant
- Add doc comments on RunnableSettingsTraitInternal constants

* docs: remove misleading SAFETY comment on static SQL

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-03 07:14:14 +00:00
hugocasa e9e72fbbf8 feat: edit scopes on existing API tokens (#8967)
* feat: edit scopes on existing API tokens

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: address PR review feedback on token scope edit

- add SECURITY DEFINER to notify_token_scopes_change so trigger fires under windmill_user/admin roles (cubic P1)
- drop banned $bindable(default) on optional props (CLAUDE.md): make ScopesPicker.value and EditTokenScopesModal.open required
- detect MCP only when *every* scope starts with mcp: so mixed/null-scope tokens fall back to standard picker without dropping non-mcp scopes
- audit log scope payload via serde_json instead of Rust {:?}

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 19:49:56 +00:00
hugocasa d6c642b170 feat: add Azure Event Grid triggers (#8888)
* feat: add Azure Event Grid triggers (EE)

Introduces a new enterprise trigger kind `azure` that supports three
modes via a single unified trigger type:
- basic_push: Azure Event Grid basic — custom topics, system topics
  (Storage, Resource Manager, Key Vault, etc.), domains (push only)
- namespace_push: Event Grid Namespace topics (CloudEvents over HTTP push)
- namespace_pull: Event Grid Namespace topics (HTTP pull with lock-token
  ack/reject for dead-lettering)

Auth uses a Service Principal resource (tenant_id, client_id,
client_secret, subscription_id). Subscriptions are created in
CloudEvents 1.0 schema so the push webhook handler and the pull listener
share one payload parser.

Backend
- New crate `windmill-trigger-azure` (OSS stubs + EE impl symlinked from
  windmill-ee-private)
- Migration `azure_trigger` table with CHECK constraints enforcing
  mode/columns coherence
- `TriggerKind::Azure`, `JobTriggerKind::Azure`,
  `DeployedObject::AzureTrigger` variants
- Push route `/api/azure/w/{workspace}/*path` handles classic
  Event Grid SubscriptionValidation handshake and CloudEvents 1.0
  abuse-protection OPTIONS handshake
- Optional inbound JWT validation (audience check only for v1)
- Feature flag `azure_trigger` propagated through windmill-api,
  windmill-store (resource helper), and added to ee_core

Frontend
- `triggers/azure/` editor with mode toggle (basic/namespace-push/
  namespace-pull) and per-mode config (topic ARM id / namespace +
  topic name / subscription / filters / push auth / pull options)
- Registered in icon map, display names, save functions, badge,
  wrapper, editor, add-trigger menu

OpenAPI
- `AzureTrigger`, `AzureTriggerData`, `AzureMode`,
  `AzureSubscriptionMode`, `AzureDeliveryConfig`, `TestAzureConnection`
  schemas; `/azure_triggers/*` endpoints; client regenerated

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to eaa7c3a9cb37a9ccc93f10a2535d929365acd2d8

This commit updates the EE repository reference after PR #541 was merged in windmill-ee-private.

Previous ee-repo-ref: 9689014e8c12c36c1059fd8fa5758d550b8b8bc9

New ee-repo-ref: eaa7c3a9cb37a9ccc93f10a2535d929365acd2d8

Automated by sync-ee-ref workflow.

* feat(azure-trigger): secret-auth push, ARM discovery, capture isolation, CLI + parity

Frontend:
- Split mode selector into Namespace/Basic + Pull/Push
- ARM resource dropdowns (namespaces, Basic topics, namespace topics)
  populated from the service principal; cascade with stale-selection
  reset on SP / edition change
- Remove stale authenticate toggle + audience input (server-managed
  push_auth_config has replaced them)
- Azure listing page: "Create from template" button; "Also delete Azure
  subscription" toggle in the delete modal; simplified trigger label
  falling back to path
- AzureCapture.svelte: "Test subscription name" with -wm-capture suffix
- CompareWorkspaces.svelte: wire Azure for fork/compare
- Drop Trigger-deployed/event-loss warning (capture subscription is
  isolated with -wm-capture)

Backend:
- Shared-secret push auth (see EE crate for detail)
- JSONB push_auth_config column (renamed from delivery_config), #[serde(skip)]
  so clients/CLI/exports never see it
- Drop redundant enabled column; mode supersedes
- Azure capture infra: AzureTriggerConfig + set_azure_trigger_config +
  azure_payload route + TriggerKind::Azure arm; PT15M queue TTL on
  capture subscriptions so they bound storage after tab close
- Granular ACLs, users offboarding, trash, git-sync deployed-object:
  all include azure_trigger

CLI:
- Add azure to TRIGGER_TYPES, pushObj dispatch, getTypeStrFromPath,
  trigger commands (get/update/create/list/template), sync delete
  switch + regex; e2e test for `trigger new --kind azure`
- system_prompts: SCHEMA_MAPPINGS + schema_names include AzureTrigger;
  auto-generated/* regenerated

Skill:
- .claude/skills/adding-a-trigger/ checklist covering every file that
  needs editing when wiring a new trigger type (learned from this PR)

ee-repo-ref bumped to b0e490cbf3724b7b64c6a5b010e3bdf24acd873c.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(azure-trigger): ci — ShareModal Kind + regenerated system_prompts

- frontend/src/lib/components/ShareModal.svelte: add 'azure_trigger'
  to the Kind type so the listing page's "Permissions" action compiles
  (ts2345 — caught by npm_check on CI, missed by fast-check locally).
- system_prompts/auto-generated/: regenerate to drop the stale
  delivery_config / AzureDeliveryConfig fields from the Azure schema
  (check-freshness on CI).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(azure-trigger): use workspace constant_time_eq crate

Drop hand-rolled constant-time compare in favour of the workspace
constant_time_eq crate (same one used by http_trigger_auth).

ee-repo-ref bumped to 9659382d47286e7f7f66d01b6f5dd8d4ed34848b.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(azure-trigger): pass placeholder + disabled via inputProps

`TextInput`'s `placeholder` and `disabled` go through its `inputProps`
prop — CI's `npm run check` caught the stale top-level passing that
`npm run check:fast` missed. Align with the DefaultEmailConfigSection
pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(azure-trigger): correct LATEST_GIT_SYNC_SCRIPT_PATH version to 28213

The hub deploy of the azure-aware sync-script is version 28213, not
28214. Backend was pinning a non-existent hub script, which broke the
git_sync_e2e suite (every deploy's sync step 404'd).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(azure-trigger): add azure_triggers to token scope selector + skill

- windmill-api/src/token.rs: `build_trigger_scope_domains` was missing
  `("azure_triggers", "Azure Event Grid")`, so the CreateToken UI's scope
  selector didn't surface azure_triggers:read/write. Backend already had
  `ScopeDomain::AzureTriggers` wired (scopes.rs), this just exposes it.
- .claude/skills/adding-a-trigger/SKILL.md: capture both scope-related
  files under the hardcoded-arrays section so future triggers don't miss
  the UI surface.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(adding-a-trigger-skill): clarify token.rs scope effect

Not a regression — nothing was working before. Skipping TRIGGER_DOMAINS
just means the scope works via API/CLI but has no UI checkbox.

* docs(adding-a-trigger-skill): trim token.rs bullet

* fix(azure-trigger): regen openapi-deref + swap textarea for TextInput

- Run build_openapi.sh to regenerate openapi-deref.{yaml,json} with the
  12 azure_triggers paths + schemas. These files are served by the
  runtime (include_str! in windmill-api/src/lib.rs) to external SDK
  consumers; without this regen the new endpoints wouldn't be advertised.
- Replace the raw <textarea> for event type filters with the
  design-system TextInput in textarea mode (frontend/CLAUDE.md bans raw
  HTML elements).

Addresses cubic + claude PR review items.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-04-23 16:30:18 +00:00
Ruben Fiszel 0cfa131254 feat: add disable_password_login global setting (#8873)
Adds an instance-level toggle that hides the email/password form on the
login page and rejects password login, password reset request, and
password reset endpoints server-side. Useful for OAuth/SAML-only
deployments.

- New `disable_password_login` global setting + lazy_static AtomicBool
- `load_disable_password_login` loader wired into monitor initial_load
  and notify_global_setting_change listener
- Unauthenticated `GET /auth/is_password_login_disabled` endpoint so the
  login page can hide the password form when enabled
- Toggle in Instance Settings → Auth/OAuth/SAML
- Login.svelte hides the password form and the "Log in without
  third-party" toggle when the setting is on

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 18:12:30 +00:00
Ruben Fiszel 5b3913052e refactor: convert read-hot globals to AtomicBool/I64 and ArcSwap (#8815)
* refactor: extract load helpers from reload_setting family

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: convert atomic primitive globals to AtomicBool/AtomicI64

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: convert CRITICAL_*/HUB_API_SECRET/INSTANCE_EVENTS_WEBHOOK/JWT_SECRET to ArcSwap

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: pin ee-repo-ref to arcswap-refactor EE branch commit

* refactor: convert BASE_URL/HUB_BASE_URL/MIN_VERSION/LICENSE_KEY*/LICENSE_KEY_ID to ArcSwap

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: convert worker hot-path globals to ArcSwap (WORKER_CONFIG et al)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: pin ee-repo-ref to combined arcswap-urls+worker EE commit

* chore: update ee-repo-ref to d8be8f88cb8898c8f6b27421989d53528223815d

This commit updates the EE repository reference after PR #532 was merged in windmill-ee-private.

Previous ee-repo-ref: c375aaaac9ec0fc0480993627d0defc8054c31a4

New ee-repo-ref: d8be8f88cb8898c8f6b27421989d53528223815d

Automated by sync-ee-ref workflow.

* fix: cleanup unused imports + fix 2 missed WORKER_CONFIG readers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to ce0f8fbbbde09c4a858312d2d8716d224e99042c

This commit updates the EE repository reference after PR #534 was merged in windmill-ee-private.

Previous ee-repo-ref: 450b601b5aba0ca0b2045f4b5071aa8701b4bfb7

New ee-repo-ref: ce0f8fbbbde09c4a858312d2d8716d224e99042c

Automated by sync-ee-ref workflow.

* fix: secret_backend_integration test — BASE_URL.write().await → .store()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: convert APP_WORKSPACED_ROUTE to AtomicBool for symmetry with HTTP_ROUTE_WORKSPACED_ROUTE

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to e587df8 (post-#535 merge)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-04-14 00:04:10 +00:00
hugocasa 435b25e6a4 feat: add user offboarding flow with object reassignment (#8647)
* feat: add user offboarding flow with object reassignment

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: require new_operator for permissioned_as when reassigning to folder

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: update on_behalf_of_email on scripts/flows during offboarding

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: extract offboarding to separate module and add integration tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: delete tokens, add operator preview counts, remove token reassignment UI

Tokens are now always deleted during offboarding. Preview now shows
scripts/flows/apps with on_behalf_of and schedules/triggers with
permissioned_as referencing the departing user (even outside their path).
Token reassignment UI removed since webhooks break on path changes anyway.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: rich preview with path lists, warnings, and downloadable report

Preview now returns full path lists (not just counts) for owned objects
and objects executing on behalf of the user. Adds warnings for:
- HTTP triggers (webhook URLs will change)
- Email triggers (addresses will change)
- Broken $var:/$res: references in resources/variables
Frontend provides "Export list" button to download affected content.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add coverage for dynamic queries (triggers, extra_perms, operator schedules)

Adds HTTP trigger, extra_perms reference, and shared schedule to test
fixture. Tests verify that non-macro sqlx queries (trigger reassignment,
extra_perms cleanup, operator schedule update) work correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove broken_references, add full dynamic query test coverage

Remove broken_references field from preview (user's resources/variables
are already in the owned paths list). Add shared HTTP trigger fixture
to test all dynamic query paths: trigger operator preview (line 232),
trigger permissioned_as update for non-user-path (line 951), and
extra_perms cleanup on trigger tables (line 983).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add referencing field to preview for content/value path references

Preview now includes a 'referencing' section listing scripts (by content),
flows (by value JSON), apps (by policy/extra_perms), and resources (by value)
that contain references to u/{username}/ paths. These references may break
after reassignment. Shown in export list and as a warning in the UI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: rename objects to items in UI, detect on_behalf_of items in hasItems

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: replace remaining objects with items in UI text

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: rename operator to on_behalf_of, separate owned vs on-behalf UI sections

- Rename new_operator to new_on_behalf_of_user in API and frontend
- Rename op_ prefixed variables to obo_ in backend
- UI now shows separate sections for owned items and items running
  on behalf, with the operator selector shown only when needed
- canSubmit logic updated: operator needed for folder targets OR
  when on-behalf items exist

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: CSV export, side-by-side layout, always accept new_on_behalf_of_user

- Export affected items as CSV instead of text
- Owned items and on-behalf items shown side by side in summary boxes
- new_on_behalf_of_user always accepted (defaults to target user for
  user targets, required for folder targets)
- On_behalf_of selector always visible, auto-defaults when user target
  is selected

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: proper pluralization and bottom-aligned counts in summary boxes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: stack info boxes vertically, referencing box as warning style at top

Info boxes (owned, on-behalf, referencing) now one per row instead of
side-by-side. Referencing box uses warning colors. Webhook/email trigger
alerts shown below boxes. Proper pluralization in global modal too.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: CSV exports only referencing items, export button inside warning box

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: use ToggleButtonGroup for user/folder, add reassign toggle on remove

- User/Folder selection now uses ToggleButtonGroup component
- When removing a user, a "Reassign items before removing" toggle lets
  the admin skip reassignment and just delete directly
- In reassign-only mode, the toggle is not shown (always reassigns)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: show token details with labels and scopes in preview

Preview now returns token label, scopes, and expiration instead of just
a count. Frontend shows a dedicated token box listing each token with
its scopes. Test updated to verify token label in preview response.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: extract shared offboarding components, per-type trigger links, hash deep linking

- Extract OffboardItemsBox, OffboardReassignControls, OffboardWorkspaceSection,
  and offboarding-utils.ts as shared components used by both workspace and global modals
- Change triggers in OffboardAffectedPaths from Vec<String> to HashMap<String, Vec<String>>
  so frontend knows which trigger page to link to
- Add hash-based deep linking to all 9 trigger pages and schedules page
- Preserve URL hash in updateQueryFilters across all trigger pages
- Only open editor drawer if the item is found in the list
- Reassign toggle at top with warning alert when disabled (both modals)
- Referencing items box uses yellow warning variant with expandable path links
- Cleaner labels: "Move u/{username}/* items to", "Update triggers/runnables permissions to"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: rename on_behalf_of section label to match flow advanced settings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: regenerate sqlx query cache

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR review issues for offboarding

- Add 9 trigger tables to check_path_conflicts for user-friendly conflict messages
- Fix submit button no-op when user has only on-behalf items (show target selector, fix canSubmit)
- Only delete workspace user when reassignment entry exists (prevent orphaned objects)
- Add $azure_kv: prefix to vault secret query (match rename_user pattern)
- Use Svelte 5 onSelected callback instead of deprecated on:selected
- Make ScriptBuilder section label conditional on canPreserve
- Fix CSV export to include trigger paths via flattenPaths utility
- Fix test_offboard_reassign_only to remove conflicts and assert on response
- Parallelize workspace config fetches in global modal with Promise.all
- Delete tokens when deleting workspace user
- Return structured JSON from global offboard endpoint

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* sqlx

* fix: address second round of PR review issues

- Accumulate per-workspace OffboardSummary in global offboard instead of returning zeros
- Delete workspace user unconditionally when delete_user=true (prevent orphaned usr rows)
- Filter archived/deleted scripts in check_path_conflicts to match preview
- Reset form state when workspace offboard modal reopens
- Move hashHandled=true inside trigger-found guard on all 10 deep-link pages

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: improve offboarding integration tests

- Add second workspace to fixture for multi-workspace global offboard testing
- Add test_global_offboard_execution: verifies items reassigned across 2
  workspaces, user deleted from both, and password row deleted from instance
- Add test_offboard_invalid_target: verifies 400 for nonexistent user,
  nonexistent folder, and invalid target format
- Fix test_offboard_to_user: use single DELETE, add explicit new_on_behalf_of_user
- Fix test_global_offboard_preview: assert 2 workspaces instead of 1

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address third round of PR review issues

- Fix ScriptBuilder tooltip to match conditional section label wording
- Clear stale conflicts in global modal on reopen
- Fix test_offboard_to_folder to assert on specific moved path, not pre-existing data
- Allow deleting user with zero items (show Offboard button, skip reassignment)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add global token deletion warning in instance-level offboard modal

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* update sqlx

* fix: add raw_app path and dependency_map path reassignment to offboarding

Audit found these tables with user-scoped paths were not being updated:
- raw_app: mirrors app paths, needs path reassignment
- dependency_map: importer_path and imported_path reference user paths

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: move user cleanup to delete_workspace_user_internal, fix review issues

- Move extra_perms, folder owners, drafts, favorites, inputs, captures
  cleanup into delete_workspace_user_internal so any user deletion gets
  proper cleanup (not just offboard path)
- Fix flow INSERT missing labels and lock_error_logs columns (data loss)
- Fix validate_target returning 404 instead of 400 for nonexistent targets
- Fix canSubmit blocking delete when user has no items to reassign
- Fix token preview query filtering out tokens without scopes
- Fix token warning messages: workspace-level mentions webhooks/HTTP triggers,
  instance-level mentions API calls using credentials
- Fix "Schedules and triggers" -> "Triggers and runnables" wording
- Show token section at instance level only when tokens exist
- Show Offboard button at instance level when user has no items but deleteUser=true

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 17:31:36 +00:00
Ruben Fiszel c86846ac19 rate limit token creation on CLOUD_HOSTED (10/min per user) (#8664)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 01:52:02 +00:00
Ruben Fiszel 754b88a52c fix: harden login rate limiting with CLOUD_HOSTED gating and memory eviction (#8602)
* fix: harden login rate limiting with CLOUD_HOSTED gating, memory eviction, and race fix

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: replace global DashMap with atomics and move extract_client_ip inside conditional

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 13:52:13 +00:00
Ruben Fiszel 06bbe7b94b fix: add per-IP and per-account brute force protection on login endpoint (#8601)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 13:16:11 +00:00
Ruben Fiszel 3959fe8297 feat: add workspace-level service accounts (#8560)
* feat: add workspace-level service accounts (EE)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* sqlx

* sqlx

* chore: update ee-repo-ref

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:23:03 +00:00
Ruben Fiszel 0389d9601c chore: upgrade axum 0.7 to 0.8 (#8539)
* chore: upgrade axum 0.7 to 0.8 and related dependencies

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add route reachability tests for ~80 previously untested endpoints

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: switch feature-gated trigger handlers from axum::async_trait to async_trait crate

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: update new trash routes to axum 0.8 path syntax

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to latest EE commit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: upgrade route tests to assert 2xx responses with proper data setup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: restore npm_proxy and ai_routes tests using local echo servers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: gate workspace fork test behind enterprise feature flag

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add ~40 more endpoint tests (jobs authed, health, favorites, ACLs, reachability)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address review findings from axum 0.8 upgrade

- Use cookie value_trimmed() instead of value() for cookie 0.18 compat
- Update comments still referencing old :workspace_id syntax

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to 61ae055ea31481f1899953e9d5f65566b8c707b1

This commit updates the EE repository reference after PR #486 was merged in windmill-ee-private.

Previous ee-repo-ref: 0059d175a6fdddf52998b183bf91059b224704ac

New ee-repo-ref: 61ae055ea31481f1899953e9d5f65566b8c707b1

Automated by sync-ee-ref workflow.

* test: add test for new get_imports endpoint

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove unused import in raw_apps test

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-03-27 09:55:04 +00:00
hugocasa 0bd756839c feat: SCIM user deprovisioning (active:false) + instance-level user disable (#8484)
* [ee] feat: handle active:false in SCIM user PATCH/PUT for deprovisioning

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref for SCIM active:false deprovision fix

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* nit sqlx

* [ee] feat: add password.disabled column for SCIM user deactivation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [ee] feat: enforce password.disabled in auth checks

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [ee] refactor: use scim_deactivated_user table instead of password.disabled

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [ee] fix: apply SCIM filters to deactivated users, add name column

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: add down migration for scim_deactivated_user

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: rename migration to avoid timestamp conflict, update sqlx cache

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [ee] refactor: use password.disabled for SCIM deactivation, block login for disabled users

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [ee] feat: show disabled toggle in superadmin user list, add disabled field to API

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add confirmation modal when disabling instance user

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: improve disable user confirmation text

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: revert toggle state when disable confirmation is cancelled

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: properly revert toggle on disable cancel using reset key

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: move disable/enable to dropdown menu, add disabled badge on email

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: rename 'Show active users only' to 'Recently active only' to avoid confusion with disabled state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove accidentally committed gen files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use .catch() for enable user error handling in dropdown action

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: delete tokens on user removal, improve confirmation modal texts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update sqlx cache for non-enterprise code paths

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: restore sqlx cache files deleted by incorrect prepare run

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add missing sqlx cache for non-enterprise git sync query

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to a1274aa11a83f608eacc32c0d449ca3527d98c15

This commit updates the EE repository reference after PR #473 was merged in windmill-ee-private.

Previous ee-repo-ref: 30f8c53b101b9e25107e793cdc038b0e07061739

New ee-repo-ref: a1274aa11a83f608eacc32c0d449ca3527d98c15

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-03-25 17:10:20 +00:00
Ruben Fiszel 4c8edd5e94 fix: restrict logout redirect to whitelisted domains (#8524)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-25 14:51:13 +00:00
centdix f2f178eb31 chore: remove dead users_oss module (#8458) 2026-03-19 17:21:07 +00:00
Alexander Petric 18b3528ba4 feat: instance groups instance-level role support (#8404)
* [ee] feat: instance groups instance-level role support

Add ability to assign instance-level roles (superadmin/devops) to
instance groups. Group members automatically receive the role with
proper precedence: manual elevated roles always win, superadmin > devops.

- Migration: add instance_role to instance_group, role_source to password
- Role propagation on all mutation paths (add/remove/update/delete/import)
- SCIM sync integration (companion PR: windmill-ee-private#463)
- Frontend: role toggle in group editor, role column in tables,
  role source indicator in superadmin settings

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

* chore: update ee-repo-ref to 278a3887f759f9d1146554baa0765518d5bc70f2

This commit updates the EE repository reference after PR #463 was merged in windmill-ee-private.

Previous ee-repo-ref: b407fe4604153d09ff223e11d43c2df83a1de5d0

New ee-repo-ref: 278a3887f759f9d1146554baa0765518d5bc70f2

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2026-03-17 01:19:46 +00:00
Ruben Fiszel f2be625348 feat: store hashed tokens instead of plaintext (#8217)
* feat: store hashed tokens in the token table instead of plaintext

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

* fix: address review issues in token hash migration

- Update all base.sql fixtures to include token_hash/token_prefix columns
- Keep plaintext token for webhook tokens (needed for URL reconstruction)
- Restore get_token_by_prefix to query DB for webhook tokens
- Fix down migration to delete NULL-token rows before restoring NOT NULL
- Update parser fixture standalone schema
- Update EE dedicated_worker_ee.rs to use token_hash/token_prefix

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

* fix: restore sqlx offline cache (only add new query files)

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

* refactor: keep writing plaintext token column for backward compat

Write to token column alongside token_hash until MIN_VERSION_SUPPORTS_TOKEN_HASH
(1.649.0) is reached. This ensures older workers can still authenticate
during rolling upgrades. Remove the separate UPDATE in new_webhook_token
since create_token_internal now writes plaintext directly.

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

* feat: branch on MIN_VERSION to write plaintext token or null

Check MIN_VERSION_SUPPORTS_TOKEN_HASH at runtime: write plaintext to
token column while old workers exist, switch to NULL once all workers
are >= 1.649.0.

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

* fix: set MIN_VERSION_SUPPORTS_TOKEN_HASH to 1.650.0

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

* fix: use token_hash for email lookup and expiry notifications

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

* refactor: rotate webhook tokens instead of recovering plaintext from DB

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

* refactor: use token_hash for native trigger token lookups and deletes

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

* sqlx

* refactor: drop webhook_token_prefix from native_trigger table

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

* fix: backward compat for token rotation and make webhook_token_hash NOT NULL

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

* fix: prevent panic on short superadmin secret token prefix

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

* fix: prevent panic on short superadmin secret token prefix

Replace all `token[0..TOKEN_PREFIX_LEN]` slicing with
`token.get(..TOKEN_PREFIX_LEN).unwrap_or(token)` to prevent
panics when a token shorter than 10 chars is provided (e.g.
malformed Authorization header, short superadmin secret).

Co-authored-by: hugocasa <hugocasa@users.noreply.github.com>

* fix: prevent panic on short token prefix slicing

Replace all `token[0..TOKEN_PREFIX_LEN]` with safe
`token.get(..TOKEN_PREFIX_LEN).unwrap_or(token)` to prevent panics
on malformed tokens shorter than 10 characters.

Co-authored-by: hugocasa <hugocasa@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Revert "fix: prevent panic on short superadmin secret token prefix"

This reverts commit 37ec2e5ad5.

* revert: remove unnecessary defensive token prefix slicing

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

* fix: add token_hash to end_user_email test fixture

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

* test: add integration tests for token hash migration

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

* fix: correct token_hash test assertions for cache and version

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

* chore: add plaintext column removal reminder to test fixtures

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

* fix: log count of orphaned triggers deleted during migration

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

* fix: preserve orphaned triggers with error instead of deleting

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

* fix: rename token_expiry_notification.token to token_hash and copy owner/expiration in rotate

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

* fix: hash existing plaintext values before renaming token_expiry_notification column

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

* fix: remove unnecessary length check in token_expiry_notification migration

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

* update dates and version

* updat ee ref + sqlx

* improve mcp migration

* fix: atomic token rotation with rollback on trigger update failure

rotate_webhook_token now atomically inserts the new token and deletes
the old one in a single transaction, preventing token leaks.

Returns new_token_hash so callers can clean up the new token if their
subsequent trigger update fails (which involves external HTTP calls
and cannot be in the same DB transaction).

- Handler: wraps post-rotation work; deletes new token on failure
- Google renewal: deletes new token if service_config update fails
- Tests updated to match new atomic semantics

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* higher min version

* fix: defer old token deletion to avoid breaking triggers on update failure

rotate_webhook_token now keeps the old token alive and returns
old_token_hash. Callers delete it only after the trigger row has been
successfully updated. If the external service call or DB update fails,
the trigger keeps working with the old token.

Worst case: if the best-effort delete fails, the old token leaks as an
extra DB row — harmless compared to breaking the trigger.

Also update summarized_schema.txt for renamed columns.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to 2d0823a471014e2bc2d898c63518323946b7474f

This commit updates the EE repository reference after PR #437 was merged in windmill-ee-private.

Previous ee-repo-ref: 7aef8b06cb6f54c2bc89dd57b70947deed72553c

New ee-repo-ref: 2d0823a471014e2bc2d898c63518323946b7474f

Automated by sync-ee-ref workflow.

* fix: prevent panic on short tokens by using safe prefix extraction

Add safe_token_prefix() helper that uses .get(..TOKEN_PREFIX_LEN).unwrap_or(token)
instead of direct slice indexing, preventing panics when tokens are shorter than
10 characters (e.g., short superadmin secrets or malformed Bearer tokens).

Co-authored-by: Ruben Fiszel <rubenfiszel@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: HugoCasa <hugo@casademont.ch>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: hugocasa <hugocasa@users.noreply.github.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <rubenfiszel@users.noreply.github.com>
2026-03-17 01:15:38 +00:00
Ruben Fiszel 2aef01d18c feat: partition audit log table by day with configurable retention (#8292)
* feat: partition audit log table by day with configurable retention

Introduce daily range partitioning for audit logs to replace expensive
DELETE-based retention with instant DROP TABLE per partition.

- Create `audit_partitioned` table alongside existing `audit` table
- New inserts go to `audit_partitioned`, reads UNION ALL both tables
- Monitor creates future partitions and drops expired ones
- Add `audit_log_retention_days` instance setting (default 365 days)
- Old `audit` table empties naturally via existing DELETE cleanup

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

* feat: add audit log retention setting to Core instance settings UI

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

* fix: bump audit partitioning migration timestamp to avoid collision

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

* chore: update ee-repo-ref.txt for audit partitioning

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

* feat: add RLS/grants to audit_partitioned, run partition mgmt hourly, CE default 14d

- Add grants for windmill_user/windmill_admin and all 5 RLS policies
- Move manage_audit_partitions to hourly via should_run(120)
- Default retention: 14 days CE, 365 days EE
- Download JSON button is now icon-only

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

* fix: address code review — quote SQL identifiers, add workspace index, deduplicate retention logic

- Quote partition names in dynamic SQL for defense in depth
- Add idx_audit_partitioned_workspace(workspace_id, timestamp DESC) index
- Extract audit_log_retention_days() helper to deduplicate retention logic

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

* chore: update ee-repo-ref for audit insert error handling

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

* chore: update ee-repo-ref to cef4dfc45e6d6344c5d8d107bd2b4d1bf9bbdd64

This commit updates the EE repository reference after PR #450 was merged in windmill-ee-private.

Previous ee-repo-ref: f09284bb257d461bcbe3c50fe31eb6f1e7eafee5

New ee-repo-ref: cef4dfc45e6d6344c5d8d107bd2b4d1bf9bbdd64

Automated by sync-ee-ref workflow.

* fix: create audit partitions on startup in initial_load

Ensures partitions exist before any requests arrive, closing the gap
between server start and the first hourly monitor run.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-03-11 00:10:29 +00:00
hugocasa e56ccd200b feat: token expiration notifications (#8190)
* feat: add token expiration notifications via email, critical alerts, and webhooks

- Monitor loop checks for tokens expiring within 7 days and sends
  email notifications to token owners. Tracks notification state via
  new `expiry_notified` column on the token table to avoid duplicates.
- When tokens expire and are deleted, owners are also notified.
- Critical alerts (in-app UI) are gated behind a new instance setting
  `critical_alerts_on_token_expiry` (off by default); emails are
  always sent regardless of the setting.
- Add TokenExpiringSoon and TokenExpired webhook message variants for
  workspace webhook integrations.
- Frontend: show expiration badges and a warning banner on the tokens
  table for tokens expiring within 30 days.
- Exclude session and ephemeral tokens from all notifications.

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

* refactor: use separate token_expiry_notification table for dedup

- Replace `expiry_notified` column on token table with a dedicated
  `token_expiry_notification` table (token, expiration)
- Insert notification row on token creation via shared
  `register_token_expiry_notification()` helper
- Delete notification row atomically when sending the notification
- Clean up orphaned rows in `delete_expired_items()`
- No FK constraint to avoid cascade overhead on token deletions
- Add index on expiration column for efficient range queries

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

* fix: calendar-based expiration badge and move notification cleanup

- Fix daysUntilExpiration to compare calendar dates instead of time diff
- Move notification row cleanup from delete_expired_items to
  check_expiring_tokens to keep it off the hot path
- Use simple expiration <= now() index scan instead of NOT EXISTS join

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 05:22:46 +00:00
Ruben Fiszel 82e5f6de48 feat: add Kubernetes operator and instance settings YAML editor (#7836)
* Add windmill-operator crate for Kubernetes CRD-based instance config

Introduces a new `windmill-operator` crate that enables declarative
management of Windmill instance configuration via a Kubernetes
`WindmillInstance` CRD. The operator watches CRD resources and performs
full declarative sync of global_settings and worker configs to the
database, supporting GitOps workflows for instance-level configuration.

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

* Add tests for windmill-operator CRD and db_sync

- 9 unit tests for CRD serialization, deserialization, metadata, and
  status field behavior
- 15 integration tests for db_sync using #[sqlx::test] with full
  declarative sync coverage: upsert, delete, protected keys,
  idempotency, worker config prefix handling, and end-to-end sync

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

* Replace untyped BTreeMap CRD fields with typed structs for schema validation

GlobalSettings, SmtpSettings, IndexerSettings, and WorkerGroupConfig now
have explicit typed fields with serde(flatten) catch-all for forward
compatibility. The generated CRD YAML includes a full OpenAPI v3 schema
that Kubernetes validates on kubectl apply.

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

* Type opaque serde_json::Value CRD fields with real structs

Replace most remaining serde_json::Value fields in WindmillInstance CRD
with properly typed structs derived from the codebase:
- oauths: BTreeMap<String, OAuthClient>
- otel: OtelSettings
- otel_tracing_proxy: OtelTracingProxySettings with ScriptLang enum
- critical_error_channels: Vec<CriticalErrorChannel> (untagged enum)
- critical_alerts_on_db_oversize: DbOversizeAlert
- ducklake_settings: DucklakeSettings with nested catalog/storage types
- custom_instance_pg_databases: CustomInstancePgDatabases
- autoscaling (worker config): AutoscalingConfig with integration struct
- custom_tags, default_tags_workspaces: Vec<String>
- default_tags_per_workspace: bool

Still opaque (serde_json::Value): object_store_cache_config (kube-core
can't generate schemas for internally-tagged enums), secret_backend
(EE-private), slack, teams (no clear struct definitions).

Regenerated CRD YAML with full OpenAPI schema (352→703 lines).

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

* Unify instance config types and add bulk GET/PUT API

Move all typed settings (GlobalSettings, WorkerGroupConfig, etc.) from
windmill-operator/crd.rs into windmill-common/instance_config.rs so both
the API server and operator share a single source of truth. Add diff/apply
logic (Merge mode for UI, Replace mode for operator) and InstanceConfig::from_db().

Add GET/PUT /settings/instance_config endpoints so the frontend loads all
settings in 1 call instead of 42, and saves with a single bulk PUT. The
backend handles the diff internally, running pre-write hooks for changed keys.

Refactor windmill-operator/db_sync.rs to use the shared diff+apply functions
and slim crd.rs down to the CRD wrapper with re-exports.

Includes 32 unit tests and 30 integration tests covering serialization,
diff logic, DB roundtrips, protected settings, and edge cases.

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

* feat: add Form/YAML toggle to instance settings UI

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

* fix: show Form/YAML toggle regardless of hideTabs prop

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

* refactor: replace toggle button group with simple YAML toggle

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

* feat: mask sensitive fields in YAML view with show/hide toggle

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

* feat: hide internal settings and mask sensitive fields in YAML view

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

* feat: hide jwt_secret and min_keep_alive_version from API and config exports

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

* all

* feat: add secretKeyRef support for sensitive fields in operator CRD

Allow sensitive fields (license_key, hub_api_secret, scim_token,
smtp_password, OAuthClient.secret, custom PG user_pwd) to reference
Kubernetes Secrets via the standard secretKeyRef pattern instead of
inlining values as plaintext YAML. The reconciler resolves all refs
by reading K8s Secrets before syncing to the database.

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

* all

* all

* all

* fix: merge main and update dev environment docs

Resolve merge conflicts from origin/main, fix duplicate
UV_INDEX_STRATEGY_SETTING import, and add Playwright MCP
testing instructions to CLAUDE.md.

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

* all

* fix: init tracing for CLI subcommands and deduplicate setting side-effects

Initialize tracing subscriber before early-return CLI paths (sync-config,
operator) so tracing calls are not silently dropped. Refactor
set_global_setting_internal to call run_setting_pre_write_hook instead of
duplicating the side-effect logic.

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

* feat: add `wmill instance get-config` CLI command

Dumps the current instance config (global settings + worker configs) as
YAML. Supports --output-file to write to a file instead of stdout.

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

* all

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 22:42:01 +00:00
Ruben Fiszel c580572252 refactor: extract windmill-api-scripts and windmill-api-users subcrates (#7850)
* refactor: extract windmill-api-scripts and windmill-api-users subcrates

Split the monolithic windmill-api crate by extracting scripts.rs, flows.rs,
users.rs, and users_oss.rs into dedicated subcrates. This reduces incremental
rebuild times when editing these modules.

Changes:
- Create windmill-api-scripts crate (scripts.rs + flows.rs, ~4.3K lines)
- Create windmill-api-users crate (users.rs + users_oss.rs, ~2.4K lines)
- Move clear_schedule to windmill-queue (shared by scripts, flows, workspaces)
- Move username utilities (VALID_USERNAME, INVALID_USERNAME_CHARS,
  generate_instance_wide_unique_username) to windmill-common/src/usernames.rs
- Move COOKIE_DOMAIN, IS_SECURE, WithStarredInfoQuery, BulkDeleteRequest,
  WebhookShared to windmill-common for cross-crate access
- Original files in windmill-api become thin stubs with pub use re-exports
- EE-dependent route handlers remain in windmill-api (create_user, rename_user,
  set_password, reset_password, etc.)
- Feature forwarding for enterprise, private, parquet, no_auth

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

* refactor: extract windmill-api-workspaces subcrate (Step 3)

Move workspaces.rs, workspaces_extra.rs, workspaces_oss.rs, and
workspaces_ee.rs into a new windmill-api-workspaces crate (~7K lines).

Routes that depend on windmill-api internals (AI copilot, teams,
tarball export, critical alerts, stripe) remain in the windmill-api
stub. The subcrate handles all other workspace management routes.

Also moved send_email_if_possible to windmill-common/email_oss.rs
to make it available across subcrates without circular deps.

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

* all

* refactor: extract windmill-api-groups subcrate (groups.rs + folders.rs)

Extract groups.rs (1,093 lines) and folders.rs (833 lines) into a new
windmill-api-groups subcrate. Both modules had clean dependencies on
already-extracted crates (windmill-api-auth, windmill-common,
windmill-api-workspaces). Also removes unused re-exports of
get_instance_username_or_create_pending and INVALID_USERNAME_CHARS
from windmill-api/src/utils.rs.

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

* refactor: add granular_acls.rs and folder_history.rs to windmill-api-groups

Extract granular_acls.rs (395 lines) and folder_history.rs (68 lines) into
the windmill-api-groups subcrate. Both modules only depend on already-extracted
crates and belong to the same access-control domain as groups and folders.

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

* fix: remove unused imports and dead code from subcrate extraction

- Remove unused BASE_URL import from lib.rs
- Remove workspaces_extra.rs and workspaces_oss.rs re-export stubs (no consumers in windmill-api)
- Remove dead send_email_if_possible OSS stub (callers moved to windmill-api-users)

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

* all

* chore: bust CI cargo cache for subcrate split

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

* fix: re-export BASE_URL for EE files that use crate::BASE_URL

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

* fix: forward no_auth feature to windmill-api-users, remove dead code

- Add "windmill-api-users/no_auth" to windmill-api's no_auth feature
  so the login bypass in users.rs:1600 activates correctly
- Remove dead send_email_if_possible from windmill-api-users/users_oss.rs

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

* chore: re-enable cargo cache for backend tests

Cache was disabled to bust stale entries from before subcrate split.
Now that a clean build has run, re-enable for faster CI.

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

* chore: install mold+clang in CI workflows

The .cargo/config.toml uses mold linker for x86_64-linux.
Build scripts require linking even during cargo check.

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

* chore: increase cargo test timeout to 30 min

Exit code 143 (SIGTERM) means the 20-min timeout was hit during
compilation without cache. Bump to 30 min as safety net.

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

* fix: limit cargo build jobs to 4 to prevent OOM in CI

Exit code 143 (SIGTERM) after 8 min = OOM kill during compilation.
8 parallel LLVM codegen jobs exhaust memory on ubicloud-standard-8.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 12:12:03 +00:00