mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-19 00:02:03 +00:00
main
414 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f34b7fbcfa |
fix: make the listScripts parent_hash filter valid SQL (#10752)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ab3c0206d7 |
fix: support @typechecked decorator in Python relative imports (#8495)
WindmillFinder's ModuleSpec lacked origin, so __file__ was never set on loaded modules. inspect.getfile() then raised "is a built-in module", breaking typeguard's @typechecked and anything else that introspects module source. Use spec_from_file_location() which sets origin correctly. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: hugocasa <hugo@casademont.ch> |
||
|
|
d97f380c87 | test: stop stranding sqlx pool permits in run_in_isolated_thread (#10707) | ||
|
|
ee533273dd |
fix: confine jobs:run tokens to the jobs of the runnables they may start (#10635)
* fix: confine path-scoped jobs:run tokens to their runnable's jobs Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: project singlestepflow onto its runnable and confine kind-only run scopes Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep every by-id job read reachable by a jobs:run token Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: whitelist the dbt and wac-approval by-id job reads for run tokens Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: let an apps:run scope satisfy job-read confinement for that app's runs Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: apply run-scope confinement on top of the approval-token read bypass Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: confine the resume-secret job reads to the run scope as well Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
3f07a1a803 |
feat: let the global AI chat call connected MCP servers as the user (#10656)
* feat: let the global AI chat call connected MCP servers as the user Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address review findings on the chat MCP tools Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: connect MCP servers from a predefined list in chat and agent steps Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: show the OAuth redirect URL in the instance connect settings Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: clarify the OAuth redirect URL copy in instance settings Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: match the instance settings warning style and drop the redirect tooltip Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: use the standard warning alert for the redirect url mismatch Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: correct the GitHub token guidance in the MCP registry Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: warn when an OAuth connect lacks the scopes an MCP server needs Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: request the connect's scopes when the oauth popup is opened directly Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: connect an oauth-app MCP server without leaving the panel Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: seed connect scopes from the instance config only Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: make the chat use only the MCP servers you turn on Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: align the MCP connect UI with the design system * feat: make a pasted url the default way to connect an mcp server * feat: show provider icons on the suggested mcp servers * fix: make both mcp sign-in paths behave the same and stop reloading on toggle * fix: clarify the mcp tool step's server field and drop its info alert * fix: name the mcp resource in the tool step and move the transport note into the connect box * fix: drop the redundant description on the mcp resource field * fix: make the mcp connections trigger icon-only * fix: scope enabled mcp servers to the account and address review nits * fix: wait for connect scopes and create session connections in the operating workspace * feat: move mcp connections into the chat's plus menu and fix review findings * fix: show mcp servers as checkboxes so off reads as a state * feat: give menu rows an on/off switch and use it for mcp servers * fix: lead the mcp menu rows with the switch * feat: keep the menu open while toggling and simplify the connect card * fix: ask for the server before the credential in the connect card * fix: show one credential path at a time in the connect card * fix: label the path field and move token guidance into its tooltip * fix: open straight into connect and keep the server menu scannable * feat: warn when an mcp connection lands outside your own space * refactor: require the workspace on the mcp connect components and rename the oauth child * fix: replace the oauth variable on reconnect and bound every mcp result * feat: show a connected server's provider icon in the connections list * feat: resolve mcp provider icons from the url and clarify the path field * style: align the mcp connect card with the design system surfaces * style: drop the redundant oauth support line and name the scopes oauth scopes * feat: keep the mcp connect card open in the connections drawer * feat: preopen the mcp connect card under the agent step resource picker * feat: resolve a typed mcp url to its registry entry and describe the token field * style: name both mcp connect actions connect * style: name the mcp oauth actions connect with the provider * style: say in the path description what the connect action will save * style: name the resource type in the mcp connect path description * feat: cache mcp provider icons and confirm disconnect in a modal * fix: keep the mcp menu switches live and the disconnect modal above the drawer * style: fall back to the plug icon in the mcp menu rows * fix: never destroy a foreign variable or resource when connecting an mcp server * fix: prove a token variable is ours before writing it and bound mcp search failures * fix: pin an mcp oauth popup to the target it was opened for * fix: bind an mcp credential to the server and popup it was requested for * fix: bound mcp tool calls with a deadline and drop stale server listings * fix: keep the disconnect confirmation handler returning void * fix: tie the mcp tool cache to the resource revision and the grant to its scopes * fix: verify mcp read-only server-side, keep oauth connector mounted Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
22eadab67d |
perf: resolve the worker external IP in the background (#10697)
* perf: resolve the worker external IP in the background `run_workers` awaited `external_ip::get_ip()` — an HTTPS GET to hub.windmill.dev — before spawning any worker, so every worker process paid that round trip before its first job pull. Measured on a CE debug build it was 120-450 ms of a ~200-500 ms startup, and behind a firewall the call does not fail fast: it burns its whole 5 s connect timeout, on every process start. That cost is per-job under EXIT_AFTER_N_JOBS. The value is informational (it is only written to `worker_ping.ip`, which the workers list displays so users can whitelist the address), so nothing needs to wait on it. It now resolves into a process-wide cache off the startup path, and `WORKER_EXTERNAL_IP` supplies it explicitly for deployments that know their egress address or have no egress at all. Until it resolves the ping carries no IP, which `insert_ping_query` now COALESCEs so a reclaimed row keeps the address the previous process wrote instead of being blanked. The main loop reports the IP as soon as it lands rather than on the next periodic tick, so a short-lived process still records it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep unknown worker IPs out of the whitelist alert Review follow-ups: - `WhitelistIp` filtered only the `'unretrievable IP'` sentinel, so the `'NO IP'` one a pending or failed lookup now leaves in the row would be offered as an address to whitelist. It filters both. - Register `WORKER_EXTERNAL_IP` in `ENV_SETTINGS` so operators can confirm from the instance settings view that it took effect. - The worker tracked whether it had reported the IP by re-reading the cache after each ping rather than remembering what the ping carried, so a lookup landing mid-ping marked it reported without it reaching the row. The value is read once and threaded through `insert_ping` / `update_worker_ping_full`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: report a sentinel IP once the lookup has definitively failed Keeping the previous process's address on a reclaimed `worker_ping` row is right while the lookup is still in flight, but not once it has failed: the row would advertise an address nothing has confirmed, and the whitelist alert would offer it. A failed lookup now reports `UNKNOWN_IP`, leaving NULL to mean "in flight". `WORKER_EXTERNAL_IP` is rejected when longer than the `varchar(50)` column rather than panicking the worker on its initial ping, which is a hard failure. Adds the regression guard for the `ON CONFLICT` semantics: reverting to `ip = EXCLUDED.ip` would compile and blank every reclaimed row. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep the agent initial ping acceptable to older servers An agent worker routinely runs against a server of a different version, and one predating the background lookup rejects an initial ping carrying no IP — which `run_worker` turns into a panic, so a newly upgraded agent would crash-loop against it. The not-resolved-yet case goes over the wire as the sentinel instead, and the server maps it back so a reclaimed row still keeps its address while resolution is pending. Also documents `ip` as the one conditional exception to `insert_ping_query`'s "only `started_at` and `jobs_executed` survive a restart", and adds `WORKER_EXTERNAL_IP` to the README env-var table. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: deliver the resolved IP to servers that only take it at registration A server predating the background lookup applies `ip` from the initial ping only, and ignores it on the periodic ones. An agent registering before its lookup resolves would therefore keep the sentinel forever on such a server, where it used to report its real address. It registers a second time once the address is known, skipping that when the address is still unknown, when the server is reached over SQL and needs no second registration, or once a job has run, since registering clears the row's current job. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: re-register the resolved IP even after a job has run Gating the second registration on "this process has not run a job yet" meant an agent that pulled queued work before its lookup resolved never delivered the address to a server that only takes one at registration. No job of the worker is in flight where that runs, so the gate bought nothing beyond the last job's id, which the next job refills. Documents the two cases where WORKER_EXTERNAL_IP stops being an optimisation and becomes the only way to report an address: an agent against such a server, and a process shorter-lived than the lookup. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * revert: drop the WORKER_EXTERNAL_IP escape hatch Supplying the address by hand skips the hub lookup, which is not something to make easy. Resolving it in the background is what keeps it off the startup path; opting out of it is a separate decision this does not need to take. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: distinguish an IP never established from one that could not be retrieved `NO IP` was doing double duty: the column default for a row whose lookup has not resolved, and the marker for one that failed. An operator reading the workers list could not tell "not resolved yet" from "this instance cannot reach the hub", and the latter is the actionable one. A failed lookup now reports `unretrievable IP`, which is also what it reported before the lookup moved off the startup path. That leaves `NO IP` meaning only "no address established", which is what an agent sends while its lookup is in flight and what the server maps back to "unresolved" — so the wire sentinel no longer collides with the failure marker, and an agent delivers the failure to a server that only reads an IP at registration. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
2714210d7c |
fix: expand AZURE_DEVOPS_TOKEN placeholder in backend git probes (#10677)
* fix: expand AZURE_DEVOPS_TOKEN placeholder in backend git probes * fix: require azure token placeholder to be http userinfo * fix: scrub probe credentials from git stderr and harden token mint * fix: confine azure token placeholder to azure devops hosts * fix: require https and authorize azure reference at write time * fix: require workspace admin to configure an azure token reference * fix: name the azure reference in the admin-required error |
||
|
|
71b9989daa |
feat: auto-build binaries to object storage on deployment (#10673)
* feat: auto-build binaries to object storage on deployment Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: queue the auto-build from pre-locked deploys and off the lock slot Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: materialize companion modules before a deploy-time build Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep a build job from stamping lock_error_logs on a healthy script Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: de-flake test_flow_lock_all and surface the lock error it hides Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: trim drafting history from the flow-lock fixture comments Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: stop a binary build from restarting dedicated workers Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep the build-job marker off the agent wire and out of user args Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
603b2012a7 |
fix: home search matches each term instead of the whole query verbatim (#10663)
* fix: home search matches each term instead of the whole query verbatim * docs: state the search term cap and drop unreachable test cases * fix: treat a term-less search as no filter and trim the comment * fix: a term-less search matches nothing instead of the whole page * feat: match the homepage fuzzy search exactly in the runnables endpoint * docs: say apostrophes stay in terms; test summary-less and draft rows * docs: separate an empty search from one holding no terms * docs: state that terms split on ASCII alphanumerics only |
||
|
|
c09de594b6 |
feat: version resource values with history, diff and restore (#10596)
* feat: version resource values with history, diff and restore Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: record resource versions in a trigger so direct writes are covered Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: show the selected version's value and tighten history write access Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * perf: gate resource version recording in trigger WHEN clauses Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: clear a resource's past versions, and address review nits Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: restore the displayed version and keep author attribution on pooled writes Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: scope history to the selected workspace and gate clearing on ownership Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: gate restore on write access and clearing on the signed-in workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor(frontend): share the version-history row between script and resource drawers Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * perf: trim resource version history in the monitor sweep, not on write Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(frontend): match the script versions drawer shell for resource history Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * perf(frontend): highlight version values instead of mounting monaco Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(frontend): match the script drawer's code preview presentation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: rank version trim in one windowed pass instead of a correlated delete Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor(frontend): treat the newest version as current by position Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * perf: gate the resource version trim to an hourly sweep Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: unnest the version row action and correct the trim cadence docs * perf: cap the history listing and use sets for reference lookup * feat: warn when a resource is written more than 60 times a minute * fix: lower the resource write advisory to 20 per minute * fix: discard stale history loads and never diff against an unread value * fix: correct the write advisory boundary and document the eviction lock * fix: read history and the live value from one snapshot * refactor: read the drawer's diff baseline from versions, not the live resource * fix: open the history drawer with no version selected * fix: disarm the clear confirmation and clear the pane when the selection moves * fix: explain the missing diff and drop a guard that can no longer fire --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
5ce29b3436 |
feat: add public sharing option for job pages (#10573)
* feat: add public sharing option for job pages Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: gate public run sharing and address review findings Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address review nits on public run sharing Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: key public run view on workspace, job and token Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
8bab579665 |
stop destroying AI sessions in workspaces reached without a usr row (#10567)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
c59b60c729 |
fix: keep the same_worker pin when a suspend ends without approval (#10552)
* fix: keep the same_worker pin when a suspend ends without approval A disapproved or timed-out approval gate hands the flow back through the UpdateFlow channel with unrecoverable = true. That flag means "the previous step's worker died", and it is read by six sites. Five of them happen to want what it does here, but continue_on_same_worker and continue_with_runners do not: the worker that ran the approval step is alive, so unpinning the error handler and routing it by tag breaks the ./shared contract of a same_worker flow and can land it on a worker group that cannot run it — the same defect #10551 fixed for the three producers that hand back a live flow. Replace the boolean with StepFailureKind so the suspend producer can say "worker alive, but this failure is not the module's to handle" instead of overstating a worker death. The failed module's error policy is deliberately still bypassed: the failure is recorded against the step the gate was holding back, which never ran, so its retry would re-open the gate and its continue_on_error would skip it outright (verified: the gated step is marked Failure with a nil job id and the flow jumps past it). suspend. continue_on_disapprove_timeout remains the way to continue past a gate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat(flow-editor): flag that continue on error does not cover the approval gate A resolved approval is recorded against the step the gate holds back, not the step carrying the suspend, so continue_on_error never sees it: the flow still stops on a disapproval or timeout. Point users at suspend.continue_on_disapprove_timeout, which is what actually continues past a gate, whenever both settings are on and that one is not. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
1aee22296e |
fix: keep the same_worker pin across a flow module that spawns no job (#10551)
When a module completes without spawning a job — an empty branch, an empty for-loop, or a module already marked Success — the flow hands itself back through the UpdateFlow channel, and the result processor resumed it with unrecoverable = true regardless of what sent it. That flag means "the previous step's worker died", which holds for none of the three producers except a suspend that ended without approval. The stale argument was inert until continue_on_same_worker and continue_with_runners started reading it, since when the step after such a module is pushed as an ordinary queued job. It is then routed by tag and can land on any worker in the pool, breaking both the ./shared directory contract and the guarantee that a same_worker flow stays on a worker able to run it — a step whose tag resolves to a worker group that cannot execute its language fails instantly, taking the flow with it. Carry the flag on the UpdateFlow message so each producer states its own case, rather than having the shared receiver assume the worst. The three that hand back a live flow forward whatever their caller reported, so a genuinely unrecoverable failure still crosses the hop unchanged. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ecae9320d0 |
fix: let admins edit the dev workspace lock ruleset (#10512)
* fix: let admins edit the dev workspace lock ruleset Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: route the empty protections panel through the owning workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: make protection rule rename actually apply Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: cache the renamed protection rule query for sqlx offline Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep verbatim rule names and scope parent-admin lookup to its workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: store renamed protection rule names verbatim Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
d9dd036edc |
fix: stop app updates from silently converting an app between raw and low-code (#10495)
* fix: stop app updates from silently converting an app between raw and low-code * fix: lock the app row for the kind guard and route MCP away from raw apps * style: condense the restore kind-change comment |
||
|
|
fb82748296 |
fix: make on_behalf_of control permissions for scripts and flows (#10438)
* fix: make on_behalf_of control permissions for scripts and flows Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: inherit the recorded on-behalf-of identity when a preserving deploy omits it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep an omitted permissioned_as from re-versioning an unchanged script Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: derive the on-behalf-of principal from the email and reject mismatched pairs Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: stop workspace deploys from carrying a source-workspace principal Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: correct the onBehalfOfPermissionedAs param doc Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: pin that workspace deploys never carry a source-workspace principal Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: correct the omitted-principal contract and refresh generated prompts Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep external-superadmin principals on email-only redeploys Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: scope the recorded principal to its workspace and prefer real accounts Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: carry the recorded principal correctly through drafts and set-permissioned-as Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: sweep draft identity pairs on email change and offboarding Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: leave group identities alone when sweeping a user's email Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: treat only g/ without an email as a group, and match the offboard preview Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: stop the group guard from skipping rows with no recorded principal Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: state the group guard once instead of restating it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: make the permissioned_as the only stored on-behalf-of identity Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * perf: skip resolving the on-behalf-of address for sync clients that discard it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address the local review of the identity refactor Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: resolve the on-behalf-of identity coherently across clones, offboarding and no-op deploys * test: pin that a fork keeps only the on-behalf-of identities that resolve in it * fix: decide a principal prefix-first everywhere and canonicalize bare addresses * fix: prefix a slash-containing address so a reader cannot take it for a group * fix: read an address as a username before the group- convention * fix: rewrite the canonical principal when an account's address moves * fix: keep the address form of a principal to accounts without a usr row * fix: reject an identity a job row cannot carry and read it uncached at dispatch * fix: count characters against the job identity width and cap the backfill * refactor: name the script/flow principal on_behalf_of, as apps do * docs: state the caller-must-authorize contract on the identity resolvers * fix: keep writing on_behalf_of_email until every worker reads the principal * fix: err high on the compatibility version and document the last resolver * fix: keep the compatibility address current through identity mutations * fix: carry the compatibility address with the principal on every copy path * chore: re-pin the EE ref to the companion branch merged with EE main * fix: key the dbt retry lookup on the stored principal * fix: keep a mixed-version address recoverable through a fork * fix: read a round-tripped address uncached so a redeploy is not rejected * fix: refuse an email change that would make a principal unenqueueable * chore: update ee-repo-ref to ac3d7d015296f041ae44ab6bc4953485f44d36e4 This commit updates the EE repository reference after PR #704 was merged in windmill-ee-private. Previous ee-repo-ref: 219b0b03905a1a0028054b3a4985724e77d09036 New ee-repo-ref: ac3d7d015296f041ae44ab6bc4953485f44d36e4 Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
dda59767c2 |
feat: stamp webhook trigger_kind on token-driven job runs (#10431)
* feat: stamp ui vs webhook trigger_kind on direct job runs * fix: gate ui trigger kind on min worker version and dedupe display names * docs: state that the ui trigger kind attributes rather than proves * refactor: fold the trigger fallback into one trigger_or_fallback helper * feat: hold trigger_kind as a tolerant label on the worker paths * chore: refresh the sqlx offline cache for the trigger_kind label queries * chore: update ee-repo-ref to 7de7daff5eed410e0c815ad6b292d2b4303f02f2 This commit updates the EE repository reference after PR #700 was merged in windmill-ee-private. Previous ee-repo-ref: 974ab910d9a30c5565e1198ee312acc6d11239f3 New ee-repo-ref: 7de7daff5eed410e0c815ad6b292d2b4303f02f2 Automated by sync-ee-ref workflow. * fix: keep the API job structs tolerant of unknown trigger kinds too * chore: point ee-repo-ref at the merged EE main --------- Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
02c4a9e515 |
fix: carry the token label into job-run audit rows (#10433)
* fix: carry the token label into job-run audit rows * docs: state the audit end-user precedence at the push signature * chore: point ee-repo-ref at the companion branch * docs: state the username/end_user split at the push signature * feat: keep the audit caller searchable when a token label takes end_user * fix: skip the caller parameter when it repeats the end user |
||
|
|
68a52f45a7 | refactor: deprecate username_to_email in favor of WM_END_USER_EMAIL (#10429) | ||
|
|
c69f08073a |
fix: add apps:run to the token scope picker and confine path-scoped app tokens (#10428)
* fix: expose apps:run in the token scope picker and let apps:write grant it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: confine path-scoped app run/write tokens to the app they name Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: let apps:run read back its own app's S3 files, condense scope comments Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: let apps:write mint apps:run and extend run read-back to app S3 display routes Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: drop stale embed-token wording from the app S3 helper summary Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
3716a71fd7 |
fix: credit the token owner instead of the token label in the audit trail (#10423)
* fix: credit the token owner instead of the token label in the audit trail Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address review findings on token-owner audit attribution Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: carry token-label provenance explicitly instead of inferring it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: point ee-repo-ref at the companion branch Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: trust only non-forgeable token labels to name the acting entity Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: reject reserved system-token labels at token creation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: narrow the token-label guard to server-minted namespaces Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: add the provenance field to the remaining ApiAuthed literals Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: stop trusting the email- label, which no mint produces Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
318c9f0073 |
feat(git-sync): dedicated base url for GitHub webhook delivery (#10411)
* feat(git-sync): let GitHub webhooks register a dedicated base url Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): validate the webhook base url and apply it on change Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: pin ee ref for the git-sync webhook base url change Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): validate and reconcile the webhook base url on every write path Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): route every declarative settings writer through the same rules Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): let the reconciler own the webhook field write-back Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): make the webhook base url validators agree across UI and server Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): lock the workspace row across git_sync read-modify-writes Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: pin the webhook base url validator to its server counterpart Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): retry a failed webhook move on every re-apply of the setting Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): retry pending webhook moves on every declarative re-apply Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): reject non-string webhook base urls and bound the sweep Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): reject credential-bearing webhook base urls Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): keep credentials out of webhook base url validation errors Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): redact through the last authority @ when reporting a bad url Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): stop echoing unparsed webhook base urls instead of scrubbing them Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): never echo a submitted webhook base url in validation errors Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): keep the submitted scheme out of validation errors Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor(git-sync): drop the webhook sweep, surface stale receivers in settings Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): refresh the stale webhook list when settings are saved Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): mark registered_url nullable and drop the duplicated field error Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: pin ee ref after dropping the reconcile lock and CAS Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(git-sync): refresh the stale webhook list on category saves too Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to aa05ca8e97fc8265cd724753a80db37f83243254 This commit updates the EE repository reference after PR #695 was merged in windmill-ee-private. Previous ee-repo-ref: 3e6cd9226b68707233ae2434511fe5131dce808b New ee-repo-ref: aa05ca8e97fc8265cd724753a80db37f83243254 Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
fbf9f04e10 |
fix: surface the real postgres error when data table migrations fail (#10371)
* fix: surface the real postgres error when data table migrations fail Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address review nits on the data table migration error fix Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: name the exact grant a data table migration needs Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: quote both identifiers in the data table grant hint Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: add a data table connection and privilege check to workspace settings Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: report data table privileges from the capability fields, not the grant list Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: read grant targets from the server and drop the public schema guess Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: render the search_path suggestion server-side and pin the granted database Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: key the connection check on request identity, not the data table name Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: declare the data table check schema field nullable and required Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
8a96e3a4ec |
fix: raw apps with no stylesheet were permanently un-deployable (#10364)
* fix: raw apps with no stylesheet were permanently un-deployable Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep js strict when defaulting the raw app bundle css Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: drop ephemeral narration from raw app bundle regression test Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: pin the extension each raw app bundle half is fetched under Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
50da65c886 |
feat: show per-owner runnable counts in the homepage tree (WIN-2253) (#10351)
* feat: show per-owner runnable counts in the homepage tree Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: exclude pipeline members from runnable owner counts Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address review findings on runnable owner counts Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: avoid tree reflow while counts load and label pipeline rows Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: drop collapsed owners' cached rows when the tree scope changes Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: untrack tree owners whose node is removed Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
4b7ab64a48 |
fix: enforce per-job authorization on cancel and force_cancel endpoints (#10341)
* fix: enforce per-job authorization on cancel and force_cancel Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: authorize force_cancel on the ancestor it actually kills Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: fail closed when the force_cancel ancestor walk is truncated Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
dc5182f86c | fix: operators cannot see flows and apps on the homepage (#10340) | ||
|
|
9cef724ff2 |
feat: bind WAC approval urls to a named wait_for_approval step (#10317)
* feat: bind WAC approval urls to a named wait_for_approval step Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: reject duplicate WAC approval step keys instead of renaming them Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: reject WAC approval links minted for a step that is not awaiting approval Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: bind WAC approval links to the awaiting step and stop step key aliasing Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: reject empty approval keys and scope minted-key writes to the workspace Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: enforce WAC approval binding at consumption and reject colliding keys Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: make WAC approval binding and collision checks atomic, harden TS step keys Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: decrement WAC suspend atomically instead of from a pre-lock snapshot Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore: add sqlx cache entry for the atomic WAC suspend decrement Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: omit empty approver param from python get_approval_urls Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test: pin the suspend-snapshot decrement and the colliding-mint race Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test: drop the suspend-snapshot interleave test, it cannot both be stable and discriminate Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: reject step keys that cannot be minted as a URL path segment Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
71b7135cf2 |
feat: multiple homepage sort orders via an efficient merged runnables endpoint (#10297)
Adds recently-updated / oldest / name A-Z / name Z-A sort orders to the homepage (WIN-2236), produced server-side by a new merged, index-backed, keyset-paginated GET /w/{workspace}/runnables/list so a chosen order is globally correct across scripts + flows + apps and stays efficient on large workspaces.
- Backend: UNION ALL of script/flow/app ordered by index (Merge Append + LIMIT); keyset (sort_key, path, kind, tiebreak) cursor; per-branch LIMIT bounds correlated projections; starred-first pinning; RLS + scope-token filters in SQL. Archived view returns the latest row per path. Migration adds time + lowered-name indexes (built CONCURRENTLY).
- Frontend: server-side sort/kind/owner filters + hybrid search (instant client + on-demand server pagination); file-explorer tree with every folder and your user namespace as lazy-loaded top-level nodes (per-owner "Load more", nested subfolders, bounded "expand all", in-place re-sort without collapse or flicker); the client sorts by the server fetch ordinal to reproduce the endpoint's exact order; empty state distinguishes an empty workspace from too-narrow filters.
Reviewed clean by Claude and Pi (good to merge) and Codex (mergeable).
|
||
|
|
2143d45815 |
fix: WAC wait_for_approval reads its own approval result, not the first (#10314)
In prepare_checkpoint_for_resume the resume_job lookup took the oldest row
for the job (ORDER BY created_at ASC LIMIT 1), so a WAC workflow with
multiple sequential wait_for_approval() calls always read the first
approval's result for every step. Consumed rows are never deleted, so the
2nd and 3rd approvals inherited the 1st's result (all showed approved:true
even if the 2nd was cancelled and the 3rd timed out).
Track the resume_job row ids consumed by earlier approval steps in the
checkpoint (consumed_resume_ids) and exclude them, so each step reads its
own row. This is channel-agnostic and needs no clock reasoning:
resume_job.resume_id is only hash(step_key) for the inline resume URL; the
approval page, the in-run approve button, Slack, Teams and resume-as-owner
all store a random resume_id, so filtering by resume_id would drop those
approvals and return approved:false even for a legitimate approval. A
timed-out step matches no row and still falls to the else branch returning
{approved: false}.
Adds a regression test driving three sequential approvals (approved,
cancelled, timed-out) against Postgres.
Fixes WIN-2241
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
f02df7fc45 |
feat(monitor): make between-steps zombie flows hand-recoverable (#10287)
* feat(monitor): make between-steps zombie flows hand-recoverable When a worker is OOM-killed mid state-transition, the flow is reaped as a between-steps zombie (children all success, module still InProgress). We do not auto-recover (a re-driven transition can OOM again), so instead: - Append actionable recovery guidance to the cancellation reason when the reaped step's state is derivable (every child a success completion): which step, iterations completed, raise memory then restart-from-step (UI + API). - Restart-from-step now reuses a zombie step verbatim (InProgress with all children successful) and restarts from the next step, so no completed child re-runs; downstream steps re-derive its result from flow_jobs on demand. - Cast flow_status ::text in the reaper query: reading the jsonb column as Box<str> included the binary version byte and silently failed FlowStatus parsing (disabling the restart-not-yet-started branch since the v2 migration). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): only reuse a between-steps zombie step that provably finished Address review findings on the zombie-restart reuse path: - Require structural completeness (FlowStatusModule::is_between_steps_complete): a serial for-loop / branch-all reaped mid-fan-out has an all-success prefix but unrun remaining iterations, so the cursor must sit on the last element; while-loops are never derivable (continuation is a post-iteration condition). Parallel containers preallocate all children, so success alone is conclusive. Shared by the monitor guidance and the restart resolution. - Decline reuse when the step carries stop_after_if / stop_after_all_iters_if: those predicates decide whether downstream steps run, and reuse would bypass them; such a step re-runs instead. - Decline reuse when the zombie step is the last module (advancing past it lands on the failure step); it falls back to the existing re-run path. - Unit tests for is_between_steps_complete and an integration test asserting a mid-iteration serial-loop zombie is re-run, not reused. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): align zombie recovery guidance with restart eligibility Address CI review findings: - Exclude skip_if / suspend / sleep (not just stop predicates) from reuse via FlowModule::allows_zombie_reuse, so a skipped/suspend-armed step is never synthesized as Success (which would strand a restart waiting on an approval it never armed). - The reaper does not load the flow definition, so it cannot know whether restart will reuse or re-run a given step; reword the guidance to state both outcomes (reuse where derivable, re-run for the flow's last step or one carrying a stop/skip condition, approval, or sleep) instead of promising "no re-run". - Make the mid-iteration regression test exercise the cursor-completeness guard: a downstream step makes the loop non-final, so reuse is prevented only by the guard; a truncated loop result would then fail the assertion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): never let zombie reuse swallow a nested restart request A nested restart (RestartedFrom.nested) descends into the restart step's child to re-run an inner step. For an eligible zombie BranchOne/Subflow the outer branch_or_iteration_n is None, so reuse fired, skipped the container, and the explicitly requested inner step never re-ran. Thread the presence of a nested chain into restarted_flows_resolution and decline reuse when set. Regression test added (RED without the guard: the nested target is reused instead of re-run). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): don't auto-requeue preprocessor zombies as unstarted flows The ::text parse fix re-activated the "hasn't started yet, restart it" branch, but its `modules[0] == WaitingForPriorSteps` check also matches a flow whose preprocessor is still InProgress (step == -1, first module waiting). Requeuing such a flow re-runs the preprocessor, duplicating side effects / repeating the OOM. Gate the branch on FlowStatus::is_not_yet_started, which also requires the preprocessor (if any) to be WaitingForPriorSteps. Unit-tested. Also drop the numbered procedural narration from the happy-path test comments. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): only emit restart guidance for restartable (deployed, top-level) flows The recovery guidance points operators at the run page's "Re-start from" button and the restart API, but both require a top-level deployed flow: a preview has no flow path (the button is hidden, the API 400s) and a subflow child restarts via its root, not itself. Gate the guidance on runnable_path IS NOT NULL AND parent_job IS NULL so previews/subflows keep the existing wording instead of being told to use a button/endpoint that isn't there. Verified end-to-end: a reaped preview gets no RECOVERY block, a reaped deployed flow does. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): gate recovery guidance on kind='flow' to match the restart surface Addresses review nit: a pathful editor preview (kind='flowpreview' with a runnable_path) satisfied the previous runnable_path check but the run page only renders the "Re-start from" button for kind='flow'. Match that condition exactly so previews/singlestepflow keep the plain wording. Verified end-to-end: a reaped pathful preview now gets no RECOVERY block. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): disable zombie reuse for raw-flow (editor preview) restarts A JobPayload::RawFlow restart queues the request's current, possibly EDITED, definition, but restarted_flows_resolution validates reuse against the completed job's STORED definition. For an eligible preview zombie, editing the restart step and restarting from it would synthesize Success from the old children and skip the edit. Thread allow_zombie_reuse into the resolver (true only for JobPayload::RestartedFlow, which queues the stored definition) and decline reuse for raw-flow restarts. Regression test added (RED without the guard: the edited step is skipped and the old result is reused). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(sqlx): add offline cache for zombie_flow_recovery test queries The integration test's UPDATE v2_job_completed queries had no .sqlx entry, so the CI SQLX_OFFLINE build of the test failed to compile. Regenerated with --all-targets --features deno_core,quickjs to capture the test-target queries. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(monitor): drop procedural narration from the raw-flow zombie test Per AGENTS.md (comments record constraints, not narration): remove the two step-describing comments the reviewer flagged; the test doc comment already carries the durable rationale. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): restrict zombie reuse to monitor-reaped flows The reuse predicate matched the InProgress/all-children-success shape without checking provenance, so an ordinary force-cancel at the same boundary (a child succeeded before its parent transition landed) would also be reused, dropping the usual restart-from-step re-run. Gate reuse on canceled_by = 'monitor' (the username the zombie reaper cancels with). Regression test added (RED without the guard: a user-cancelled flow reuses the child instead of re-running it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): reuse zombie step on Some(0) too, so the run-page button works The run page's "Re-start from" button always sends branch_or_iteration_n = 0 (never omits it), but reuse only fired for None, so the exact UI path the recovery message points to would re-run the children instead of reusing them. Treat a whole-step restart (None or Some(0)) as reuse-eligible; Some(n>=1) keeps the explicit partial-container restart. Verified against the live EE restart API with branch_or_iteration_n=0: all loop-iteration child UUIDs are reused. Happy- path test now sends Some(0) to match the button. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
c50a2abad0 |
fix(jobs): sanitize NUL in completed job result before jsonb insert (#10274)
## Summary
A job whose result contains a real NUL (U+0000) serializes to a `\u0000` JSON escape that the `jsonb`-typed `v2_job_completed.result` column rejects with Postgres `22P05` ("unsupported Unicode escape sequence"). This aborts the `INSERT` in `commit_completed_job`, which then retries 10 times and leaves the job unable to complete (surfaced as `Could not add completed job <id>: ... unsupported Unicode escape sequence`).
The fix sanitizes the serialized result immediately before the insert, with effectively zero overhead on the common NUL-free path.
## Changes
- **Promote `strip_json_nul` into `windmill-common`** (`utils.rs`): `fn strip_json_nul(&str) -> Cow<str>` — a `contains("\\u0000")` fast guard returns the input borrowed when clean; only a genuine odd-parity NUL escape triggers the O(n) rebuild. `Cow::Owned` is returned **only** when a NUL was actually stripped, so a legitimate `\\u0000` (escaped backslash + literal text) borrows through untouched. Replaces the two duplicated copies previously in `windmill-api/src/drafts.rs` (`strip_json_nul`) and `windmill-api/src/apps.rs` (`strip_null_chars`); both call sites now use the shared helper.
- **Add `serialized_json()` to the `ValidableJson` trait** (`windmill-queue/src/jobs.rs`): `Box<RawValue>` returns `Cow::Borrowed(self.get())` (zero-cost, already serialized); other impls serialize on demand via `to_raw_value`.
- **`commit_completed_job`** binds `strip_json_nul(result.serialized_json())` as `$3::text::jsonb` in both the `INSERT ... SELECT` and the `ON CONFLICT ... result = $3` (was `result as Json<&T>`). Stored data is unchanged (Postgres parses JSON text into `jsonb` identically); `wm_labels`/`result_metadata` still operate on the typed `T`.
- **Regenerated the sqlx offline cache** (one query file swapped; EE caches preserved).
- **Doc:** updated the stale `strip_null_chars` reference in `windmill-api-workspaces/src/workspaces.rs` to point at the shared `strip_json_nul`.
## Test plan
- [x] `cargo check -p windmill-queue -p windmill-api -p windmill-common -p windmill-api-workspaces` — clean, no warnings
- [x] `strip_json_nul` unit tests in `windmill-common` (clean-borrow, real-NUL, legit-escape borrow no-op, collision, nested keys/values, odd-run): 6 passed
- [x] End-to-end regression in `backend/tests/nativets_jobs.rs` (`--features deno_core`): a JS job returning a genuine NUL and a literal `\\u0000` completes, storing `"ab"` (stripped) and `"a\\u0000b"` (preserved). Without the fix the insert aborts and the job never completes.
- [x] `backend/tests/drafts_nul.rs` integration test still passes (helper refactor intact)
|
||
|
|
2d24b3ac49 |
fix(jobs): enforce self_approval_disabled on the UI resume path (#10262)
* fix(jobs): enforce self_approval_disabled on the UI resume path The "Resume" button in the run detail UI calls the resume_suspended endpoint, whose owner shortcut skipped the approval-condition checks entirely. A flow owner/operator who triggered the run could therefore self-approve despite self_approval_disabled, unlike the owner endpoint which enforces it. Only admins should bypass self-approval. - Extract require_not_self_approval and enforce it before the owner shortcut in resume_suspended and can_approve_step (button visibility), matching resume_suspended_flow_as_owner. - Persist approval_conditions when self_approval_disabled is set even without user_auth_required, so the restriction is not silently dropped at the resume boundary for raw-flow/CLI authors. Fixes WIN-2223 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(jobs): keep self-approval capability-based on the secret path; docs/tests Scope the self_approval_disabled enforcement to identity-based resume boundaries only. Possession of the full HMAC resume URL is the authorization on the secret path (the URL is disclosed only to intended approvers, e.g. when a step returns it), so resume_suspended_job intentionally keeps skipping approval conditions and token-only (anonymous) resumes on resume_suspended are not gated either. The logged-in owner/operator self-approval fix stays. - Add extract_approval_conditions helper (WAC vs classic) reused in resume_suspended. - Update can_approve_step doc to reflect that self_approval_disabled bars the triggerer before the owner shortcut (codex nit). - Reword new test comments to state the invariant, not prior behavior (codex nit). - Add test_self_approval_disabled_without_user_auth_required covering the persistence + authenticated self-approval check for a non-owner triggerer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4a898247a2 |
fix(apps): let entitled viewers read pre-existing S3 files from deployed apps (#10245)
* fix(apps): let entitled viewers read pre-existing S3 files from deployed apps Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): confine S3 viewer fallback to full unscoped sessions A scope-restricted token (e.g. apps:read:<app>, or an app-embed token) is allowed on apps_u/* but rejected by the route-scope middleware on job_helpers/*, so granting it the viewer fallback would be a new capability it cannot obtain directly. Gate the fallback on scopes.is_none() so only full sessions (which can already read via job_helpers) delegate; scoped and anonymous callers stay gated. Add a scoped-token isolation assertion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): treat filter-tags-only tokens as unscoped for S3 viewer fallback The scopes.is_none() guard wrongly denied the viewer fallback to tokens that are effectively unscoped (empty scope arrays and if_jobs:filter_tags:-only tokens), which the route-scope middleware treats as unrestricted and which can therefore read the same file via job_helpers directly. Reuse that semantics via a shared is_effectively_unscoped helper so the relaxation covers exactly the tokens that gain no new capability, while genuinely scoped tokens stay gated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
39058c0a01 |
fix(postgres-triggers): enforce resource-path scopes on ancillary routes (#10222)
* fix(postgres-triggers): enforce resource-path scopes on slot/publication routes
The Postgres trigger ancillary routes (slot management, publication
management, version/logical-level checks, template script creation) relied
only on the route-level middleware, which validates the scope domain+action
but delegates resource-path enforcement to each handler. These handlers made
no check_scopes call, so a token scoped to one postgres resource path (e.g.
postgres_triggers:write:u/alice/*) could drive these endpoints against any
postgres resource in the workspace, including the destructive drop_slot_name
(pg_terminate_backend + pg_drop_replication_slot).
Add a check_scopes call at the top of each affected handler, before any
connection is opened, mirroring the generic trigger CRUD handlers. Read
endpoints require postgres_triggers:read:{path}; write endpoints require
postgres_triggers:write:{path}.
Fixes WIN-2213
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs: condense postgres_trigger_scope test module comment
Address Codex nit: state the durable constraint (mismatched scope must fail
before DB access) instead of narrating pre-fix behavior and change history.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
710a13a59d |
fix(apps): cover script/flow component outputs in deployed-app S3 provenance gate (#10070)
* fix(apps): cover script/flow component outputs in deployed-app S3 provenance gate Deployed apps read S3 files on-behalf of the app author for logged-in viewers (#10048). A confused-deputy guard confines those reads to files the app "produced", but the recent-production check only matched inline `appscript`/ `preview` jobs nested under the app path. Files produced by the deployed script/flow components an app is wired to run (e.g. a SQL query persisted to S3) were therefore denied "File restricted" for every viewer, admins included. Expand the provenance check to also match completed `script`/`flow`/`flowscript`/ `flownode` jobs whose `runnable_path` is one of the app's declared triggerables, and accept the author identity via `permissioned_as = on_behalf_of` (not only `created_by = caller`) so files produced on-behalf of the author are covered. Reads outside the app's declared triggerables stay denied. Adds a regression test seeding a script-kind produced file that reproduces the "File restricted" denial before the fix and passes after. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): key S3 provenance on on-behalf identity + cover flow steps (review) Addresses the CI review on the S3 provenance gate: - P1 (confused deputy): the recent-production check keyed on `created_by = caller`, so a viewer who can run a declared script/flow directly (outside the app, with un-pinned inputs) could craft a result naming an author-only key and read it back through the app as the author. Key provenance instead on the producing job's `permissioned_as` matching the on-behalf identity the download reads as (the author in author-mode); a viewer's direct run has `permissioned_as = viewer` and no longer clears the gate. Drops `created_by` from both the appscript/preview and script/flow branches, closing the same latent hole in the pre-existing inline-script branch. - P2 (dead flow-step branch): `flowscript`/`flownode` jobs have `runnable_path = <flow_path>/<step_id>`, which exact `= ANY(...)` never matched. Split script vs flow triggerable paths; flow kinds now match the flow's own job (bare path) and its step jobs via a `<flow_path>/%` prefix, bounded to declared flows. - P2 (test realism): the regression test now uses the production component-prefixed triggerable key format (`<id>:script/...`), exercises a flow-step-produced key, and asserts a viewer's own direct run of a declared script stays denied (the P1 case). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): tie deployed-app S3 provenance to an app-origination marker (review) Second CI-review round flagged that `permissioned_as` still does not prove a job was app-launched: a runnable configured with its own `on_behalf_of` makes a direct `/jobs/run` resolve `permissioned_as` to that identity (the app author), so a viewer with run access could execute a declared runnable directly, craft an S3 result, and read it back through the app. The flow-path `LIKE fp || '/%'` match also let `_`/`%` in a declared path admit unrelated flows. Introduce a real app-origination marker instead of inferring provenance: - Add `JobTriggerKind::App`; `execute_component` stamps every app-launched job with `trigger_kind = 'app'` + `trigger = <app path>`. A direct `/jobs/run` cannot set this, so it is the authoritative signal that a file was produced *by the app*. - The provenance gate's recent-production check collapses to `trigger_kind = 'app' AND trigger = <this app path>` (+ the 3h window and result containment). This drops the forgeable `created_by`/`permissioned_as`/ `runnable_path`/kind logic entirely and removes the `LIKE` wildcard issue. - Provenance is scoped to THIS app's path, so another app's jobs (even same author) do not authorize this app's reads. Regression test rewritten to the marker model: an app-produced key clears for viewer and admin; a direct run whose `permissioned_as` resolves to the author stays denied (the forgery); another app's output stays denied. Adds `app` to the OpenAPI JobTriggerKind enum. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(apps): assert execute_component stamps trigger_kind='app' at runtime Adds an end-to-end test that runs a real script component through the app runtime (`apps_u/execute_component`) and asserts the enqueued job carries the app-origination marker `trigger_kind = 'app'` + `trigger = <app path>` (not the runnable path). The provenance-gate tests seed the marker directly; this proves the runtime actually produces the exact marker the gate depends on. execute_component commits the job row and returns its id, so the assertion reads the row directly — no worker needed to run the job. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): reject trigger_kind=app for suspended-job reassignment (review) `JobTriggerKind::App` (added for the app-origination S3 marker) became a valid value for the resume/cancel suspended-trigger routes, whose handler derives the table name `<kind>_trigger`. There is no `app_trigger` table, so both endpoints would fail with a missing-relation database error (500). Reject `App` in `get_suspended_trigger` alongside webhook/schedule so it returns a clean 400. Adds a regression test asserting the reassignment route returns 400 (not 500) for trigger_kind=app. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): don't stamp app-origination marker on preview runs (review) The app-origination marker (trigger_kind='app') was stamped unconditionally, including preview mode. A preview lets a `jobs:run` caller supply arbitrary `raw_code` against ANY app path without that app's deployed policy (raw_code with no path/id skips all app authorization), so a preview returning `{"s3":"<author-only-key>"}` would forge the exact marker the S3 provenance gate trusts and read the victim app author's file. Gate the marker on `!is_preview`: only deployed, policy-checked executions are app-provenanced. Preview/editor S3 display does not rely on this marker (the editor routes reads through the force_viewer allowlist), so nothing legitimate regresses. Adds a regression test asserting a preview run's job is not stamped trigger_kind='app'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): editor-authorize preview marker + per-viewer S3 provenance isolation (review) Closes the codex P1 (preview forgery) without breaking editor preview downloads, and adds cross-viewer isolation to the provenance gate. - Preview marker now requires app write: `execute_component` stamps the app-origination marker on a preview only when the caller can EDIT that app (`require_is_writer`), instead of never stamping previews. An app editor already wields the app's author identity (they can deploy a component that reads the same file), so marking their own preview is no escalation and keeps preview-produced S3 results downloadable in the editor; a `jobs:run`-only caller who cannot edit the app still cannot forge the marker. Deployed runs are unchanged (always marked). - Per-viewer isolation: the provenance gate now also requires `j.created_by = <this caller>`. The security boundary stays the un-forgeable `trigger_kind='app'` marker; `created_by` is an additional filter ANDed under it, so it only narrows — a viewer can only download keys their OWN app runs produced, not another viewer's result. Restores the per-caller scoping #10048 had, now safe on top of the marker. Tests: preview marked iff caller can edit the app; cross-viewer isolation (another viewer's app-marked key denied, no admin bypass); direct-run and other-app keys still denied; deployed run still stamped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): require apps:write scope (not just writer ACL) to mark preview provenance (review) require_is_writer checks the user's underlying ACL but ignores token scopes, so a writer's token deliberately scoped to apps:run/apps:read/jobs:run but WITHOUT apps:write could still mark a preview and forge provenance — even though that token cannot deploy the app (update_app requires apps:write), breaking the "any marked caller can deploy equivalent code" rationale. Require BOTH apps:write:<path> scope (check_scopes) AND the writer ACL (require_is_writer) before stamping a preview's app-origination marker. Deployed runs unchanged. Adds a scope-restricted-writer token to the test (apps:run/read + jobs:run, no apps:write) and asserts its preview stays unmarked; retains the full-editor positive case and the non-editor negative case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): never app-provenance preview runs; read editor S3 as the caller (review) Simplifies the preview handling: a preview executes as the *caller* (Viewer mode), never as the author, so its results must be read back as the caller — never author-mode — and must never carry the app-origination marker. This removes the whole `require_is_writer` / `apps:write` / `can_preserve_on_behalf_of` reasoning (which was also unsound: a writer's token or session may not be able to deploy a component running as the app's on-behalf identity, so marking their preview could still escalate). - Backend: mark the app-origination marker for deployed runs only (`!is_preview`). - Frontend: `getS3File` (AppImage/AppPdf/AppDownload) now routes editor/preview reads through the viewer-scoped `job_helpers/download_s3_file` endpoint (reads as the caller), matching what DisplayResult/ParqetCsvTableRenderer already do; only a deployed app view uses the provenance-gated `apps_u` endpoint. This is the path that previously relied on marking previews, so nothing regresses. Test: a preview is never app-provenanced (owner's own preview and a non-editor's both stay unmarked). Cross-viewer isolation, deployed marking, and the reassignment guard are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): app components run on-behalf of the app, not the referenced runnable (review) Root-causes codex's on-behalf-preview finding: `execute_component` was overriding the app's resolved on-behalf identity with the referenced script/flow's OWN `on_behalf_of` (its `on_behalf_of_email`). That is wrong in the app context — the app's execution mode should govern: - A Viewer-mode app could execute a component AS the referenced runnable's on_behalf identity (privilege confusion / escalation), instead of as the viewer. - A preview would run as that identity rather than as the caller, so its S3 output could not be read back as the caller — the download-identity mismatch codex flagged. Always use the app-resolved identity (author in author-mode, caller in viewer/preview); a referenced runnable's own `on_behalf_of` no longer leaks into app execution. Direct `/jobs/run` still honors a runnable's `on_behalf_of` (unchanged). With this, previews always run as the caller, so reading editor/preview S3 as the caller (viewer-scoped `job_helpers`) is unconditionally correct. - Test: the deployed-component e2e now seeds the script with a distinct on_behalf and asserts the component job's `permissioned_as` is the app identity, not the script's. - Also reword the getS3File `configuration` param comment to describe current state only (AGENTS.md comment rule). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(apps): surface 'app' trigger kind in Runs UI; condense provenance comments (review) Addresses codex review nits: - Add `app` to `jobTriggerKinds`, `triggerIconMap` (LayoutDashboard), and `triggerDisplayNamesMap` so app-component jobs (which now carry `trigger_kind = 'app'`) are filterable in Runs and render their trigger info. - Condense the app-origination marker, on-behalf-identity, and provenance-gate comments to state each invariant once in <=4 lines at its relevant site (AGENTS.md comment rule). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1e192f2d86 |
feat(apps): authorize deployed-app S3 reads on-behalf of the author for logged-in viewers (#10048)
* feat(apps): authorize deployed-app S3 reads on-behalf of the author for logged-in viewers A logged-in user viewing a deployed app now reads S3 files (rich result, table/image/PDF preview, CSV export, download, metadata) the same way an anonymous viewer already does: on-behalf of the app author per the app policy's execution_mode, gated by an app-provenance check — instead of against the viewer's own S3 permissions. This aligns S3 with every other thing an app does (scripts, flows, resources all already run on-behalf of the author) and lets an operator who lacks folder S3 permission still see data rendered inside the app. The raw job_helpers/* S3 API stays viewer-scoped: a viewer who lacks folder permission is still denied there. Only which endpoint the app frontend uses for logged-in deployed viewers changes. Backend: - Add app-scoped, provenance-gated apps_u/* variants for all S3 display ops (download_s3_file already existed; add download_s3_parquet_file_as_csv, load_file_metadata, load_file_preview, load_parquet_preview, load_csv_preview, load_table_count). Each routes through one shared helper (app_s3_on_behalf_and_provenance) that scope-confines an app embed token, resolves the on-behalf identity, and runs the provenance gate ONCE before dispatching to the EE *_internal S3 helpers. - Close the confused-deputy hole in check_if_allowed_to_access_s3_file_from_app: the unconditional Ok() bypass for a logged-in, non-embed session now only applies in viewer execution mode (where the on-behalf identity IS the viewer, so the viewer's own permissions still bound the read downstream). Author-mode reads (anonymous/publisher) always enforce provenance, for anonymous and logged-in viewers alike, so a viewer cannot launder the author's S3 permissions with an arbitrary file_key. Frontend: - Route the deployed-app view through apps_u/* using the app-viewer isEditor signal instead of login state (the old $userStore proxy wrongly sent logged-in deployed viewers to the viewer-scoped job_helpers API). Editor and preview keep viewer identity via job_helpers. execution_mode: viewer remains the escape hatch for per-viewer S3 enforcement. Fixes provenance-gated S3 display for logged-in operators on deployed apps. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(backend): document cargo features, restarting the dev backend, and filesystem object storage The dev backend runs `cargo watch --features quickjs` by default, which omits S3, EE, MCP, and non-JS runtimes — feature-gated routes then 404 or return a "requires <feature>" stub at runtime. Add a backend/CLAUDE.md section that: - explains that you must restart the backend with the appropriate features to exercise gated functionality, with the pid/cwd-scoped restart recipe (never pkill target/debug/windmill) and the PORT=$BACKEND_PORT gotcha; - documents what each commonly-toggled feature gate does (private, enterprise, license, parquet, duckdb, language runtimes, mcp, trigger kinds, no_auth) plus common combinations; - documents using the built-in FilesystemStorage large-file storage for dev workspace object storage (hidden from the UI dropdown; set via edit_large_file_storage_config), including the advanced_permissions shape. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): don't flatten inner query in app-scoped S3 preview routes axum's `Query` uses `serde_urlencoded`, which cannot deserialize the typed (numeric/bool) fields of a `#[serde(flatten)]`-ed struct and 400s on `limit` / `offset` ("invalid type: string, expected u32"). The app-scoped load_csv_preview / load_parquet_preview / load_table_count routes flattened LoadPreviewQuery / LoadCountQuery, so their previews were broken. Restate the fields directly on the outer query structs (with an into_inner() to rebuild the inner query) and extend the CE OSS stub to match. Also bumps ee-repo-ref.txt for the companion EE csv-separator panic fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: address CI review — nested DisplayResult routing, byte-range contract, docs, tests - [P1] Thread `appPath` into the nested `DisplayResult`s (render_all children and the expanded-result drawer) so logged-in deployed viewers route nested/expanded S3 tables, images, PDFs, and downloads through `apps_u/*` too, not job_helpers. - [P2] Mark `read_bytes_from`/`read_bytes_length` required on the `apps_u/load_file_preview` route (they are non-optional in LoadFilePreviewQuery), and mirror the full query shape in the CE OSS stub so the byte-range contract is enforced identically on CE and EE. - [P2] Fix the backend retrigger command in backend/CLAUDE.md: cargo watch runs from `backend/`, so `touch README.md` (not `backend/README.md`). - [P2] Trim app_s3_onbehalf.rs comments per AGENTS.md (state the invariant once, no drafting-history narration). - Extend the integration test to cover the table-count, csv-preview (numeric limit/offset deserialization), and file-preview (byte-range required) routes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(apps): tighten S3 provenance-gate comments per AGENTS.md Consolidate the viewer-mode / author-mode rationale to ≤4 lines at each branch of the gate, and drop the repeated explanation from the shared app_s3_on_behalf_and_provenance doc comment (which now just states what the helper does). No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to f292a1040da6a667ce7c22abf63ec0debfdd480f This commit updates the EE repository reference after PR #657 was merged in windmill-ee-private. Previous ee-repo-ref: a582389084eb363997cb5e8053f29220e0d3eaec New ee-repo-ref: f292a1040da6a667ce7c22abf63ec0debfdd480f 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> |
||
|
|
5844c32ac5 |
fix: enforce read authorization when signing S3 objects (#10049)
`sign_s3_objects` minted a long-lived HMAC bearer signature for any S3 key handed to it, by any authenticated workspace member, with no check that the caller was allowed to read that key. Since `validate_s3_signature` only verifies the HMAC and expiry at fetch time, any member (operators included) could mint a transferable capability to read arbitrary S3 keys, bypassing the advanced S3 permission rules (`check_lfs_object_path_permissions`). Authorize the read at mint time: add an `ApiAuthed` extractor and, before signing each key, require the caller's own `S3Permission::READ` via `get_workspace_s3_resource_and_check_paths`. A caller can no longer sign a key they cannot themselves read. The fetch-side validators are left unchanged. The only legitimate caller is the wmill SDK invoked from an app-author job, whose token authenticates as the executing (author) identity — which can read the key — so authorized app display is unaffected. Adds an integration test proving an authorized caller can sign a readable key (and the signature validates end-to-end through the presigned fetch route) while an unauthorized caller is refused. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ab38e1418e |
fix: keep agent-worker server job-completed processor alive & self-healing (#10033)
* fix: keep agent-worker server job-completed processor alive on init-script failure The agent-worker API server's background job-completed processors relay completions on behalf of many remote agent workers. The processor loop exited (dropping its receiver) on an init-script failure, but on the server that failed init script belongs to a remote worker, not the server. Once enough processors exited, the shared completion channel disconnected and every /send_result POST returned 500, stranding completions and creating zombie-job restart loops. Add an is_agent_server flag so server relay processors don't self-terminate on init-script failure. Pins the EE companion change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump EE ref for send_result wait-for-processor change Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: agent-worker server survives a failed init script End-to-end regression for the agent-worker-server processor bug: an agent worker runs a failing init script, POSTs the failed init-script completion to /send_result, and the test asserts the server's background job-completed processor stays alive (a subsequent job completes and no bg-processor critical alert is raised). Fails if the is_agent_server guard is removed (the processor breaks, the supervisor raises a critical alert). Requires --features enterprise,license,private,agent_worker_server. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: replace heavyweight init-script e2e with focused unit tests The panic/respawn/alert and 503 timeout paths are now covered by fast, deterministic unit tests in windmill-api-agent-workers (supervise_processor, classify_send). Drop the enterprise-only, global-config-mutating e2e in favor of those. Bump EE ref. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to bc45d9275d4307132927dc8ad3e82049b1aed463 This commit updates the EE repository reference after PR #653 was merged in windmill-ee-private. Previous ee-repo-ref: 2aca03f28bb37e938ae548b81f1620b2e00dc0f7 New ee-repo-ref: bc45d9275d4307132927dc8ad3e82049b1aed463 Automated by sync-ee-ref workflow. * chore: bump EE ref for bg-processor alert rate-limiting Picks up windmill-ee-private#654: exponential backoff + rate-limited critical alerts in supervise_processor, addressing the code-review nit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to d48c0e01e8601a372353c032dd237ddb6fa3bbad This commit updates the EE repository reference after PR #654 was merged in windmill-ee-private. Previous ee-repo-ref: f89eeb6e333614850ef650e7df78e3c2335f107c New ee-repo-ref: d48c0e01e8601a372353c032dd237ddb6fa3bbad Automated by sync-ee-ref workflow. * chore: bump EE ref for graceful-shutdown-during-backoff fix Picks up windmill-ee-private#655: supervise_processor re-checks shutdown before respawn and selects on the shutdown broadcast during backoff, so a crash-loop backoff can't hang graceful shutdown. Addresses the Codex P1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to 9bc5dfb9ce73a2d9b981a1de86eea6aa26688b79 This commit updates the EE repository reference after PR #655 was merged in windmill-ee-private. Previous ee-repo-ref: 8dc3b3d9ec8f9c28b227d36c2a1327b4b2017665 New ee-repo-ref: 9bc5dfb9ce73a2d9b981a1de86eea6aa26688b79 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> |
||
|
|
927b8d064f | fix: clear old path asset usage when renaming a script (#9979) | ||
|
|
5d7fb6deca |
feat(pipelines): asset freshness — fresh/stale badge (CE) + watchdog (EE) (#9909)
* feat(pipelines): passive asset freshness tracking on the graph Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(pipelines): drop dead freshness-enforcement stub, document query ordering Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(pipelines): freshness watchdog (EE) — auto re-run stale producers Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(pipelines): watchdog review fixes — archived workspaces, badge kind parity, scan index Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(pipelines): CI review — no singlestepflow in freshness, +N parity, completion-time fallback Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(pipelines): CI review — history completedAt, freshness/asset trigger UI metadata Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: update ee-repo-ref to 6f5fe0f7f56696fbef5a8349da38496c32e71666 This commit updates the EE repository reference after PR #643 was merged in windmill-ee-private. Previous ee-repo-ref: 1f13380354bf591ae25a2c20d36917534bcc5459 New ee-repo-ref: 6f5fe0f7f56696fbef5a8349da38496c32e71666 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> |
||
|
|
af36498432 |
feat(pipelines): record upstream snapshot ids on cascade-dispatched jobs (#9910)
* feat(pipelines): record upstream snapshot ids on cascade-dispatched jobs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor: batch upstream-snapshot lookup and memoize per subscriber Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d15033cde6 |
fix: invalidate bun bundle cache on transitive relative-import changes (#9891)
* fix: invalidate bun bundle cache on transitive relative-import changes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: do not memoize transient fetch errors in bundle-key import cache Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: use regular comment on lazy_static block (deny unused_doc_comments) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: align bundle-key import version selection with loader content endpoint Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
76a9523009 |
feat: use derived username instead of email for non-member superadmins (#9857)
* feat: use derived username instead of email for non-member superadmins Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: address review - drop redundant username cache, guard whoami membership by email Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor: use explicit non_member boolean instead of role string for superadmin banner Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: resolve email from password table for non-member superadmin permissioned_as Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: resolve non-member superadmin drafts via shared username->email resolver Adds resolve_username_to_email (usr, then super_admin password fallback for both derived-username and email modes) and uses it in get_email_from_permissioned_as and the drafts get/list endpoints, so a non-member superadmin's drafts resolve and no email leaks into the drafts payload. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: superadmin-not-in-workspace schedule uses derived username as permissioned_as Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: resolve non-member superadmin identity in draft owner-circles, username_to_email, and home filter Applies the password-fallback username resolution to the script/flow/app/draft owner-circle subqueries and the username_to_email endpoint (was an admins-workspace 'username == email' hack), and switches the home items-list user-folder filter to the non_member flag instead of the now-broken username-contains-@ heuristic. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: backfill non-member superadmin favorites from email to derived username Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: propagate DB errors in username resolution instead of leaking email (CI review) Addresses cubic-dev-ai P2: get_instance_username_or_fallback_to_email now returns Result and only falls back to the email for a genuine 'no derived username'; a query error propagates so callers fail closed rather than leaking the raw email as the acting username. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: clarify non-member superadmin popover (username used + admin permissions) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: keep username_to_email endpoint member-only to not disclose non-member superadmin email (CI review) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: forbid disabling automate_username_creation once usernames assigned (CI review) Makes the setting effectively one-way once instance-wide usernames exist, so the global-uniqueness invariant that keeps stored u/<username> identities (schedules/triggers/drafts/superadmin ownership) unambiguous can never be dropped back to workspace-local uniqueness. Re-saving false on an already-disabled instance stays a no-op. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
40110bc715 |
fix: skipped suspend step no longer parks the flow forever (#9821)
* fix: skipped suspend step no longer parks the flow forever A flow step that declares a `suspend` (approval) but is skipped via `skip_if` was leaving the flow stuck waiting for a resume that would never arrive. Suspend gates the *next* step: before pushing step N, `needs_resume` checks whether step N-1 declared a non-zero `suspend` and finished as `Success`. A step skipped via `skip_if` is also recorded as `FlowStatusModule::Success` (with `skipped: true`), so `needs_resume` treated a skipped approval gate as a real one and parked the flow waiting for an event that nothing ever sends — until the suspend timeout (up to 24h). The skip is most visible when the skipped suspend step is followed by a branch/subflow: the flow appears stuck on the *following* predicate node with a generic resume button, while none of the branch/subflow steps ran. Fix: honor the `skipped` flag in `needs_resume` and do not gate the next step on a suspend that was skipped. Adds regression test `skipped_suspend_step_does_not_block_next_step` (times out without the fix, completes with it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: reword regression test comment as a current invariant Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |