LimeLM
wyBuild
Support forum
wyDay blog
wyDay Home

Using TurboFloat with NodeJS or Electron

NodeJS and ElectronThis article will give step-by-step instructions on how to add floating-licensing to your NodeJS or Electron app. There's also a full example app that you can download and play with without needing to add licensing to your app.

By the end of this article you'll have working floating-licensing integrated with your application, and thus the ability to sell individual copies of your software.

This article shows you how to add floating-licensing to your NodeJS or Electron app using TurboFloat. To add hardware-locked licensing to your NodeJS or Electron app see the "Using TurboActivate with NodeJS or Electron" article.

Sign up or login and download the native TurboFloat library

Before you can do anything, you need to login to your LimeLM account (or sign up). Then download TurboFloat for Windows, macOS, Linux, or BSD. It contains the native library and source code examples needed to integrate floating-licensing in your NodeJS app:

Step-by-step walkthrough: adding floating licenseing to your NodeJS app

We're going to walk you through adding floating licensing to your app by using our NodeJS example application. If you haven't downloaded it already you can get the example app inside the TurboFloat Library package.

Step 1. Signup for LimeLM, download TurboFloat

If you haven't already signed up for LimeLM then sign up now. All plans have a 30-day free trial. Or, if you're just putting your toes in the water, there's even a free plan that has no time limit and doesn't require a credit card.

After you've created your account, download TurboFloat and extract it anywhere. After you extract it, inside the extracted folder you'll find 2 folders: the "API" directory and the "bin-*" directory (for example, bin-windows, for the Windows TurboFloat package) that contains the TurboFloat library and the helper "systf" app required by NodeJS.

Step 2. Install NodeJS

This article and example app requires that you at least have NodeJS LTS installed. You can get the NodeJS LTS release over at nodejs.org.

Step 3. Create a new product in LimeLM

If you haven't already created a new product in LimeLM, do it now. You can change any value later, so don't worry about making a mistake.

Adding your first product to LimeLM

Step 4. Download TurboActivate.dat

Go to your version page in LimeLM. Download the "TurboActivate.dat" file, and make a note of the Version GUID you see on your product version page (you'll be using it in the next step).

TurboActivate.dat and Version GUID

You'll be copying this TurboActivate.dat files in the next step to sit alongside the native "systa.exe" or "systa" files in each of the platforms you want to support.

Side-note about TurboActivate.dat file: this file is an "information file" about your product version, and gives TurboFloat enough information to verify product keys and cryptographically signed activation data locally. It's a read-only file (license data will never be written to it).

Step 5. Native library and helper app (systf)

TurboFloat uses native system calls to generate our proprietary "fingerprint" of the computer your customer is activating your software on. And because JavaScript / NodeJS doesn't currently have the ability to directly call native libraries you need to use the "helper" app that we provide ("systf.exe" on Windows, "systf" on macOS, Linux, FreeBSD) alongside the native TurboFloat library. See step 1, above for where to find the TurboFloat library and helper "systf" app.

If you're playing around with our example NodeJS app, the layout of the JavaScript files and the native files is like so:


main.js
turbofloat.js
native
...Windows
......systf.exe
......TurboFloat.dll
......TurboActivate.dat
...Linux
......systf
......libTurboFloat.so
......TurboActivate.dat
...FreeBSD
......systf
......libTurboFloat.so
......TurboActivate.dat
...Mac
......systf
......libTurboFloat.dylib
......TurboActivate.dat

The systf file needs to be executable (meaning have the executable permission). To do this, navigate to the directory in a terminal and give the systf file the correct permissions. For example, on macOS this would look something like this:

cd /path/to/your/app/native/Mac
chmod u+x systf

Then, to test that the permissions were granted correctly, run the systf ("./systf") and you should see a brief description with the version numbers:

systf (NodeJS helper process) version: 4.1.2.0
TurboFloat library version: 4.1.2.0

Always use the latest version of TurboFloat and
the latest version of this helper executable (systf).

If you didn't set the executable permissions correctly then you will get an error something like this:

-bash: ./systf: Permission denied

As you can see there's the "main.js" file (which is the example app), there's the "turbofloat.js" (which calls the native apps / libraries) and there's the actual native apps organized in the "native" directory which is relative to your app entry-point (which, in the example, is the "main.js" file).

Step 6. Install the TurboFloat Server

Next, you need to start a TurboFloat Server instance. We recommend spinning-up a TurboFloat Server instance on our infrastructure using LicenseChest. Alternatively, your customers can host the TurboFloat Server on their own infrastructure by activating and installing the TurboFloat Server locally.

We recommend using our hosted TurboFloat Server option because it's fast, stable, up-to-date, and incredibly easy to deploy.

Step 7. Request "license lease"

In the example NodeJS application the "main.js" file is the entry-point for the simple app. And because it's the entry point of the application it's where we'll be requesting the "license lease" from the TurboFloat Server.

First, create the TurboFloat instance in your main form:

var TurboFloat = require('./turbofloat.js');

//TODO: goto the version page at LimeLM and paste this GUID here
var tf = new TurboFloat("Paste GUID Here", TFLeaseChange);

Next, we'll actually request the lease from the TurboFloat Server:

tf.RequestLease()
    .then((retObj) => {

        // if activated, begin the app immediately
        if (retObj === TF_OK)
        {
            BeginYourApp();
            return null;
        }
        else if (retObj === TF_E_SERVER)
        {
            /*
               We're just hardcoding the localhost for testing
               purposes in real life you'd want to let the user
               enter the host address / port you can either do
               this in your app, or in your installer.

               If your customer is using LicenseChest hosted TFS
               instances, you might want to make a simple prompt
               for the LicenseChest TFS UUID. 

               More information: https://wyday.com/licensechest/help/create-tfs-instance/

               Then call TF_SaveServer(...) using
               "floating.wyday.com/?server=[UUID]" as the host address.

               For example:

               return tf.SaveServer("floating.wyday.com/?server=00000000-0000-0000-0000-000000000000", 443, TF_USER | TF_REQUEST_OVER_HTTPS);
            */
            return tf.SaveServer("127.0.0.1", 13, TF_USER)
        }
        else if (retObj === TF_E_INET
            || retObj === TF_E_INET_TIMEOUT
            || retObj === TF_E_WRONG_SERVER_PRODUCT
            || retObj === TF_E_SERVER_UUID_MISMATCH
            || retObj === TF_E_USERNAME_NOT_ALLOWED)
        {
            /*
              Give the user an option to try another server if they
              couldn't connect to the first one, or if the first one
              is for a different product.
            */

            PromptRetryServer();
            return null;
        }
        else
        {
            // If the lease wasn't acquired then output an error and exit.
            // You can view all the error codes in TurboFloat.h near the bottom of the file.
            HardFailure("Failed to get the floating license lease (TF_RequestLease() returned 0x%x). Look in TurboFloat.h for a human readable explanation of the error: " + retObj, retObj);
        }
    })
    .then((retObj) => { // result from SaveServer()

        // if null, fall through
        if (retObj === null)
            return null;

        if (retObj === TF_OK)
            return tf.RequestLease();
        else
            HardFailure("Failed to save the sever details (TF_SaveServer() returned 0x%x). Look in TurboFloat.h for a human readable explanation of the error: " + retObj, retObj);
    })
    .then((retObj) => { // result from RequestLease()

        // if null, fall through
        if (retObj === null)
            return null;

        if (retObj === TF_OK)
        {
            BeginYourApp();
            return null;
        }
        else if (retObj === TF_E_INET
            || retObj === TF_E_INET_TIMEOUT
            || retObj === TF_E_WRONG_SERVER_PRODUCT
            || retObj === TF_E_SERVER_UUID_MISMATCH
            || retObj === TF_E_USERNAME_NOT_ALLOWED)
        {
            /*
              Give the user an option to try another server if they
              couldn't connect to the first one, or if the first one
              is for a different product.
            */

            PromptRetryServer();
            return null;
        }
        else
        {
            // If the lease wasn't acquired then output an error and exit.
            // You can view all the error codes in TurboFloat.h near the bottom of the file.
            HardFailure("Failed to get the floating license lease (TF_RequestLease() returned 0x%x). Look in TurboFloat.h for a human readable explanation of the error: " + retObj, retObj);
        }
    })
    .catch((retObj) => { // an error somewhere in the processing

        HardFailure("Something failed! " + retObj, 1);
    });

Also notice how we're handling the case where the end-user hasn't already specified the floating license server (the TF_E_SERVER error). Instead of just hardcoding the server and port (like we show in the example), you should prompt the user to enter the details. Then you can call the TF_SaveServer() function to save the details.

You should also give your customers the ability to easily choose using TFS instances hosted on our infrastructure using LicenseChest by simply prompting them to enter the server UUID (a small string that uniquely identifies their TFS instance). Then it's simply a matter of calling TF_SaveServer() and appending the user-entered UUID to the hosted TFS address like so:

return tf.SaveServer("floating.wyday.com/?server=UUID_HERE", 443, TF_USER | TF_REQUEST_OVER_HTTPS);

Step 8. Handle the "LeaseChange" event

The native TurboFloat library handles all the details about renewing leases, retrying, etc. All you have to do is handle the cases where TurboFloat talks to your app and tells it something has changed (license lease failing to be renewed or new license field data). To do this you need to handle the LeaseChange event:

/*
 This callback function is called by the TurboFloat object
 when the lease is dropped, couldn't be renewed, or
 if the custom license fields have changed.
 */
function TFLeaseChange(leaseStatus)
{
    //TODO: implement
    switch (leaseStatus)
    {
        case TF_CB_FEATURES_CHANGED:

            console.log("TODO: reload any custom license fields using TF_GetFeatureValue().");
            break;

        case TF_CB_LEASE_DROPPED_SLEEP:
            console.log("The lease has been dropped due to computer sleeping.");

            /* TODO: prompt the user to re-connected -- hide this prompt upon receipt of         TF_CB_LEASE_REGAINED */
            break;

        case TF_CB_LEASE_REGAINED:
            console.log("The lease has been successfully regained after a sleep.");

            /* TODO: hide a prompt to the user if shown during TF_CB_LEASE_DROPPED_SLEEP */
            break;

        case TF_CB_EXPIRED:
        case TF_CB_EXPIRED_INET:
        case TF_CB_LEASE_DROPPED:
        default:

            //TODO: disable any features in your app.
            console.log("The lease expired or has been dropped.");


            /*
            After disabling the user's access to your app, we recommend
            you do 3 things:

            1. Give the user the option to save their progress.

            2. Give the user the option to save their progress to a
            separate file (i.e. "Save as" in case the work they were
            doing was incomplete).

            3. Give the user the option to retry. For example a
            "Try again" button that calls TF_RequestLease(tfHandle).

            */

            // Don't just exit the app without warning or without giving the user options.
            // For example, this behavior right here is a terrible example to be setting:
            console.log("The app is exiting. In your app you shouldn't just abruptly exit! That's bad. See the comments in the example app.");
            process.exit(1);

            break;
    }
}

Step 9. Dropping the lease when your app closes

After you've successfully requested a lease from the TurboFloat Server, the TurboFloat library integrated in your app takes care of renewing the leases automatically and silently. You'll only ever get a notification of something going wrong in the handler for the LeaseChange event that we covered in Step 8.

When your app is closing you should "drop" the lease using the DropLease() method:

tf.HasLease()
    .then((retObj) => {

        // if activated, begin the app immediately
        if (retObj === TF_OK)
        {
            tf.DropLease().then((retObj) => {
                //TODO: exit the app
            });

            return null;
        }
    });

What this does is tell the TurboFloat Server that you're through using the lease in this instance of your app, and another instance of your app on another computer or another session can now use that "free slot".

If you can't drop the lease (because your app can't connect to the internet, or for any other reason), and you choose to exit your app anyway, then the "lease" on the TurboFloat Server will be a "zombie". The lease will expire eventually on the TurboFloat Server, and thus the free slot will open back up.

Step 10: Testing the lease change event

Testing requesting leases, dropping them, and everything else in the TurboFloat Library is intuitive: just call the function and it does the thing you want it to do. Testing the lease change event is, however, slightly less intuitive. Here's how you can test the lease change event:

  1. If your app is open and has a lease, close your app and make sure it drops the license lease from the TurboFloat Server.

  2. Stop the TurboFloat Server instance.

  3. Open the TurboFloat Server config file, and edit <lease .../> element and set it to "30". This will set the lease length to 30 seconds.

  4. Save the changes you made to the configuration file.

  5. Start your TurboFloat Server instance again.

  6. Start your app again, and make sure it successfully gets a license lease from the TurboFloat Server.

  7. Now, stop the TurboFloat Server instance, but leave your app running.

  8. Within the next 30 seconds the lease change event will be called because the TurboFloat Library was not able to renew the license lease automatically.

You should also test the "TF_CB_LEASE_DROPPED_SLEEP" and "TF_CB_LEASE_REGAINED" callback types. These callbacks are raised when your app has a lease and the device your app is running on goes to sleep and then eventually wakes up. To test these scenarios:

  1. Run a TurboFloat Server on a separate computer than the one you're running the test from (or spin up a TurboFloat Server instance on our infrastructure).

  2. Start your app and acquire a lease from that TFS instance.

  3. Put the computer which your app is running on to sleep.

  4. After the computer has gone to sleep, wake it up.

  5. Your app should handle both of those events seamlessly. Namely, prompting the user to reconnect when the computer goes to sleep, and if the lease is regained automatically, hiding that prompt from the user.