diff --git a/docs/advanced/16_external_auth_with_jwt/index.mdx b/docs/advanced/16_external_auth_with_jwt/index.mdx
index 20da3539a..8811ba583 100644
--- a/docs/advanced/16_external_auth_with_jwt/index.mdx
+++ b/docs/advanced/16_external_auth_with_jwt/index.mdx
@@ -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.
diff --git a/docs/core_concepts/16_roles_and_permissions/index.mdx b/docs/core_concepts/16_roles_and_permissions/index.mdx
index e82d9a3d8..042e40264 100644
--- a/docs/core_concepts/16_roles_and_permissions/index.mdx
+++ b/docs/core_concepts/16_roles_and_permissions/index.mdx
@@ -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
@@ -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.
### Service accounts
diff --git a/docs/misc/19_enterprise_onboarding/index.mdx b/docs/misc/19_enterprise_onboarding/index.mdx
index 4bde95d3d..2b47b1907 100644
--- a/docs/misc/19_enterprise_onboarding/index.mdx
+++ b/docs/misc/19_enterprise_onboarding/index.mdx
@@ -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.
### Telemetry and usage tracking
diff --git a/docs/misc/7_plans_details/index.mdx b/docs/misc/7_plans_details/index.mdx
index f1aa3f5ac..3a6906cbf 100644
--- a/docs/misc/7_plans_details/index.mdx
+++ b/docs/misc/7_plans_details/index.mdx
@@ -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:
@@ -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:
diff --git a/src/components/pricing/FeatureList.js b/src/components/pricing/FeatureList.js
index 9deb18b06..98b5c08da 100644
--- a/src/components/pricing/FeatureList.js
+++ b/src/components/pricing/FeatureList.js
@@ -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,
diff --git a/src/components/pricing/PriceCalculator.js b/src/components/pricing/PriceCalculator.js
index 8b430cbae..6b0cc5e1b 100644
--- a/src/components/pricing/PriceCalculator.js
+++ b/src/components/pricing/PriceCalculator.js
@@ -83,7 +83,7 @@ const SeatsSummary = ({ developers, operators }) => (
{operators.toLocaleString()}{' '}
- {operators <= 1 ? 'operator' : 'operators'}
+ {operators <= 1 ? 'operator or ext JWT token' : 'operators or ext JWT tokens'}
{' '}= {operators/2} {operators/2 <= 1 ? 'seat' : 'seats'}
@@ -474,13 +474,13 @@ export default function PriceCalculator({ period, tier, selectedOption }) {
/>
- {' '}{operators <= 1 ? 'operator' : 'operators'}{' '}
+ {' '}{operators <= 1 ? 'operator or ext JWT token' : 'operators or ext JWT tokens'}{' '}
- An operator 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 operator is a user who can only execute scripts, flows and apps, but not create and edit them. Each external JWT token in use also counts here. Operators and ext JWT tokens are 1/2 price of developers (or 1/2 seats).
@@ -807,7 +807,7 @@ export default function PriceCalculator({ period, tier, selectedOption }) {
{operators.toLocaleString()}{' '}
- {operators <= 1 ? 'operator' : 'operators'}
+ {operators <= 1 ? 'operator or ext JWT token' : 'operators or ext JWT tokens'}
{' '}= {operators/2} {operators/2 <= 1 ? 'seat' : 'seats'}
diff --git a/src/components/pricing/PricingFAQ.js b/src/components/pricing/PricingFAQ.js
index 2162ee373..077a6f2b2 100644
--- a/src/components/pricing/PricingFAQ.js
+++ b/src/components/pricing/PricingFAQ.js
@@ -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: (
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.
@@ -25,7 +25,12 @@ const faqs = [
className="text-blue-600 hover:text-blue-800 dark:text-blue-500 dark:hover:text-blue-600"
>
operator
- {' '}(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.
+ {' '}(who can only execute scripts, flows, and apps) counts as half a seat. Each
+ external JWT token
+ {' '}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.
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.
@@ -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: (
An{' '}
@@ -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.
- 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{' '}
+
+ external JWT token
+ {' '}
+ 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.
+
+
+ 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.
)
},
@@ -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
+ external JWT token
+ {' '}used in the last 30 days also counts as half a seat (same as an operator).
The number of compute units considered is the number of production compute units, not of development