This article will give step-by-step instructions on how to add software licensing (specifically, hardware-locked or node-locked licensing) to your Go 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 licensing integrated with your application, and thus the ability to sell individual copies of your software.
Before you can do anything, you need to login to your LimeLM account (or sign up). Then download TurboActivate for Windows, macOS (Mac OS X), Linux, or BSD. It contains the native library and source code examples needed to integrate hardware-locked licensing in your Go app:
Now we're going to walk you through each step you need to take in adding licensing, online activation, and timed-trials to your Go app. There are lots of ways you can add licensing to your app, but there are 2 popular styles:
Separate versions of your app. This style of licensing is where you build 2 versions of your product: a trial version and a full version.
Hybrid version of your app. This style of licensing is where your product is the trial version or the full version depending on whether the user is activated.
The first method (the separate versions method) is possible with TurboActivate, but we're not going to talk about it here because it's not very user friendly and it requires more work on your end. Instead we'll talk about making your app a hybrid Full/Trial app. That is, your users will be able to use your app in "trial mode" until either the trial expires or they purchase a product key and use it to activate your app.
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 TurboActivate and extract it anywhere. After you extract it, inside the extracted folder you'll find 2 folders: the "API
" directory and the the "bin-*
" directory (for example, bin-windows
, for the Windows TurboActivate package).
This article and example app makes use of "CGo" (a way to use C-libraries in Go programs). To use CGo on Windows, you need to install the latest version of Mingw-w64 (the quickest way to download is on their sourceforge page). After downloading and launching the installer you'll be presented with a screen like this:
We recommend using the latest version of gcc/g++ (in the screenshot it's 7.2.0, but newer versions are released frequently). The only thing you must pay attention to is the "Architecture" value. If you downloaded and are using the Go package for "windows-amd64", then you must choose the "x86_64" architecture for MinGW. Similarly, if you are using the Go package built for "windows-386" then you need to use the "x86" architecture.
After clicking next, the installer will tell you where it will download and install everything. Copy this path because you will be using it after the installation finishes. It will generally be in this form (although it will differ depending on the options you've chosen):
C:\Program Files\mingw-w64\x86_64-7.2.0-win32-seh-rt_v5-rev0\mingw64
Now, you'll need to configure the "PATH" variable in Windows to reference the location of gcc
so that Go can build. Open the "Evironmental Variables" dialog by running this command from the "Run" dialog (Windows Key + R on your keyboard) or a cmd
window:
rundll32 sysdm.cpl,EditEnvironmentVariables
This will open up the "Environment Variables" dialog, and you can select the "Path" variable for either your "User variables" or "System variables", click the "Edit..." button:
Add the full path to the "bin" folder to where your installed MinGW-64. For example:
C:\Program Files\mingw-w64\x86_64-7.2.0-win32-seh-rt_v5-rev0\mingw64\bin
Click "OK" on that "Edit" dialog, and then click "OK" on the main "Environment Variables" dialog. Now restart your computer. Windows needs to be restarted in order to use the new Path variables. If you skip this step then compiling your app will not work.
After restarting your computer you can check to make sure you installed everything correctly by opening a "cmd.exe" window and typing gcc --version
. You should see something like this:
gcc (x86_64-win32-seh-rev0, Built by MinGW-W64 project) 7.2.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
If, instead, you see this:
'gcc' is not recognized as an internal or external command,
operable program or batch file.
This could be caused by one or all of the following:
You either didn't correctly install MinGW
You didn't set the Path to the "bin" subdirectory for where you installed MinGW
You didn't restart your computer after setting the path
gcc
There are lots of examples online for your particular version of Linux, FreeBSD, or macOS, and how to install the latest version of gcc
(which can be used by Go). But we'll cover a few of the most common distros and versions.
Run the following commands from terminal to install the latest available gcc
and neccessary libraries and tools:
sudo apt-get update
sudo apt-get install build-essential
Run the following commands from terminal to install the latest available gcc
and neccessary libraries and tools:
sudo yum install gcc
Or, depending on your configuration, ...
sudo yum install gcc.x86_64
You don't have to do anything. Clang is installed by default.
Either install the full Xcode app or, if you don't want to install such a large development evironment, you can install just the tools required by Go. Open a terminal and run the following command:
xcode-select --install
This will open a dialog that will prompt you if you want to install "xcode-select". Click "Install" and continue through the process until it completes successfully.
To use TurboActivate in your Go app, you'll need to add the "turboactivate*.go" files to your GOPATH
. You can do this 1 of 2 ways. The easiest method is to run this command from commandline or terminal:
go get -d -u golang.wyday.com/turboactivate
Or you can copy over the files in the downloaded TurboActivate package for Windows, macOS, Linux, or FreeBSD in the "API/Go" directory and copy the "golang.wyday.com" subdirectory (and all the files and directories it contains) over to your GOPATH/src/
directory (wherever that may be on your system).
golang.wyday.com/turboactivate
packageBefore you can use the golang.wyday.com/turboactivate
package in your app or package you need to build it. This is fairly straight forward:
Ensure gcc
or clang
is installed on your system (explained above for Windows and for Unix)
Ensure the latest golang.wyday.com/turboactivate
package is in your GOPATH/src
directory (explained above).
Open a command prompt or terminal and "cd
" into the directory where you extracted the TurboActivate package. Copy the TurboActivate.h from the API/C
directory into the GOPATH/src/golang.wyday.com/turboactivate
directory. And copy the TurboActivate library to the GOPATH/src/golang.wyday.com
directory. You can do it like this on Windows:
xcopy /s/y ".\API\C\TurboActivate.h" "%GOPATH%\src\golang.wyday.com\turboactivate\TurboActivate.h"
xcopy /s/y ".\bin-windows\x64\TurboActivate.dll" "%GOPATH%\src\golang.wyday.com\TurboActivate.dll"
Or on macOS you can do it like this:
cp -f "./API/C/TurboActivate.h" "$GOPATH/src/golang.wyday.com/turboactivate/TurboActivate.h"
cp -f "./bin-macos/libTurboActivate.dylib" "$GOPATH/src/golang.wyday.com/libTurboActivate.dylib"
Or on Linux and other Unix flavors you can do it like this:
cp -f "./API/C/TurboActivate.h" "$GOPATH/src/golang.wyday.com/turboactivate/TurboActivate.h"
cp -f "./bin-linux/x64/libTurboActivate.so" "$GOPATH/src/golang.wyday.com/libTurboActivate.so"
Now you can build the package for use in your app. Simply "cd
" into the GOPATH/src/golang.wyday.com/turboactivate
directory and run the following command:
go install
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.
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).
Copy the TurboActivate.dat file you just downloaded to the same folder as your target binary. When you distribute your app to your customers you'll be including this TurboActivate.dat alongside your binary.
Side-note about TurboActivate.dat file: this file is an "information file" about your product version, and gives TurboActivate 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).
Now, inside your app, you need to create a new TurboActivate object with the Version GUID found on the same page you downloaded the TurboActivate.dat from. In the example app we define the "ta
" variable in the "main" package, and then create the TurboActivate object in the main()
function:
import "golang.wyday.com/turboactivate"
func main() {
//TODO: goto the version page at LimeLM and paste this GUID here
ta, err := turboactivate.NewTurboActivate("Paste GUID Here", "")
if err != nil {
// failed to construct the TurboActivate object
// bail out now.
panic(err)
}
// ...
Replace the "Paste GUID Here" string with the Version GUID string your copied from your version page.
There are many ways you can use TurboActivate to add licensing to your application. For this example we're going to keep it simple.
Add a global "bool" "isGenuine
" variable to save whether the customer is activated and genuine. And then it's simply a matter of making a call to IsGenuineEx()
to see if the user is activated and to re-verify the activation with the LimeLM servers every 90 days, with a 14 day grace period:
var isGenuine = false
// Don't use 0 for either of these values.
// We recommend 90, 14. But if you want to lower the values
// we don't recommend going below 7 days for each value.
// Anything lower and you're just punishing legit users.
const daysBetweenChecks = 90
const gracePeriodDays = 14
func main() {
//TODO: goto the version page at LimeLM and paste this GUID here
ta, err := turboactivate.NewTurboActivate("Paste GUID Here", "")
if err != nil {
// failed to construct the TurboActivate object
// bail out now.
panic(err)
}
// Check if we're activated, and every 90 days verify it with the activation servers
// In this example we won't show an error if the activation was done offline
// (see the 3rd parameter of the IsGenuineEx() function)
isGr, err := ta.IsGenuineEx(daysBetweenChecks, gracePeriodDays, true, false)
if err != nil {
panic(err)
}
if isGr == turboactivate.IGRGenuine ||
isGr == turboactivate.IGRGenuineFeaturesChanged ||
// an internet error means the user is activated but
// TurboActivate failed to contact the LimeLM servers
isGr == turboactivate.IGRInternetError {
isGenuine = true
}
if !isGenuine {
// If IsGenuineEx() is telling us we're not activated
// but the IsActivated() function is telling us that the activation
// data on the computer is valid (i.e. the crypto-signed-fingerprint matches the computer)
// then that means that the customer has passed the grace period and they must re-verify
// with the servers to continue to use your app.
//Note: DO NOT allow the customer to just continue to use your app indefinitely with absolutely
// no reverification with the servers. If you want to do that then don't use IsGenuine() or
// IsGenuineEx() at all -- just use IsActivated().
isAct, err := ta.IsActivated()
if err != nil {
panic(err)
}
if isAct {
// We're treating the customer as is if they aren't activated, so they can't use your app.
// However, we show them a prompt where they can reverify with the servers immediately.
//TODO: prompt the user however you want
var userWantsToRetry = true
for userWantsToRetry {
// Now we're using TA_IsGenuine() to retry immediately. Note that we're not using
// TA_IsGenuineEx() because TA_IsGenuineEx() waits 5 hours after an internet failure
// before retrying to contact the servers. TA_IsGenuine() retries immediately.
isGr, err := ta.IsGenuine()
if err != nil {
panic(err)
}
if isGr == turboactivate.IGRGenuine ||
isGr == turboactivate.IGRGenuineFeaturesChanged {
fmt.Println("Successfully reverified with the servers! You can now continue to use the app!")
isGenuine = true
// no longer need to retry
userWantsToRetry = false
} else {
fmt.Println("Failed to reverify with the servers.")
fmt.Println("Make sure you're connected to the internet and that you're not blocking access to the activation servers.")
//TODO: prompt the user again
userWantsToRetry = false
}
}
}
}
// ...
}
This is a complete example showing how to check if the customer is genuinely activated, and how to handle the error cases. While it's longer than "toy" licensing solutions, it's built for the real world.
The code does the following:
It creates the new TurboActivate instance with your Version GUID.
Checks if the customer is activated and re-verifies with the servers every 90 days (with a 14-day grace period).
And if IsGenuineEx()
tells you the customer is not genuine then you can use IsActivated()
to determine if they're "not genuine" because they were never activated (or have been deactivated remotely by your organization) or if it's because the customer has gone more than daysBetweenChecks + gracePeriodLength
days since re-verifying with the activation servers.
And if it's a case where the customer must re-verify with the activation servers, then prompt the user in a "for
loop" that lets them do that.
If the user has never activated, or if they've since deactivated, then you'll need to prompt the user to enter their product key. You can do this a couple of ways:
If you're just targeting Windows you can use our pre-built the TurboActivate Wizard to prompt the customer to enter their product key.
Or, prompt the user directly in your app and then save the product key (using the CheckAndSavePKey()
function) and activate your app (using the Activate()
function)
Here's an example of how you might prompt the user for their product key and activate the product key on the computer:
var userEnteredPkey string
//TODO: prompt the user for the product key
// validate and save the product key if it's valid
validKey, err := ta.CheckAndSavePKey(userEnteredPkey, turboactivate.TAUser)
if err != nil {
// a critical error happened -- likely
// a failure to save the key
panic(err)
}
if !validKey {
fmt.Println("The product key entered is not valid.")
} else {
err = ta.Activate("")
if err != nil {
fmt.Println("Failed to activate: " + err.Error())
//TODO: ask the user for their product key again
}
}
TAUser
vs. TASystem
In the example above you can see we're using TAUser
in the CheckAndSavePKey
function. This tells TurboActivate to store the activation data in directories that the user has the ability to write to. If you were using TASystem
instead, this would tell TurboActivate to store the activation data to directories where all users on that machine would have the ability to read/write the activation data.
In both cases the activation data locks to the machine. The only difference is where the activation data is stored. We typically recommend using TASystem
, however the only downside to using that flag is that the first time (and only the first time) you call a function with the TASystem
flag your app needs "elevated" or "sudo" permission.
In other parts of this example you've seen references to a ShowTrial()
function. This is a function that you'll need to create to add trial functionality to your app. In this example we're going to use verified trials (because they are accurate, fast, and allows you to track conversion of customers).
The first step is to actually tell TurboActivate you'll be using verified trials. Create a trialFlags
at the top of your main form that will store this information:
var isGenuine = false
// Set the trial flags you want to use. Here we've selected that the
// trial data should be stored system-wide (TA_SYSTEM) and that we
// should use un-resetable verified trials (TA_VERIFIED_TRIAL).
var trialFlags = turboactivate.TASystem | turboactivate.TAVerifiedTrial
// Don't use 0 for either of these values.
// We recommend 90, 14. But if you want to lower the values
// we don't recommend going below 7 days for each value.
// Anything lower and you're just punishing legit users.
const daysBetweenChecks = 90
const gracePeriodDays = 14
func main() {
//TODO: goto the version page at LimeLM and paste this GUID here
ta, err := turboactivate.NewTurboActivate("Paste GUID Here", "")
// ...
Starting the verified trial and seeing how many days are remaining is as simple as this:
if !isGenuine {
// trial days is 0 by default
var trialDaysRemain uint32
// begin or re-verify the trial
trialNotExpired, err := ta.UseTrial(trialFlags, "")
if err != nil {
panic(err)
}
if trialNotExpired {
// get the number of remaining trial days
trialDaysRemain, err = ta.TrialDaysRemaining(trialFlags)
if err != nil {
panic(err)
}
}
if trialDaysRemain == 0 {
// no more trial days, just exit the app
fmt.Println("There are no more trial days. Exiting the app immediately.")
os.Exit(1)
} else {
fmt.Println("Your trial expires in " + strconv.FormatUint(uint64(trialDaysRemain), 10) + " days.")
}
}