* fix: trigger git sync for re-encrypted secrets on encryption key change
When changing a workspace encryption key, the secret variables get
re-encrypted with the new key, but the git sync was only dispatched for
the encryption_key.yaml metadata file. Repos with Secrets sync enabled
were left with stale ciphertexts until the next per-variable deployment.
Now, after the transaction commits, we also dispatch a Variable git sync
event for each re-encrypted secret so the new encrypted values are
pushed to the configured repos. Errors are logged but don't roll back
the key rotation.
Fixes WIN-1994
Fixes#9344
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: batch encryption-key rotation into one git-sync job
Workspace encryption key rotation now re-encrypts every secret variable
and then dispatches a single batched git-sync job carrying the Key event
plus one Variable item per re-encrypted secret. Repos with Secrets sync
enabled receive every new ciphertext in one commit instead of nothing
(previously only `encryption_key.yaml` was pushed) — and instead of N
separate jobs the debouncer might or might not merge.
Wires through the new `handle_deployment_metadata_batch` entry point
added in the companion EE PR; OSS has a no-op shim so the build stays
green.
Adds an integration test (`workspace_encryption_key_git_sync`) asserting
that rotating the key with 3 secret variables in scope produces exactly
one deployment-callback job whose `items` array contains the Key event
+ all 3 variable entries and `skip_secret=false`.
Fixes WIN-1994
Fixes#9344
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: bump ee-repo-ref for git-sync helper simplification
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test: cover non-debouncing git-sync fallback on key rotation
Adds a regression test exercising a workspace whose sync script predates
hub version 28103: the rotation must still queue a legacy-format
deployment-callback job per item (encryption_key + each re-encrypted
secret) instead of silently skipping the repo. Bumps ee-repo-ref to the
EE fallback fix.
Addresses the P1 raised in the PR review (Codex/Pi/Claude): batch path
dropped git sync entirely for repos without sync-job debouncing support.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: add sqlx offline cache for encryption-key git-sync test queries
The cargo_test CI job builds with SQLX_OFFLINE=true; the two new
sqlx::query!/query_as! calls in
windmill-api-integration-tests/tests/workspace_encryption_key_git_sync.rs
had no cached entries, failing the build with E0282. Regenerated and
added only the two new query caches (no EE/feature cache loss).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: bump ee-repo-ref to updated EE companion PR (08e3b9b)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ai): disable redirect following on AI proxy client to close SSRF
The AI proxy validates the configured base_url against SSRF rules but the
shared HTTP client followed up to 10 redirects without revalidating the
hops, so a public base_url could 3xx the server into a private/internal
address (e.g. the Docker socket or cloud metadata). Disable redirect
following so the validated host is the only one the server connects to.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(ai): remove heavy redirect SSRF integration test
Drop the integration-test-level regression for redirect following; it
spins up a full API server + DB for a one-line client-config change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(api): don't follow symlinks when reading service log files
Defense in depth on top of the existing `..` path-traversal check in
the get_log_file handler: reject the request if the final path
component is a symlink, so a planted symlink in the logs directory
cannot be used to read arbitrary files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(api): authorize and harden the jobs_u get_log_file endpoint
The unauthenticated jobs_u get_log_file endpoint served any job's log
file to anyone who knew the job UUID, with no authorization. Gate it the
same way as get_job_logs: look up the job (the log directory name is the
job id) filtered by workspace and the caller's scope tags, and only allow
non-logged-in callers to read logs of jobs created by the anonymous user.
Also add defense in depth: refuse to read through a symlink so a planted
symlink in the logs directory cannot be used to exfiltrate arbitrary files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The fork-branch deployment callback runs the hub script
sync-script-to-git-repo-windmill, which imports `windmill-cli` as a pinned
npm dependency and runs it in-process (it does NOT shell out to a PATH wmill).
hub/28236 pinned windmill-cli@1.706.1, whose `git-deploy --only-create-branch`
path returns early without pushing — so the fork branch was checked out
locally but never published to the remote. #9366 fixed the CLI and shipped it
as windmill-cli@1.712.0, but without a hub-script bump the running callback
still used 1.706.1.
Bump LATEST_GIT_SYNC_SCRIPT_PATH to hub/28238, which is identical to 28236
except it pins windmill-cli@1.712.0 (content + lockfile). This fixes
test_workspace_fork_creates_branch and production fork-branch creation.
Also add backend/windmill-common/src/workspaces.rs to the git-sync-test
path-gate so future script-path bumps trigger the e2e (the bump alone is not
otherwise covered by the gate).
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(oauth): support per-provider sandbox URLs in registry + instance settings
* fix(oauth): polish sandbox review nits (cc lookup, header label, ee ref)
* refactor(oauth): drop dead build_oauth_clients duplicate in windmill-oauth
* refactor(oauth): derive sandbox-capable provider list from registry
* chore(docker): copy oauth_connect.json into frontend build stage
* test(oauth): cover sandbox helpers (as_sandbox, canonical_name, resolve)
* chore: update ee-repo-ref to 9297d8f790346e6a6ad540c7bca1a67f91ec11a2
This commit updates the EE repository reference after PR #595 was merged in windmill-ee-private.
Previous ee-repo-ref: 3ab3eca9ac15ebab6db991e7964bc5e48ce21f42
New ee-repo-ref: 9297d8f790346e6a6ad540c7bca1a67f91ec11a2
Automated by sync-ee-ref workflow.
---------
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* [ee] fix(git-sync): publish fork branch on only_create_branch from the CLI
Fixes WIN-1997. Forking a git-sync-configured workspace must push a
`wm-fork/<branch>/<id>` branch to the repo, but the integration test
`test_workspace_fork_creates_branch` failed: the fork callback job
succeeded yet no branch appeared.
Root cause: the fork-branch callback runs the sync script with
`only_create_branch: true` and no items. The hub sync script delegates
branch checkout to `wmill sync git-deploy --only-create-branch` and runs
its own in-process commit+push ONLY for the `!only_create_branch` path
(`if (!only_create_branch) git_push(...)`). #9284 had moved commit+push
out of the CLI to the caller for the GPG-cache-warmth invariant
(WIN-1974) — but it also dropped the CLI's push for the branch-only
case. A branch-only publish has no commit, so no signing is involved and
the GPG concern does not apply; with neither the CLI nor the hub script
pushing, the empty fork branch was never published.
Restore the CLI push for the `only_create_branch` path (a bare
`git push --porcelain` of the checked-out branch ref). Adds a
deterministic CLI regression test that runs `git-deploy
--only-create-branch` for a fork workspace and asserts the branch
reaches the remote with no caller-side push.
EE companion: format the fork-branch commit message with Display instead
of Debug (no more `Some("...")` leak).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to a30079e75dc5b7d7413aa8ee20e40e80bfea9cbd
This commit updates the EE repository reference after PR #597 was merged in windmill-ee-private.
Previous ee-repo-ref: 8b02336fcebdfae4b9d2795cbb74fa7046530bcb
New ee-repo-ref: a30079e75dc5b7d7413aa8ee20e40e80bfea9cbd
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
This commit updates the EE repository reference after PR #597 was merged in windmill-ee-private.
Previous ee-repo-ref: 55c19293232be379a3044eb78f677b545882ffd6
New ee-repo-ref: a30079e75dc5b7d7413aa8ee20e40e80bfea9cbd
Automated by sync-ee-ref workflow.
* [ee] feat(queue): duration-weighted fairness admission atomic
Add the `WORKSPACE_FAIRNESS_ADMISSION_PPM` atomic that the EE
`workspace_fairness_ee::refresh_overloaded` writes on each refresh
(see companion EE PR). The atomic is read on every pull by
`should_admit_capped` to decide whether the dispatch goes down the
standard or fairness path. Defaults to 10_000 (= admit all) so the
pre-fairness behaviour is preserved until the first refresh fires.
OSS stub in `workspace_fairness.rs` continues to return `true`
unconditionally, so non-EE builds are bit-identical.
* docs(queue): consolidate full fairness algorithm into workspace_fairness.rs
Move the algorithm doc — what "overloaded" means in worker-seconds, the
duration-weighted admission derivation, coordinated refresh structure,
audit emission, the SQL perf constraints (no params CTE, drive running
side from v2_job_runtime), and EE gating — into the OSS surface module
where it is readable without EE access. The EE file becomes implementation
only.
Also bump ee-repo-ref to the EE commit that strips the duplicate doc.
* docs(queue): clarify ADMISSION_PPM default is "admit all", not count-based
Addresses CI review (claude[bot]): the `10_000` initial value is the
"admit all" no-op default that applies before the first refresh
classifies an overloaded set — not the count-based value (which would
be `target * 10_000`). The count-based form is the empty-bucket fallback
inside `compute_admission_ppm`, a different thing.
* chore(queue): point ee-repo-ref at EE main (fairness admission merged via #593)
* fix(queue): duration-weighted admission uses unclamped service-time window
Bumps ee-repo-ref to the EE fix (windmill-ee-private#596) that sources
D_c/D_u for the admission probability from a separate 60s service-time
window of true `duration_ms`, instead of the occupancy aggregation whose
per-job contributions are clamped to the 10s occupancy window. The clamp
truncated D_c for capped jobs longer than the window, under-admitting the
duration skew (true 34s jobs → ~86% effective share instead of the target
65%). Occupancy worker-seconds still drive overload classification.
Updates the algorithm doc in workspace_fairness.rs accordingly.
Note: ee-repo-ref points at the EE feature branch; re-point to EE main
once #596 merges.
MultiSelect read `value.length` directly while `value` is a bindable
prop with no default, so a parent passing `undefined` (e.g. an
enum-array approval form field with no initial value via ArgInput)
threw a TypeError that blanked the entire approval page. Guard all
reads behind a `value ?? []` derived.
Fixes WIN-1996
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
referencesIncludeLine required the include token to be the entire
trimmed line. The wmill-default CLAUDE.md template is
`Instructions are in @AGENTS.md` — include mid-sentence — so the
migration prompt fired every run on files wmill itself wrote.
Accept the include as a whitespace-separated token on any non-comment
line.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Help menu lived in a separate Menubar from the Settings/Workers/
Folders/Logs group, so melt-ui's hover-to-switch logic (which only
spans menus within the same Menubar) did not close the Help popup
when the cursor moved to a sibling group, causing menus to stack.
Merge both bottom Menubars into a single Menubar, wrapping each
group in its own flex container to preserve the visual spacing.
Fixes WIN-1993
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>