Skip to content

Seat policies

Every offline product has a seat policy, set by the merchant per product in the Waveform admin: lease (the default, and every existing product) or deactivate. It governs how a seat is freed, not how it is granted. The token format, the verifier, and every endpoint are identical under both. Only the number signed into the token’s exp and the set of budgeted release paths differ.

lease (default) deactivate
Token lease (exp) 30 days by default, renewed on every check-in The product’s longLeaseDays: 90 to 3650, default 365, renewed on every check-in
How a seat is freed The lease lapses, or the user or support releases it The user deactivates: in the app on that machine (free, unlimited), or remotely (budgeted, default 2 per rolling 365 days; 409 support-limit once spent)
When revocation reaches an offline machine Within lease plus grace (up to 44 days on defaults) Only when the machine reconnects; otherwise not until longLeaseDays plus grace after its last check-in
Your client The code in Offline integration, as written The same code with the offline deadline disabled

What the client does differently under deactivate

Section titled “What the client does differently under deactivate”

Nothing else changes.

  • Do not impose your own offline deadline. The server’s long lease governs how long a machine can run without contact. Pass maxOfflineDays: 0 to evaluateOffline (zero disables the deadline; any positive value is applied) and keep everything else, including signature, product and machine binding, and the sealed clock, exactly as before. With the JUCE kit, call cfg.useDeactivateSeatPolicy() after your other Config fields: it disables the kit’s own deadline, keeps the online re-check on, and switches the Manage devices copy.
  • Keep acting on definitive verdicts. A heartbeat answered revoked, expired, not-activated, or blocked still clears the stored token immediately. Under deactivate that reconnect is the only way a revocation reaches a machine, so do not skip heartbeats because the lease is long.
  • Give the user a way to deactivate from inside the app. POST /v1/deactivate { key, fingerprint } releases this machine (free). { key, deviceId } releases another (budgeted under deactivate). Confirm before either, wipe the local token and key after a successful self-release, and on 409 support-limit show the server’s used, limit, and windowDays rather than a bare error code.

A client built for deactivate that receives a 30-day token behaves exactly as under lease: the token’s own exp binds first. A client built with the default deadline that receives a long token still asks for a check-in when its deadline passes, and locks only if it cannot get one. The client can only be stricter than the server, never looser, so the server never needs to know how a build was configured.

The token’s hb claim (the product’s heartbeat interval) governs how often the client attempts a check-in, and attempts are how a revocation eventually lands. Raising it slows revocation without adding a single day of offline tolerance. The lease is the only offline dial. Under deactivate, leave the cadence alone.

The bounded loss this model accepts: up to maxDevices + 2 + extraActivations machines per key, each for at most longLeaseDays plus grace from its last check-in. The distinct-machine budget (the number of different machines a key may ever activate in a rolling window) is enforced either way and answers 409 activation-limit; freeing a seat does not clear it.