Commit Graph

6812 Commits

Author SHA1 Message Date
hugocasa 11fda89b52 feat(telemetry): generic feature-usage telemetry with AI session metrics (#10200)
* feat(telemetry): add generic feature_usage table and batched logging endpoint

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(telemetry): log AI session usage events and document them in telemetry settings

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(telemetry): use escape sequence instead of literal NUL bytes in buffer key

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(telemetry): validate dimensions, decouple retention, keepalive flush

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(telemetry): allowlist feature-usage dimensions and index retention scans

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(telemetry): pin tool-name allowlist and deploy session attribution

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(telemetry): route AI chat usage through feature_usage and drop ai_chat_usage

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(telemetry): slim dimension validation to registered kinds plus key shape

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(telemetry): backfill ai_chat_usage into feature_usage before dropping it

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(telemetry): disclose provider and model identifiers in telemetry settings text

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(telemetry): issue all flush chunks before awaiting so pagehide keeps them

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: update ee-repo-ref to 6306c072a50937ea9af44a5bcf42345543207486

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

Previous ee-repo-ref: 964f242a0eb44db7f7d26636cc8d76aeabea2b73

New ee-repo-ref: 6306c072a50937ea9af44a5bcf42345543207486

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Fable 5 <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-07-20 20:56:57 +02:00
windmill-internal-app[bot] f635bd5ae7 chore: refresh vendored docs snapshot (#10068)
Co-authored-by: hugocasa <15649739+hugocasa@users.noreply.github.com>
2026-07-20 20:43:03 +02:00
hugocasa 87be041c09 fix(git-sync): avoid percent-encoded colon in git-sync hub script path (#10213)
* fix(git-sync): avoid percent-encoded colon in git-sync hub script path

The git-sync init/detection hub script slug contained a colon stored as
`%3A` in the run-by-path URL. The generated API client re-encodes path
params with encodeURI, turning `%3A` into `%253A` (double-encoding). Some
hardened reverse proxies / WAFs reject double URL-encoding and return a
bare 400 before the request reaches Windmill, breaking git-sync repository
detection on those instances.

The hub resolves scripts by numeric id and ignores the slug, so dropping
the colon from the slug is behavior-neutral (same script, same id-keyed
worker cache) while producing a colon-free run URL.

Also force-cache GIT_SYNC_PULL_SCRIPT_PATH at build alongside
LATEST_GIT_SYNC_SCRIPT_PATH so the backend-driven pull script is always
baked into the image for airgapped workers, instead of relying on an
incidental hubPaths.json overlap.

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

* chore: bump ee-repo-ref for git-init slug match fix

Pulls in windmill-ee-private#676 so the EE is_git_init_script check matches
the colon-free git-init hub slug (GitHub App token grant for git-sync jobs).

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

* chore: bump ee-repo-ref for git-init slug helper + test

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

* chore: update ee-repo-ref to a3adea1ffb406e709cc480871df58fab6c51aca1

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

Previous ee-repo-ref: cef4e008ef62dec434aa9bb3ec783db8aff6a1c1

New ee-repo-ref: a3adea1ffb406e709cc480871df58fab6c51aca1

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-07-20 20:36:34 +02:00
hugocasa 2b58df57fc feat(ai): extract prompt cache token usage from OpenAI and Azure providers (#10214)
* feat(ai): extract prompt cache token usage from OpenAI and Azure providers

Parse the nested cache token details OpenAI returns and thread them into
TokenUsage, matching the Anthropic and Bedrock providers.

- sse.rs: add OpenAIPromptTokensDetails / OpenAIInputTokensDetails and the
  optional prompt_tokens_details / input_tokens_details fields.
- other.rs (Chat Completions) and openai.rs (Responses): populate
  cache_read via .with_cache(cached_tokens, None).

OpenAI's prompt_tokens/input_tokens already include cached tokens (cached is
a subset), so total/prompt are unchanged; cache_read is recorded separately
for reporting. For the same reason the frontend token-usage conversions are
left as-is (adding cached would double-count); optional cache fields and a
clarifying comment are added to prevent a future incorrect Anthropic-style fix.

Fixes WIN-2207

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

* test(ai): pin OpenAI/Azure cache-token deserialization paths

Add regression tests deserializing the real Chat Completions and Responses
usage payloads, guarding the prompt_tokens_details.cached_tokens /
input_tokens_details.cached_tokens paths against a silent rename.

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

* refactor(ai): extract to_token_usage() and test the cache mapping

Address review nit: move the usage->TokenUsage conversion into
OpenAIChatUsage::to_token_usage / OpenAIResponsesUsage::to_token_usage so
the providers call one method and the tests exercise the real mapping.
Tests now assert cache_read is populated while input/total are unchanged.

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-07-20 18:47:25 +02:00
Ruben Fiszel 38ceae1a09 fix(db): grant schema usage and re-run windmill role grants (#10212)
* fix(db): grant schema usage and re-run windmill role grants

Migration 20250205131523 grants the windmill_user and windmill_admin
roles access to the schema and its tables, but its first statement is
LOCK TABLE pg_catalog.pg_roles, which requires superuser. On managed
Postgres (RDS, Cloud SQL) the migration user is not one, the lock
raises, and the block's EXCEPTION WHEN OTHERS handler downgrades the
failure to a NOTICE, so every GRANT after it is skipped. Core tables end
up ungranted and queries on a user_db transaction (SET LOCAL ROLE
windmill_user/windmill_admin) fail with "permission denied for table"
or, when schema USAGE was never granted, "relation does not exist".

Add a migration that re-runs those grants without the lock and without a
catch-all handler, and add the missing GRANT USAGE ON SCHEMA public to
init-db-as-superuser.sql, which PostgreSQL 15+ no longer implies for
PUBLIC. The init script also now creates windmill_admin before the table
grants so role membership is in place when they run.

Fixes WIN-2208

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

* docs(db): correct schema-privilege rationale in grant migration

PostgreSQL 15 revoked CREATE, not USAGE, from PUBLIC on the public
schema, so USAGE is still granted by default there. The explicit grant
is what a hardened database that revoked it needs, not a PG15 default.
Also correct the description of what 20250205131523's failure actually
loses: 20221105003256 grants the tables outside any locked block, so the
gap is the ALTER DEFAULT PRIVILEGES covering later-created tables.

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

* fix(db): grant only runner-owned objects to avoid aborting upgrade

GRANT ALL ON ALL TABLES IN SCHEMA raises a hard "permission denied for
table X" the moment the schema holds an object the migration runner does
not own (a superuser-installed extension such as PostGIS spatial_ref_sys,
or a co-located application table). With no catch-all handler that would
abort the whole upgrade -- a regression against 20250205131523, which
tolerated it only by swallowing every error.

Grant per-object over just the tables and sequences the runner owns,
which is exactly the set the GRANT can succeed on. Windmill's own tables
are all runner-owned, so coverage is unchanged; foreign objects are
skipped rather than aborted on. ALTER DEFAULT PRIVILEGES stays (it only
governs the runner's future objects, so it cannot conflict).

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

* fix(db): guard each grant so the migration can never abort an upgrade

Wrap every grant in its own BEGIN/EXCEPTION/WARNING so no single failure
can abort the migration -- the opposite of 20250205131523's one
block-wide WHEN OTHERS, whose flaw was granularity: a single early
failure there silently skipped every remaining grant. Here each grant
that cannot be applied is isolated, re-raised as a named WARNING, and the
rest still run.

This closes the last abort paths: a grant on an object dropped by another
session between the catalog scan and the GRANT, USAGE on a schema the
runner cannot grant, or a missing role, now warn and continue instead of
failing the upgrade. On a clean owned schema the guards never fire (zero
warnings, verified). The owner filter stays so foreign objects are
skipped without even a warning; the guard is the backstop.

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

* fix(db): also grant runner-owned views and materialized views

pg_tables returns only ordinary and partitioned tables, so the previous
loop left owned views (flow_workspace_runnables and any materialized
views) ungranted -- a coverage regression against the GRANT ... ON ALL
TABLES form, which grants views too. Those views are read through
user_db transactions, so windmill_user/windmill_admin need access.

Enumerate pg_class over the relkinds ALL TABLES covers (r, p, v, m, f),
keeping the ownership filter so foreign objects are still skipped.

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

* fix(db): grant by effective authority, not owner-name equality

Filtering relations by pg_get_userbyid(relowner) = current_user skips
objects the runner can legally grant but does not own by name: after a
migration-credential rotation, tables and sequences stay owned by the
previous runner while the new runner grants through inherited membership
(or as a superuser). Owner-name equality would leave those ungranted and
their user_db access broken.

Filter by pg_has_role(current_user, owner, 'USAGE') instead -- objects
the runner owns directly, inherits ownership of, or reaches as superuser.
Genuinely foreign objects (owner the runner is not a member of) are still
skipped, so the per-object guards remain the backstop.

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

* fix(db): scope grants by explicit membership, not superuser authority

pg_has_role treats a superuser as a member of every role, so a superuser
migration runner would grant windmill_user/windmill_admin access to every
co-located relation in the schema -- another application's tables, an
extension's tables -- not just Windmill's.

Compute the runner's role set from pg_auth_members (recursive explicit
membership) and grant only relations owned by it. This still covers the
credential-rotation case owner-name equality missed (the new runner is a
real member of the previous owner) without inheriting the superuser's
implicit authority over unrelated roles.

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-07-20 18:32:47 +02:00
Ruben Fiszel 71f2d47cb4 feat: cap queued jobs per concurrency key on cloud (#10197)
* feat: cap queued jobs per concurrency key on cloud

* fix: close preprocessed-flow bypass and bound concurrency cap scan

* fix: only cap concurrency keys with an active concurrent_limit

* chore: only load concurrency key cap setting when cloud hosted

* fix: reject queued-job import on cloud
2026-07-20 12:33:40 +02:00
Ruben Fiszel b5e69ffba6 fix(security): enforce token scope filtering on folder list endpoints (#10193)
* fix(security): enforce token scope filtering on folder list endpoints

* fix: apply folder scope filtering before pagination

* fix: keep SQL pagination for unrestricted tokens on folder lists

* fix: bound scoped folder-list scan with chunked pagination

* fix: use keyset pagination for scoped folder-list scan

* refactor: reduce folder scope fix to the minimal predicate filter

* chore: drop folder list scope test
2026-07-20 07:50:50 +02:00
Ruben Fiszel 70359e3a76 fix(schedules): apply scope-path filtering to schedule list endpoints (#10192)
`list_schedule` and `list_schedule_with_jobs` returned every schedule in
the workspace regardless of the token's declared scope. A token limited
to `schedules:read:<prefix>/*` could enumerate all schedule paths (and
their script paths, cron expressions and recent job outcomes) through
these two endpoints, while `get_schedule` correctly rejected them.

Apply `build_scope_path_predicate(&authed, "schedules", "read")` to the
returned rows, mirroring scripts, flows, apps, resources and variables.
In `list_schedule` the filter runs after the draft-only append so both
deployed and draft rows are covered.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 07:32:52 +02:00
Ruben Fiszel f32d7702bc fix(inputs): add ownership check to saved-input args read endpoint (#10194)
`get_args_from_history_or_saved_input` looked up saved-input rows by
`id + workspace_id` only, without checking ownership or `is_public`. The
`input` table has no RLS policy, so any authenticated workspace member
could read another member's private saved-input args (which may hold
credentials) just by knowing the UUID, even though `inputs/list` already
scopes to `is_public OR created_by = caller`.

Add `AND (is_public IS true OR created_by = $4)` to both `input`-table
queries in the handler (the `?input=true` single-query branch and the
default `v2_job UNION ALL input` branch), matching the predicate already
used by `list_saved_inputs`.

The `v2_job` branches are already covered by the table's RLS policies
enforced via `SET LOCAL ROLE windmill_user` in `UserDB::begin()`, so they
are left unchanged.

Fixes WIN-2202

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 07:31:02 +02:00
Ruben Fiszel 60a7a23a04 chore(main): release 1.763.0 (#10186)
* chore(main): release 1.763.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-19 10:45:40 +02:00
Ruben Fiszel 89948bb441 chore(git-sync): bump hub scripts to windmill-cli@1.762.3 (#10189) 2026-07-19 10:40:29 +02:00
Ruben Fiszel d5b0a1b5c7 chore(main): release 1.762.2 (#10184)
* chore(main): release 1.762.2

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-18 08:49:56 +02:00
Ruben Fiszel f6e36f862e chore(schedules): lower reconciler re-arm back-off cap to 8 passes (#10182)
Follow-up to #10179. The exponential back-off between reconciler re-arm
retries of a persistently-failing schedule capped at 32 passes (~2.7h at
the default 5-min reconcile cadence). Lower the cap to 8 (~40min) so a
schedule fixed out of band (a lapsed license renewed, a bad cron corrected
directly in the DB) auto-recovers within a few passes, while still cutting
the retry rate sharply versus retrying every pass. Fixes via the UI/API
re-arm immediately and are unaffected.

Fixes WIN-2198

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 08:49:41 +02:00
Ruben Fiszel cba5f0d8a8 fix(scripts): populate auto_kind from draft JSON for draft-only scripts (#10183)
The scripts list endpoint (include_draft_only=true) only populated
auto_kind for the pipeline case, leaving library draft-only scripts
(no `main` function) with auto_kind: null even though the frontend
saves auto_kind: "lib" into the draft JSON. This made it impossible
to distinguish library draft-only scripts from regular ones without a
separate per-script API call.

Fall back to the auto_kind saved in the draft value after the
content-derived pipeline check, which keeps priority since it mirrors
the deploy-time computation.

Fixes WIN-2199

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 08:39:22 +02:00
Ruben Fiszel 252ffd0401 chore(main): release 1.762.1 (#10181)
* chore(main): release 1.762.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-17 22:52:46 +02:00
Ruben Fiszel c82056cfde fix(schedules): stop disabling schedules on transient push errors (#10179)
* fix(schedules): stop disabling schedules on transient push errors

A scheduled flow whose next-occurrence push failed after retry exhaustion
used to be disabled, killing a healthy schedule over a transient DB blip
(pool contention, statement timeout). Now that the unarmed-schedule
reconciler exists (#10174), transient failures no longer disable: the
current occurrence runs to completion and the reconciler re-arms the next
occurrence once this run leaves the queue.

In the flow schedule-push path after retry exhaustion we now branch on the
error: QuotaExceeded/NotFound still disable (the schedule's own fault, and
rearm_schedule would otherwise leave them enabled-yet-unarmed forever),
while transient errors are only reported and the flow continues.

The previous iteration returned a SchedulePushZombieError to force a zombie
restart; that is removed, because zombie detection cancels (does not
restart) same-worker flows, so it would have lost the current run of a
same-worker scheduled flow. The now-obsolete SchedulePushZombieError type
and its catch in worker.rs are deleted.

Fixes WIN-2198

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

* fix(schedules): back off and surface repeated reconciler re-arm failures

The unarmed-schedule reconciler retried a schedule that could not be
re-armed on every pass, forever, logging only to the server. With the flow
schedule-push path no longer disabling on non-transient errors, a
persistently-broken push (bad stored cron/timezone/args, lapsed license
key) now stays enabled and would spin in that loop silently.

The reconciler now tracks consecutive re-arm failures per schedule:
exponential back-off (2, 4, 8, … passes, capped) between retries so a
broken schedule is not hammered, and after 3 consecutive failures it
surfaces the cause once (records schedule.error + raises a critical alert)
without disabling. Both reset the moment the schedule re-arms, which also
clears the recorded error.

Verified end-to-end on a running server: a flow schedule with a corrupted
cron stays enabled, retries back off, the error is surfaced after the
third failure, and it re-arms and clears the error once the cron is fixed.

Fixes WIN-2198

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-07-17 22:48:15 +02:00
Ruben Fiszel 203f6c69dd fix(worker): gate ansible socket-dir name check to unix (#10180)
is_persistent_control_path_dir_name is only referenced from
prepare_socket_root, which is #[cfg(unix)]. On Windows nothing used it,
so -D dead-code failed the build. Gate the function and its unit test to
unix, matching the reaping code they support.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 21:26:40 +02:00
Ruben Fiszel 3ffce7e2cc chore(main): release 1.762.0 (#10175)
* chore(main): release 1.762.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-17 20:15:13 +02:00
Ruben Fiszel 0b6bd376db ci: drop stale path_field_renames from mcp runner test constructor
#10176 removed `path_field_renames` from `EndpointTool`, while #10162 added a
new `ep()` test constructor in runner.rs that still set it. Each passed CI
against its own base, but together on main the test build fails with E0560.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 18:11:49 +00:00
hugocasa ae3d9ce2c0 fix(mcp): apply token scopes consistently across mcp endpoint tools (#10162)
* fix(mcp): apply token scopes consistently across mcp endpoint tools

Endpoint-tool authorization is now shared between single- and
multi-workspace modes, honors the token's script/flow path patterns for
every path-taking tool, and the JWT minted for proxied endpoint calls
carries scopes derived from the caller's own.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore(mcp): review nits: restore create_http_request doc, reword comments

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 19:34:17 +02:00
hugocasa d0aa7dca13 fix: parse all names in grouped go param declarations (#10165)
* fix: parse all names in grouped go param declarations

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: bump windmill-parser-wasm-go to 1.761.0

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 19:24:07 +02:00
Ruben Fiszel 7d2c5ceb0f fix(flows): make updateFlow body path optional so AI can update flows (#10176)
* fix(mcp): default a body field to its same-named path param so updateFlow works

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

* docs: trim mcp path-param fallback helper comment

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

* refactor(mcp): keep path params un-mangled so update tools take plain `path`

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

* fix(flows): default update_flow body path from URL via EditFlow

Harmonizes updateFlow with the EditVariable/EditResource/EditApp convention: the
flow to update is identified by the URL, so the body path is optional and only
needed to rename. Fixes the 422 at the API layer for every client (MCP, the
in-app AI chat, raw HTTP), not just the MCP tool schema.

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

* refactor(mcp): drop redundant body-path fallback now that the server defaults it

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

* docs: fix stale generator comment after removing mcp body-path fallback

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

* fix(flows): mark updateFlow body path optional in the openapi contract

Adds an `EditFlow` schema (path optional) for the update route so the public
contract matches the server; createFlow keeps `OpenFlowWPath` (path required).
Also trims two test comments to record constraints rather than history.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 19:22:31 +02:00
Ruben Fiszel 2ff5a918d5 feat(worker-tags): add * fork marker to workspace-scoped custom tags (#10177)
* feat(worker-tags): add `*` fork marker to workspace-scoped custom tags

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

* test(worker-tags): pin fork-marker tag admission through real lineage

Also render WorkspaceMatcher in its authored form in the operator-facing
"not in the allowed CUSTOM_TAGS" error, and correct the authorization note
on workspace_with_fork_ancestors.

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

* fix(worker-tags): gate fork-lineage tag lookup on workspace membership

exists_workers_with_tags is a global route whose workspace is a query
param. Resolving its fork lineage for a caller who is not a member
disclosed whether an arbitrary workspace descends from one named by a
tag(parent*) rule. Require membership before the lineage lookup, and
sharpen the workspace_with_fork_ancestors contract accordingly.

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

* chore(sqlx): add offline cache for fork-marker test INSERT query

The new test_fork_marker_tag_admission_through_lineage integration test
inserts a fork workspace with a bind-parameterized query that had no
cached entry, failing the SQLX_OFFLINE=true CI build.

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-07-17 18:18:54 +02:00
Ruben Fiszel 9762089fcb fix(schedules): re-arm enabled schedules left with no queued occurrence (#10174)
* fix(schedules): re-arm enabled schedules left with no queued occurrence

* fix(schedules): lock schedule row while re-arming and report outcome

* fix(schedules): make reconcile lock cancellation-safe, re-check armed under lock

Address review feedback on the schedule reconciler:

- Use a transaction-scoped advisory lock (pg_try_advisory_xact_lock) instead of
  a session-scoped one. monitor_db runs under a 600s timeout; on cancellation a
  session lock on a pooled connection would be stranded, wedging reconciliation
  on every replica. An xact lock releases when its transaction is dropped.
- rearm_schedule re-checks for a queued occurrence under the row lock and
  returns NoOp if already armed, closing the scan→lock window that could
  double-push across a cron boundary. Add a regression test.
- Make reconcile_unarmed_schedules private (its only caller is in monitor.rs)
  and document its system-only contract.
- Log the disable only after the guarded UPDATE actually disables the schedule.

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

* fix(schedules): never disable from reconciliation and cap re-arms per pass

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 18:08:10 +02:00
Alexander Petric 396fb1c475 feat(otel-tracing-proxy): trust internal endpoints with untrusted CAs (#10139)
* [ee] feat(otel-tracing-proxy): trust internal endpoints with untrusted CAs

Add `insecure_upstream_hosts` and `upstream_ca_certs` to the HTTP Request Tracing
settings so the OTEL tracing proxy can reach internal endpoints with untrusted or
private-CA certificates while keeping them traced. Wires the two settings through
the worker config and live reload, adds the inputs to the instance settings UI,
and pulls in the rustls upstream-client deps (hyper-rustls/tokio-rustls/rustls/
rustls-native-certs/rustls-pemfile; hyper-http-proxy switched to its rustls
feature).

The proxy-side implementation lives in the companion EE PR.

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

* chore: bump ee-repo-ref to otel_ca companion commit

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

* fix(otel-tracing-proxy): expose new fields in declarative config; bump ee-ref

Addresses code-review findings:
- Add `insecure_upstream_hosts` and `upstream_ca_certs` to the declarative
  `OtelTracingProxySettings` in instance_config.rs so operator/GitOps-managed
  installs can set them and reconciliation no longer drops values saved via the UI.
- Restore the trailing newline on ee-repo-ref.txt and bump it to the companion
  EE commit carrying the strict host-matching / port-ordering fixes.

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

* chore: update ee-repo-ref to 51e50629f48dbc4f5520a787b4bdfb76f4cd38d3

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

Previous ee-repo-ref: 49f458e4446395e98915c220baa757ab3b2ed2d8

New ee-repo-ref: 51e50629f48dbc4f5520a787b4bdfb76f4cd38d3

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-07-17 15:57:00 +02:00
Ruben Fiszel 97f4477069 feat(forks): let a fork's creator manage developers on it without being an admin (#10166)
* feat(forks): let a fork's creator manage developers on it without being an admin

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

* fix(forks): bind the fork-creator grant to the member's parent username and lock the delete

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-07-17 15:13:36 +02:00
Ruben Fiszel be57dd91e4 fix(ansible): keep persistent-connection socket path under the AF_UNIX limit (#10167)
* fix(ansible): keep persistent-connection socket path under the AF_UNIX limit

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

* fix(ansible): root socket dir at WINDMILL_DIR and verify we own it

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

* fix(ansible): fail closed on an untrusted socket root and honor commented section headers

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

* fix(ansible): validate the socket root after creating it, not before

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

* fix(ansible): root socket dir at sticky /tmp so the image's 0777 windmill dir cannot disable it

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

* fix(ansible): only sweep socket dirs the worker could have created

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

* fix(ansible): drop the configurable socket root, fixing its ancestor and sweep hazards

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

* fix(ansible): require the socket root be usable, not just safe

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

* fix(ansible): do not override a control_path_dir the job set itself

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

* refactor(ansible): drop the dead parent-creation step for the fixed /tmp root

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-07-17 15:09:24 +02:00
Ruben Fiszel 6131f7d2ae chore(main): release 1.761.0 (#10148)
* chore(main): release 1.761.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-17 00:22:56 +02:00
Alexander Petric 91d6606868 fix(mcp): push granular scope patterns into SQL so scoped scripts/flows aren't truncated (#10140)
* fix(mcp): push granular scope patterns into SQL so scoped scripts/flows aren't truncated

MCP `list_tools` fetched scripts/flows capped at the 100 newest by
`created_at` and only *then* filtered by the token's granular
folder/custom scope in Rust. In a workspace with more than 100
scripts/flows, in-scope items outside that newest-100 window were
truncated before the scope filter ran, so a folder- or custom-scoped
token could see zero tools even though matching items existed.

Push the scope patterns into the query via a new `PathFilter::Patterns`
(mirroring `is_resource_allowed`: `*` disables filtering, exact paths
match by equality, `x/*` matches the folder or its subtree, empty grants
nothing) so the filter applies before the `ITEMS_FETCH_MAX_LIMIT` cap.
The existing hashed-name resolution path keeps its prefix behavior via
`PathFilter::Prefix`, and the Rust post-filter stays as defense in depth.

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

* feat(mcp): warn in scope selector when a scope exceeds the MCP tool cap

The server exposes at most ITEMS_FETCH_MAX_LIMIT (100) scripts and 100
flows per token; a scope matching more silently drops the overflow, which
bloats the assistant's context with a partial, arbitrary tool set.

McpScopeSelector now computes how many scripts/flows the current scope
would expose (per type, mirroring the backend's is_resource_allowed) and
shows a warning Alert when either exceeds the cap, so the user can narrow
the scope before generating the URL/token. An async sequence guard keeps
rapid scope changes from applying stale counts.

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

* fix(mcp): address review — dedup count fetches, boundary-aware folder counting, fix copy

Follow-up to the MCP scope-selector truncation warning:

- Reuse a single per-type (scripts/flows) cache for both the preview list and
  the exposed count, instead of a second concurrent fetch of the same rows.
- Count a folder scope against the `f/{folder}/*` subtree (via the same
  boundary-aware matcher), so a folder like `team` no longer over-counts a
  sibling like `team2` and falsely warns.
- Custom-mode counts are derived synchronously from the already-loaded
  scripts/flows — no fetch.
- Reword the warning to "most recent" (flows are ordered by edited_at, not
  created_at).

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

* fix(mcp): anchor folder count fetch at the folder boundary

Follow-up to review: the folder count fetched the unbounded prefix
`f/{folder}` (backend `path LIKE 'f/{folder}%'`), so a prefix-sharing
sibling like `f/team2` shared the page. With a page limit, enough newer
sibling rows could fill the first page ahead of the target folder's older
rows; the client-side boundary filter then dropped them all, wrongly
suppressing the warning and emptying the preview.

Fetch `f/{folder}/` instead so the backend prefix (`LIKE 'f/{folder}/%'`)
is anchored at the folder boundary and never returns siblings. The
client-side matcher stays as a backstop for folder names whose LIKE
wildcards (`_`, `%`) can still let the backend prefix over-match.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2026-07-17 00:15:34 +02:00
Diego Imbert 4fc3f304c6 feat(forks): add "Hide unchanged drafts" toggle to fork deploy-draft tab (#10022)
A fork clones the parent workspace's drafts on creation, so the fork's
Deploy-draft tab listed all of them as deployable even though the fork
never touched them. Add a "Hide unchanged drafts" toggle (fork-only, on
by default) that hides drafts identical to the parent's.

Backend: the drafts-list endpoint accepts an optional `compare_to_workspace`
(honored only when it is the workspace's actual parent) and flags each row
with `unchanged_from_parent` via a jsonb-equality subquery against the
parent's draft at the same (path, kind, owner).

Frontend: the fork compare page passes its parent as the compare workspace;
CompareDrafts renders the toggle and filters out unchanged rows, which also
drops them from the selection/deploy count.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 17:47:12 +02:00
Ruben Fiszel 0e547adf23 fix(migrations): grant zombie_job_counter to windmill roles (#10159)
The zombie_job_counter table (20250205131522) was never granted explicitly
to windmill_user / windmill_admin. The generic GRANT ALL ON ALL TABLES in
20250205131523 swallows failures via EXCEPTION WHEN OTHERS, and the
ALTER DEFAULT PRIVILEGES it sets only covers objects created by that same
role, so external-database deployments whose migration runner differs from
the init-script runner leave the table ungranted.

This stayed invisible while the table was only reached through ON DELETE
CASCADE, which bypasses caller permissions. 20260625092813 replaced those
cascades with explicit DELETEs in delete_jobs(), which run as the invoking
role and fail with "permission denied for table zombie_job_counter".

Same fix already applied to notify_event (20260619091631), script_trigger
(20260619112847) and dispatch_event (20260701080313).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 17:22:28 +02:00
hugocasa 51d8db6602 feat: automatic git-to-windmill sync (polling, webhooks, in-app PRs + checks) (#9552)
* docs: add design doc for automatic git-to-windmill pull sync

* docs: add migration plan and implementation phases to git-sync pull design

* feat(git-sync): add auto_pull settings schema and pull enqueue primitive

Adds AutoPullSettings/AutoPullMode/AutoPullStatus on GitRepositorySettings
(workspace_settings.git_sync JSONB), the GIT_SYNC_PULL_SCRIPT_PATH constant,
and should_pull/effective_poll_interval_s helpers with unit tests. Exports the
EE enqueue_git_pull_job primitive. Foundation for repo→Windmill auto-pull.

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

* feat(git-sync): poll repos and auto-pull new commits into the workspace

Phase 1 of automatic repo → Windmill sync. A monitor task (EE-licensed,
single-replica via advisory lock) git ls-remotes each auto-pull-enabled
repository ~every minute and enqueues a pull when the tracked branch moves,
reusing the {workspace_id}:git_sync concurrency key so pulls serialize with
in-flight push commits.

- windmill-store: background (no-authed) resolver get_git_repo_head_for_autopull
  that resolves the repo resource (incl. $var: refs) and ls-remotes; GitHub-App
  repos are skipped here and will sync via webhooks (phase 2).
- monitor.rs: poll/reconcile/persist with optimistic sha advance and failure
  status; targeted jsonb update so concurrent settings edits aren't clobbered.
- edit_git_sync_repository: preserve server-owned auto_pull state on UI save.
- openapi: AutoPullSettings/AutoPullMode/AutoPullStatus + auto_pull field.
- frontend: per-repo "Automatically deploy changes from Git" toggle with last
  sync status; demote the GitHub Actions link to an advanced CI option.

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

* feat(git-sync): wire webhook lifecycle + receiver; share reconcile logic

OSS side of phase 2 auto-pull webhooks:
- edit_git_sync_repository creates/removes the repo webhook on save (EE-gated,
  best-effort → falls back to polling).
- monitor poller now delegates to the shared windmill_git_sync reconcile/persist
  helpers (also used by the webhook receiver), removing duplicated logic.
- export the shared reconcile/persist/failure helpers; bump EE ref.

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

* chore(git-sync): bump EE ref for phase 3 in-app PR creation

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

* feat(git-sync): show webhook vs polling status on the auto-pull toggle

When a repo has an active webhook (auto_pull.webhook_id set), the status line
reads "instant via webhook"; otherwise it reads the ~1-minute polling cadence.

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

* feat(git-sync): post PR diff check on dry-run completion (phase 4)

Worker completion hook in process_completed_job: when a DeploymentCallback job
carrying the __git_sync_pr_check marker finishes, parse the dry-run SyncResponse
and patch the GitHub check run with the diff summary (success/neutral/failure).
Export enqueue_git_pull_dry_run; bump EE ref.

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

* chore(git-sync): bump EE ref (drop unused GHES webhook_secret)

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

* revert(git-sync): defer phase 4 PR diff checks (OSS side)

Remove the worker completion hook that posted the PR check run, drop the
enqueue_git_pull_dry_run re-export and the orphaned sqlx cache, bump EE ref.
Phases 1-3 (polling, webhooks, in-app PR creation) are unaffected.

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

* Revert "revert(git-sync): defer phase 4 PR diff checks (OSS side)"

This reverts commit 0137d3ca48.

* chore(git-sync): point EE ref at restored phase 4 commit

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

* chore(git-sync): bump EE ref for clone_ref dry-run

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

* chore(git-sync): bump init-repository hub script to v28784

Picks up the clone_ref param (windmill-integrations#158) so the phase 4 PR-check
dry-run can clone the PR head. Backward compatible; manual pull/push and the
automated pull/poller/webhook all move to the same published version.

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

* chore(git-sync): bump EE ref for auto-pull admin-permissioning fix

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

* chore(git-sync): bump EE ref for superadmin pull fallback

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

* fix(git-sync): refresh auto-pull tooltip; bump EE ref for webhook secret encryption

The auto-pull toggle tooltip claimed GitHub App repos would sync via
webhooks "in a future update"; webhook delivery now works, so describe
the webhook-vs-polling behavior accurately. Bump the EE ref to pick up
encrypting the webhook HMAC secret at rest.

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

* fix(git-sync): poll app-backed repos in auto/polling mode

The auto-pull poller skipped app-backed repos (the ls-remote head check
can't authenticate a tokenless URL), so auto- and polling-mode app repos
never synced when their webhook wasn't live. Wire the poller to fetch the
head via the GitHub API for app repos and reconcile. Bump the EE ref.

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

* feat(git-sync): auto-pull UI — direction split, delivery mode, fallback notice

Reorganize the repository card into two clearly labeled directions:
"Push to Git on deploy (Windmill → Git)" and "Pull from Git (Git →
Windmill)". In the pull section:
- new connections default to auto-pull enabled (webhook with polling
  fallback); existing repos load with auto-pull off and are unchanged
- a Delivery selector chooses "Webhook with polling fallback" or
  "Polling only (air-gapped)"
- a notice surfaces webhook_error when delivery falls back to polling
- a reminder to remove any pre-existing GitHub Action that pushed into
  Windmill, to avoid conflicting double-syncs

Adds the webhook_error field to AutoPullSettings (+ openapi) and bumps
the EE ref.

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

* feat(git-sync): clearer push indicator + gate webhook delivery to app repos

- Push-on-deploy is shown with a check icon + concise line (via the
  shared GitSyncModeDisplay, restyled from the oversized "Sync:" text);
  the setup wizard reuses it without the check (pre-save preview).
- The delivery-mode selector only shows for GitHub App-backed repos;
  token-based repos show a "webhooks require the GitHub App (managed or
  GHES)" note with a docs link and poll instead. Bumps the EE ref.

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

* feat(git-sync): fork auto-sync (phase 5) + live deploy check (phase 6)

Phase 5 — fork auto-sync configured at the parent (replaces the *-to-forks
GitHub Actions):
- Add fork_open_prs + fork_pull_sync to GitRepositorySettings (openapi + UI).
- UI: two "Forks of this workspace" toggles in the repo card, gated on
  app-backed and not-a-fork; serialize the flags on save.
- On fork creation, strip the inherited auto_pull block (and fork_* flags) from
  the copied git_sync repo: a fork must not carry the parent's webhook id (it
  would delete the parent's hook on disable) or self-poll on top of the parent's
  fan-out. Push-direction config + installation are still inherited unchanged.

Phase 6 — live deploy status check on the commit (Cloudflare-style): an
in-progress "Windmill" check on the head commit that flips to "Deployed N
changes"; completion handled by the generalized git-sync check hook.

Bump EE ref for the phase 5-6 EE implementation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* chore(git-sync): bump EE ref for PAT auto-pull mode normalization

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): address PR review findings

- webhook_secret: redact from the settings API response and Debug output (still
  persisted encrypted); it's a server-only HMAC key the UI never needs.
- poller: honor each repo's effective poll interval (relaxed ~10 min when a
  webhook is live) instead of probing every ~60s tick.
- settings save: roll back a just-created webhook if the settings transaction
  doesn't commit, so a failed save can't orphan a hook.
- auto-pull head check: fail SSH remotes with an actionable message (background
  polling has no SSH identity) instead of a confusing ls-remote error.
- deploy/PR check summary: a pull result carrying neither changes nor a settings
  diff now falls back to the unsummarized path instead of a false "in sync".
- UI: reset isGithubApp on resource change / failed fetch so webhook + fork
  controls can't show for the wrong repo.
- tests: cover parse_git_sync_changes and format_change_list edge cases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): correct feature gating for OSS builds

- monitor.rs: keep the AUTO_PULL_LAST_POLL static, slack const, and
  poll_git_auto_pull_inner all behind #[cfg(feature = "private")] (an inserted
  static had split the cfg off the function, ungating it in OSS builds).
- edit_git_sync_repository: the webhook create/rollback block references
  windmill_common::git_sync_ee (private module), so gate it on
  all(enterprise, private) instead of enterprise only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* chore(sqlx): cache workspace_diff query pulled in from origin/main

Re-merged origin/main (advanced past the earlier merge); regenerate the offline
sqlx entry for the new workspace_comparison test query so SQLX_OFFLINE builds
(cargo_test) pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): address Codex review findings (webhook cleanup on delete)

- Deleting a git-sync repository now tears down its managed GitHub webhook
  (deletion bypassed the sync_repo_webhook lifecycle, orphaning the hook so
  GitHub kept delivering to the instance).
- Worker completion hook rolls back the optimistic auto-pull sha on job failure
  (OSS side of the EE change) + caches the new marker query. Bump EE ref.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): delete repo webhook after the removal commits

Codex re-review nits:
- delete_git_sync_repository deleted the webhook before the settings transaction
  committed; a failed save would then leave the repo pointing at a hook that no
  longer exists (sync_repo_webhook treats a set webhook_id as live and won't
  recreate it). Capture the hook id, commit the DB removal, then delete the hook.
- Reword a fork-copy comment to drop drafting-history wording per AGENTS.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): reconcile the edit-path webhook after the settings commit

Codex nit: edit_git_sync_repository ran sync_repo_webhook before the transaction
committed. The rollback only covered created hooks, but sync_repo_webhook also
deletes a hook on disable/switch-to-polling — a commit failure then left the DB
with a webhook_id whose hook was already gone (and it wouldn't be recreated).
Save + commit first, then reconcile the webhook against the durable config and
persist any hook id/secret change (best-effort). Bump EE ref.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): preserve webhook secret on whole-config save + default on visible add

Codex nits:
- edit_git_sync_config saved the client config verbatim, so the webhook_secret
  redacted from the GET response would be dropped (breaking delivery). Preserve
  server-owned auto-pull state (webhook id/secret, synced sha, last status) per
  repo from the existing settings, matching edit_git_sync_repository.
- addSyncRepository (the visible add path) didn't set the auto_pull default, so
  new sync repos added from the UI came up with auto-deploy off. Match
  addRepository's default (webhook + polling fallback).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* refactor(git-sync): drop fork_pull_sync (parent-level keep-forks-in-sync)

Removes the "Keep forks in sync with the tracked branch" toggle and its
fan-out. Pulling the tracked branch straight into every fork was the
inconsistent piece; the consistent model is per-fork branch sync (each
fork tracks its own wm-fork/** branch), which is a separate follow-up.
fork_open_prs is kept. Also tightens the fork toggle-section spacing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): detect dev workspaces in CLI fork branch derivation

isForkWorkspace / computeGitSyncDeployBranch keyed off the wm-fork- id
prefix. Dev workspaces are forks with a custom, prefix-less id, so their
wm-fork/** branch was never derived or created. Detect them via
parent_workspace_id too (which the backend already passes), mirroring the
backend's `parent.is_some() || wm-fork- prefix` rule.

Pairs with the hub-script clone-flag fix (windmill-integrations#163); both
take effect once the CLI is released and the pinned version is bumped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): reconcile webhooks on full-config save

edit_git_sync_config preserved server-owned webhook fields but never
created or deleted the managed GitHub webhook, so enabling auto-pull
through the whole-config endpoint only polled, and disabling or removing
a repo left an orphan hook still delivering. Mirror the per-repository
endpoint: after the commit is durable, reconcile every saved repo's
webhook (sync_repo_webhook) and delete the hooks of repos the save
removed, including the clear-whole-config case. Addresses the Codex nit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): address Codex nits (webhook orphan on cleared auto_pull, fork detection)

- edit_git_sync_config: also delete a repo's old webhook when the save drops
  the repo OR clears its auto_pull. Webhook fields are only preserved onto a
  Some auto_pull, so a save that present-but-clears a repo would otherwise
  orphan its hook.
- GitSyncRepositoryCard: isFork now uses parent_workspace_id OR the wm-fork-
  prefix (was AND), matching the backend/CLI rule, so prefix-less dev
  workspaces are detected as forks and don't show the parent fork-PR toggle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* docs(git-sync): update design doc for the dropped fork_pull_sync

Phase 5 documented "Keep forks in sync with the tracked branch"
(fork_pull_sync) and its fan-out as implemented; that feature was removed.
Rewrite the section to reflect what ships (fork_open_prs), note the drop +
the per-fork-branch follow-up, and remove the stale fan-out mentions
elsewhere. Addresses the Codex nit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): redact webhook secrets from workspace export; fix doc endpoints

- Export (P1): strip the server-owned auto_pull state (webhook secret/id/error
  + synced sha + last pull status) from git_sync before it is written into an
  export's settings.json for both settings formats. The HMAC webhook secret
  must never leave the server (matching the GET-settings redaction), and a
  re-imported workspace must not inherit another install's hook/sync state.
- Docs: the webhook receiver is a single per-workspace endpoint
  /api/w/{workspace}/github_app/webhook (host-aware for managed + self-managed);
  update the stale push_webhook/{id} and instance-global /api/github_app/webhook
  references.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): skip deleted/archived workspaces in the auto-pull poller

The poller scanned workspace_settings directly, so an archived (soft-deleted)
or renamed-away workspace — whose settings row persists — kept polling and
could enqueue a pull into a dead workspace. Join workspace and require
NOT deleted. The EE webhook receiver gets the same filter (ee ref bumped).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): never trust client-supplied server-owned auto-pull fields

Both write endpoints (edit_git_sync_repository, edit_git_sync_config)
persisted caller-supplied auto_pull.webhook_id / webhook_secret /
webhook_error / last_synced_sha / last_pull_status when adding a repo or
newly enabling auto-pull, letting a client inject a webhook id/secret or
fake sync state. Strip those server-owned fields from the request up front;
existing repos re-derive them from the DB (carried over), new ones start
clean and the server (re)creates the webhook.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): merge webhook fields post-commit instead of clobbering the row

The post-commit webhook reconcile in edit_git_sync_repository and
edit_git_sync_config wrote the whole pre-reconcile git_sync snapshot back
after the main save committed. A concurrent git-sync edit or poller status
write that landed in the gap could then be dropped by the stale snapshot.
Re-read the current row and merge only the reconciled webhook id/secret/error
for the repos the reconcile actually changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* feat(git-sync): parent-managed fork sync + PR-on-deploy toggles

Fork sync (push-on-merge-to-forks parity): a parent-level
auto_pull.sync_forks toggle routes changes on each fork's wm-fork/** branch
into that fork workspace, via the parent's existing webhook and one extra
fork-heads listing per poll tick (git ls-remote pattern for token repos,
git/matching-refs for app-backed). Fork state is a server-written
status-only auto_pull blob on the fork's own repo entry; the fork's card
shows a read-only "managed in the parent workspace" line with its branch
and last pull status. Dev workspaces (prefix-less ids) use the same branch
parsing (unit-tested in windmill-common).

PR-on-deploy: opening PRs for Windmill-pushed branches moves into the
deploy pipeline, per repo toggle (promotion_open_prs on the promotion
repo; parent-level fork_open_prs for fork deploys). The push job carries a
marker and the job-completion hook derives the pushed branch (helper
unit-tested against the CLI formula) and opens the PR outbound, so it
works without inbound webhooks; the webhook-side wm_deploy PR arm is
removed. The documented open-pr-* GitHub Actions remain valid alternatives
(PR creation is idempotent).

Fork guards: promotion mode, enabled auto-pull, and fork_open_prs are
rejected on fork workspaces (they are parent-managed; a fork's deploys
always target its wm-fork/** branch) and the promotion card is hidden in a
fork's settings. Enabling auto-pull now also requires EE, and the
post-commit webhook reconcile persists the normalized delivery mode.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* feat(git-sync): dev workspaces sync with their environment-label branch

A dev workspace's git branch is its environment label verbatim (dev/
staging, default dev) — a first-class env branch like the documented
push-on-merge-staging layout — instead of the wm-fork/** form. The label
rides the deploy job args (backend → hub script → CLI
--dev-workspace-label), the PR completion hook derives the same head, the
webhook/poller route label branches into the matching dev-workspace child
(poller lists them alongside wm-fork/* via extra ls-remote refs / per-label
API lookups), and manual pulls from the UI pass clone_ref accordingly. The
CLI refuses to deploy when the label branch equals the checked-out tracked
branch, which would otherwise commit fork content straight to it.

Because the branch is keyed on the label, the label is now immutable after
creation: set at create/attach only, the set_dev_workspace_label endpoint
is removed and the settings tab shows it read-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* feat(git-sync): nested fork routing + fork-of-dev branch rooting

A fork of a dev workspace now roots its wm-fork/** branch on the dev's
environment-label branch (the content it diverged from) and its PR merges
back into that branch: the backend passes parent_dev_workspace_label with
the deploy (parent row joined in both enqueue paths), the CLI gains
--parent-dev-workspace-label and checks it before the wm-fork- prefix
fallback when rooting a fork-of-a-fork branch, and the PR completion hook
uses it as the PR base.

Fork sync routing covers the whole live descendant chain of the
webhook/poller workspace (recursive, depth-capped) instead of direct
children only, and fork_open_prs is resolved at the root ancestor — only
the root can hold auto-pull config, so grandchild forks sync through it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* feat(git-sync): PR deploy-preview comment, clearer check copy, app-only hints

- The PR diff completion hook maintains one managed comment on the PR
  (Cloudflare deploy-preview style: workspace, status, commit, collapsible
  change list), upserted per synchronize via a hidden marker. The check run
  stays for required-check gating.
- A settings difference in the diff summary is worded by cause: the PR
  changes wmill.yaml, vs pre-existing drift between the repo's wmill.yaml
  and the workspace, vs undetermined (neutral wording).
- Deploy-status check titles name the target workspace ("Deployed 2
  change(s) to staging"), since GitHub shows a head commit's checks on any
  PR containing it and a bare "Deployed" read as if the PR had deployed.
- Token-based repos see a hint pointing at the open-pr-on-commit /
  open-pr-on-fork-commit workflows where the app-only PR toggles would be;
  an API-set toggle on a non-app repo now logs a warning naming the
  fallback; the design doc lists app-only features and their degradation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): EE-gate auto-pull UI, fork pull clone_ref, no-op push PR gate

- CE: the auto-pull and fork-PR toggles are disabled with an EE badge, and
  new sync repos only default them on when licensed (basic git sync is
  available on CE since #8493, but auto-pull is EE and the backend rejects it)
- The pull modal passes clone_ref for wm-fork- forks (wm-fork/<tracked>/<id>)
  so a manual pull fetches the fork branch instead of the tracked branch head
- PR-on-deploy skips no-op pushes: when the push script reports pushed=false
  (e.g. the deploy was caused by an auto-pull), the completion hook no longer
  ensures a PR, so closed PRs aren't recreated by the sync loop

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* chore: refresh package-lock after main merge (windmill-utils-internal 1.8.2)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* test: auto-pull e2e integration tests; fix PR comment table formatting

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): runtime license gate for auto-pull saves; user/group promotion-branch parity

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): explain in-sync PR verdicts with the repo's sync filter scope

A PR that only touches files outside the repository's include paths gets
"In sync", which reads as a wrong verdict; the check summary (and managed
comment) now name the filters, e.g. "Only files matching this repository's
sync filters deploy on merge: `f/**` (excluding `f/pat/**`)."

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* feat(git-sync): clearer card copy/structure; surface PR-creation failures

- Fork sync toggle renamed and kept in the pull section; the fork PR toggle
  moves to the push section with a note that push settings apply to forks
- Fork/dev workspaces' push section names their actual branch instead of the
  tracked-branch line; promotion repos hide the pull direction (promotion
  pushes deploy branches on top of a sync-mode setup)
- Promotion mode line describes the wm_deploy/** branch + merge-to-promote
  flow; workflow-fallback hints lead with the how-to and link to the docs;
  test connection button demoted from accent per brand guidelines
- New server-owned open_pr_error on repo settings: the deploy completion hook
  records why a PR couldn't be opened (e.g. app permission not yet approved)
  and clears it on the next success; shown as a warning under the PR toggles

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix: cfg-gate scope-note helper (dead code on OSS builds)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): license-gate preserved auto-pull; attach strips parent-only settings

- edit_git_sync_repository re-checks the runtime Enterprise gate against the
  EFFECTIVE repo state after preservation: the older-client arm copies the
  existing auto_pull back, which the request-side check never saw
- attach_dev_workspace now mirrors the fork-creation copy on the attached
  workspace's own git sync: promotion repos dropped, auto_pull/fork PRs/PR
  error stripped, and any managed webhook deleted after commit (the attached
  workspace is parent-managed and must not keep pulling its old tracked branch)
- integration test: attaching an auto-pull-enabled workspace strips it

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): detach clears standalone parent; reject label == tracked branch

- detach_dev_workspace clears parent_workspace_id for prefix-less (attached
  standalone) workspaces so they stop classifying as forks and deploying to
  wm-fork/** branches; wm-fork- re-designated forks keep their parent; cache
  invalidations mirror attach
- dev-workspace create/attach reject an environment label that equals a
  git-sync repository's tracked branch (prod's or the candidate's): deploys
  would target the very branch the repo syncs from, and the CLI guard would
  fail every push job after the fact
- CLI unit tests: prefix-less fork beats wm_deploy derivation; isForkWorkspace
  parent-id argument

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* chore(git-sync): bump hub script pins (push 28786, pull 28785)

Published from windmill-integrations #163 with windmill-cli@1.753.1-gitsync.0:
dev-workspace label deploys, fork-of-dev rooting, fork checkout on the
existing remote branch, and the pushed-flag result.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): no parent-only defaults on fork repos; rename strips webhook state

- addSyncRepository skips the auto_pull/fork_open_prs defaults on fork/dev
  workspaces where the backend rejects them (saving a new sync repo from an
  EE fork 400'd deterministically)
- change_workspace_id strips webhook id/secret/error from the copied git_sync
  and deletes the stale GitHub hooks post-commit: they deliver to the old
  (archived) workspace URL, so the new workspace would report a live webhook
  while polling at the relaxed interval; next save re-registers cleanly
- EE: PR diff checks for contributor-fork PRs clone the synthetic
  pull/<n>/head ref (head.ref doesn't exist in the base repo)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* chore(git-sync): bump pull script pin to hub/28787 (synthetic PR ref support)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): targeted jsonb update for open_pr_error (no full-blob clobber)

The full read-modify-write raced the poller's concurrent last_synced_sha /
last_pull_status writes on the same column; mirror the EE status writer and
update only the matching repository element's open_pr_error key.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* style(git-sync): inline EE badge on gated toggles (matches settings nav)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* copy(git-sync): both directions in page/card descriptions; clearer promotion flow

- Page header and sync-card description mention the pull direction, not only
  push-on-deploy
- Promotion description walks the actual flow (wm_deploy/** branch, merge to
  promote, sync the target workspace) and points at the PR toggle / workflow;
  the Git Promotion docs link now also shows on configured cards, not only in
  the empty state

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): resolve branch-less resources' default branch for fork sync

A git resource without an explicit branch polled as the bare "HEAD" ref,
which the fork/dev-label fan-out cannot scope (wm-fork/<branch>/*), so fork
sync silently never ran on polling-only repos. Resolve the remote's default
branch name with `ls-remote --symref HEAD` (one call for name + head sha);
"HEAD" only remains when resolution fails. The polling e2e test now uses a
branch-less resource to cover this shape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): runtime license gate for in-app PR creation

promotion_open_prs/fork_open_prs are rejected on save without an Enterprise
plan (like auto_pull), and the deploy completion hook re-checks the plan
before opening PRs so flags stored while licensed stop driving GitHub calls
after a lapse.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* feat(git-sync): app-aware pull defaults, always webhook delivery, token-repo guidance

- Pull-from-Git defaults on only for app-backed repos (applied when the
  selected resource resolves); polling is opt-in for token repositories,
  with a warning alert recommending the GitHub App (instant pull + in-app
  PRs) or the sync GitHub workflow
- App repos always use webhook delivery with polling fallback: the delivery
  selector is gone and a stored polling mode is normalized back to auto
- Post-save modal reflects the auto-pull state instead of telling the user
  to turn on a toggle that is already on
- Non-app PR hints recommend the GitHub App explicitly

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* copy(git-sync): single info box for token-repo pull guidance

Merges the instant-pull recommendation with the GitHub Action conflict note,
shown only for non-app repos; app repos need neither, and the redundant
'instant webhook sync requires' line is gone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* copy(git-sync): keep the GitHub Action conflict note on app repos

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* copy(git-sync): anchor docs links to their exact sections

GitHub App references point at integrations/git_repository#github-app, the
workflow hints at deploy_gh_gl#github-actions-setup, and the sync workflow
at git_sync#github-actions (all anchors verified against the live docs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* copy(git-sync): fork workflow hint links to git_sync#github-actions

open-pr-on-fork-commit is documented on the git_sync page, not deploy_gh_gl.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* fix(git-sync): fork PRs are opt-in on new connections too

Only auto-pull and fork sync default on for new app-backed connections;
opening pull requests stays a deliberate per-repo decision.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* feat(git-sync): default the managed PR on for new app-backed promotion repos

A promotion deploy's wm_deploy/** branch exists to be merged; without a PR
it's an orphaned branch. Fork PRs stay opt-in. Also scope the sync-repo
auto-pull default to sync mode so promotion repos can't pick it up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* docs(git-sync): GHES self-managed app permission setup

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* docs(git-sync): frame permission update against GitHub Actions, not polling

Existing installations don't have polling; their git-to-Windmill direction
runs on GitHub Actions today, so the approval text describes the update as
replacing those workflows and notes every feature is opt-in.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm

* copy(workspaces): drop 'cosmetic' qualifier from dev-workspace label UI

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

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

Previous ee-repo-ref: 99eef24e2f0402b9a997cde5f67be52ee5d54b0e

New ee-repo-ref: 9b2a6375f838436cf68cff449cc9bc621cca5281

Automated by sync-ee-ref workflow.

* fix(git-sync): reject '/' in fork and dev workspace ids

* fix(git-sync): bound auto-pull git probes with a per-command timeout

* fix(git-sync): persist webhook reconcile via targeted jsonb updates

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-07-16 15:55:44 +02:00
Diego Imbert 7d5009e392 fix: heartbeat job ping during s3object materialization in SQL executors (#10152)
* fix: heartbeat job ping during s3object materialization in SQL executors

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KrFtZjXc8GB6VtSMVJFXjE

* chore: update ee-repo-ref.txt

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KrFtZjXc8GB6VtSMVJFXjE

* chore: update ee-repo-ref to e19948fa2974a7d89bec12957fc6d9fa0a421da8

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

Previous ee-repo-ref: a3828dcd67f026c0e983a1a5dc5c5b33af3c3120

New ee-repo-ref: e19948fa2974a7d89bec12957fc6d9fa0a421da8

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Fable 5 <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-07-16 15:48:11 +02:00
Ruben Fiszel 3bd9f05938 fix(alerts): identify server replica in low-disk alert + per-host dedup tag (#10143)
* fix(alerts): identify server replica in low-disk alert + per-host dedup tag

The server-mode low-disk alert keyed its dedup tag on the mountpoint alone,
so `simple_alert_helper` mapped every server replica onto a single alert row
per mountpoint. With more than one replica that row flaps every monitor pass:
a replica seeing low disk raises the alert while a replica seeing healthy disk
recovers it. The alert text also could not say which replica tripped.

The fix lives in windmill-ee-private (`low_disk_alerts` in
windmill-common/src/ee.rs) and appends the hostname to both the message and
the dedup tag, mirroring the worker branch.

Also add a regression test pinning the server tag as per-host, and correct the
monitor cadence comments: iterations are LISTEN_NEW_EVENTS_INTERVAL_SEC
(10s by default), not 30s, so "~60s (2 iterations * 30s)" was wrong on both
factors.

* fix(alerts): widen healthchecks.check_type so per-host disk tags fit

Alert tags embed a mountpoint and a hostname, both unbounded, but check_type
was varchar(50). create_alert only logs the insert error while the
notification still fires, so an overflowing tag re-alerts every monitor pass
and never records recovery state.

The server tag overflows for ordinary pod-length hostnames, and the existing
worker tag already overflows for every tracked mount except "/". Widening the
column fixes both; bounding the hostname would not, since the mountpoint alone
can consume the budget.

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

* chore: update ee-repo-ref to b3d01f2c0d2c0714ae95b8a348af22b0fcc30ee4

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

Previous ee-repo-ref: ccd1e42cf6b2d051ca17074fbdf5b80a46cffe0f

New ee-repo-ref: b3d01f2c0d2c0714ae95b8a348af22b0fcc30ee4

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-07-16 15:21:54 +02:00
Ruben Fiszel 4e0fd4db55 feat(alerts): include disk total and top consumers in low-disk alert (#10144)
* feat(alerts): include disk total and top consumers in low-disk alert

Point ee-repo-ref at the companion windmill-ee-private commit.

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

* fix(alerts): pass alert message lazily from the min-version check

simple_alert_helper now takes the error message as a future so callers can
put diagnostic work behind it. Update this call site and point ee-repo-ref
at the companion windmill-ee-private commit.

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

* chore: point ee-repo-ref at rebased EE branch

Rebase onto EE main so the pin keeps the SAML metadata fixes (394ad23)
that the previous ref carried, and pick up the mount-scoped consumer walk.

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

* chore: bump ee-repo-ref for bind-mounted file exclusion

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

* chore: update ee-repo-ref to 4d7aafdba33a879b3c60d390c960e57679da9e87

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

Previous ee-repo-ref: 08d3aa4c5bf630d15a28289cca62a0f1da7b7386

New ee-repo-ref: 4d7aafdba33a879b3c60d390c960e57679da9e87

Automated by sync-ee-ref workflow.

* chore: point ee-repo-ref at the merged EE work plus the test fix

ee#667 squash-merged, so the previous pin was a branch commit no longer
reachable from EE main. Point at ee#669, which branches from EE main and
carries the /proc test-portability fix.

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

* chore: update ee-repo-ref to d8a7ac6ae97642a7f4928e6be6846a32dabf4e26

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

Previous ee-repo-ref: 5526aedd73654b9aa4086dae0441b9687ff6415d

New ee-repo-ref: d8a7ac6ae97642a7f4928e6be6846a32dabf4e26

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-07-16 14:58:26 +02:00
Ruben Fiszel a935d06c8e chore(main): release 1.760.1 (#10142)
* chore(main): release 1.760.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-15 21:29:39 +02:00
Ruben Fiszel 8c725d9e44 fix(apps): honor presigned S3 signature on app display/preview routes (#10141)
The app provenance gate short-circuits on a valid presigned signature, but only the raw download_s3_file route parsed it. The parquet/csv/table-count/file-preview/metadata routes discarded sig/exp and always fell through to the provenance gate, so a presigned S3 object rendered as a table showed "File restricted" for any viewer who did not produce it. Thread sig/exp through every apps_u S3 display route and forward the presigned bearer from ParqetCsvTableRenderer/DisplayResult.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 21:18:15 +02:00
Ruben Fiszel 2092155191 chore(main): release 1.760.0 (#10128)
* chore(main): release 1.760.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-15 18:00:53 +02:00
hugocasa 8bfe5c9340 fix(ai): stop sending the AI agent system prompt twice for OpenAI (#10126)
* fix(ai): stop sending the AI agent system prompt twice for OpenAI

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

* fix(ai): document collect_system_prompt precedence and trim duplicate comments

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

* fix(ai): hoist only the leading system prompt for OpenAI

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-07-15 17:53:09 +02:00
Ruben Fiszel 17872018cc feat(nsjail): make python/ansible rlimit_as configurable per worker (GIT-921) (#10138)
nsjail caps a jailed job's virtual address space at rlimit_as (4096 MiB for
python3 and ansible). JIT runtimes (Bun/JavaScriptCore, the JVM) reserve large
virtual ranges up front, so a subprocess spawned from a jailed Python/Ansible
job can crash against this cap even when its physical memory use is modest
(e.g. the Bun-compiled claude CLI hitting JSC/pthread allocation failures).

Most other language protos already run with disable_rl: true (unlimited);
python3 and ansible are the outliers with an explicit rlimit_as. This exposes
that cap via a per-language env var (NSJAIL_PY_RLIMIT_AS_MB,
NSJAIL_ANSIBLE_RLIMIT_AS_MB) so operators can raise or lift it on a dedicated
worker pool without a source patch/rebuild and without weakening the
mount/PID/user-namespace isolation that provides the real security boundary.
Only the address-space limit changes; cpu/fsize/nofile rlimits are untouched.

Value is in MiB, or unlimited/none/inf/0 to uncap (rlimit_as_type: INF). Unset
keeps the historical 4096 default.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 17:52:39 +02:00
Ruben Fiszel f7eb5c460d fix(apps): invalidate cached app policy on change or deletion (GHSA-r5v4-cxh9-7qhq) (#10121)
* fix(apps): invalidate cached app policy on change or deletion (GHSA-r5v4-cxh9-7qhq)

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

* docs(agents): keep PR tests and comments minimal and non-ephemeral

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-07-15 17:41:54 +02:00
Ruben Fiszel 188647a942 fix(security): enforce variables:write scope on resource-delete var cascade (GHSA-xmr2-98m6-cjf7) (#10123)
A token scoped only to resources:write:<path> could delete linked secret
variables it had no variables:write scope for, by embedding $var:<victim>
in an attacker-controlled resource value and triggering the resource-delete
cascade. #9712 re-enforced scoped-token boundaries broadly but missed this path.

Add check_linked_var_delete_scopes, called before the cascade in both
delete_resource and delete_resources_bulk: require variables:write for every
linked variable, failing (and rolling back) the delete otherwise. No-op for
unscoped tokens, so full-token cascade cleanup is unchanged.

No co-located-path exemption: a resource and a variable may share a path, and a
resource-write token can create a resource over an existing standalone variable
and self-reference it, so "same path as the deleted resource" is attacker-
forgeable and cannot stand in for variable scope.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:19:17 +02:00
Ruben Fiszel 6407d9ff5c fix(bash): normalize CRLF line endings before running scripts (#10131)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 12:46:26 +02:00
Ruben Fiszel 27ead8d084 fix(ai): disable redirects on worker AI provider client (GHSA-5q4v) (#10122)
* fix(ai): disable redirects on worker AI provider client (GHSA-5q4v)

The worker AI request path issued provider requests with the shared
HTTP_CLIENT, which follows up to 10 redirects without revalidating each
hop. SSRF validation on the provider base_url is single-shot, so a public
base_url could 3xx the worker into a private/internal host (e.g. cloud
metadata), bypassing the private-endpoint protection. The API proxy was
already hardened in #9370; the worker path was missed.

Add a dedicated AI_HTTP_CLIENT with redirects disabled and use it for the
user-controlled provider endpoint, mirroring the API proxy client.

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

* fix(ai): add ALLOW_AI_BASE_URL_REDIRECTS debug escape hatch

Off by default (redirects stay disabled). When set, restores redirect
following on the AI HTTP client for debugging non-standard/self-hosted
gateways, with a startup warning that it weakens SSRF protection.

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

* docs(ai): correct redirect comment for the escape hatch override

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

* docs(ai): condense redirect invariant comments per review

Anchor the SSRF rationale to ALLOW_AI_BASE_URL_REDIRECTS (the knob that
would break it) and shorten the AI_HTTP_CLIENT and call-site comments to
avoid restating it at multiple sites (AGENTS.md).

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-07-15 11:24:26 +02:00
Ruben Fiszel 73c8d7f08a fix: reject git URL fragment/query SSRF bypass (GHSA-p5cj-8cfh-mjv6) (#10120)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 11:07:04 +02:00
Ruben Fiszel 360e783b1d chore(main): release 1.759.0 (#10108)
* chore(main): release 1.759.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-15 10:02:01 +02:00
Ruben Fiszel 6d1e12d5e9 feat(nativets): expose the standard web-platform globals deno_web provides (#10112)
* feat(nativets): expose standard web-platform globals for bun parity

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

* feat(nativets): wire bun-present Event subclasses and add construction smoke test

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

* fix(nativets): seed performance.timeOrigin per isolate, drop broken reportError

Addresses CI Codex review on #10112:
- performance.timeOrigin was undefined (setTimeOrigin never called); seed it
  per isolate via __wmInitPerIsolate executed from create_nativets_runtime.
- reportError needs a global EventTarget this runtime never installs; drop it.
- reword the namespace-import comment to not describe drafting history.

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

* feat(nativets): wire DOMException global + broad edge-case smoke sweep

DOMException is present in bun and, more importantly, deno_web references it as
a global: AbortController.abort() with no reason constructs a
DOMException("...", "AbortError"), so the already-wired AbortController/
AbortSignal threw "DOMException is not defined" on abort. Surfaced by a new
functional edge-case sweep (smoke_web_globals_edge_cases) that exercises every
wired global for real (not just presence) — DOMException/abort, AbortSignal.timeout,
EventTarget dispatch, stream tee/reader/writer, all 3 compression formats,
structuredClone Map/Set/Date/circular/reject-function, performance mark/measure,
MessagePort delivery — plus a check that the merged Web Crypto globals still work.

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

* test(nativets): restore arg-default smoke tests dropped in merge, drop history comments

Addresses CI Codex/Pi review on the merge commit:
- Merge conflict resolution (checkout --ours) dropped smoke_missing_optional_arg_uses_default
  and smoke_explicit_null_arg_is_preserved (added on main by #10111); restore them.
- Reword edge-case-sweep comments to state the constraint, not how the gaps were found.

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

* fix(nativets): give reportException a global dispatch target; wire stream reader/controller globals

Addresses CI Codex review on #10112:
- P1: a throwing EventTarget listener (and reportError) is routed through
  deno_web's reportException, which dispatches on a saved global reference.
  With none set, dispatchEvent threw a masking error that hid the original.
  Wire a dedicated EventTarget as that target so the ORIGINAL error is reported
  (async unhandled, matching bun). Does NOT make globalThis an EventTarget (bun's
  isn't either). Re-adds reportError, now functional. Regression test asserts the
  original error is surfaced, not a masking one.
- P2: wire the stream reader/controller globals bun also exposes
  (ReadableStreamDefaultReader/BYOBReader, ReadableStreamDefault/ByteStreamController,
  ReadableStreamBYOBRequest, WritableStreamDefaultWriter/Controller,
  TransformStreamDefaultController) for instanceof parity; sweep verifies via real
  reader/writer/controller instances.

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

* fix(nativets): make globalThis an EventTarget so globalThis.reportError() works

Addresses follow-up CI Codex review on #10112:
- P1: the prior fix saved a *separate* EventTarget as the global reference, so
  globalThis.reportError() still failed its receiver check (this === globalThis_)
  with 'Illegal invocation'. Make globalThis itself the saved reference by turning
  it into a functional EventTarget (setPrototypeOf to DedicatedWorkerGlobalScope +
  setEventTargetData + webidl brand + saveGlobalThisReference), per isolate in
  __wmInitPerIsolate. Both reportError(e) and globalThis.reportError(e) now surface
  the original error (async, matching bun) instead of throwing. New test
  smoke_report_error_both_call_forms covers both call forms.
- P2: reword the regression-test comment to state the invariant, not the patch history.

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

* feat(nativets): wire performance constructor globals for bun parity

Addresses the P2 nit in the CI Codex review: bun exposes Performance,
PerformanceEntry, PerformanceMark, and PerformanceMeasure as globals (deno_web
exports all four), so wire them alongside the performance singleton. The
edge-case sweep verifies instanceof against real mark/measure entries.

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

* docs(nativets): state global-wiring comment as a constraint, not patch history

Addresses the P2 in the CI Codex review: reword the block comment to describe
the current bun-parity constraint and the deliberate EventSource/ImageData
exclusions, without narrating what was or wasn't wired before (per AGENTS.md).

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-07-15 09:47:54 +02:00
Ruben Fiszel 95d9ff02ee fix(jseval): raise QuickJS eval memory cap to 128MB with clear OOM error (#10116)
* fix(jseval): raise QuickJS eval memory cap to 128MB with clear OOM error

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

* docs(jseval): note bare null/undefined throws are absorbed into OOM bucket

Addresses CI review P2 nit on map_quickjs_error.

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

* fix(jseval): keep batch-rerun eval on a conservative 32MB cap; tighten OOM match

Addresses CI review: eval_simple_js runs in the API process with unbounded request concurrency, so it must not inherit the raised flow-transform cap. Tighten the Exception OOM match to exact string. Reword drafting-history comments per AGENTS.md.

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

* fix(jseval): gate OOM on InternalError kind; path-specific remediation hint

Require the OOM InternalError name (not just the message) so a user throw new Error('out of memory') is not misclassified, and only suggest QUICKJS_MEMORY_LIMIT_MB on the env-tunable flow path (not the fixed-cap eval_simple_js path).

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-07-15 09:35:55 +02:00
Ruben Fiszel a6191e2a85 fix(mcp): advertise flow input variables in MCP tools (#10117)
Flow input schemas omit `required` (they carry an `order` key instead),
which made `serde_json::from_str::<SchemaType>` fail in
`convert_schema_to_schema_type`. The error was swallowed and callers fell
back to an empty `SchemaType::default()`, so MCP flow tools advertised no
inputs. Add `#[serde(default)]` to `type`, `properties`, and `required` on
`SchemaType` so these schemas deserialize correctly. Scripts always include
`required` and were unaffected.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 09:04:50 +02:00
Ruben Fiszel e9fd4e7554 perf(rls): wrap session GUC reads in RLS policies for per-statement InitPlan (GIT-919) (#10110)
* perf(rls): wrap session GUC reads in RLS policies for per-statement InitPlan

RLS policies read current_setting('session.user' / 'session.groups' /
'session.pgroups' / 'session.folders_read' / 'session.folders_write')
directly inside their USING / WITH CHECK predicates. Postgres treats those
unwrapped calls as potentially row-varying and re-evaluates them once per
scanned row, on the read path of every workspace-scoped table.

The GUCs are set with SET LOCAL (set_config(..., true)) in
set_session_context(), so they are constant for the duration of a statement.
Wrapping each session-derived subexpression in a scalar sub-select lets the
planner hoist it to a one-time InitPlan (evaluated once per statement, reused
for every row) — same rows in, same rows out, N per-row GUC lookups collapse
to 1. Array-producing subexpressions keep an explicit ::text[] cast on the
sub-select so `= ANY (...)` / `?|` stay in their array-operand form rather
than being reparsed as a row-returning subquery.

The consolidating migration recreates every existing policy (across ~30 prior
migrations) whose predicate reads a session GUC, by deparsing the current
predicate and substituting the wrapped forms; the down migration is the exact
inverse (byte-identical round-trip). The adding-a-trigger skill documents the
wrapped form so new trigger tables inherit it.

Surfaced by pgrls (PERF001).

Fixes GIT-919

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

* docs(adding-a-trigger): fix RLS example cast placement for = any context

The `= any(...)` example put the ::text[] cast inside the sub-select, which
Postgres parses as a row-returning subquery and rejects at CREATE POLICY with
`operator does not exist: text = text[]`. Move the cast outside the sub-select
(matching the migration's canonical form) so the operand stays in array form,
and note why.

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-07-15 08:56:05 +02:00