* 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>
* 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>
* 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>
* fix: clearer errors on auto-draft save failure (WIN-2157)
When an autosave draft save fails, the cloud indicator now surfaces the
backend reason on hover (native title tooltip) in addition to the
existing click popover, so the cause is discoverable without a click.
Backend now returns a clearer, actionable message:
- `require_can_write_path` distinguishes a malformed path (unrecognized
namespace prefix -> BadRequest) from a genuine permission denial, and
the deny message spells out where the user *can* write.
- `require_owner_of_path` no longer panics with an out-of-bounds index on
a malformed single-segment path (e.g. a bare `u`/`f`); it returns a
clear BadRequest instead. Covered by a regression test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: trim narrative comment to invariant in drafts.rs (WIN-2157)
Address CI review (AGENTS.md: comments record constraints, not narration,
≤4 lines): keep the malformed-path invariant, drop the motivation tail.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: don't let a malformed stored draft 400 the draft listing (WIN-2157)
Address CI review (P1): require_can_write_path can now return BadRequest
for a malformed path, and list_drafts propagated it — so a single
malformed stored draft row (the draft table has no path constraint;
legacy/admin-authored rows may be malformed) would make GET /drafts/list
return 400. Treat BadRequest like NotAuthorized there: the row is simply
not writable. Verified e2e on EE — listing returns 200 with can_write
false for the malformed rows.
Also trim "unchanged"/"still" drafting-history narration from the
regression test comments (P2, AGENTS.md).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: compress list_drafts comment to 4 lines (WIN-2157)
Address CI review P2: keep the constraint (draft table has no path
constraint) and the invariant (one malformed row must not 400 the
listing) within the AGENTS.md ≤4-line limit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(auth): add runtime NO_AUTH mode for authentication bypass
Adds a runtime `NO_AUTH` env flag that makes every request resolve as the
`admin@windmill.dev` superadmin with no login required, so self-hosted
deployments can front Windmill with their own authenticating gateway
without building a dedicated `oss` (compile-time `no_auth`) binary.
- `NO_AUTH` is honored in any build but is force-disabled when
`CLOUD_HOSTED` is set, so the managed cloud always enforces real auth.
- The existing compile-time `no_auth` feature keeps its always-on behavior
(`cfg!(feature = "no_auth") || *NO_AUTH`), so `oss` builds are unchanged.
- `Tokened` now yields a synthetic token in no-auth mode so handlers that
require it (e.g. global_whoami, called by the frontend on load) resolve.
- A loud startup banner warns when the mode is on; `HIDE_NO_AUTH_BANNER`
silences it once the operator has deliberately deployed behind a gateway.
Fixes WIN-2131
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(auth): dismissable NO_AUTH warning banner via global setting
Replaces the HIDE_NO_AUTH_BANNER env flag with a UI warning banner that
can be permanently dismissed for all users from within the running
instance (not exposed in instance settings).
- New `no_auth_banner_dismissed` global setting, only ever written by
dismissing the banner itself.
- `GET /api/settings/no_auth_banner` returns whether to show the banner
(true only when NO_AUTH is active and it hasn't been dismissed).
- NoAuthBanner.svelte renders a top-of-app warning in NO_AUTH mode; its
dismiss button opens a confirmation modal, then writes the global
setting via the existing setGlobal endpoint so it stays hidden for
everyone.
- The server still logs the startup NO_AUTH warning unconditionally.
Fixes WIN-2131
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(auth): resolve NO_AUTH in AuthCache so all_runnables works
Codex/Pi review flagged that `/api/users/all_runnables` still failed in
NO_AUTH mode: `get_all_runnables` extracts `Tokened` and re-validates the
request token per workspace via `AuthCache::get_authed`, which rejected the
fabricated `"no_auth"` token (no matching DB row) with a 400.
Short-circuit `AuthCache::get_opt_job_authed` (the resolver behind
`get_authed`) to the admin superadmin in no-auth mode, so any direct cache
caller resolves without a real token. Single-source the mode check and the
synthetic identity via `is_no_auth()` / `no_auth_admin_authed()` and reuse
them across the extractor, resolver, and login paths.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* revert(auth): drop the NO_AUTH dismissable UI banner
The in-app banner added a GET /api/settings/no_auth_banner request to every
instance load for little benefit. The startup log warning already surfaces
that auth is bypassed to operators, so drop the banner, its endpoint, and the
no_auth_banner_dismissed global setting entirely.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: forbid superadmin job tokens from global user and token management
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: extend superadmin job token guard to offboard and export routes
Apply forbid_superadmin_job_token to offboard_global_user and
export_global_users, the remaining global user-management routes that
were gated only by require_super_admin. Offboarding can delete a user
along with their tokens, password, invites and instance-group
membership, and export returns every user's password_hash, so both must
be unreachable by a superadmin job token.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(api): add endpoint to update token label
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(api): prevent renaming the session token label
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(api): restrict token-label edits to user tokens, not just session
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(frontend): edit token label in the edit modal instead of inline
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(api): reject relabeling tokens to reserved system-token names
Centralize the is_user_token classifier in windmill-common and reuse it
to reject labels colliding with system-token namespaces (ephemeral*,
debugger-token, mcp-oauth-*), not just session.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(api): match ephemeral label case-insensitively and cap label length
Align the canonical is_user_token, the SQL guard and the frontend mirror on
a case-insensitive `ephemeral` match (so a token can't be relabeled to a
casing the backend allows but the UI hides), reject labels over the
VARCHAR(1000) column limit with a 400, and add unit tests for is_user_token.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A token scoped to a single resource (e.g. `resources:read:u/alice/foo`)
could call `GET /api/w/{w}/resources/list_search` and receive `path` and
`value` for unrelated resources in the workspace. Route-level scope
checks only validate `domain:action`; per-resource handlers do a
`check_scopes` against the path, but the listing endpoints did not —
leaking integration credentials, API keys, and other secrets stored as
resource values to narrowly-scoped tokens.
Add `build_scope_path_predicate` to `windmill-api-auth` (mirrors
`check_scopes` semantics but parses the token's scopes once, suitable
for filtering many rows). Apply it to `list_search_resources`,
`list_resources`, `list_names` (resources) and `list_variables`
(non-secret value leak), so a scope-restricted token only ever sees the
paths it is authorized to read. Unscoped tokens and tokens whose only
scopes are `if_jobs:filter_tags:*` are unaffected.
Includes regression tests covering: unscoped, tag-filter-only,
single-resource, wildcard, wrong-domain, and write-implies-read.
Fixes WIN-1981
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: read-only flag on API tokens, orthogonal to scopes
Add a per-token `read_only` boolean set at creation time. When true, the
token can only call HTTP methods classified as Read (GET/HEAD/OPTIONS).
Mutating methods and job-run actions are rejected with 403, regardless of
which scopes are attached. Surfaced as a prominent toggle in the standard
token-creation flow and a discreet `2xs` toggle in MCP mode (where users
often want write access, so we don't bias them toward enabling it).
MCP enforcement: read-only tokens hide all script/flow/hub tools from
`list_tools` and only see endpoint tools whose method is GET, and the
runner rejects `call_tool` on anything mutating.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: review fixes for read-only token flag
- Exempt /api/mcp/* and /mcp/* paths from the read-only middleware check.
MCP transport runs over POST (streamable HTTP / SSE), so otherwise the
middleware would 403 every MCP request before the runner could enforce
read-only at the tool-call level.
- Tighten is_endpoint_read_only to GET only, matching the read_only_hint
that create_endpoint_annotations actually emits.
- Add unit test for check_read_only_for_route covering GET/HEAD/OPTIONS,
mutating methods, and run paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: bump ee-repo-ref to read-only-trigger-toggle
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(frontend): make read-only toggle discreet in both modes
Match the MCP-mode treatment in standard mode: text-tertiary, 2xs, shared
"Read-only" label. The tooltip switches per mode so the explanation still
fits the context.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(frontend): gate read-only toggle behind Limit token permissions
The read-only toggle now only shows when the user has limited the token's
scopes (standard mode) or in MCP mode (which always picks an MCP scope).
Turning the limit off also resets read-only so it doesn't silently stick.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(frontend): hide incompatible MCP tools when read-only is on
When the read-only toggle is on in MCP mode:
- Endpoint badges and the custom-mode endpoint MultiSelect filter to GET.
- Already-selected non-GET endpoints are pruned from the scope.
- The scripts/flows preview is replaced with a note explaining they're
hidden (the runner already rejects script/flow runs for read-only).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(frontend): place read-only toggle at top of limited scope area
The previous gate required at least one scope to be picked before the
read-only toggle appeared, which made it look missing while the user was
still building their scope list. Move the toggle inside ScopesPicker:
- Standard mode: sits directly under the "Limit token permissions" toggle
whenever Limit is on, before the scope selector.
- MCP mode: sits at the top of the MCP scope block.
readOnly is now $bindable on ScopesPicker so CreateToken still owns the
value. The auto-reset on un-limit moves into ScopesPicker too.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(frontend): nest read-only toggle inside the scope list card
Place the read-only toggle at the top of the scope list (between the
Selected Scopes summary and the bordered domain list) via a new optional
topSlot snippet on ScopeSelector. Keeps ScopeSelector decoupled from
read-only specifics; ScopesPicker fills the slot.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 9bc8160be50b3e57a60daf4e1b71c389a6e02b8a
This commit updates the EE repository reference after PR #571 was merged in windmill-ee-private.
Previous ee-repo-ref: f53d26e6685dfd60bfa67686fbd7358169cfd130
New ee-repo-ref: 9bc8160be50b3e57a60daf4e1b71c389a6e02b8a
Automated by sync-ee-ref workflow.
* fix: address CI review for read-only token flag
- P1 (Codex): narrow the MCP middleware exemption from "any /api/mcp/*"
to just the streamable HTTP transport endpoints
(/api/mcp/gateway, /api/mcp/w/{ws}/{mcp,sse,list_tools}). Without this,
a read-only token could POST /api/mcp/gateway/oauth/server/approve and
mint a follow-on non-read-only MCP token via the OAuth code/token
exchange.
- P2 (Claude/cubic): fix test comment/assertion mismatch — the run-path
assertion now exercises GET (which is what the RUN_PATH_ACTIONS
elevation comment describes) in addition to POST. Add a regression
assertion for /api/mcp/gateway/oauth/server/approve.
- P2 (cubic): short-circuit script/flow/hub-script/resource fetches in
MCP list_tools when read_only is on — they would only be discarded
below, so skipping the DB and resource fan-out is pure win.
- P2 (cubic): when scopes are pre-supplied via the CreateToken prop, the
ScopesPicker isn't rendered, which previously hid the read-only
toggle entirely. Render it next to the pre-supplied scopes display.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix: surface scope errors as 403 and show real message in CLI
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address review feedback on scope error PR
- Backend: also patch handler-level check_scopes (lib.rs:223) — without
this, endpoints using check_scopes (scripts, flows, jobs, …) still
returned 401 for scope failures, which the CLI would render as the
misleading auth message.
- CLI: strip backend file refs and the duplicated "Permission denied:" /
"Not authorized:" prefix from the surfaced error body.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* chore: upgrade axum 0.7 to 0.8 and related dependencies
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add route reachability tests for ~80 previously untested endpoints
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: switch feature-gated trigger handlers from axum::async_trait to async_trait crate
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update new trash routes to axum 0.8 path syntax
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to latest EE commit
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: upgrade route tests to assert 2xx responses with proper data setup
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: restore npm_proxy and ai_routes tests using local echo servers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: gate workspace fork test behind enterprise feature flag
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add ~40 more endpoint tests (jobs authed, health, favorites, ACLs, reachability)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address review findings from axum 0.8 upgrade
- Use cookie value_trimmed() instead of value() for cookie 0.18 compat
- Update comments still referencing old :workspace_id syntax
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 61ae055ea31481f1899953e9d5f65566b8c707b1
This commit updates the EE repository reference after PR #486 was merged in windmill-ee-private.
Previous ee-repo-ref: 0059d175a6fdddf52998b183bf91059b224704ac
New ee-repo-ref: 61ae055ea31481f1899953e9d5f65566b8c707b1
Automated by sync-ee-ref workflow.
* test: add test for new get_imports endpoint
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remove unused import in raw_apps test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* feat: store hashed tokens in the token table instead of plaintext
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address review issues in token hash migration
- Update all base.sql fixtures to include token_hash/token_prefix columns
- Keep plaintext token for webhook tokens (needed for URL reconstruction)
- Restore get_token_by_prefix to query DB for webhook tokens
- Fix down migration to delete NULL-token rows before restoring NOT NULL
- Update parser fixture standalone schema
- Update EE dedicated_worker_ee.rs to use token_hash/token_prefix
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: restore sqlx offline cache (only add new query files)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: keep writing plaintext token column for backward compat
Write to token column alongside token_hash until MIN_VERSION_SUPPORTS_TOKEN_HASH
(1.649.0) is reached. This ensures older workers can still authenticate
during rolling upgrades. Remove the separate UPDATE in new_webhook_token
since create_token_internal now writes plaintext directly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: branch on MIN_VERSION to write plaintext token or null
Check MIN_VERSION_SUPPORTS_TOKEN_HASH at runtime: write plaintext to
token column while old workers exist, switch to NULL once all workers
are >= 1.649.0.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: set MIN_VERSION_SUPPORTS_TOKEN_HASH to 1.650.0
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use token_hash for email lookup and expiry notifications
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: rotate webhook tokens instead of recovering plaintext from DB
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: use token_hash for native trigger token lookups and deletes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* sqlx
* refactor: drop webhook_token_prefix from native_trigger table
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: backward compat for token rotation and make webhook_token_hash NOT NULL
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: prevent panic on short superadmin secret token prefix
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: prevent panic on short superadmin secret token prefix
Replace all `token[0..TOKEN_PREFIX_LEN]` slicing with
`token.get(..TOKEN_PREFIX_LEN).unwrap_or(token)` to prevent
panics when a token shorter than 10 chars is provided (e.g.
malformed Authorization header, short superadmin secret).
Co-authored-by: hugocasa <hugocasa@users.noreply.github.com>
* fix: prevent panic on short token prefix slicing
Replace all `token[0..TOKEN_PREFIX_LEN]` with safe
`token.get(..TOKEN_PREFIX_LEN).unwrap_or(token)` to prevent panics
on malformed tokens shorter than 10 characters.
Co-authored-by: hugocasa <hugocasa@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Revert "fix: prevent panic on short superadmin secret token prefix"
This reverts commit 37ec2e5ad5.
* revert: remove unnecessary defensive token prefix slicing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add token_hash to end_user_email test fixture
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: add integration tests for token hash migration
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: correct token_hash test assertions for cache and version
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: add plaintext column removal reminder to test fixtures
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: log count of orphaned triggers deleted during migration
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: preserve orphaned triggers with error instead of deleting
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: rename token_expiry_notification.token to token_hash and copy owner/expiration in rotate
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: hash existing plaintext values before renaming token_expiry_notification column
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: remove unnecessary length check in token_expiry_notification migration
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* update dates and version
* updat ee ref + sqlx
* improve mcp migration
* fix: atomic token rotation with rollback on trigger update failure
rotate_webhook_token now atomically inserts the new token and deletes
the old one in a single transaction, preventing token leaks.
Returns new_token_hash so callers can clean up the new token if their
subsequent trigger update fails (which involves external HTTP calls
and cannot be in the same DB transaction).
- Handler: wraps post-rotation work; deletes new token on failure
- Google renewal: deletes new token if service_config update fails
- Tests updated to match new atomic semantics
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* higher min version
* fix: defer old token deletion to avoid breaking triggers on update failure
rotate_webhook_token now keeps the old token alive and returns
old_token_hash. Callers delete it only after the trigger row has been
successfully updated. If the external service call or DB update fails,
the trigger keeps working with the old token.
Worst case: if the best-effort delete fails, the old token leaks as an
extra DB row — harmless compared to breaking the trigger.
Also update summarized_schema.txt for renamed columns.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 2d0823a471014e2bc2d898c63518323946b7474f
This commit updates the EE repository reference after PR #437 was merged in windmill-ee-private.
Previous ee-repo-ref: 7aef8b06cb6f54c2bc89dd57b70947deed72553c
New ee-repo-ref: 2d0823a471014e2bc2d898c63518323946b7474f
Automated by sync-ee-ref workflow.
* fix: prevent panic on short tokens by using safe prefix extraction
Add safe_token_prefix() helper that uses .get(..TOKEN_PREFIX_LEN).unwrap_or(token)
instead of direct slice indexing, preventing panics when tokens are shorter than
10 characters (e.g., short superadmin secrets or malformed Bearer tokens).
Co-authored-by: Ruben Fiszel <rubenfiszel@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: HugoCasa <hugo@casademont.ch>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: hugocasa <hugocasa@users.noreply.github.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <rubenfiszel@users.noreply.github.com>
* fix: skip token expiry notifications for debugger and mcp-oauth tokens
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: update frontend isUserToken to match backend filter
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: add cross-reference comments to token filter functions
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add token expiration notifications via email, critical alerts, and webhooks
- Monitor loop checks for tokens expiring within 7 days and sends
email notifications to token owners. Tracks notification state via
new `expiry_notified` column on the token table to avoid duplicates.
- When tokens expire and are deleted, owners are also notified.
- Critical alerts (in-app UI) are gated behind a new instance setting
`critical_alerts_on_token_expiry` (off by default); emails are
always sent regardless of the setting.
- Add TokenExpiringSoon and TokenExpired webhook message variants for
workspace webhook integrations.
- Frontend: show expiration badges and a warning banner on the tokens
table for tokens expiring within 30 days.
- Exclude session and ephemeral tokens from all notifications.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: use separate token_expiry_notification table for dedup
- Replace `expiry_notified` column on token table with a dedicated
`token_expiry_notification` table (token, expiration)
- Insert notification row on token creation via shared
`register_token_expiry_notification()` helper
- Delete notification row atomically when sending the notification
- Clean up orphaned rows in `delete_expired_items()`
- No FK constraint to avoid cascade overhead on token deletions
- Add index on expiration column for efficient range queries
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: calendar-based expiration badge and move notification cleanup
- Fix daysUntilExpiration to compare calendar dates instead of time diff
- Move notification row cleanup from delete_expired_items to
check_expiring_tokens to keep it off the hot path
- Use simple expiration <= now() index scan instead of NOT EXISTS join
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: reuse existing transaction in push instead of acquiring new connection
In push_inner, fetch_authed_from_permissioned_as was acquiring a new
connection from the pool to fetch job permissions, even though a
transaction was already open. Use fetch_authed_from_permissioned_as_conn
with the existing transaction instead, reducing pool pressure when many
jobs are pushed concurrently.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* improve contention
* improve contention
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: extract windmill-api into 4 subcrates (api-auth, store, api-sse, api-jobs)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: eliminate refresh_token OnceLock bridge in windmill-store
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: eliminate FromRequestParts OnceLock bridge in windmill-api-auth
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: wire subcrates into workspace and clean up unused re-exports
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve cargo check --all-features errors in subcrate wiring
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* sqlx
* all
* chore: update ee-repo-ref for warning fixes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: extract windmill-trigger crate and expand windmill-api-jobs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract windmill-trigger-kafka crate from windmill-api
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract windmill-trigger-postgres crate from windmill-api
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract windmill-trigger-websocket and windmill-trigger-mqtt crates
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract windmill-trigger-nats, sqs, gcp, and email crates
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract windmill-trigger-http crate from windmill-api
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: move token creation and permission helpers to windmill-api-auth
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract windmill-native-triggers crate from windmill-api
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* sqlx
* all
* refactor: extract windmill-api-embeddings crate and fix CI warnings
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: resolve type mismatch in oauth2_oss and remaining warnings
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: use correct HTTP_CLIENT config in embeddings crate (30s timeout, cert override)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* all
* fix: gate oauth_refresh_ee on oauth2 feature to fix warnings
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* all
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>