Choosing a Licensing Model

When first starting out, choosing a licensing model can be a bit daunting. There are so many choices, how do you choose which one is best for your business? A quick Google search is likely to confuse you more than educate you, thanks to a plethora of enterprise jargon and buzzwords. Today, we'll take an in-depth look at licensing from a business perspective so that you can make an educated decision on what type of model is best for you and your business.

Licensing terminology

Below is a non-exhaustive reference for some of the terminology used in this guide and throughout the licensing space. Feel free to refer back to this throughout the guide, but we'll try to keep things as straight forward as possible, regardless. If you're a developer implementing Keygen, this list will also provide you with a good overview of all of our resources.

  1. Users are a particular resource within Keygen that represents a user of your software, allowing them to authenticate with Keygen's API and manage the licenses and machines which are associated with their user resource.
  2. Licenses are a particular resource within Keygen that, in their simplest form, represent a permission i.e. you give the license owner permission to do x within your software, whether x is to use your software as whole, or only a particular feature your software offers.
  3. Policies are resources within Keygen that define how individual licenses should behave, and what rules those licenses must follow. A license is always an implementation of a policy i.e. you cannot create a license without first having a policy that defines its behavior.
  4. Machines are a device or node that is associated with a license within Keygen. When creating a machine and associating it with a license, you must specify a unique string called a fingerprint which the machine is identified by.
  5. A fingerprint is a unique string that identifies a particular machine. There are many ways to create a fingerprint, but most use a combination of device-specific components such as MAC address, HDD serial number, OS, etc., but it could also simply be a domain name (e.g. when licensing a WordPress plugin), or even a file you store locally on their machine containing the unique string.

Licensing goals

Before we go any further, we should take a step back and think about why we want to implement licensing and what we want to accomplish through licensing. Only then can we begin to decide what licensing model will best accomplish those goals.

  • I want to limit access of my software to only licensed users so that I can make sure all of my users are paying customers.
  • I want to offer timed licenses to each of my users so that they must renew their license at the end of the expiry.
  • I want to offer a limited trial version of my software to users so that users can try out my software before purchasing.
  • I want to limit access of my software to a single machine so that customers have to purchase a new license for each additional machine.
  • I want to limit access of my software to only x number of machines so that a license cannot be used on unlimited machines.
  • I want to offer a limited supply of "special" licenses so that I can run a promotional campaign with my audience.
  • I want to license individual features of my software to users so that I can upsell premium features.
  • I want to require periodic "check-ins" for licenses to remain valid so that I can offer offline support but still require a periodic internet connection.

Licensing models

You may create resources directly using Keygen's API, or you can use your account dashboard to do so instead. The policies below use default attributes where possible, so be sure to review the Policy resource's default attributes.

I want to limit access of my software to only licensed users

Keeping things simple—I like that. Below is a policy which implements the bare-minimum to implement a licensing system for your software.

{
  data: {
    type: "policies",
    attributes: {
      name: "Pro License"
    }
  }
}

Licenses that implement this policy will never expire and will be valid on an unlimited number of machines. The only way for a license implementing this policy to fail validation is for it to be suspended.

I want to offer timed licenses to each of my users

To offer licenses which have an expiration date, we'll need to create a policy similar to the above one, but one which also has a value for the duration attribute.

{
  data: {
    type: "policies",
    attributes: {
      name: "Pro License",
      duration: 1209600 // 2 weeks
    }
  }
}

The duration attribute is how long, in seconds, the license is valid for from the license's creation timestamp, e.g. a license is created at epoch 1503520001, so its expiration will be 1503520001 + 1209600, which is 09/06/2017 at 8:26pm UTC.

When a license's expiration gets close, we will fire off an license.expiring-soon webhook event so that you can notify your customer and even automate renewing the license server-side.

I want to offer a limited trial version of my software to unlicensed users

This is similar to the above implementation, but instead of only having a single policy, we'll have 2. The first one will be our "full" policy, while the second will be our "trial" policy.

// Full license policy
{
  data: {
    type: "policies",
    attributes: {
      name: "Full License",
      duration: 31557600 // 1 year
    }
  }
}
// Trial license policy
{
  data: {
    type: "policies",
    attributes: {
      name: "Trial License",
      duration: 1209600 // 2 weeks
    }
  }
}

The only difference is the duration of each policy, i.e. the "trial" version will expire in 2 weeks, while the "full" version will be valid for 1 year.

I want to limit access of my software to a single machine

To limit access to a single machine (also called node-locked licensing), we'll take advantage of a few other policy attributes. Node-locked is a fancy way to say "I want a license to only be valid on a single machine" i.e. you "lock" the license to a particular machine resource.

{
  data: {
    type: "policies",
    attributes: {
      name: "Node-Locked License",
      maxMachines: 1, // Licenses should only be associated with a single machine
      floating: false,
      strict: true // Invalidate licenses which do not meet machine requirements
    }
  }
}

In order for a license to pass validation, it must be associated with a single machine—no more, no less.

I want to limit access of my software to only x number of machines

Similar to implementing "node-locked" licenses, we can also implement "floating" licenses. Floating is a term used to describe the opposite of "node-locked" i.e. a "floating" license is valid across multiple machines, but only up to a maximum machine count.

{
  data: {
    type: "policies",
    attributes: {
      name: "Floating License",
      maxMachines: 5, // Licenses can be associated to a maximum of 5 machines
      floating: true,
      strict: true // Invalidate licenses which do not meet machine requirements
    }
  }
}

In order for a license to pass validation, it must be associated with at least 1 machine, but no more than 5 machines total.

I want to offer a limited supply of "special" licenses

In order to implement a "limited supply" of licenses, we'll need to create a pooled policy. Before we start, let's go over a little bit of terminology:

  1. A pooled policy is a term used within Keygen to describe a policy which has a limited amount of pre-determined keys available for use. Once a policy's pool is depleted, you must add additional keys in order to continue to create new licenses.
  2. Keys are a special resource within Keygen that represent an unused key within a particular pooled policy's key "pool". Keys are not valid "license keys" until a license resource is created from the pooled policy. When a license is created which implements a pooled policy, a key is taken from the top of the pool and is used for the new license.

Below is a representation of a pooled policy,

{
  data: {
    type: "policies",
    attributes: {
      name: "Limited Edition License",
      usePool: true // Pull keys from the policy's pool when creating licenses
    }
  }
}

Now, if we tried to create a license which implements this policy right away, we would receive an error saying that our policy's pool is empty. So before we can allow license creation, we need to add keys to our policy's pool. Once a policy's pool is depleted, you must add additional keys in order to continue to create new licenses.

I want to license individual features of my software to users

This particular model is called "feature licensing", and it's one of the models that Keygen was originally built around. For reference, we even have an example application which implements feature licensing.

// Feature "X"
{
  data: {
    type: "policies",
    attributes: {
      name: "Feature X"
    }
  }
}
// Feature "Y"
{
  data: {
    type: "policies",
    attributes: {
      name: "Feature Y"
    }
  }
}
// Feature "Z"
{
  data: {
    type: "policies",
    attributes: {
      name: "Feature Z"
    }
  }
}

You can then store each policy's ID within your product to determine if the current user is allowed to use each particular feature of your product. You can find a policy's ID by viewing the policy from your account dashboard.

For example,

// Policies representing our product's features. You can get this information
// from your dashboard: https://app.keygen.sh/policies
const KEYGEN_FEATURE_X = "aac4905c-84d0-41a3-af6e-1026e28c04d3"
const KEYGEN_FEATURE_Y = "dd025847-42fb-49b0-b898-80c34d7734b4"
const KEYGEN_FEATURE_Z = "b6a5ae11-ec60-4ecd-9902-ae48a1077623"
const ALLOWED_FEATURES = {}

const licenses = /* fetch user's licenses */

for (let license of licenses) {
  const { id, relationships: { policy: { data: policy } } } = license

  switch (policy.id) {
    case KEYGEN_FEATURE_X:
    case KEYGEN_FEATURE_Y:
    case KEYGEN_FEATURE_Z:
      // Validate the current license
      const validation = await fetch(`https://api.keygen.sh/v1/accounts/{ACCOUNT}/licenses/${id}/actions/validate`, {
        headers: { "Accept": "application/vnd.api+json", "Authorization": "Bearer {TOKEN}" },
      }).then(r => r.json())

      ALLOWED_FEATURES[policy.id] = validation.valid

      break
    default:
      // This version of our product doesn't use this policy so it's okay to skip
      // it (we can implement new features without breaking old versions)
      break
  }
}

// … later in the product

if (ALLOWED_FEATURES[KEYGEN_FEATURE_X]) {
  // … do something
}

I want to require periodic "check-in" for licenses to remain valid

You can require licenses to periodically "check-in" using the check-in action. Below is a policy which requires licenses to check in every 2 weeks in order to remain valid. Check-in policies are great for allowing offline usage of your product, but still requiring a periodic "check-in" to validate licenses e.g. to ensure they aren't expired or suspended.

{
  data: {
    type: "policies",
    attributes: {
      name: "Check-In License",
      requireCheckIn: true, // Require periodic check-ins
      checkInInterval: "week", // Can be: "day", "week", "month" or "year"
      checkInIntervalCount: 2 // How many intervals e.g. every 2 "weeks"
    }
  }
}

When a license's check-in gets close, we will fire off a license.check-in-required-soon webhook event so that you can notify your customer to let them know that they need to connect to the internet soon and validate their license.


Don't see the licensing model you're looking for? Reach out and let us know, and we'll get it added here plus answer any questions you have about implementation.