{#if !onLatest} By deploying, you may overwrite changes made by other users. Press 'Deploy' to see diff.
{/if}
{ e.stopPropagation() }, onkeyup: () => { if (appPath == '' && summary?.length > 0 && !dirtyPath) { path?.setName( summary .toLowerCase() .replace(/[^a-z0-9_]/g, '_') .replace(/-+/g, '_') .replace(/^-|-$/g, '') ) } } }} bind:value={summary} />
A viewer of the app will execute the runnables of the app on behalf of the publisher (you) It ensures that all required resources/runnable visible for publisher but not for viewer at time of creating the app would prevent the execution of the app. To guarantee tight security, a policy is computed at time of deployment of the app which only allow the scripts/flows referred to in the app to be called on behalf of. Furthermore, static parameters are not overridable. Hence, users will only be able to use the app as intended by the publisher without risk for leaking resources not used in the app. {#if canPreserve}
Because you are either an admin or part of the {WM_DEPLOYERS_GROUP} group, you can select another user to run this app on behalf of. Once deployed the app will be run on behalf of { onBehalfOfChoice = choice if (choice === 'me') { policy.on_behalf_of_email = $userStore?.email policy.on_behalf_of = `u/${$userStore?.username}` customOnBehalfOfEmail = '' preserveOnBehalfOf = false } else if (choice === 'target') { policy.on_behalf_of_email = savedOnBehalfOfEmail policy.on_behalf_of = savedOnBehalfOf customOnBehalfOfEmail = '' preserveOnBehalfOf = true } else if (choice === 'custom' && details) { policy.on_behalf_of_email = details.email policy.on_behalf_of = details.permissionedAs customOnBehalfOfEmail = details.email preserveOnBehalfOf = true } }} kind="app" {canPreserve} customValue={customOnBehalfOfEmail} isDeployment={false} />
{/if}

Sandbox isolation

Alpha
{ policy.sandbox = e.detail || undefined // Frontend API access exists only for a sandboxed app, so turning // isolation off drops the declared scopes with it rather than leaving // them set but inert. if (!e.detail) { policy.frontend_sdk_scopes = undefined } // A not-yet-deployed app has no row to PATCH — `setPublishState` (POST // /apps/update) would 404. The flag rides along in the `policy` the first // deploy sends (createApp), so here we only mutate it locally. Persist // incrementally once the app exists. if (savedApp && !newApp) { setPublishState(e.detail ? 'Sandbox isolation enabled' : 'Sandbox isolation disabled') } }} disabled={!savedApp} />
Controls what the app's browser-side code can reach in each viewer's browser — distinct from the on-behalf-of model above (which sets who its runnables run as). Off by default, the app's code uses the viewer's own session; enable it to confine the app to a narrowly-scoped token instead, on every surface (public URL and in-workspace). Leave it off if the app needs full browser features (IndexedDB, third-party auth/SDKs, OAuth redirects).
{#if newApp}
Takes effect when you first deploy this app.
{/if} {#if policy.sandbox == true}
Sandbox isolation is in alpha. After enabling, open the app from its public URL to confirm it still works, and report any broken behavior.
{/if}
{#if rawApp && policy.sandbox == true}

Frontend API access

Let the app's frontend code call the Windmill API through the windmill-client SDK, authenticated as the viewer (unlike runnables, which run on behalf of the publisher). Each viewer is asked to approve the scopes below before the app runs. Grant only what the app needs: its code — or an XSS bug in it — can use them as that viewer. Add windmill-client to the app's dependencies to import it; it configures itself from the token handed to the bundle.
{#each FRONTEND_SDK_SCOPES as scope (scope.value)}
{ const current: string[] = policy.frontend_sdk_scopes ?? [] const next = e.detail ? [...current, scope.value] : current.filter((s) => s !== scope.value) // Keep the curated order so the consent banner and the stored // consent compare stably across deploys. const ordered = FRONTEND_SDK_SCOPES.map((s) => s.value).filter((s) => next.includes(s)) policy.frontend_sdk_scopes = ordered.length > 0 ? ordered : undefined // Same as sandbox: a not-yet-deployed app has no row to PATCH, so the // scopes ride along in the first deploy's policy instead. if (savedApp && !newApp) { setPublishState('Frontend API access updated') } }} disabled={!savedApp} />
{scope.description}
{/each} {#if newApp}
Takes effect when you first deploy this app.
{/if} {#if policy.frontend_sdk_scopes?.length}
A sandboxed app calls the API cross-origin, which older windmill-client versions cannot do. An app bundled before this Windmill version fails with a CORS error until you deploy it again, which re-bundles it against a current client.
{/if}
{/if} {#if !hideSecretUrl}

Access

{#if rulesetsLoaded && !canSetAnonymous && policy.execution_mode != 'anonymous'} Opening this app to anyone with the link is restricted to workspace admins and bypass users by a workspace protection rule
{/if} {#if rulesetsLoaded && !canSetGuest && policy.execution_mode != 'guest'} Opening this app to guests is restricted to workspace admins and bypass users by a workspace protection rule
{/if}
onAccessModeChange(e.detail)} disabled={!savedApp} > {#snippet children({ item })} {/snippet}
{#if policy.execution_mode == 'anonymous'} Anyone holding the secret URL below can open this app without signing in. {:else if policy.execution_mode == 'guest'} {#if !$enterpriseLicense} Guest sign-in is a Windmill Enterprise Edition feature, so this app still admits members only. {:else if guestAccessEnabled === undefined} Checking whether this workspace allows guests… {:else if guestAccessEnabled === false} Guests are turned off for this workspace, so this app still admits members only. A workspace admin can turn them on in the workspace settings. {:else} Anyone your identity provider authenticates can open this app without a Windmill account. They join no workspace and take no seat. Members of this workspace can open it too. {/if} {:else} Only workspace members with read access on this app can open it. {/if}
{#if !savedApp || newApp} {:else if secretUrlHref}
(embedMode = e.detail)} options={{ left: 'URL', right: 'Embed' }} />
{:else} {/if}
{#if embedMode} Paste this iframe snippet into another app. {#if rawApp} The wm_coep flag Sets the cross-origin isolation headers (COEP) so the app can be embedded inside another Windmill app or any cross-origin-isolated page. Without it the browser blocks the iframe. lets it load inside a cross-origin-isolated page. {/if} (if requiring login, top-level domain of embedding app must be the same as the one of Windmill) {:else} Share this url directly, or switch to Embed to get an iframe snippet. {/if}
{#if !($userStore?.is_admin || $userStore?.is_super_admin)} Custom path can only be set by workspace admins
{/if} {#if !$enterpriseLicense} {/if} { customPath = detail ? '' : undefined if (customPath === undefined) { customPathError = '' } }} checked={customPath !== undefined} options={{ right: 'Use a custom URL' }} disabled={!$enterpriseLicense || !($userStore?.is_admin || $userStore?.is_super_admin)} /> {#if customPath !== undefined}
Custom path
{ dirtyCustomPath = true }} />
Custom public URL
{dirtyCustomPath ? customPathError : ''}
{/if}
You will still need to deploy the app to make visible the latest changes Embed this app in your own product to be used by your own users {/if}