Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/advanced/16_external_auth_with_jwt/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ This way, you control what permissions your users have without having to create

This feature is [Enterprise Edition](/pricing) and [Whitelabel](../../misc/8_white_labelling/index.mdx) only.

For [billing](/pricing), each external JWT token used in the last 30 days counts as half a [Seat](../../core_concepts/16_roles_and_permissions/index.mdx#users) - the same as an [operator](../../core_concepts/16_roles_and_permissions/index.mdx#operator). Operators and external JWT tokens are interchangeable in seat counting (e.g. 1 operator + 1 external JWT token = 1 Seat).

For JWT verification, the first option is to pass as an environment variable called `JWT_EXT_PUBLIC_KEY` the public key in PEM format (RS256).
The second option is to pass as `JWT_EXT_JWKS_URL` the url of the JWKs endpoint to retrieve the public keys from which the JWTs will be matched on the `kid` field.
In the latter case, the instance will refresh its cache of public keys every 15 minutes.
Expand Down
4 changes: 2 additions & 2 deletions docs/core_concepts/16_roles_and_permissions/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Recap of how are permissioned items (scripts, flows, apps, resources, variables,

Users are uniquely identified globally by their email. They also have a unique username with respect to each workspace they are members of.

In terms of billing for Windmill [Enterprise Edition](/pricing), we only count active users, i.e. users who have at least logged in to the platform in the last 30 days according to the audit logs. [Operators](#operator) (i.e. users who are Operators in all workspaces they are members of) are counted as half of a regular seat. Logging in once in 30 days is enough to be considered 'Active'.
In terms of billing for Windmill [Enterprise Edition](/pricing), we only count active users, i.e. users who have at least logged in to the platform in the last 30 days according to the audit logs. [Operators](#operator) (i.e. users who are Operators in all workspaces they are members of) are counted as half of a regular seat. Each [external JWT token](../../advanced/16_external_auth_with_jwt/index.mdx) used in the last 30 days is also billed as half a seat (same as an operator). Logging in once in 30 days is enough to be considered 'Active'.

## Workspace

Expand Down Expand Up @@ -94,7 +94,7 @@ From the workspace settings, you can configure the operator visibility settings
- [Folders](../8_groups_and_folders/index.mdx#folders)
- [Workers](../9_worker_groups/index.mdx)

Regarding to [Pricing](/pricing), operators are counted as half of a regular seat ([developers](#developer)) as long as they are operators in all workspaces they are members of. Operators are not set as the instance-level. On the billing side, 1 developer seat or 2 operators seats count as 1 seat, there is no need to differentiate between developers and operators when purchasing the license.
Regarding to [Pricing](/pricing), operators are counted as half of a regular seat ([developers](#developer)) as long as they are operators in all workspaces they are members of. Operators are not set as the instance-level. On the billing side, 1 developer seat or 2 operator/[external JWT token](../../advanced/16_external_auth_with_jwt/index.mdx) seats count as 1 seat, and each external JWT token in use also counts as half a seat (same as an operator). There is no need to differentiate between developers, operators and external JWT tokens when purchasing the license.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 — redundant clause. The first half of the sentence already establishes the half-seat math via "1 developer seat or 2 operator/external JWT token seats count as 1 seat". The added trailing clause "and each external JWT token in use also counts as half a seat (same as an operator)" then restates the same fact, which makes the sentence harder to parse. Consider dropping the trailing clause:

Suggested change
Regarding to [Pricing](/pricing), operators are counted as half of a regular seat ([developers](#developer)) as long as they are operators in all workspaces they are members of. Operators are not set as the instance-level. On the billing side, 1 developer seat or 2 operator/[external JWT token](../../advanced/16_external_auth_with_jwt/index.mdx) seats count as 1 seat, and each external JWT token in use also counts as half a seat (same as an operator). There is no need to differentiate between developers, operators and external JWT tokens when purchasing the license.
Regarding to [Pricing](/pricing), operators are counted as half of a regular seat ([developers](#developer)) as long as they are operators in all workspaces they are members of. Operators are not set as the instance-level. On the billing side, 1 developer seat or 2 operator/[external JWT token](../../advanced/16_external_auth_with_jwt/index.mdx) seats count as 1 seat. There is no need to differentiate between developers, operators and external JWT tokens when purchasing the license.


### Service accounts

Expand Down
2 changes: 1 addition & 1 deletion docs/misc/19_enterprise_onboarding/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ You can adjust your subscription anytime via the [Customer Portal](#customer-por

### Seats

A Seat is a user who is active on the platform. A regular user counts as one Seat, while an [operator](../../core_concepts/16_roles_and_permissions/index.mdx#operator) (who can only execute scripts, flows, and apps) counts as half a Seat. We only count active users, i.e. users who have logged in to the platform in the last 30 days according to the audit logs. Seats are counted across all instances (dev, prod) but Windmill only counts once the same user.
A Seat is a user who is active on the platform. A regular user counts as one Seat, while an [operator](../../core_concepts/16_roles_and_permissions/index.mdx#operator) (who can only execute scripts, flows, and apps) counts as half a Seat. Each [external JWT token](../../advanced/16_external_auth_with_jwt/index.mdx) used in the last 30 days is also billed as half a Seat (same as an operator) — for seat counting, you can mix operators and external JWT tokens freely (e.g. 1 operator + 1 external JWT token = 1 Seat). We only count active users, i.e. users who have logged in to the platform in the last 30 days according to the audit logs. Seats are counted across all instances (dev, prod) but Windmill only counts once the same user.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 — em dash. AGENTS.md says: "Avoid using symbol '—' as it looks like obvious AI. Use sentences not using it or use '-' instead." This added clause is the only em dash this PR introduces in this file.

Suggested change
A Seat is a user who is active on the platform. A regular user counts as one Seat, while an [operator](../../core_concepts/16_roles_and_permissions/index.mdx#operator) (who can only execute scripts, flows, and apps) counts as half a Seat. Each [external JWT token](../../advanced/16_external_auth_with_jwt/index.mdx) used in the last 30 days is also billed as half a Seat (same as an operator) — for seat counting, you can mix operators and external JWT tokens freely (e.g. 1 operator + 1 external JWT token = 1 Seat). We only count active users, i.e. users who have logged in to the platform in the last 30 days according to the audit logs. Seats are counted across all instances (dev, prod) but Windmill only counts once the same user.
A Seat is a user who is active on the platform. A regular user counts as one Seat, while an [operator](../../core_concepts/16_roles_and_permissions/index.mdx#operator) (who can only execute scripts, flows, and apps) counts as half a Seat. Each [external JWT token](../../advanced/16_external_auth_with_jwt/index.mdx) used in the last 30 days is also billed as half a Seat (same as an operator). For seat counting, you can mix operators and external JWT tokens freely (e.g. 1 operator + 1 external JWT token = 1 Seat). We only count active users, i.e. users who have logged in to the platform in the last 30 days according to the audit logs. Seats are counted across all instances (dev, prod) but Windmill only counts once the same user.


### Telemetry and usage tracking

Expand Down
4 changes: 2 additions & 2 deletions docs/misc/7_plans_details/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ To adjust the number of seats, you can update your usage on the subscription pag
Windmill employs lightweight [telemetry](#usage-checks) to automatically track and report the usage of memory and seats for your subscription. Even on self-hosted plans, telemetry associated to your license key is reported to Windmill.

How the data is calculated:
- Seats: number of users (1 developer, or 2 operators) who are active (from logging in to running or deploying a script) on the platform in the last 30 days, according to the [audit logs](../../core_concepts/14_audit_logs/index.mdx). User count is across all instances (dev, prod) but Windmill only counts once the same user.
- Seats: number of users (1 developer, or 2 operators) who are active (from logging in to running or deploying a script) on the platform in the last 30 days, according to the [audit logs](../../core_concepts/14_audit_logs/index.mdx). Each [external JWT token](../../advanced/16_external_auth_with_jwt/index.mdx) used in the last 30 days also counts as 1/2 seat (same as an operator). User count is across all instances (dev, prod) but Windmill only counts once the same user.
- Memory: we aggregate the limits of all production containers. Workers come in different sizes: small (1GB), standard (2GB), and large (> 2GB). For each [Compute Unit](pathname:///pricing#compute-units), you pay for, you get a quota of 2 worker-gb-month.

Using a number of seats, workers, or memory greater than the terms of your subscription is technically possible, but if you do not adjust your subscription accordingly (via the [Customer Portal](#windmill-customer-portal)), we will ask you to take steps to correct this, with 3 options:
Expand Down Expand Up @@ -491,7 +491,7 @@ When minimal telemetry is disabled, the following is also collected:
- job usage (language, total duration, count)

How the data is calculated:
- Seats: number of users (1 developer, or 2 operators) who are active (from logging in to running or deploying a script) on the platform in the last 30 days, according to the [audit logs](../../core_concepts/14_audit_logs/index.mdx). User count is across all instances (dev, prod) but Windmill only counts once the same user.
- Seats: number of users (1 developer, or 2 operators) who are active (from logging in to running or deploying a script) on the platform in the last 30 days, according to the [audit logs](../../core_concepts/14_audit_logs/index.mdx). Each [external JWT token](../../advanced/16_external_auth_with_jwt/index.mdx) used in the last 30 days also counts as 1/2 seat (same as an operator). User count is across all instances (dev, prod) but Windmill only counts once the same user.
- Memory: we aggregate the limits of all production containers. Workers come in different sizes: small (1GB), standard (2GB), and large (> 2GB). For each compute unit, you pay for, you get a quota of 2 worker-gb-month. [Non-prod instances](../../advanced/18_instance_settings/index.mdx#non-prod-instance) are not counted in the billing for computation usage. For development environments, a [dev license key](#license-keys) from the portal is an alternative to marking instances as non-prod manually.

Using a number of seats, workers, or memory greater than the terms of your subscription is technically possible, but if you do not adjust your subscription accordingly (via the [Customer Portal](#windmill-customer-portal)), we will ask you to take steps to correct this, with 3 options:
Expand Down
2 changes: 1 addition & 1 deletion src/components/pricing/FeatureList.js
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ export const sections = [
link: '/docs/misc/saml_and_scim'
},
{
name: 'External auth with JWT',
name: 'External auth with JWT (each token billed as 1/2 seat)',
tiers: {
'tier-free-selfhost': false,
'tier-enterprise-selfhost': true,
Expand Down
8 changes: 4 additions & 4 deletions src/components/pricing/PriceCalculator.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ const SeatsSummary = ({ developers, operators }) => (
<span className="whitespace-nowrap text-sm text-gray-600 dark:text-gray-200 ml-4">
{operators.toLocaleString()}{' '}
<a href="#operator" className="custom-link text-gray-600 dark:text-gray-200">
{operators <= 1 ? 'operator' : 'operators'}
{operators <= 1 ? 'operator or ext JWT token' : 'operators or ext JWT tokens'}
</a>
{' '}= {operators/2} {operators/2 <= 1 ? 'seat' : 'seats'}
</span>
Expand Down Expand Up @@ -474,13 +474,13 @@ export default function PriceCalculator({ period, tier, selectedOption }) {
/>
</span>
<span className="text-sm font-semibold tracking-tight text-gray-600 dark:text-gray-200">
{' '}{operators <= 1 ? 'operator' : 'operators'}{' '}
{' '}{operators <= 1 ? 'operator or ext JWT token' : 'operators or ext JWT tokens'}{' '}
<span className="relative group">
<svg className="inline-block w-3 h-3 text-blue-800 hover:text-blue-400 dark:text-blue-300 dark:hover:text-blue-500 cursor-help" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span className="invisible group-hover:visible absolute z-10 w-96 p-2 mt-2 text-sm font-normal text-white bg-slate-800 rounded-lg shadow-lg">
An <a href="#operator" className="text-blue-400 hover:text-blue-500">operator</a> is a user who can only execute scripts, flows and apps, but not create and edit them. Operators are 1/2 price of developers (or 1/2 seats).
An <a href="#operator" className="text-blue-400 hover:text-blue-500">operator</a> is a user who can only execute scripts, flows and apps, but not create and edit them. Each <a href="/docs/advanced/external_auth_with_jwt" className="text-blue-400 hover:text-blue-500">external JWT token</a> in use also counts here. Operators and ext JWT tokens are 1/2 price of developers (or 1/2 seats).
</span>
</span>
</span>
Expand Down Expand Up @@ -807,7 +807,7 @@ export default function PriceCalculator({ period, tier, selectedOption }) {
<span className="whitespace-nowrap text-sm text-gray-600 dark:text-gray-200 ml-4">
{operators.toLocaleString()}{' '}
<a href="#operator" className="custom-link text-gray-600 dark:text-gray-200">
{operators <= 1 ? 'operator' : 'operators'}
{operators <= 1 ? 'operator or ext JWT token' : 'operators or ext JWT tokens'}
</a>
{' '}= {operators/2} {operators/2 <= 1 ? 'seat' : 'seats'}
</span>
Expand Down
32 changes: 26 additions & 6 deletions src/components/pricing/PricingFAQ.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const faqs = [
{
id: 'pricing-explained',
question: "What is the logic behind Windmill's pricing?",
textAnswer: "Windmill's pricing is based on compute units (2 worker-gb-month each) and user seats. Operators count as half a seat. Billing uses minute granularity for auto-scaling. This ensures pricing scales linearly with your usage and team size.",
textAnswer: "Windmill's pricing is based on compute units (2 worker-gb-month each) and user seats. Operators and external JWT tokens count as half a seat each. Billing uses minute granularity for auto-scaling. This ensures pricing scales linearly with your usage and team size.",
answer: (
<span>
Windmill's pricing is designed to align with the value we deliver to our customers. Our pricing model reflects the core value of Windmill, which is primarily related to the amount of compute resources used, and the number of users accessing the platform. We've structured our pricing to scale with your usage, ensuring you're paying for the actual value you derive from our platform.
Expand All @@ -25,7 +25,12 @@ const faqs = [
className="text-blue-600 hover:text-blue-800 dark:text-blue-500 dark:hover:text-blue-600"
>
operator
</Link>{' '}(who can only execute scripts, flows, and apps) counts as half a seat. This allows for flexible team structures and cost-effective scaling of your user base. We only count active users, i.e. users who have logged in to the platform in the last 30 days according to the audit logs.
</Link>{' '}(who can only execute scripts, flows, and apps) counts as half a seat. Each <Link
to="/docs/advanced/external_auth_with_jwt"
className="text-blue-600 hover:text-blue-800 dark:text-blue-500 dark:hover:text-blue-600"
>
external JWT token
</Link>{' '}in use is also billed as half a seat. This allows for flexible team structures and cost-effective scaling of your user base. We only count active users, i.e. users who have logged in to the platform in the last 30 days according to the audit logs.
<br /><br />
Our billing is meant to be fair and transparent: we only count the actual memory allocated to your workers in your production instance, with minute granularity if you use auto-scaling. This approach ensures that you're only charged for the resources actively contributing to your production environment. If you scale your workers up and down, the compute units will be accounted with minute granularity. For instance, you can run 10 workers with 2GB each for half a month at the same price as 5 workers with 2GB each for the full month.
<br /><br />
Expand All @@ -50,8 +55,8 @@ const faqs = [
},
{
id: 'operator',
question: 'What is an operator?',
textAnswer: 'An operator is a user who can only execute scripts, flows and apps, but not create or edit them. Operators count as half a seat. 1 developer seat or 2 operator seats count as 1 seat for billing.',
question: 'What is an operator? How are external JWT tokens billed?',
textAnswer: 'An operator is a user who can only execute scripts, flows and apps, but not create or edit them. Operators count as half a seat. External JWT tokens (used for external auth) are also billed as half a seat each. 1 developer seat or 2 operator/JWT seats count as 1 seat for billing.',
answer: (
<span>
An{' '}
Expand All @@ -68,7 +73,17 @@ const faqs = [
Operators are 1/2 price of developers (or 1/2 seats) as long as they are operators in all workspaces they are members of. Operators are not set as the instance-level.
<br />
<br />
On the billing side, 1 developer seat or 2 operators seats count as 1 seat, there is no need to differentiate between developers and operators when purchasing the license.
Each{' '}
<Link
to="/docs/advanced/external_auth_with_jwt"
className="text-blue-600 hover:text-blue-800 dark:text-blue-500 dark:hover:text-blue-600"
>
external JWT token
</Link>{' '}
in use is billed the same way as an operator (1/2 seat). For seat counting, you can mix operators and external JWT tokens freely — for example, 1 operator + 1 external JWT token = 1 seat.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 — em dash. AGENTS.md: "Avoid using symbol '—' as it looks like obvious AI. Use sentences not using it or use '-' instead." This is one of two new em dashes the PR introduces.

Suggested change
in use is billed the same way as an operator (1/2 seat). For seat counting, you can mix operators and external JWT tokens freely for example, 1 operator + 1 external JWT token = 1 seat.
in use is billed the same way as an operator (1/2 seat). For seat counting, you can mix operators and external JWT tokens freely (for example, 1 operator + 1 external JWT token = 1 seat).

<br />
<br />
On the billing side, 1 developer seat or 2 operator/ext JWT seats count as 1 seat, there is no need to differentiate between developers, operators and external JWT tokens when purchasing the license.
</span>
)
},
Expand Down Expand Up @@ -260,7 +275,12 @@ const faqs = [
Seats reported to Windmill are the number of users (1 developer, or 2 operators) who are
active (from logging in to running or deploying a script) on the platform in the last 30
days, according to the audit logs. User count is across all instances (dev, prod) but
Windmill only counts once the same user.
Windmill only counts once the same user. Each <Link
to="/docs/advanced/external_auth_with_jwt"
className="text-blue-600 hover:text-blue-800 dark:text-blue-500 dark:hover:text-blue-600"
>
external JWT token
</Link>{' '}used in the last 30 days also counts as half a seat (same as an operator).
<br />
<br />
The number of compute units considered is the number of production compute units, not of development
Expand Down
Loading