Use Keygen to license and distribute self-updating Go programs

Securely license and distribute Go applications with a single API.


Free during development, no upfront commitment

5 stars
Demo of the Keygen admin dashboard
keygen.Account = "1fddcec8-8dd3-4d8d-9b16-215cac0f9b52"
keygen.Product = "1f086ec9-a943-46ea-9da4-e62c2180c2f4"
keygen.LicenseKey = "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3"
 
opts := keygen.UpgradeOptions{CurrentVersion: "1.0.0", Channel: "stable", PublicKey: "5ec69b78d4b5d4b624699cef5faf3347dc4b06bb807ed4a2c6740129f1db7159"}
 
// Check for an upgrade
release, err := keygen.Upgrade(opts)
switch {
case err == keygen.ErrUpgradeNotAvailable:
fmt.Println("No upgrade available, already at the latest version!")
 
return nil
case err != nil:
return err
}
 
// Install the upgrade
if err := release.Install(); err != nil {
return err
}
 
fmt.Println("Upgrade complete! Please restart.")

From license activation to binary distribution, we can help.

  • checkUse our software distribution API to distribute self-updating Go apps to your customers. Keygen is a perfect fit for CLI tools and other on-prem Go software.
  • checkProtect your app with our flagship software licensing API. Add fine-grained license entitlements, restrict upgrades to certain versions, enforce activation limits, and more.
Get Started with Keygen

Bring your own programming language

Quickly implement Keygen's API in any programming language.

package main
 
import (
"github.com/denisbrodbeck/machineid"
"github.com/keygen-sh/keygen-go/v2"
)
 
func main() {
keygen.Account = "YOUR_KEYGEN_ACCOUNT_ID"
keygen.Product = "YOUR_KEYGEN_PRODUCT_ID"
keygen.LicenseKey = "key/..."
 
fingerprint, err := machineid.ProtectedID(keygen.Product)
if err != nil {
panic(err)
}
 
// Validate the license for the current fingerprint
license, err := keygen.Validate(fingerprint)
switch {
case err == keygen.ErrLicenseNotActivated:
// Activate the current fingerprint
machine, err := license.Activate(fingerprint)
switch {
case err == keygen.ErrMachineLimitExceeded:
panic("Machine limit has been exceeded!")
case err != nil:
panic("Machine activation failed!")
}
case err == keygen.ErrLicenseExpired:
panic("License is expired!")
case err != nil:
panic("License is invalid!")
}
 
fmt.Println("License is activated!")
}
const fetch = require('node-fetch')
 
const res = await fetch('https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key', {
method: 'POST',
headers: {
'Content-Type': 'application/vnd.api+json',
'Accept': 'application/vnd.api+json'
},
body: JSON.stringify({
meta: {
key: 'C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3'
}
})
})
 
const { meta } = await res.json()
 
if (meta.valid) {
// Do something
} else {
// Do something else
}
import SwiftyJSON
import Alamofire
 
Alamofire.request("https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key",
method: .post,
headers: [
"Content-Type": "application/vnd.api+json",
"Accept": "application/vnd.api+json"
],
parameters: [
"meta": [
"key": "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3"
]
],
encoding: JSONEncoding.default
).responseJSON { response in
let json = JSON(data: response.data!)
let valid = json["meta"]["valid"].bool
 
if valid {
// Do something
} else {
// Do something else
}
}
using RestSharp;
using System;
using System.Collections.Generic;
 
var client = new RestClient("https://api.keygen.sh/v1/accounts/demo");
var request = new RestRequest(
"licenses/actions/validate-key",
Method.POST
);
 
request.AddHeader("Content-Type", "application/vnd.api+json");
request.AddHeader("Accept", "application/vnd.api+json");
request.AddJsonBody(new {
meta = new {
key = "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3"
}
});
 
var response = client.Execute<Dictionary<string, object>>(request);
var meta = (Dictionary<string, object>) response.Data["meta"];
 
if ((bool) meta["valid"])
{
// Do something
}
else
{
// Do something else
}
import com.mashape.unirest.http.exceptions.*
import com.mashape.unirest.http.*
import org.json.*
 
val body = JSONObject(mapOf(
"meta" to mapOf(
"key" to "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3"
)
))
 
val res = Unirest.post("https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key")
.header("Content-Type", "application/vnd.api+json")
.header("Accept", "application/vnd.api+json")
.body(body)
.asJson()
 
val data = res.getBody().getObject()
val meta = data.getJSONObject("meta")
 
if (meta.getBoolean("valid")) {
// Do something
} else {
// Do something else
}
import com.mashape.unirest.http.exceptions.*;
import com.mashape.unirest.http.*;
import org.json.*;
 
import static java.util.Map.ofEntries;
import static java.util.Map.entry;
 
JSONObject body = new JSONObject(ofEntries(
entry("meta", ofEntries(
entry("key", "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3")
))
));
 
HttpResponse<JsonNode> res = Unirest.post("https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key")
.header("Content-Type", "application/vnd.api+json")
.header("Accept", "application/vnd.api+json")
.body(body)
.asJson();
 
JSONObject data = res.getBody().getObject();
JSONObject meta = data.getJSONObject("meta");
 
if (meta.getBoolean("valid"))
{
// Do something
}
else
{
// Do something else
}
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let validation: serde_json::Value = reqwest::Client::new()
.post("https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key")
.header("Content-Type", "application/vnd.api+json")
.header("Accept", "application/vnd.api+json")
.json(&serde_json::json!({
"meta": {
"key": "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3"
}
}))
.send()
.await?
.json()
.await?;
 
if validation["meta"]["valid"].as_bool().unwrap() {
// Do something
} else {
// Do something else
}
 
Ok(())
}
#include <iostream>
#include <string>
#include <cpprest/http_client.h>
#include <cpprest/filestream.h>
 
using namespace std;
using namespace web;
using namespace web::http;
using namespace web::http::client;
using namespace web::json;
using namespace utility;
 
http_client client("https://api.keygen.sh/v1/accounts/demo");
http_request req;
 
value meta;
meta["key"] = value::string("C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3");
 
value body;
body["meta"] = meta;
 
req.headers().add("Content-Type", "application/vnd.api+json");
req.headers().add("Accept", "application/json");
 
req.set_request_uri("/licenses/actions/validate-key");
req.set_method(methods::POST);
req.set_body(body.serialize());
 
client.request(req)
.then([](http_response res)
{
auto data = res.extract_json().get();
auto meta = data.at("meta");
 
if (meta.at("valid").as_bool())
{
// Do something
}
else
{
// Do something else
}
})
.wait();
import requests
import json
 
data = requests.post(
"https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key",
headers={
"Content-Type": "application/vnd.api+json",
"Accept": "application/vnd.api+json"
},
data=json.dumps({
"meta": {
"key": "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3"
}
})
).json()
 
if data["meta"]["valid"]:
# Do something
else:
# Do something else
require 'httparty'
 
data = HTTParty.post(
'https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key',
headers: {
'Content-Type' => 'application/vnd.api+json',
'Accept' => 'application/vnd.api+json'
},
body: {
meta: {
key: 'C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3'
}
}
)
 
if data['meta']['valid']
# Do something
else
# Do something else
end
<?php
 
$url = 'https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key';
$ch = curl_init($url);
 
$body = json_encode([
'meta' => [
'key' => 'C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3'
]
]);
 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Accept: application/json'
]);
 
$json = curl_exec($ch);
$data = json_decode($json);
 
if ($data->meta->valid) {
// Do something
} else {
// Do something else
}

Resources for Licensing and Distribution


  • library_booksKeygen Quickstarts

    Guides and API references for developers of all skill levels.

    View Docs

  • insert_chartKeygen Dashboard

    Manage your products from an intuitive dashboard.

    View Dashboard

  • businessKeygen Pricing

    From indie to enterprise, we have plans for all business sizes.

    View Pricing