Licensing & pricing
Value-based module pricing (company-wide, role seat, usage, tenant/company, custom), signed license tokens, expiry, and revocation.
Updated
Business modules use value-based pricing — the metric that fits the work, not one flat per-seat rate for everything. This page explains the pricing models, what a seat is for the modules that use one, what a license is, and how your instance enforces it.
Module pricing models
The core is free to self-host. Commercial business modules are priced by the value metric that suits them:
| Model | pricing_kind |
Reads as |
|---|---|---|
| Free core | — | Free to self-host |
| Flat company | flat_monthly |
$X / company / month |
| Usage-based company plan | flat_usage_monthly |
$X / company / month incl. quota |
| Billable role seat | seat_monthly |
$X / role seat / month, viewers free |
| Host seat | host_seat_monthly |
$X / host / month, participants free |
| Tenant / company plan | tenant_monthly |
$X / tenant / month |
| Custom | custom |
Contact sales |
Each module's detail page on this site shows the model and price that apply to
it. The sections below explain the role-seat model in detail, because it is
the one with per-user enforcement; flat_monthly, flat_usage_monthly, and
tenant/company plans do not count per-user seats. (host_seat_monthly meters
a different unit — hosts — and custom is enforced however its contract
specifies, so don't assume it's seat-free.)
Role-seat modules
For modules that use the billable role seat model, a license carries a seat count and your instance enforces it as described below. Modules on a flat, usage, tenant/company or custom plan do not consume seats.
What counts as a seat
A seat is a user who holds a billable role in a plugin — not every user.
- Billable — operator- and approver-style roles that do the work (post a journal entry, approve a requisition, manage stock).
- Free — viewers, self-service users, and public/customer roles never consume a seat.
Each plugin declares its own roles and marks which are billable; the core
learns them when the signed plugin bundle is installed (see
How plugins work).
seats(plugin) is simply the number of users with at least one billable role in
that plugin. A plugin's detail page on this site lists which roles are billable.
Note
Seats are counted per plugin. A user who operates two plugins is a seat in each. This is why adding a plugin to an existing team is priced on that plugin's billable users, not your whole user base.
What a license is
On payment, the vendor issues a short, signed license token bound to your project. On the wire it looks like:
lic1.<base64url(payload)>.<base64url(signature)>
The payload is deliberately tiny — for a role-seat module it carries
{ project, plugin, seats, roles, kid, iat, exp }; modules on other models
carry the claims that fit their metric instead (e.g. a plan and quota, or a
tenant/company scope), so it can be stored and passed around cheaply. (A license
may also carry an optional node_lock that binds it to one machine's hardware
fingerprint, and a trial flag for self-serve trials.) Your instance verifies the Ed25519
signature against the pinned vendor public key, then reads the seat count
and expiry locally. No call to the vendor is needed to check a license;
verification is offline and fast.
First activation or a move to a new machine may require a marketplace approval step before the token is bound to that node. Once bound, normal enforcement is local and continues offline. The heartbeat brings in renewals, seat changes and revocations — and it is not inbound-only: each ~60-second poll also sends this machine's hardware fingerprint (for the node-lock), the core version, and an aggregate usage snapshot.
Note
The bound seat count is enforced as a turnstile at role assignment. Once
the distinct holders of a billable role reach your licensed count (per-role,
or the aggregate pool on older tokens), granting that role to one more user is
refused — the API returns 409 seat_limit_reached. To add a seat you must
free one (revoke the role from someone) or buy more capacity on railbase.app;
you can't simply add past the limit and reconcile later. The operator surface
for who holds a seat is the Settings → Plugin access screen
(/_/settings/plugin-access), which shows used / limit per plugin and flags
an over-cap state. An over state only arises when a license downgrade drops
the count below existing holders, or via an operator override — and in that
case the plugin keeps running rather than bricking mid-month. The teeth are on
expiry and revocation, below.
Expiry, renewal, and grace
Your instance periodically re-checks the license with the vendor (a heartbeat). This is how renewals, seat changes, and revocations propagate without you doing anything:
- A renewed license rotates in automatically — the heartbeat re-binds the new seat count and expiry on the next tick.
- A lapsed license (the subscription expired and no renewal re-issued one) puts the plugin dormant once grace runs out: its encrypted bundle is no longer decrypted or run, but your data is preserved untouched. Paying again issues a fresh license, and the next heartbeat re-binds it and re-activates the plugin automatically — no reinstall needed.
- An explicitly revoked license makes the plugin dormant immediately, bypassing grace — a revoke means the bundle is no longer decrypted or run. Data is still preserved.
- A refund or chargeback revokes the license the same way — immediately, with no grace. Reversing the payment reverses the entitlement; your data is still preserved, and re-subscribing brings the plugin back.
- A grace window (14 days by default, set with
RAILBASE_LICENSE_GRACE_DAYS;0disables it) covers an in-flight renewal — e.g. a webhook still settling, a payment retry over a weekend, or a box that was briefly offline — so a transient lapse doesn't suspend production. A self-serve trial gets no grace at all — it stops exactly at expiry. (The license API exposesin_grace/grace_until, though the admin doesn't currently render a grace banner from them.)
Key rotation
The vendor can rotate its signing key; the token's kid (key id) records which
key signed it. To keep already-shipped binaries working across a rotation, your
Railbase build pins a set of trusted vendor keys (the active key plus
retired ones), and a license verifies if any pinned key accepts its signature.
New licenses are signed by the current active key. A brand-new signing key is
added to the pinned set in the next Railbase release — the trust root is fixed
at build time, so it can't be swapped at runtime.
Managing your subscription
Buying, changing seats, switching billing cycle, and cancelling all happen on railbase.app — from your account here (the in-app admin marketplace links you out to it; there is no in-admin billing portal). Details in Managing billing.